本文为作者原创:https://www.cnblogs.com/leisure_chn/p/10040202.html,转载请注明出处
基于FFmpeg和SDL实现的简易视频播放器,主要分为读取视频文件解码和调用SDL播放两大部分。
本实验仅实现最简单的视频播放流程,不考虑细节,不考虑音频。本实验主要参考如下两篇文章:
[1]. 最简单的基于FFMPEG+SDL的视频播放器ver2(采用SDL2.0)
[2]. An ffmpeg and SDL Tutorial
FFmpeg简易播放器系列文章如下:
[1]. FFmpeg简易播放器的实现-最简版
[2]. FFmpeg简易播放器的实现-视频播放
[3]. FFmpeg简易播放器的实现-音频播放
[4]. FFmpeg简易播放器的实现-音视频播放
[5]. FFmpeg简易播放器的实现-音视频同步
1. 视频播放器基本原理
下图引用自“雷霄骅,视音频编解码技术零基础学习方法”,因原图太小,看不太清楚,故重新制作了一张图片。
如下内容引用自“雷霄骅,视音频编解码技术零基础学习方法”:
解协议
将流媒体协议的数据,解析为标准的相应的封装格式数据。视音频在网络上传播的时候,常常采用各种流媒体协议,例如HTTP,RTMP,或是MMS等等。这些协议在传输视音频数据的同时,也会传输一些信令数据。这些信令数据包括对播放的控制(播放,暂停,停止),或者对网络状态的描述等。解协议的过程中会去除掉信令数据而只保留视音频数据。例如,采用RTMP协议传输的数据,经过解协议操作后,输出FLV格式的数据。解封装
将输入的封装格式的数据,分离成为音频流压缩编码数据和视频流压缩编码数据。封装格式种类很多,例如MP4,MKV,RMVB,TS,FLV,AVI等等,它的作用就是将已经压缩编码的视频数据和音频数据按照一定的格式放到一起。例如,FLV格式的数据,经过解封装操作后,输出H.264编码的视频码流和AAC编码的音频码流。解码
将视频/音频压缩编码数据,解码成为非压缩的视频/音频原始数据。音频的压缩编码标准包含AAC,MP3,AC-3等等,视频的压缩编码标准则包含H.264,MPEG2,VC-1等等。解码是整个系统中最重要也是最复杂的一个环节。通过解码,压缩编码的视频数据输出成为非压缩的颜色数据,例如YUV420P,RGB等等;压缩编码的音频数据输出成为非压缩的音频抽样数据,例如PCM数据。音视频同步
根据解封装模块处理过程中获取到的参数信息,同步解码出来的视频和音频数据,并将视频音频数据送至系统的显卡和声卡播放出来。
2. 最简播放器的实现
2.1 实验平台
实验平台:openSUSE Leap 42.3
FFmpeg版本:4.1
SDL版本:2.0.9
FFmpeg开发环境搭建可参考“FFmpeg开发环境构建”
2.2 源码清单
/*****************************************************************
* ffplayer.c
*
* history:
* 2018-11-27 - [lei] created file
*
* details:
* A simple ffmpeg player.
*
* refrence:
* 1. https://blog.csdn.net/leixiaohua1020/article/details/38868499
* 2. http://dranger.com/ffmpeg/ffmpegtutorial_all.html#tutorial01.html
* 3. http://dranger.com/ffmpeg/ffmpegtutorial_all.html#tutorial02.html
******************************************************************/
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <SDL2/SDL.h>
#include <SDL2/SDL_video.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_rect.h>
int main(int argc, char *argv[])
{
// Initalizing these to NULL prevents segfaults!
AVFormatContext* p_fmt_ctx = NULL;
AVCodecContext* p_codec_ctx = NULL;
AVCodecParameters* p_codec_par = NULL;
AVCodec* p_codec = NULL;
AVFrame* p_frm_raw = NULL; // 帧,由包解码得到原始帧
AVFrame* p_frm_yuv = NULL; // 帧,由原始帧色彩转换得到
AVPacket* p_packet = NULL; // 包,从流中读出的一段数据
struct SwsContext* sws_ctx = NULL;
int buf_size;
uint8_t* buffer = NULL;
int i;
int v_idx;
int ret;
SDL_Window* screen;
SDL_Renderer* sdl_renderer;
SDL_Texture* sdl_texture;
SDL_Rect sdl_rect;
if (argc < 2)
{
printf("Please provide a movie file\n");
return -1;
}
// 初始化libavformat(所有格式),注册所有复用器/解复用器
// av_register_all(); // 已被申明为过时的,直接不再使用即可
// A1. 打开视频文件:读取文件头,将文件格式信息存储在"fmt context"中
ret = avformat_open_input(&p_fmt_ctx, argv[1], NULL,