FFmpeg实现实时反向播放(ffmpeg+sdl+vs2017)
在某些场合下需要实现视频的倒放效果,实现倒放有很多种思路。比如直接用libavfilter下提供的reverse命令行输出一个反转过来的视频;或者比较直接的想法是把所有frame缓存起来,然后再反向逐个输出到屏幕。
如果我们需要实时的在屏幕预览,那命令行的方法大概是不行了,而第二种方法需要在播放前预先加载所有需要播放的帧,这一方面增加了耗时,另一方面占用了大量的内存空间。所以基于第二种方法的倒放必须要进行优化。
本文使用的思路是基于av_seek_frame实现的方法,大致思路如图所示(原谅我对XML图形的滥用)。

1.在解码线程我们需要得到即将被输出的帧,使用cur变量初始化为视频的末尾的位置,用来记录当前seek执行的时间戳,使用av_seek_frame函数设置参数为backward,也就是寻找cur这一位置之前的最近的关键帧。
2.随后我们就可以单单解码从找到的关键帧到cur之间的frame,把他们输出到一个vector中,然后更新cur = vector[0] - 1也就是关键帧的上一个位置,接下来再次对cur执行seek得到的就会是上一个关键帧了。
3.得到的vector输出到一个队列中,由sdl的线程获取,最后从vector的end到begin反向输出到屏幕,这样就可以实现倒放预览了。
分析这一过程不难发现,只有储存在队列中即将的frame占用了内存空间,而且使用双线程配合队列,这样在解码下个I帧及其后继B帧P帧时不会出现需要等待而卡顿的情况。

最后贴一下代码,这里的代码是直接从雷霄骅先生的代码进行更改完成的,如果增加一个packet队列进一步提高并发性应该能进一步优化性能。
#include <stdio.h>
#include <string>
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <thread>
#include <mutex>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "SDL2/SDL.h"
};
//Output YUV420P data as a file
#define OUTPUT_YUV420P 0
using namespace std;
queue<vector<AVFrame*>> Q;
mutex mutex_;
condition_variable cvfull;
condition_variable cvempt;
//SDL---------------------------
int screen_w = 0, screen_h = 0;
SDL_Window *screen;
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;
SDL_Rect sdlRect;
void Show() {
while (1) {
vector<AVFrame*> V;
{
unique_lock<mutex> lk(mutex_);
cvfull.wait(lk, [&]() {
return Q.size() > 0; });
cout <<"队列大小:"<< Q.size()<<endl;
V = Q.front();
Q.pop();
cvempt.notify_one();
}
if (V.size() ==

8812

被折叠的 条评论
为什么被折叠?



