QT_project
1.首先需要先初始化一下,使用如下函数
1 | av_register_all(); |
2.接着需要分配一个AVFormatContext,FFMPEG所有的操作都要通过这个AVFormatContext来进行
1 | AVFormatContext *pFormatCtx = avformat_alloc_context(); |
3.接着调用打开视频文件
1 | char *file_path = "E:in.mp4"; |
4.文件打开成功后就是查找文件中的视频流
1 | ///循环查找视频中包含的流信息,直到找到视频类型的流 |
5.现在根据视频流 打开一个解码器来解码:
1 | ///查找解码器 |
可以看出 我们可以直接根据查找到的视频流信息获取到解码器。
而且我们并不知道他实际用的是什么编码器。
这就是为什么一开始我们使用FFMPEG来操作,因为很多东西我们可以不关系。
6.现在开始读取视频了
1 | int y_size = pCodecCtx->width * pCodecCtx->height; |
可以看出 av_read_frame读取的是一帧视频,并存入一个AVPacket的结构中。
7.前面我们说过 视频里面的数据是经过编码压缩的,因此这里我们需要将其解码
1 | if (packet->stream_index == videoStream) |
8.基本上所有解码器解码之后得到的图像数据都是YUV420的格式,而这里我们需要将其保存成图片文件,因此需要将得到的YUV420数据转换成RGB格式,转换格式也是直接使用FFMPEG来完成
1 | if (got_picture) { |
至于YUV420和RGB图像格式的具体内容,这里不用去了解。这里只需要知道有这么个东西就行了, 对我们使用FFMPEG转换没有影响。
9.得到RGB数据之后就是直接写入文件了:
1 | SaveFrame(pFrameRGB, pCodecCtx->width,pCodecCtx->height,index++); //保存图片 if (index > 50) return 0; //这里我们就保存50张图片 |
10.SDL
SDL播放音频是通过回调函数的方式播放,且这个回调函数是在新的线程中运行,此回调函数固定时间激发一次,这个时间和要播放的音频频率有关系。
因此我们用FFMPEG读到一帧音频后,不是急着解码,而是将数据存入一个队列,等SDL回调函数激发的时候,从这个队列中取出数据,然后解码 播放。
11.音视频同步
现在我们就将视频和音频合并,并让声音和画面同步。
加入音频的部分就不做讲解了,这里主要讲下声音和视频同步的步骤。
首先刚开始播放的时候通过av_gettime()获取系统主时钟,记录下来。
以后便不断调用av_gettime()获取系统时钟 减去之前记录下的差值,便得到了一个视频播放了多久的实际时间。
对于视频的同步我们这样做:
从视频读取出的数据中包含一个pts的信息(每一帧图像都会带有pts的信息,pts就是播放视频的时候此图像应该显示的时间)。
这样只需要使用pts和前面获取的时间进行对比,pts比实际时间大,就调用sleep函数等一等,否则就直接播放出来。这样就达到了某种意义上的同步了。
而对于音频:
从前面使用SDL的例子,其实就能够发现一个现象:我们读取音频的线程差不多就是瞬间读完放入队列的,但是音频播放速度却是正常的,
并不是一下子播放完毕。因此可以看出,在音频播放上,SDL已经帮我们做了处理了,只需要将数据直接交给SDL就行了。
12.音频同步
1.读取视频的线程
2.解码视频的线程
3.解码播放音频的线程(这个是由SDL创建的)