您当前的位置:首页 > 电脑百科 > 软件技术 > 音/视频编辑

音视频处理之FFmpeg+SDL视频播放器

时间:2021-05-19 13:41:29  来源:今日头条  作者:Linux后端开发

一、FFmpeg视频解码器

1.视频解码知识

1).纯净的视频解码流程

压缩编码数据->像素数据。

例如解码H.264,就是“H.264码流->YUV”。

2).一般的视频解码流程

视频码流一般存储在一定的封装格式(例如MP4、AVI等)中。封装格式中通常还包含音频码流等内容。

对于封装格式中的视频,需要先从封装格式中提取中视频码流,然后再进行解码。

例如解码MKV格式的视频文件,就是“MKV->H.264码流->YUV”

2.VC下FFmpeg开发环境的搭建

新建控制台工程

打开visual studio

文件 新建 项目 win32控制台应用程序

拷贝FFmpeg开发文件

头文件(*.h)拷贝至项目文件夹的include子文件夹下

导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下

动态库文件(*.dll)拷贝至项目文件夹下

PS:如果直接使用官网上下载的FFmpeg开发文件。则可能还需要将MinGW安装目录中的inttypes.h,stdint.h,_mingw.h三个文件拷贝至项目文件夹的include子文件夹下。

配置开发文件

打开属性面板

解决方案资源管理器->右键单击项目->属性

头文件配置

配置属性->C/C++->常规->附加包含目录,输入“include”(刚才拷贝头文件的目录)

导入库配置

配置属性->链接器->常规->附加库目录,输入“lib” (刚才拷贝库文件的目录)

配置属性->链接器->输入->附加依赖项,输入“avcodec.lib; avformat.lib; avutil.lib; avdevice.lib; avfilter.lib; postproc.lib; swresample.lib; swscale.lib”(导入库的文件名)

动态库不用配置

main()中调用一个FFmpeg的接口函数

例如下面代码打印出了FFmpeg的配置信息

int main(int argc, char* argv[]){ printf("%s", avcodec_configuration()); return 0; }

如果运行无误,则代表FFmpeg已经配置完成。

3.FFmpeg简介

FFmpeg一共包含8个库:

avcodec:编解码(最重要的库)。

avformat:封装格式处理。

avfilter:滤镜特效处理。

avdevice:各种设备的输入输出。

avutil:工具库(大部分库都需要这个库的支持)。

postproc:后加工。

swresample:音频采样数据格式转换。

swscale:视频像素数据格式转换。

1).FFmpeg解码流程图见图1:

音视频处理之FFmpeg+SDL视频播放器

2).FFmpeg解码函数简介

av_register_all():注册所有组件。

avformat_open_input():打开输入视频文件。

avformat_find_stream_info():获取视频文件信息。

avcodec_find_decoder():查找解码器。

avcodec_open2():打开解码器。

av_read_frame():从输入文件读取一帧压缩数据。

avcodec_decode_video2():解码一帧压缩数据。

avcodec_close():关闭解码器。

avformat_close_input():关闭输入视频文件。

3).FFmpeg相关结构体(类型)见图2:

音视频处理之FFmpeg+SDL视频播放器

I、FFmpeg数据结构简介

AVFormatContext

封装格式上下文结构体,也是统领全局的结构体,保存了视频文件封装格式相关信息。

AVInputFormat

每种封装格式(例如FLV, MKV, MP4, AVI)对应一个该结构体。

AVStream

视频文件中每个视频(音频)流对应一个该结构体。

AVCodecContext

编码器上下文结构体,保存了视频(音频)编解码相关信息。

AVCodec

每种视频(音频)编解码器(例如H.264解码器)对应一个该结构体。

AVPacket

存储一帧压缩编码数据。

AVFrame

存储一帧解码后像素(采样)数据。

II、FFmpeg数据结构分析

AVFormatContext

iformat:输入视频的AVInputFormat

nb_streams :输入视频的AVStream 个数

streams :输入视频的AVStream []数组

duration :输入视频的时长(以微秒为单位)

bit_rate :输入视频的码率

AVInputFormat

name:封装格式名称

long_name:封装格式的长名称

extensions:封装格式的扩展名

id:封装格式ID

一些封装格式处理的接口函数

AVStream

id:序号

codec:该流对应的AVCodecContext

time_base:该流的时基

r_frame_rate:该流的帧率

AVCodecContext

codec:编解码器的AVCodec

width, height:图像的宽高(只针对视频)

pix_fmt:像素格式(只针对视频)

sample_rate:采样率(只针对音频)

channels:声道数(只针对音频)

sample_fmt:采样格式(只针对音频)

AVCodec

name:编解码器名称

long_name:编解码器长名称

type:编解码器类型

id:编解码器ID

一些编解码的接口函数

AVPacket

pts:显示时间戳

dts :解码时间戳

data :压缩编码数据

size :压缩编码数据大小

stream_index :所属的AVStream

AVFrame

data:解码后的图像像素数据(音频采样数据)。

linesize:对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小。

width, height:图像的宽高(只针对视频)。

key_frame:是否为关键帧(只针对视频) 。

pict_type:帧类型(只针对视频) 。例如I,P,B。

III、注意事项

裸流文件,即如h264文件,是没有时长等信息的,封装格式的才有

数据结构第一层为封装格式相关的,下面层为编解码相关的

其中

AVStream中的time_base 表示该流的时基,也就是时间的基数,简单可理解为时间的单位,比如时间数是2,时基是1s,则时间为2s

AVPacket:可以理解为h264中的数据包,编码后的包

其中,pts:显示时间戳,表示该视频帧几分几秒的时候放到屏幕上。这个值只是一个整数 没有单位,所以要得到具体的几分几秒需要和前面的time_base时基参数相乘得到。

dts:解码时间戳

stream_index:标识,表示该流属于音频流还是视频流。

data:压缩编码的数据,如h264的编码,就可以取出该数据保存起来形成的文件就是h264文件。

通过av_read_frame可从大的数据结构指针中读取到AVPacket数据

注意播放的顺序不一定按照存储的顺序来。即有一个显示的顺序和一个解码的顺序。

AVFrame

data:解码后的图像像素数据,如YUV等,可形成yuv文件(依次存储的是y、u、v分量,其中u 宽和高都只有y一半,整个数据量就y的四分之一,v的也一样)。data是一个指针数组,其中一个分量对应一个数组

数据先通过解码函数avcodec_decode_video2进行解码,然后由于解码出来的数据由于是有黑边(多出一块多余的数据),所以还需用sws_scale函数把这块多余的裁了。

4).解码后的数据要经过sws_scale()函数处理

解码后YUV格式的视频像素数据保存在AVFrame的data[0]、data[1]、data[2]中。但是这些像素值并不是连续存储的,每行有效像素之后存储了一些无效像素。

以亮度Y数据为例,data[0]中一共包含了linesize[0]*height个数据。但是出于优化等方面的考虑,linesize[0]实际上并不等于宽度width,而是一个比宽度大一些的值。

因此需要使用sws_scale()进行转换。转换后去除了无效数据,width和linesize[0] 取值相等。如下图3:

音视频处理之FFmpeg+SDL视频播放器

其中,sws_scale()函数需要用到的转换信息,即第一个参数,是由sws_getContext函数获得的

转换后保存在一个新的帧数据结构体,由于使用的转换函数而不是解码函数,所以这个结构体还需要填充其内部的缓冲区,用于存储像素数据,填充的方法使用avpicture_fill函数:

int avpicture_fill(AVPicture *picture, uint8_t *ptr,int pix_fmt, int width, int height);

这个函数的使用本质上是为已经分配的空间的结构体(AVPicture *)ptFrame挂上一段用于保存数据的空间,

这个结构体中有一个指针数组data[AV_NUM_DATA_POINTERS],挂在这个数组里。一般我们这么使用:

I、pFrameRGB=avcodec_alloc_frame(); II、numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height); buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); III、avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height);

以上就是为pFrameRGB挂上buffer。这个buffer是用于存缓冲数据的。

ptFrame为什么不用fill空间。主要是下面这句:

avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size);

很可能是ptFrame已经挂上了packet.data,所以就不用fill了。

二、SDL视频显示

SDL(Simple DirectMedia Layer)库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度。

主要用来做游戏,现在只用到其视频显示部分。

特点:跨平台,开源

1.库的结构图见图:

音视频处理之FFmpeg+SDL视频播放器

实际上它调用了底层api完成了和硬件的交互,比如linux下,就操作了framebuffer.

2.配置vc工程和之前ffmpeg的配置几乎都是一样的:

1).新建控制台工程

打开VC++

文件->新建->项目->Win32控制台应用程序

2).拷贝SDL开发文件

头文件(*.h)拷贝至项目文件夹的include子文件夹下

导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下

动态库文件(*.dll)拷贝至项目文件夹下

3).配置开发文件

打开属性面板

解决方案资源管理器->右键单击项目->属性

4).头文件配置

配置属性->C/C++->常规->附加包含目录,输入“include”(刚才拷贝文件的目录)

5).导入库配置

配置属性->链接器->常规->附加库目录,输入“lib” (刚才拷贝文件的目录)

配置属性->链接器->输入->附加依赖项,输入“SDL2.lib; SDL2main.lib”(导入库的文件名)

6).动态库不用配置

7).是否配置成功,调用sdl的初始化函数,查看其返回值即可确定:

创建源代码文件

在工程中创建一个包含main()函数的C/C++文件(如果已经有了可以跳过这一步),后续步骤在该文件中编写源代码。

包含头文件

如果是C语言中使用SDL,则直接使用下面代码

#include "SDL2/SDL.h"

如果是C++语言中使用SDL,则使用下面代码

extern "C" { #include "SDL2/SDL.h" }

main()中调用一个SDL的接口函数

例如下面代码初始化了SDL

int main(int argc, char* argv[]){ if(SDL_Init(SDL_INIT_VIDEO)) { printf( "Could not initialize SDL - %sn", SDL_GetError()); } else{ printf("Success init SDL"); } return 0; }

如果运行无误,则代表SDL已经配置完成。

3.SDL视频显示的流程图见图5:

音视频处理之FFmpeg+SDL视频播放器

其中特别要注意的是创建纹理数据时要传入渲染器,原因是,

纹理数据依赖于渲染器,只有通过渲染器创建才能得到该渲染器合法的纹理数据((数据格式)符合该渲染器的要求),

填充了像素数据且符合要求的纹理数据才可以拷贝给渲染器,并且渲染器才能正确显示出来。

1).SDL视频显示函数简介

SDL_Init():初始化SDL系统

SDL_CreateWindow():创建窗口SDL_Window

SDL_CreateRenderer():创建渲染器SDL_Renderer

SDL_CreateTexture():创建纹理SDL_Texture

SDL_UpdateTexture():设置纹理的数据

SDL_RenderCopy():将纹理的数据拷贝给渲染器

SDL_RenderPresent():显示

SDL_Delay():工具函数,用于延时。

SDL_Quit():退出SDL系统

其中SDL_Delay 延时函数,控制显示的速度,即控制帧率。通常每秒25帧,所以通常延时也就是40ms

2).SDL视频显示的数据结构

I、SDL视频显示的数据结构如图6所示:

音视频处理之FFmpeg+SDL视频播放器

II、SDL数据结构简介

SDL_Window

代表了一个“窗口”

SDL_Renderer

代表了一个“渲染器”

SDL_Texture

代表了一个“纹理”

SDL_Rect

一个简单的矩形结构

具体来说,

SDL_Window 窗口,类似弹出的窗口

SDL_Renderer 渲染器,把纹理数据画(渲染)到window上

一个window上不仅仅只有一副画面,类似多组监控画面,即一个window可对应多个yuv数据

SDL_Rect,正方形结构,存了矩形的坐标,长宽,以便确定纹理数据画在哪个位置,确定位置用,比如画在左上角就用这个来确定。被渲染器调用

SDL_Rect中的x y值是左上角为圆点开始的坐标值,调整x y值以及w h值,就可以实现在窗口的指定位置显示,没有画面的地方为黑框。

当x y等于0,w h等于窗口的宽高时即为全屏显示,此时调整宽高大小,只需调整窗口大小即可。

4.进阶-SDL中事件和多线程

1).SDL多线程

函数

SDL_CreateThread():创建一个线程

数据结构

SDL_Thread:线程的句柄

SDL事件

函数

SDL_WaitEvent()等待一个事件

SDL_PushEvent()发送一个事件

数据结构

SDL_Event:代表一个事件

SDL中事件和多线程,可用于解决上个程序播放过程中鼠标不能动的问题,

使用事件,等待事件中,就会响应鼠标键盘等事件,就不会卡在那了

将延时操作放到一个子线程中,用事件通知主线程,这样主线程就不用延时了,就可以及时响应事件了

SDL_WINDOWENVENT sdl系统自带的事件,当拉伸窗口的时候会触发

SDL_QUIT 也是SDL自带的事件,当点击窗口的×时触发

三、FFmpeg+SDL视频播放器

1.FFmpeg+SDL整合之后实现:视频文件->YUV->屏幕

2.SDL_UpdateTexture函数最后一个参数表示的是,一行像素数据的数据量

窗口的宽和高无所谓,但是纹理数据的宽和高要和视频数据的宽和高一致。

3.对于大数据的调试,如果直接打印出来,数据太多不好分析,一般是写入到文件,然后用工具打开文件去分析。

比如视频像素数据,可以写入到yuv文件,再用yuv分析工具来分析。

4.脱离开发环境的独立播放器

在解决方案中的debug目录下的*.exe文件即为编译好的可执行文件。

执行这个文件就可以脱离开发环境运行。

5.调试时,在属性->调试->命令参数 中填的东西,就是程序的输入参数,也就是argv中的内容

平时独立的exe程序,只需在执行的时候后面加上参数就是输入参数了,如ffplay.exe那样。

四、FFmpeg+SDL视频播放器代码实现

主要是FFmpegAndSDL.cpp文件,代码如下(基本上每一行都有注释):

1 /***************************************************************************** 2 * Copyright (C) 2017-2020 Hanson Yu All rights reserved. 3 ------------------------------------------------------------------------------ 4 * File Module : FFmpegAndSDL.cpp 5 * Description : FFmpegAndSDL Demo 6 7 8 * Created : 2017.09.21. 9 * Author : Yu Weifeng 10 * Function List : 11 * Last Modified : 12 * History : 13 * Modify Date Version Author Modification 14 * ----------------------------------------------- 15 * 2017/09/21 V1.0.0 Yu Weifeng Created 16 ******************************************************************************/ 17 18 #include "stdafx.h" 19 #include <stdio.h> 20 21 22 /*解决错误: 23 LNK2019 无法解析的外部符号 __imp__fprintf,该符号在函数 _ShowError 中被引用 24 25 原因: 26 ……这是链接库问题 27 就是工程里面没有添加那两个函数需要的库,#progma这个是代码链接库 28 第二句是vs2015兼容的问题。 29 lib库的vs编译版本 和 工程的vs开发版本 不一致。 30 导出函数定义变了。所以要人为加一个函数导出。 31 */ 32 #pragma comment(lib, "legacy_stdio_definitions.lib") 33 extern "C" { FILE __iob_func[3] = { *stdin,*stdout,*stderr }; } 34 35 /* 36 __STDC_LIMIT_macROS and __STDC_CONSTANT_MACROS are a workaround to allow C++ programs to use stdint.h 37 macros specified in the C99 standard that aren't in the C++ standard. The macros, such as UINT8_MAX, INT64_MIN, 38 and INT32_C() may be defined already in C++ Applications in other ways. To allow the user to decide 39 if they want the macros defined as C99 does, many implementations require that __STDC_LIMIT_MACROS 40 and __STDC_CONSTANT_MACROS be defined before stdint.h is included. 41 42 This isn't part of the C++ standard, but it has been adopted by more than one implementation. 43 */ 44 #define __STDC_CONSTANT_MACROS 45 46 extern "C" 47 { 48 #include "libavcodec/avcodec.h" 49 #include "libavformat/avformat.h" 50 #include "libswscale/swscale.h" 51 #include "SDL2/SDL.h" 52 }; 53 54 55 //Refresh Event 自定义事件 56 #define PLAY_REFRESH_EVENT (SDL_USEREVENT + 1)//自定义刷新图像(播放)事件 57 #define PLAY_BREAK_EVENT (SDL_USEREVENT + 2) //自定义退出播放事件 58 59 60 static int g_iThreadExitFlag = 0; 61 /***************************************************************************** 62 -Fuction : RefreshPlayThread 63 -Description : RefreshPlayThread 64 -Input : 65 -Output : 66 -Return : 67 * Modify Date Version Author Modification 68 * ----------------------------------------------- 69 * 2017/09/21 V1.0.0 Yu Weifeng Created 70 ******************************************************************************/ 71 int RefreshPlayThread(void *opaque) 72 { 73 g_iThreadExitFlag = 0; 74 SDL_Event tEvent={0}; 75 76 while (!g_iThreadExitFlag) 77 { 78 tEvent.type = PLAY_REFRESH_EVENT; 79 SDL_PushEvent(&tEvent);//发送事件给其他线程 80 SDL_Delay(20);//延时函数 填40的时候,视频会有种卡的感觉 81 } 82 //Break 83 g_iThreadExitFlag = 0; 84 tEvent.type = PLAY_BREAK_EVENT; 85 SDL_PushEvent(&tEvent);//发送事件给其他线程 发送一个事件 86 87 return 0; 88 } 89 90 /***************************************************************************** 91 -Fuction : main 92 -Description : main 93 -Input : 94 -Output : 95 -Return : 96 * Modify Date Version Author Modification 97 * ----------------------------------------------- 98 * 2017/09/21 V1.0.0 Yu Weifeng Created 99 ******************************************************************************/ 100 int main(int argc, char* argv[]) 101 { 102 /*------------FFmpeg----------------*/ 103 const char *strFilePath = "屌丝男士.mov"; 104 AVFormatContext *ptFormatContext = NULL;//封装格式上下文,内部包含所有的视频信息 105 int i = 0; 106 int iVideoindex=0;//纯视频信息在音视频流中的位置,也就是指向音视频流数组中的视频元素 107 AVCodecContext *ptCodecContext;//编码器相关信息上下文,内部包含编码器相关的信息,指向AVFormatContext中的streams成员中的codec成员 108 AVCodec *ptCodec;//编码器,使用函数avcodec_find_decoder或者,该函数需要的id参数,来自于ptCodecContext中的codec_id成员 109 AVFrame *ptFrame=NULL;//存储一帧解码后像素(采样)数据 110 AVFrame *ptFrameAfterScale=NULL;//存储(解码数据)转换后的像素(采样)数据 111 unsigned char *pucFrameAfterScaleBuf=NULL;//用于存储ptFrameAfterScale中的像素(采样)缓冲数据 112 AVPacket *ptPacket=NULL;//存储一帧压缩编码数据 113 int iRet =0; 114 int iGotPicture=0;//解码函数的返回参数,got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero 115 116 /*------------SDL----------------*/ 117 int iScreenWidth=0, iScreenHeight=0;//视频的宽和高,指向ptCodecContext中的宽和高 118 SDL_Window *ptSdlWindow=NULL;//用于sdl显示视频的窗口(用于显示的屏幕) 119 SDL_Renderer* ptSdlRenderer=NULL;//sdl渲染器,把纹理数据画(渲染)到window上 120 SDL_Texture* ptSdlTexture=NULL;//sdl纹理数据,用于存放像素(采样)数据,然后给渲染器 121 SDL_Rect tSdlRect ={0};//正方形矩形结构,存了矩形的坐标,长宽,以便确定纹理数据画在哪个位置,确定位置用,比如画在左上角就用这个来确定。被渲染器调用 122 SDL_Thread *ptVideoControlTID=NULL;//sdl线程id,线程的句柄 123 SDL_Event tSdlEvent = {0};//sdl事件,代表一个事件 124 125 /*------------像素数据处理----------------*/ 126 struct SwsContext *ptImgConvertInfo;//图像转换(上下文)信息,图像转换函数sws_scale需要的参数,由sws_getContext函数赋值 127 128 129 130 /*------------FFmpeg----------------*/ 131 av_register_all();//注册FFmpeg所有组件 132 avformat_network_init();//初始化网络组件 133 134 ptFormatContext = avformat_alloc_context();//分配空间给ptFormatContext 135 if (avformat_open_input(&ptFormatContext, strFilePath, NULL, NULL) != 0) 136 {//打开输入视频文件 137 printf("Couldn't open input stream.n"); 138 return -1; 139 } 140 if (avformat_find_stream_info(ptFormatContext, NULL)<0) 141 {//获取视频文件信息 142 printf("Couldn't find stream information.n"); 143 return -1; 144 } 145 //获取编码器相关信息上下文,并赋值给ptCodecContext 146 iVideoindex = -1; 147 for (i = 0; i<ptFormatContext->nb_streams; i++) 148 { 149 if (ptFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) 150 { 151 iVideoindex = i; 152 break; 153 } 154 } 155 if (iVideoindex == -1) 156 { 157 printf("Didn't find a video stream.n"); 158 return -1; 159 } 160 ptCodecContext = ptFormatContext->streams[iVideoindex]->codec; 161 162 ptCodec = avcodec_find_decoder(ptCodecContext->codec_id);//查找解码器 163 if (ptCodec == NULL) 164 { 165 printf("Codec not found.n"); 166 return -1; 167 } 168 if (avcodec_open2(ptCodecContext, ptCodec, NULL)<0) 169 {//打开解码器 170 printf("Could not open codec.n"); 171 return -1; 172 } 173 174 ptPacket = (AVPacket *)av_malloc(sizeof(AVPacket));//分配保存解码前数据的空间 175 ptFrame = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换前的像素数据 176 177 /*------------像素数据处理----------------*/ 178 ptFrameAfterScale = av_frame_alloc();//分配结构体空间,结构体内部的指针指向的数据暂未分配,用于保存图像转换后的像素数据 179 pucFrameAfterScaleBuf = (uint8_t *)av_malloc(avpicture_get_size(PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height));//分配保存数据的空间 180 /*int avpicture_fill(AVPicture *picture, uint8_t *ptr,int pix_fmt, int width, int height); 181 这个函数的使用本质上是为已经分配的空间的结构体(AVPicture *)ptFrame挂上一段用于保存数据的空间, 182 这个结构体中有一个指针数组data[AV_NUM_DATA_POINTERS],挂在这个数组里。一般我们这么使用: 183 1) pFrameRGB=avcodec_alloc_frame(); 184 2) numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width,pCodecCtx->height); 185 buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t)); 186 3) avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24,pCodecCtx->width, pCodecCtx->height); 187 以上就是为pFrameRGB挂上buffer。这个buffer是用于存缓冲数据的。 188 ptFrame为什么不用fill空间。主要是下面这句: 189 avcodec_decode_video(pCodecCtx, pFrame, &frameFinished,packet.data, packet.size); 190 很可能是ptFrame已经挂上了packet.data,所以就不用fill了。*/ 191 avpicture_fill((AVPicture *)ptFrameAfterScale, pucFrameAfterScaleBuf, PIX_FMT_YUV420P, ptCodecContext->width, ptCodecContext->height); 192 //sws开头的函数用于处理像素(采样)数据 193 ptImgConvertInfo = sws_getContext(ptCodecContext->width, ptCodecContext->height, ptCodecContext->pix_fmt, 194 ptCodecContext->width, ptCodecContext->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//获取图像转换(上下文)信息 195 196 /*------------SDL----------------*/ 197 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) 198 {//初始化SDL系统 199 printf("Could not initialize SDL - %sn", SDL_GetError()); 200 return -1; 201 } 202 //SDL 2.0 Support for multiple windows 203 iScreenWidth = ptCodecContext->width; 204 iScreenHeight = ptCodecContext->height; 205 ptSdlWindow = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 206 iScreenWidth, iScreenHeight, SDL_WINDOW_OPENGL);//创建窗口SDL_Window 207 208 if (!ptSdlWindow) 209 { 210 printf("SDL: could not create window - exiting:%sn", SDL_GetError()); 211 return -1; 212 } 213 ptSdlRenderer = SDL_CreateRenderer(ptSdlWindow, -1, 0);//创建渲染器SDL_Renderer 214 //IYUV: Y + U + V (3 planes) 215 //YV12: Y + V + U (3 planes) 216 //创建纹理SDL_Texture 217 ptSdlTexture = SDL_CreateTexture(ptSdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, ptCodecContext->width, ptCodecContext->height); 218 219 tSdlRect.x = 0;//x y值是左上角为圆点开始的坐标值,调整x y值以及w h值,就可以实现在窗口的指定位置显示,没有画面的地方为黑框 220 tSdlRect.y = 0;//当x y等于0,w h等于窗口的宽高时即为全屏显示,此时调整宽高大小,只需调整窗口大小即可 221 tSdlRect.w = iScreenWidth; 222 tSdlRect.h = iScreenHeight; 223 224 ptVideoControlTID = SDL_CreateThread(RefreshPlayThread, NULL, NULL);//创建一个线程 225 226 while (1) 227 {//Event Loop 228 SDL_WaitEvent(&tSdlEvent);//Wait,等待其他线程过来的事件 229 if (tSdlEvent.type == PLAY_REFRESH_EVENT) //自定义刷新图像(播放)事件 230 { 231 /*------------FFmpeg----------------*/ 232 if (av_read_frame(ptFormatContext, ptPacket) >= 0) //从输入文件读取一帧压缩数据 233 { 234 if (ptPacket->stream_index == iVideoindex) 235 { 236 iRet = avcodec_decode_video2(ptCodecContext, ptFrame, &iGotPicture, ptPacket);//解码一帧压缩数据 237 if (iRet < 0) 238 { 239 printf("Decode Error.n"); 240 return -1; 241 } 242 if (iGotPicture) 243 { 244 //图像转换,sws_scale()函数需要用到的转换信息,即第一个参数,是由sws_getContext函数获得的 245 sws_scale(ptImgConvertInfo, (const uint8_t* const*)ptFrame->data, ptFrame->linesize, 0, ptCodecContext->height, ptFrameAfterScale->data, ptFrameAfterScale->linesize); 246 247 /*------------SDL----------------*/ 248 SDL_UpdateTexture(ptSdlTexture, NULL, ptFrameAfterScale->data[0], ptFrameAfterScale->linesize[0]);//设置(更新)纹理的数据 249 SDL_RenderClear(ptSdlRenderer);//先清除渲染器里的数据 250 //SDL_RenderCopy( ptSdlRenderer, ptSdlTexture, &tSdlRect, &tSdlRect ); //将纹理的数据拷贝给渲染器 251 SDL_RenderCopy(ptSdlRenderer, ptSdlTexture, NULL, NULL);//将纹理的数据拷贝给渲染器 252 SDL_RenderPresent(ptSdlRenderer);//显示 253 } 254 } 255 av_free_packet(ptPacket);//释放空间 256 } 257 else 258 { 259 g_iThreadExitFlag = 1;//Exit Thread 260 } 261 } 262 else if (tSdlEvent.type == SDL_QUIT) //也是SDL自带的事件,当点击窗口的×时触发//SDL_WINDOWENVENT sdl系统自带的事件,当拉伸窗口的时候会触发 263 { 264 g_iThreadExitFlag = 1; 265 } 266 else if (tSdlEvent.type == PLAY_BREAK_EVENT) //自定义退出播放事件 267 { 268 break; 269 } 270 271 } 272 273 /*------------像素数据处理----------------*/ 274 sws_freeContext(ptImgConvertInfo);//释放空间 275 276 /*------------SDL----------------*/ 277 SDL_Quit();//退出SDL系统 278 279 /*------------FFmpeg----------------*/ 280 av_frame_free(&ptFrameAfterScale);//释放空间 281 av_frame_free(&ptFrame);//释放空间 282 avcodec_close(ptCodecContext);//关闭解码器 283 avformat_close_input(&ptFormatContext);//关闭输入视频文件 284 285 return 0; 286 } FFmpegAndSDL.cpp

具体代码见github:

https://github.com/fengweiyu/FFmpegAndSDL



Tags:FFmpeg   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
FFmpeg的作用: FFmpeg是一个优秀的多媒体框架; FFmpeg可以运行在Linux,Mac,Windows等平台; FFmpeg可以解码,编码,转码,复用,解复用,过滤音视频数据。播放器基本架构:image.pngSDL视频渲...【详细内容】
2021-10-27  Tags: FFmpeg  点击:(30)  评论:(0)  加入收藏
ffmpeg 常用基本命令资料推荐【免费】FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发-学习视频教程-腾讯课堂1.分离视频音频流ffmpeg -i input_file -vcodec copy -an...【详细内容】
2021-10-15  Tags: FFmpeg  点击:(52)  评论:(0)  加入收藏
一、FFmpeg视频解码器1.视频解码知识1).纯净的视频解码流程压缩编码数据->像素数据。例如解码H.264,就是“H.264码流->YUV”。2).一般的视频解码流程视频码流一般存储在一定...【详细内容】
2021-05-19  Tags: FFmpeg  点击:(163)  评论:(0)  加入收藏
1.前言当我们在在看腾讯视频或者其他网页上的视频时,浏览器会先缓冲ts文件到你本地.如果你想把这些ts转成mp4文件保存在本地,可以使用ffmpeg进行格式转换。准备需要合成mp4而...【详细内容】
2021-04-25  Tags: FFmpeg  点击:(440)  评论:(0)  加入收藏
在实际工作中,通常需要ffmpeg作为工具来验证一个问题,比如播放一个视频,提取一个码流,转码视频,转封格式等,用的时候才发现忘记了相关命令,Google一番花老大的力气才找到自己需要的...【详细内容】
2020-09-27  Tags: FFmpeg  点击:(74)  评论:(0)  加入收藏
1.环境搭建和整体工程说明命令行输入:-i 3.flv -vcodec copy 2_audio.mp4 转码的CPU占有率。 需要在这个目录下,拷贝正确的SDL2.dll 转码过程: 转码成功后,MP4的大小要比3.fl...【详细内容】
2020-09-24  Tags: FFmpeg  点击:(122)  评论:(0)  加入收藏
1.框架分析 ffplay.c是FFmpeg源码⾃带的播放器,调⽤FFmpeg和SDL API实现⼀个⾮常有⽤的播放器。例如哔哩哔哩著名开源项⽬ijkplayer也是基于ffplay.c进⾏⼆次开发。ffplay实...【详细内容】
2020-09-09  Tags: FFmpeg  点击:(99)  评论:(0)  加入收藏
一、前言采用ffmpeg解码,是所有视频监控开发人员必备的技能,绕不过去的一个玩意,甚至可以说是所有音视频开发人员的必备技能。FFmpeg是一套可以用来记录、转换数字音频、视频,并...【详细内容】
2020-08-08  Tags: FFmpeg  点击:(94)  评论:(0)  加入收藏
Linux安装操作系统:CentOS7 ffmpeg版本:3.4.1安装先访问ffmpeg官网下载linux安装包 http://ffmpeg.org/download.html ,这里我下载的是官方已经编译过的版本,别问我为啥不自己编...【详细内容】
2020-06-24  Tags: FFmpeg  点击:(96)  评论:(0)  加入收藏
安装ffmpeg:下载FFmpeg和libx264的包ffmpeg-2.4.1.tar.bz2 last_x264.tar.bz2libx264需要yasm,所以先安装yasm apt-get install yasm然后安装libx264 aptitude install libx...【详细内容】
2020-05-18  Tags: FFmpeg  点击:(45)  评论:(0)  加入收藏
▌简易百科推荐
今天还真是有点小激动啊,B站看来要和抖音杠上了,之前抖音推出网页端要对标B站,不过似乎大家没有用网页刷抖音的习惯。但是这次B站竟然推出了“必剪”电脑客户端,激动的我赶紧下...【详细内容】
2021-12-14  最佳应用    Tags:剪辑   点击:(20)  评论:(0)  加入收藏
做短视频的话我们需不需要跟着热点走呢?答案当然是肯定的!而且还是一定要的!为什么呢?因为热点是最大的流量池,比如最近的大S离婚事件就是热点,这是最大的流量池。所有人的目光都...【详细内容】
2021-12-01  易撰    Tags:短视频   点击:(24)  评论:(0)  加入收藏
平时工作的时候想必大家都有听音乐(摸鱼)的视频,音乐给我们的生活带来了很大的乐趣,但是很多时候我们会发现一些格式的限制,无法播放我们用各种工具录制的音乐,我们现在就来对音频...【详细内容】
2021-11-16  下科技小助手    Tags:MP3   点击:(26)  评论:(0)  加入收藏
游戏视频录制,用什么软件录屏好?小关这几周在某鱼看到了好多的游戏主播,这些游戏主播有的比技术,有的比能说会道,有的比样貌。都在各自得直播间里面玩得热火朝天,有自己的一处小天...【详细内容】
2021-11-11  杨姐爱智能    Tags:录屏   点击:(35)  评论:(0)  加入收藏
怎么在电脑上录制高清视频?小米最近在学校有点闲,因为这个学期的课程较少,自己也提前完成了许多的学习任务,想着怎么有效的把这些空闲的时间好好利用起来,不能白白浪费这些时间吧...【详细内容】
2021-11-11  张哥聊科技    Tags:高清视频   点击:(22)  评论:(0)  加入收藏
时长:4h 14m | 32节| 视频:1280&times;720,44 KHz | 2.6 GB语言:英语+中英文字幕(根据原英文字幕机译更准确)成为Adobe Premiere Pro专业版视频编辑专家的唯一要求你会学到什么a...【详细内容】
2021-11-03  CG达人  搜狐号  Tags:Premiere   点击:(23)  评论:(0)  加入收藏
我们在制作短视频的时候,需要自己给视频配音,然后与拍摄内容合成还是很难的,所以少不了后期配音的环节。今天小编给大家推荐几款,超好用的短视频配音软件。1、剪映小伙伴是不是...【详细内容】
2021-11-03  语音dub爱好者  搜狐号  Tags:视频配音   点击:(65)  评论:(0)  加入收藏
视频怎么压缩变小?视频的出现虽然说给我们的日常生活带来了极大的消遣与事业发展,但是不得不说视频格式非常占用内存,如果想要进行视频的拍摄、剪辑、放映对工具都需要较高的要...【详细内容】
2021-11-03  娱乐情怀    Tags:视频   点击:(44)  评论:(0)  加入收藏
如何把m4v转换成mp4格式?M4V是一种应用于网络视频点播网站和移动手持设备的视频格式,是MP4格式的一种特殊类型。由苹果公司创造,此种格式为 iPod 、iPhone 和 PlayStation Port...【详细内容】
2021-11-03  纸折兔    Tags:m4v   点击:(38)  评论:(0)  加入收藏
FFmpeg的作用: FFmpeg是一个优秀的多媒体框架; FFmpeg可以运行在Linux,Mac,Windows等平台; FFmpeg可以解码,编码,转码,复用,解复用,过滤音视频数据。播放器基本架构:image.pngSDL视频渲...【详细内容】
2021-10-27  linux音视频    Tags:FFmpeg   点击:(30)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条