IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 音视频播放器设计(二)——ffmpeg视频处理流程 -> 正文阅读

[C++知识库]音视频播放器设计(二)——ffmpeg视频处理流程

编解码流程图

 _______              ______________
|       |            |              |
| 输入  |  分路       | 编码的数据分组|   解码
| 文件  |  --------->|              | -----+
|_______|            |______________|      |
                                           v
                                       _________
                                      |         |
                                      |  解码   |
                                      |  帧率   |
                                      |_________|
 ________             ______________       |
|        |           |              |      |
|  输出  | <-------- |编码的数据分组 | <----+
|  文件  |   复用器   |             |   编码
|________|           |______________|

ffmpeg库分析

libavcodec     编码/解码库
libavfilter    基于图的框架编辑库
libavformat    I/O 和复用/解复用库
libavdevice    特殊设备复用/解复用库
libavutil      常用实用程序库
libswresample  音频重采样、格式转换和混合
libpostproc    后处理库
libswscale     颜色转换和缩放库

关于ffmpeg可以参考:https://ffmpeg.org/ffmpeg.html

源代码分析

1. 头文件

#include <iostream>
#include <QMutex>
extern "C"
{
	//调用FFMpeg的头文件
	#include <libavformat/avformat.h>
	#include <libswscale/swscale.h>
	#include <libswresample/swresample.h>
}

class XFFmpeg
{
public:
    // 优化单例实现
    static XFFmpeg* m_instance; 
    static XFFmpeg* getInstance(); 

    /*
   	打开文件返回视频文件的总时间
    若时间小于0,同样未打开成功
    */
	int Open(const char *path);
    
    /*
    关闭文件
    */
	void Close();

	/*
	读取视频的每一帧,返回每帧后需要清理空间
	*/
	AVPacket Read();

	/*
	读取到每帧数据后需要对其进行解码,返回它的pts
	*/
	int Decode(const AVPacket *pkt);

	/*
	将解码后的YUV视频帧转化为RGB格式
	*/
	bool ToRGB(char *out,int outwidth,int outheight);

    /*
    音频的重采样
    */
	int ToPCM(char *out);      

	std::string GetError();    //获取错误信息
	virtual ~XFFmpeg();
	int totalMs = 0;           //总时长
	int videoStream = 0;       //视频流
	int audioStream = 1;       //音频流
	int fps = 0;               //每秒的视频帧数

	bool Seek(float pos);      //设置进度条的拖动位置后播放

	bool isPlay = false;       //播放暂停

	int pts;                   //获得当前解码帧的时间
	int GetPts(const AVPacket *pkt);//获得视频解码前视频帧的pts

	int sampleRate = 48000;    //样本率
	int sampleSize = 16;       //样本大小
	int channel = 2;           //通道数
protected:
	char errorbuff[1024];        //打开时发生的错误信息
	QMutex mutex;                //互斥变量,多线程时避免同时间的读写
	AVFormatContext *ic = NULL;  //解封装上下文
	AVFrame *yuv = NULL;         //解码后的视频帧数据
	AVFrame *pcm = NULL;         //解码后的音频数据
	SwsContext  *cCtx = NULL;    //视频转码上下文
	SwrContext *aCtx = NULL;     //音频重采样上下文
    
private:
    XFFmpeg();
};

2. 注册ffmpeg

av_register_all:注册ffmpeg库。

XFFmpeg* XFFmpeg::m_instance = nullptr;
XFFmpeg* XFFmpeg::getInstance()
{
    if (nullptr == m_instance )
    {
        std::mutex mtx;           //函数结束时锁资源释放
        m_instance = new(std::nothrow) Singleton();
        if (nullptr == m_instance )
        {
            return nullptr;
        }
    }

    return m_instance;
}


XFFmpeg::XFFmpeg()
{
	errorbuff[0] = '\0';
    
    //注册FFMpeg的库
	av_register_all();
}

3. 视频打开及处理

avformat_open_input:打开解封装器

avcodec_find_decoder: 查找解码器

avcodec_open2:打开解码器

int  XFFmpeg::Open(const char *path)
{
	Close();//打开前先关闭清理
	mutex.lock();

	//打开解封装器
	int iRet = avformat_open_input(&ic, path, 0, 0);
	if (0 != iRet)
	{
		mutex.unlock();//解锁
		av_strerror(iRet, errorbuff, sizeof(errorbuff));//错误信息
		printf("open %s failed :%s\n", path, errorbuff);
		return 0;
	}
	totalMs = ic->duration / (AV_TIME_BASE);//获取视频的总时间

	//打印视频详细信息
	av_dump_format(ic, 0, path, 0);
    
	//解码器
	for (int i = 0; i < ic->nb_streams; i++)
	{
		AVCodecContext *enc = ic->streams[i]->codec;//解码上下文

		if (enc->codec_type == AVMEDIA_TYPE_VIDEO)//判断是否为视频
		{
			videoStream = i;
			fps = r2d(ic->streams[i]->avg_frame_rate);//获得视频得fps
			AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器
			if (!codec)//未找到解码器
			{
				mutex.unlock();
				printf("video code not find\n");
				return 0;
			}
            
			int err = avcodec_open2(enc, codec, NULL);//打开解码器
			if (err != 0)//未打开解码器
			{
				mutex.unlock();
				char buf[1024] = { 0 };
				av_strerror(err, buf, sizeof(buf));
				printf("not open!  %s",buf);
				return 0;
			}
			printf("open codec success!\n");
		}
		else if (enc->codec_type == AVMEDIA_TYPE_AUDIO)//若未音频流
		{
			audioStream = i;//音频流
			AVCodec *codec = avcodec_find_decoder(enc->codec_id);//查找解码器
			if (avcodec_open2(enc, codec, NULL) < 0)
			{
				mutex.unlock();
				return 0;
			}
            
			this->sampleRate = enc->sample_rate;//样本率
			this->channel = enc->channels;//通道数
			switch (enc->sample_fmt)//样本大小
			{
			case AV_SAMPLE_FMT_S16://signed 16 bits
				this->sampleSize = 16;
				break;
			case  AV_SAMPLE_FMT_S32://signed 32 bits
				this->sampleSize = 32;
			default:
				break;
			}
			printf("audio sample rate:%d sample size:%d chanle:%d\n",this->sampleRate,this->sampleSize,this->channel);

		}
	}//至此为打开解码器过程

	printf("file totalSec is %d-%d\n", totalMs / 60, totalMs % 60);//以分秒计时
	mutex.unlock();
	return totalMs;
}

4. 视频读取

av_read_frame:读取视频帧

AVPacket XFFmpeg::Read()
{
	AVPacket pkt;
	memset(&pkt, 0, sizeof(AVPacket));
	mutex.lock();
	if (!ic)
	{
		mutex.unlock();
		return pkt;
	}
    
	int err = av_read_frame(ic, &pkt);//读取视频帧
	if (err != 0)//读取失败
	{
		av_strerror(err, errorbuff, sizeof(errorbuff));
	}
    
	mutex.unlock();
	return pkt;
}

5. 视频解码

av_frame_alloc:内存空间分配

avcodec_send_packet:发送数据帧

avcodec_receive_frame:接收数据帧

int XFFmpeg::Decode(const AVPacket *pkt)
{
	mutex.lock();
	if (!ic)//若未打开视频
	{
		mutex.unlock();
		return nullptr;
	}
    
    //申请解码的对象空间
	if (nullptr == yuv )
	{
		yuv = av_frame_alloc();
	}
    
	if (nullptr == pcm)
	{
		pcm = av_frame_alloc();
	}
    
	AVFrame *frame = yuv;//此时的frame是解码后的视频流
	if (pkt->stream_index == audioStream)//若未音频
	{
		frame = pcm;//此时frame是解码后的音频流
	}
    
    //发送之前读取的pkt
	int iRet = avcodec_send_packet(ic->streams[pkt->stream_index]->codec, pkt);
	if (0 != iRet)
	{
		mutex.unlock();
		return nullptr;
	}
    
    //解码pkt后存入yuv中
	iRet = avcodec_receive_frame(ic->streams[pkt->stream_index]->codec, frame);
	if (0 != iRet)
	{
		mutex.unlock();
		return nullptr;
	}
	qDebug() << "pts=" << frame->pts;
	
	mutex.unlock();
    //当前解码的显示时间
    int p = frame->pts*r2d(ic->streams[pkt->stream_index]->time_base);
	if (pkt->stream_index == audioStream)//为音频流时设置pts
		this->pts = p;

	return p;
}

6. YUV转RGB

sws_getCachedContext:初始化SwsContext

sws_scale:开始转码

bool XFFmpeg::ToRGB(char *out, int outwidth, int outheight)
{
	mutex.lock();
	if (!ic||!yuv)//未打开视频文件或者未解码
	{
		mutex.unlock();
		return false;
	}
    
	AVCodecContext *videoCtx = ic->streams[this->videoStream]->codec;
	cCtx = sws_getCachedContext(cCtx, videoCtx->width,//初始化一个SwsContext
		videoCtx->height,
		videoCtx->pix_fmt, //输入像素格式
		outwidth, outheight,
		AV_PIX_FMT_BGRA,//输出像素格式
		SWS_BICUBIC,//转码的算法
		NULL, NULL, NULL);

	if (!cCtx)
	{
		mutex.unlock();
		printf("sws_getCachedContext  failed!\n");
		return false;
	}
	uint8_t *data[AV_NUM_DATA_POINTERS] = { 0 };
	data[0] = (uint8_t *)out;//第一位输出RGB
	int linesize[AV_NUM_DATA_POINTERS] = { 0 };

    //开始转码
	linesize[0] = outwidth * 4;//一行的宽度,32位4个字节
	int h = sws_scale(cCtx, yuv->data, //当前处理区域的每个通道数据指针
		yuv->linesize,//每个通道行字节数
		0, videoCtx->height,//原视频帧的高度
		data,//输出的每个通道数据指针	
		linesize//每个通道行字节数

		);

	if (h > 0)
	{
		printf("(%d)", h);
	}
	mutex.unlock();
	return true;
}

7. 音频重采样

swr_alloc_set_opts:设置音频参数

swr_init:音频参数初始化

swr_convert:音频转码

av_samples_get_buffer_size:获取采样大小

int XFFmpeg::ToPCM(char *out)
{
	mutex.lock();
	if (!ic || !pcm || !out)//文件未打开,解码器未打开,无数据
	{
		mutex.unlock();
		return 0;
	}
	AVCodecContext *ctx = ic->streams[audioStream]->codec;//音频解码器上下文
	if (aCtx == NULL)
	{
		aCtx = swr_alloc();//初始化
		swr_alloc_set_opts(aCtx,ctx->channel_layout,
			AV_SAMPLE_FMT_S16,
			  ctx->sample_rate,
			  ctx->channels,
			  ctx->sample_fmt,
			  ctx->sample_rate,
			  0,0
			  );
		swr_init(aCtx);
	}
	uint8_t  *data[1];
	data[0] = (uint8_t *)out;
	int len = swr_convert(aCtx, data, 10000,
		(const uint8_t **)pcm->data,
		pcm->nb_samples
		);
	if (len <= 0)
	{
		mutex.unlock();
		return 0;
	}
	int outsize = av_samples_get_buffer_size(NULL, ctx->channels,
		pcm->nb_samples,
		AV_SAMPLE_FMT_S16,
		0);

	mutex.unlock();
	return outsize;
}

8. 视频偏移

av_seek_frame:视频偏移

bool XFFmpeg::Seek(float pos)
{
	mutex.lock();
	if (!ic)//未打开视频
	{
		mutex.unlock();
		return false;
	}
	int64_t stamp = 0;
	stamp = pos * ic->streams[videoStream]->duration;//当前它实际的位置
	pts = stamp * r2d(ic->streams[videoStream]->time_base);//获得滑动条滑动后的时间戳

    //将视频移至到当前位置
	int iRet = av_seek_frame(ic, videoStream, stamp,
		AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);
	avcodec_flush_buffers(ic->streams[videoStream]->codec);//刷新缓冲,清理掉
    mutex.unlock();
	if (iRet > 0)
		return true;
	return false;
}

9. 视频关闭

avformat_close_input:关闭上下文空间

av_frame_free:释放空间

void XFFmpeg::Close()
{
	mutex.lock();//需要上锁,以防多线程中你这里在close,另一个线程中在读取,
	
	if (ic) avformat_close_input(&ic); //关闭ic上下文空间
	if (yuv) av_frame_free(&yuv);      //关闭时释放解码后的视频帧空间
	if (pcm) av_frame_free(&pcm);      //关闭时释放解码后的音频空间
	if (cCtx)
	{
		sws_freeContext(cCtx);         //释放转码器上下文空间
		cCtx = NULL;
		printf("shifang zhuangmaqi");
	}
	if (aCtx)
	{
		swr_free(&aCtx);//释放音频上下文空间
	}
	
	mutex.unlock();
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 10:59:30  更:2022-05-05 11:00:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:13:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码