18. MFC使用VLC库开发视频播放器指南

1.前言

上一章我们讲到了C/C++开发聊天室,主要运用的知识点是TCP网络编程。

而本章我将再讲解一个项目的开发实现,即我们常见的视频播放器,比如腾讯、爱奇艺、优酷等等,看看这些视频播放器都是怎么开发出来的?

放心,既然将本章放到了这个,那么就请相信你目前是有能力开发的。

本文的目的也并不是非要你能够自制播放器,只是为了给你拓宽知识面,比如,如何去使用别人写好的开源库?

毕竟只有站在巨人的肩膀上我们才能走的更远。

2.VLC库介绍

让我们现在立马自己开发一个能播放视频的功能库,那明显是不现实的,因为先不说目前视频格式就一大堆,比如mp4、AVI、WMV等等等等。

就算只开发一种格式播放的播放器,如mp4,那都不是一件简单的事情。

所以这种时候,肯定第一时间想到的就是使用现成的库,而VLC就是本章选择的视频播放器库。

这个库经过近20年的发展,目前已经是相当强悍的地步了,基本支持播放我们常见的所有视频格式。

库文件的下载地址为:这里

image-20231224100718468

我这里就直接选用最新版本的库文件好了,目前最新版本的是3.0.20

不过由于我们是开发windows视频播放器,所以肯定选择上面的两个,但目前已经普遍是64位电脑了,所以我这里也就直接选用win64的:

image-20231224100803638

然后我们要下载的是这个包含库文件的压缩包:

image-20231224100852193

解压后,可以看到目录:

image-20231224101030736

首先就是将plugins文件夹以及下方两个动态库libvlc.dlllibvlccore.dll复制到我们的项目文件夹中。

然后来到sdk文件夹中,将头文件以及库文件目录也要复制到我们的项目中:

image-20231224101118648

3.库函数详解及封装

3.1 初步使用

一般在正式使用一个库前,我们还需要先学会如何使用这个库里面包含的函数,最简单的方式当然是使用控制台了。

先随便建立一个测试用的控制台项目:

image-20231224101228824

然后,将上面的文件复制到当前项目中:

image-20231224101342151

接着,我们还要在项目里面添加它们:

#include"include/vlc/vlc.h"
#pragma comment(lib,"lib/libvlc.lib")
#pragma comment(lib,"lib/libvlccore.lib")

int main() {
	
}

但这样直接运行会报错,因为vlc.h这个头文件里面包含了很多其它头文件,它是一个vlc所有头文件的集合:

image-20231224101444691

但其使用的路径都为:vlc/...,所以就会找不到,因为按相对路径来看,这些头文件是在include文件夹下的vlc文件夹中,而且这些都是使用的尖括号<>,所以必须将vlc文件夹添加到VS的搜索目录中。

而vlc文件夹在include文件夹中,所以我们只需要将include文件夹添加到当前项目属性的包含目录中即可:

image-20231224101553030

这里要注意上面的配置,最好将releasedebug都添加上为好。

因为include目录也在vs的包含目录中,所以我们就可以使用尖括号,直接引用vlc/vlc.h了:

#include<vlc/vlc.h>
#pragma comment(lib,"lib/libvlc.lib")
#pragma comment(lib,"lib/libvlccore.lib")

int main() {
	
}

但运行依旧会报错,而且还是一堆莫名其妙的错误,这就是vlc这个库的一个小bug

image-20231224101713640

只需要我们在vlc.h头文件前面,添加一个定义类型就好了:

typedef long long ssize_t;
#include<vlc/vlc.h>
#pragma comment(lib,"lib/libvlc.lib")
#pragma comment(lib,"lib/libvlccore.lib")

int main() {
	
}

这样我们就完成了库的引入,接下来是如何使用这个库播放视频

typedef long long ssize_t;
#include<vlc/vlc.h>
#pragma comment(lib,"lib/libvlc.lib")
#pragma comment(lib,"lib/libvlccore.lib")
int main() {
	libvlc_instance_t* m_instance = libvlc_new(0, NULL); //创建实例
	libvlc_media_t* m_media = libvlc_media_new_location(m_instance, "file:///D:\\Program\\vlcPractice\\vlcPractice\\1.mp4"); //根据视频文件创建媒体
	libvlc_media_player_t* m_player = libvlc_media_player_new_from_media(m_media); //根据媒体创建播放器

	libvlc_media_player_play(m_player); //播放
	getchar();

	libvlc_media_player_pause(m_player); //暂停

	libvlc_media_player_stop(m_player); //停止播放


	libvlc_media_player_release(m_player); 
	libvlc_media_release(m_media);
	libvlc_release(m_instance);
}

因为这个库是用C语言写的,所以看起来会很复杂,而且函数名相当长,所以后面我们肯定得进行封装,不然太难用了,这里主要理解一下它的主要使用流程即可:

  1. 创建实例:libvlc_new
  2. 根据实例,以及要进行播放的视频,新建媒体实例:libvlc_media_new_location
  3. 然后根据这个新建的媒体实例,创建播放器:libvlc_media_player_new_from_media
  4. 然后我们就可以通过这个播放器实例,对视频进行一些操作,比如暂停停止播放等等,使用特定的函数即可。
  5. 最后依次释放掉这三个实例。

这里注意,视频文件必须得是全路径,并且由于它采用的特定格式,如果你是本地的文件,那就还需要在前面添加:file:///,这是本地协议。

如果你是网络上面的视频文件,前面就会有其它格式,直接复制即可,这个库是支持网络路径视频的。

上面之所以有个getchar函数,目的是让程序暂停下来,否则就会一下子执行结束,看不到效果,当你按下任意一个键之后,就会继续运行下面的代码。

还有一个很重要的问题,vlc库采用的编码是utf-8,也就是说,如果你的视频路径里面包含了中文,那么这样直接写肯定会报错打不开的,下面封装这个库的时候我会给出解决方案。

3.2 封装

CVlc.h头文件:

#pragma once
#include<string>
typedef long long ssize_t;
#include<vlc/vlc.h>
#pragma comment(lib,"lib/libvlc.lib")
#pragma comment(lib,"lib/libvlccore.lib")
#include<Windows.h>
struct MediaSize {
	int width;
	int hight;
	MediaSize(int _width,int _hight) {
		width = _width;
		hight = _hight;
	}
};
class CVlc
{
private:
	libvlc_instance_t* m_instance;
	libvlc_media_t* m_media;
	libvlc_media_player_t* m_player;
	HWND m_hWnd;
private:
	std::string ToUTF8(const std::wstring& str);
public:
	CVlc();
	~CVlc();
	//加载视频媒体
	bool LoadMedia(const std::wstring &path);
	//设置播放窗口
	void SetHWnd(HWND hWnd);
	//播放视频
	bool Play();
	//暂停视频
	bool Pause();
	//停止视频
	bool Stop();
	//获取播放进度
	float GetPosition();
	//设置播放进度
	void SetPosition(float pos);
	//获取音量
	int GetVolume();
	//设置音量
	void SetVolume(int volume);
	//获取媒体时长(秒)
	int GetLenth();
	//设置播放速率
	bool SetRate(float rate);
	//获取媒体宽高
	MediaSize GetMediaInfo();
};

CVlc.cpp源文件:

#include "CVlc.h"

std::string CVlc::ToUTF8(const std::wstring& str)
{
	int len = WideCharToMultiByte(CP_UTF8, 0, str.data(), -1, 0, 0, 0, 0);
	std::string s;
	s.resize(len + 1);
	WideCharToMultiByte(CP_UTF8, 0, str.data(), -1, (LPSTR)s.data(), len + 1, 0, 0);
	return s;
}

CVlc::CVlc()
{
	m_instance = libvlc_new(0, NULL);
	m_media = NULL;
	m_player = NULL;
	m_hWnd = NULL;
}

CVlc::~CVlc()
{
	if (m_player != NULL) {
		libvlc_media_player_release(m_player);
		m_player = NULL;
	}
	if (m_media != NULL) {
		libvlc_media_release(m_media);
		m_media = NULL;
	}
	if (m_instance != NULL) {
		libvlc_release(m_instance);
		m_instance = NULL;
	}
}

bool CVlc::LoadMedia(const std::wstring& path)
{
	if (m_instance == NULL) return false;
	std::string s=ToUTF8(path);
	if (m_media != NULL) {
		libvlc_media_release(m_media);
		m_media = NULL;
	}
	m_media=libvlc_media_new_location(m_instance,s.data());
	if (m_media == NULL) return false;
	if (m_player != NULL) {
		libvlc_media_player_release(m_player);
		m_player = NULL;
	}
	m_player=libvlc_media_player_new_from_media(m_media);
	if (m_player == NULL) return false;
	return true;
}

void CVlc::SetHWnd(HWND hWnd)
{
	m_hWnd = hWnd;
}

bool CVlc::Play()
{
	if (m_player == NULL) return false;
	if (m_hWnd != NULL) {
		libvlc_media_player_set_hwnd(m_player, m_hWnd);