An ffmpeg and SDL Tutorial

合集下载

基于FFmpeg和SDL实现多路实时流变换及播放

基于FFmpeg和SDL实现多路实时流变换及播放

基于FFmpeg和SDL实现多路实时流变换及播放李科;李璐;兰时勇【期刊名称】《计算机技术与发展》【年(卷),期】2014(000)004【摘要】针对现有不同采集设备的实时视频流采集、解码和播放,以及对大场景监视和多路多画面监视的需求,提出结合FFmpeg跨平台视频方案、SDL多媒体开发库、SDL_gfx扩展库以及多线程编程技术的整体解决方案,实现基于实时流传输协议( RTSP)的多路网络视频流实时播放,并可对各路视频进行旋转、放大、仿射变换等操作。

由于采用了SDL多媒体开发包及扩展库、FFmpeg视频方案等技术,因此该软件的跨平台可移植性和兼容性较高,并且该软件使用RTSP协议,在监控方面能够达到实时效果。

%Targeted at the needs of different acquisition devices for existing real-time video streaming capture,decode and playback,as well as a large scene to monitor and multi-screen multi-channel monitoring, proposed a total solution based on RTSP ( Real Time Streaming Protocol) to realize multi-channel network video streams real-time playback,and rotating,zooming,affine transformation and other operation on the brightest video,which combined with FFmpeg cross-platform video solutions,SDL multimedia development librar-ies andSDL_gfx extensions and multi-threaded programming technical. As a result of SDL multimedia development kit and extension li-brary,FFmpeg video solutions and other technologies,the portability and cross-platformcompatibility of the software is high,and because of the RTSP,the controls can achieve real-time results.【总页数】4页(P65-68)【作者】李科;李璐;兰时勇【作者单位】四川大学视觉合成图形图像技术国防重点学科实验室,四川成都610064;四川大学视觉合成图形图像技术国防重点学科实验室,四川成都610064;四川大学视觉合成图形图像技术国防重点学科实验室,四川成都610064【正文语种】中文【中图分类】TP301【相关文献】1.FFmpeg+SDL实时播放摄像机视频设计 [J], 孟成;陈亚2.基于FFmpeg和SDL的多路视频播放器设计与实现 [J], 刘文华3.基于FFMPEG的多路流媒体播放器的设计与实现 [J], 刘志军;袁然;黄奇;郑桂林4.基于DirectShow的MPEG-2流媒体多路播放与实时存储系统设计与实现 [J], 杜健;张白愚;杨莉云5.基于FFmpeg和SDL的视频流播放存储研究综述 [J], 邓正良因版权原因,仅展示原文概要,查看原文内容请购买。

ffmpeg教程

ffmpeg教程

ffmpeg教程FFmpeg是一款强大的音视频处理工具,可以实现音视频的转码、剪辑、合并、混音等功能。

本教程将为您介绍一些常用的FFmpeg命令及其用法。

1.音视频转码:将视频转换为其他格式的命令:```bashffmpeg -i input.mp4 output.avi```将音频转换为其他格式的命令:```bashffmpeg -i input.mp3 output.wav```2.剪辑视频:在一段视频中截取指定时长的视频片段的命令:```bashffmpeg -ss 00:00:10 -i input.mp4 -t 00:00:30 -c copy output.mp4 ```3.合并视频:合并多段视频为一段视频的命令:```bashffmpeg -i input1.mp4 -i input2.mp4 -filter_complexconcat=n=2:v=1:a=1 output.mp4```4.混音:将音频与视频进行混音的命令:```bashffmpeg -i video.mp4 -i audio.mp3 -c:v copy -c:a aac -strict experimental output.mp4```5.提取音视频:从视频中提取音频的命令:```bashffmpeg -i input.mp4 -vn -acodec copy output.mp3```从视频中提取视频的命令:```bashffmpeg -i input.mp4 -an -vcodec copy output.mp4```以上是几个常用的FFmpeg命令,您可以根据需要进行使用。

请注意,在实际使用时,命令中的文件名需要根据您的实际情况进行修改。

ffmpeg 和 SDL 教程

ffmpeg 和 SDL 教程
// Save the frame to disk if(++i<=5) SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } } // Free the packet that was allocated by av_read_frame av_free_packet(&packet); } 这个循环过程是比较简单的:av_read_frame()读取一个包并且把它保存到 AVPacket结构体中。注意我们仅仅申请了一个包的结构体 ――ffmpeg为我们申请 了内部的数据的内存并通过packet.data指针来指向它。这些数据可以在后面通过 av_free_packet()来释 放。函数avcodec_decode_video()把包转换为帧。然而当解码 一个包的时候,我们可能没有得到我们需要的关于帧的信息。因此,当我们得 到 下一帧的时候,avcodec_decode_video()为我们设置了帧结束标志frameFinished。 最后,我们使用 img_convert()函数来把帧从原始格式(pCodecCtx->pix_fmt)转 换成为RGB格式。要记住,你可以把一个 AVFrame结构体的指针转换为AVPicture 结构体的指针。最后,我们把帧和高度宽度信息传递给我们的SaveFrame函数。 关于包Packets的注释 从技术上讲一个包可以包含部分帧或者其它的数据,但是ffmpeg的解释器保证了 我们得到的包Packets包含的要么是完整的要么是多种完整的帧。 现在我们需要做的是让SaveFrame函数能把RGB信息定稿到一个PPM格式的文件 中。我们将生成一个简单的PPM格式文件,请相信,它是可以工作的。 void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; char szFilename[32]; int y; // Open file sprintf(szFilename, "frame%d.ppm", iFrame); pFile=fopen(szFilename, "wb");

协议软件部培训PPT-H264视频编解码技术

协议软件部培训PPT-H264视频编解码技术
2021/7/14
培训内容
• 视频编码标准H.263 – 协议结构
H.263有效载荷头定义了三种格式(模式A、模式B和模式C)。在模式A 中,在实际压缩H.263视频比特流之前存在4字节的H.263有效载荷头。 这样允许在GOB边界有分段。在模式B中,使用的是8字节的H.263有效 载荷头,且每个数据包从MB边界开始,没有PB帧选项。最后,模式C中 使用的是12字节的H.263有效载荷头,采用PB帧选项支持在MB边界的帧 分段。
4:4:4 ,Y、Cb 和Cr 具有同样的水平和垂直清晰度,在每一像素位 置,都有Y,Cb 和Cr分量,即不论水平方向还是垂直方向,每4个亮 度像素相应的有4个Cb和4个Cr色度像素。
4:4:4
4:2:0
4:2:2
Y Cb Cr
2021/7/14
• 视频编码的相关术语
1. 场和帧 2. 片和宏块 3. 片组 4. 档次和级 5. SP和SI 6. SPS和PPS 7. 图像序列号(POC) 8. RBSP和SODB
协议软件部培训PPTH264视频编解码技术
2021年7月14日星期三
•目标 •重点 •培训内容 •参考资料
2021/7/14
目录
培训目标 1. 掌握视频信息和视频编码的相关概念 2. 理解H.264编解码器的工作原理和关键算法 3. 掌握主流的H.264开源编解码器的架构和实
现 4. 掌握H.264视频数据RTP传输封包格式
1988年,ISO/IEC信息技术联合委员会成立了活动图像专家 组(MPEG,Moving Picture Expert Group)。1991年 公布了MPEG-1视频编码标准,码率为1.5Mbps,主要 应用于家用VCD的视频压缩;1994年11月,公布了 MPEG-2标准,用于数字视频广播(DVB)、家用 DVD的视频压缩及高清晰度电视(HDTV)。

基于ffmpeg网络播放器的教程与总结

基于ffmpeg网络播放器的教程与总结

一、概述为了解决在线无广告播放youku网上的视频。

(youku把每个视频切换成若干个小视频)。

视频资源解析可以从网上获取,此网站根据你输入的优酷的播放网页地址解析成若干个真实的视频地址。

二、实现首先搜索关闭网络播放器(流媒体播放器的实现方法)得出的结论,目前主流的播放器分三大阵营微软,苹果,基于FFmpeg内核的。

所以我决定从ffmpeg开源的播放器入手。

最出名的ffmpeg播放器vcl播放器,开源免费。

最后选择放弃。

原因1 依赖于vcl的68M的plugins和libvlccore.dll,libvlc.dll项目生成文件过大。

2即使这样不能解决播放多段视频卡顿现象。

最后决定使用ffmpeg官方的ffpaly播放器只有1000多行(很激动),使用ffmpeg编解码,使用sdl做显示。

本想只修改下就行了。

结果发现里面代码结构过于复杂,搞懂每行很是吃力。

而且是用sdl做显示,sdl需要句柄。

而我这个是为wpf项目量身定做的。

Wpf只有顶层窗口有句柄。

如果是使用wpf嵌入winform控件。

导致此winform控件只能最上层显示(原因是wpf是directui思想实现的)。

所以也放弃了。

决定使用ffmpeg库,自己开发查看网上关于ffmpeg开发的总结。

对ffmpeg开发有个总体方向。

首先我们先把视频搞出来,参考网上 100行代码搞定视频。

然后100行搞定音频网上这样视频音频都已经搞出来了。

但是我们怎么把视频音频一起搞出来呢?Csdn有一份文档网上此文档介绍了用ffmpeg开发视频播放器的详细方法,有注解。

但是已经过时了。

最新的代码在网上但是文档中的思想还是挺受用的。

代码不同,思想是通的。

结论,视频包含视频流,音频流,字幕流(一般没有),音视频同步跟进播放时间戳pts来做的。

视频和音频得出pts的方式有所不同。

具体看文档。

如果按文档的注释,然后根据github的代码,编译我们发现视频可以显示,音频出现乌拉乌拉的杂音。

基于FFMPEG和SDL的遥测视频解析技术

基于FFMPEG和SDL的遥测视频解析技术

收稿日期:2018-06-19 修回日期:2018-10-24 网络出版时间:2018-12-20基金项目:国家国防科技工业国防基础科研计划重点项目(JCKY 2016205B 006)作者简介:郝 朝(1990-),男,工程师,硕士,研究方向为飞行试验软件开发和数据处理㊂网络出版地址:http :// /kcms /detail /61.1450.tp.20181220.1035.038.html基于FFMPEG 和SDL 的遥测视频解析技术郝 朝,刘升护(中国飞行试验研究院,陕西西安710089)摘 要:在飞行试验中,视频数据是试飞测试数据中的重要组成部分㊂视频实时监控是保障试飞安全的重要环节㊂为解决机载多路遥测视频实时监控的问题,提出了基于FFMPEG 和SDL 的遥测视频实时解析方案㊂通过UDP 组播协议接收PCM 遥测视频数据包,经过解包,拼成完整的一帧视频图像,采用FFMPEG 进行视频解码并利用SDL 进行显示㊂首先介绍了遥测视频PCM 帧格式,重点介绍视频数据包解析方法,然后论述了FFMPEG 和SDL 对视频流进行解码与显示的流程㊂软件采用模块化㊁多线程并发和多缓冲区设计思路,提高处理效率,保证视频监控的实时性和功能的可扩展性㊂目前该软件已成功应用于多个型号任务视频实时监控中,实际应用效果表明该软件工作稳定,实现了多路视频图像的解析与显示,能够满足型号试飞需求㊂关键词:FFMPEG;SDL;遥测视频;实时监控中图分类号:TP311.1 文献标识码:A 文章编号:1673-629X (2019)04-0191-04doi:10.3969/j.issn.1673-629X.2019.04.038Analysis Technology of Telemetry Videos Based onFFMPEG and SDLHAO Zhao ,LIU Sheng -hu(Chinese Flight Test Establishment ,Xi ’an 710089,China )Abstract :Video data is an important part of flight test data in flight test.Video real -time monitoring is an important part of ensuring flight safety.In order to solve the problem of real -time monitoring of airborne multi -channel telemetry videos ,we propose a telemetry real -time video resolution scheme based on FFMPEG and SDL.The PCM telemetry video packets are received through UDP multicast protocol.After unpacking and frame matching ,video is decoded by FFMPEG and displayed by SDL.First the PCM frame format of te⁃lemetry video is introduced ,focusing on the method of video packet analysis.Then the process of decoding and displaying the video stream by FFMPEG and SDL is discussed.Modularity ,multithreading concurrency and multi buffer design are adopted to ensure the real -time performance and scalability of the video monitoring.At present ,the software has been successfully applied to the video real -timemonitoring of multi type mission.The actual application shows that the software works stably to realize the analysis and display of multi⁃channel video images and meets the requirements of type test flight.Key words :FFMPEG ;SDL ;telemetry videos ;real -time monitoring0 引 言飞行试验是验证航空产品设计指标要求和检验航空产品质量改进提高航空产品性能进行航空新理论和航空新技术研究的重要手段㊂在飞行试验中,机载视频影像能以最为直观与准确的方式描述飞机内部各独立子系统的健康状态,为地面飞行指挥人员和试飞工程师提供及时丰富的信息,对保障飞行安全㊁提高试飞效率有不可替代的作用[1]㊂目前机载抽引的视频是以PCM 流[1]的形式进行遥测下传㊂地面遥测天线接收到射频信号后通过PCM 接收机进行解调,视频服务器接收视频数据并进行分路提取处理,然后以UDP 组播[2-3]的形式发送给客户端进行监控显示㊂基于以上分析,提出基于FFMPEG [4-6]和SDL [7-8]的遥测视频实时解析方案㊂客户端接收服务器发送的多路视频数据包,经过解包㊁拼帧,将一帧完整的视频数据送给FFMPEG 进行解码,得到YUV [9-10]图像数据,然后采用SDL 进行显示㊂第29卷 第4期2019年4月 计算机技术与发展COMPUTER TECHNOLOGY AND DEVELOPMENT Vol.29 No.4Apr. 20191 遥测视频PCM 帧格式PCM (pulse -code moduliation )称为脉冲编码调制㊂PCM 数据传输以其抗干扰能力强㊁数据带宽大等特点,广泛应用于航空试验领域㊂PCM 数据中一个完整的帧称为全帧,每一个全帧可以包含一个或者若干个子帧㊂数据中的每个子帧由同步字和数据字组成㊂目前飞行试验中,应用的机载视频采集器有MiniR 700和通用采集器两种㊂不同的采集器对应的PCM 帧视频数据存放格式也不相同㊂对于MiniR 700视频采集器,PCM 帧视频格式如图1所示㊂1N 图1 MiniR 700采集器视频PCM 帧格式假设该飞机一共有三路视频(V 1㊁V 2㊁V 3)进行遥测下传,则每一路视频均是以TS 流[11-12]的形式存储在PCM 帧结构中,每一子帧中各路视频数据交替出现㊂为了保证PCM 带宽,数据中会存在填充字(FADE )的现象㊂服务器进行处理时,首先通过ID 字将每个子帧拼成一个完整的PCM 全帧,然后分别提取每一路的视频数据,最后通过UDP 组播的形式进行发送,发送数据包格式为【第几路】【该路视频数据】㊂对于通用采集器,PCM 帧视频格式如图2所示㊂服务器进行处理时,只需要将接收到的数据包以组播的形式进行转发即可㊂51221-N图2 通用采集器视频PCM 帧格式通用采集器视频PCM 帧只有一个子帧,帧长512个字㊂一个完整的视频画面数据需要拆分为上百个PCM 帧,每个PCM 帧内的图像数据定义如表1所示㊂2 视频数据包解析客户端通过UDP 组播接收服务器发送的视频数据包㊂UDP 组播初始化流程为:采用WSAStartup 的初始化Winsock ;创建套接字;采用bind 绑定端口号;采用IP _ADD _MEMBERSHIP 加入组播组;采用非阻塞的异步套接字WSAAsyncSelect ()实现网络接收,对网络事件采用基于消息的异步存取策略,能够方便地处理网络通信㊂当接收到服务器视频数据包时,会触发FD _READ 消息㊂接收到每路视频数据包后,存入相应的FIFO 队列㊂表1 影像数据帧头格式字节数名称变量名2飞机型号PlaneNo 2视频总路数TotalCh 2第几路ChNo 2视频格式videoFormat 2影像宽度width2影像高度height2影像帧ID frameID2影像帧长度frameLength 2总数据帧个数frameCount 2第几个数据帧vpID2有效数据长度vpLength 6时间time 996影像数据data 对于MiniR 700采集器的视频PCM 帧,首先需要剔除填充字,然后对TS 流进行解封装,拼成完整的一帧视频数据㊂TS 流为188字节的固定包长度,好处是便于找到帧的起始位置,易于从包丢失中恢复,适合于有误码的环境㊂TS 流格式如图3所示㊂包头为4个字节,负载为184个字节㊂188图3 TS 流格式同步字节固定为0x 47,占1个字节,该字段是MPEG -2TS 传送包标识符㊂PID 占13位,表示传送包的有效净荷中的数据类型㊂根据PID 将TS 上从不同ES (elementary stream ,视频基本流)来的TS 包区分开,以重建原来的ES ㊂为了还原视频数据,还需要传㊃291㊃ 计算机技术与发展 第29卷输节目随带信息及解释有关TS特定结构的信息(元数据),即节目特定信息(program specific information, PSI),用于说明:1个视频是由多少个ES组成的;1个视频是由哪些个ES组成的;在哪些个PID情况下,1个相应的解码器能找到TS中的各个数据包㊂为了重建原来的ES,就要追踪从不同ES来的TS包及其PID㊂因此,一些映射结构(mapping mechanism),如节目源结合表(PAT,PID=0)和节目源映射表(PMT),会以打包的形式存在于TS上,即借助于PSI传输一串描述了各种ES的表格来实现㊂有了PAT及PMT这两种表,就可以根据PID将TS上从不同ES来的TS 包区分开㊂首先从PID=0的PAT上找出带有PMT 的那个节目源,然后从所选择的PMT中找到组成该节目源的各个ES的PID㊂将TS流还原成一帧完整视频数据后存入解码缓冲区㊂对于通用采集器视频PCM帧,接收到每路视频数据包后,将多个视频数据包依据有效数据长度㊁影像帧长度拼成完成一帧视频后送给解码器解码㊂拼帧流程如图4所示㊂首先从接收缓冲区中取出一个视频数据包,如果当前数据帧ID不等于上一数据帧ID,说明为新的一帧视频,将Frame.length置0,将Frame.ID置为新的视频帧ID㊂否则说明为同一视频帧㊂将有效视频数据进行提取放入解码缓冲区,如果Frame.length 等于该影像帧的总长度,则说明已拼成一帧完整视频,送给解码器进行解码㊂图4 拼帧流程为了保证在视频解码的同时不丢失数据包,采用多线程并发[13-14]与多缓冲区[15]机制,针对数据接收㊁数据包解析㊁视频解码与显示分别开辟单独的线程㊂设置两个缓冲区:数据接收缓冲区与解码缓冲区㊂数据接收线程接收到遥测视频数据包后将数据包存入接收缓冲区㊂数据包解析线程从缓冲区中提取数据包进行解包与组帧,将一帧完整视频数据存入解码缓冲区㊂视频解码线程从缓冲区中提取一帧视频进行解码并显示㊂各线程之间采用互斥量进行同步操作㊂3 基于FFMPEG的视频解码技术FFMPEG是一个开源且跨平台的音视频流方案,具备高可移植性和编解码质量,为音视频转换㊁解码以及流化提供了完整的解决方案㊂libavcodec包含全部FFMPEG音频/视频编解码库,相关数据结构包括AV⁃FormatContext㊁AVCodecContext㊁AVCodec㊁AVPacket㊁AVFrame与AVPicture等㊂FFMPEG解码视频流的流程如下:(1)对FFMPEG进行初始化㊂av_register_all()avformat_network_init();(2)对pFormatCtx进行初始化设置,包括width㊁height㊂(3)找到对应格式的视频解码器㊂pCodec=avcodec_find_decoder(pCodecCtx->co⁃dec_id);(4)打开对应格式的视频解码器㊂avcodec_open2(pCodecCtx,pCodec,NULL); (5)对一帧视频数据采用相应的解码器进行解码㊂avcodec_decode_video2(pCodecCtx,pFrame,&got _picture,packet);在程序运行时读取配置文件,执行步骤1~4对FFMPEG进行设置,然后循环从解码缓冲区中提取一帧视频数据,调用步骤5进行解码得到YUV数据,向图像显示子线程发送消息㊂4 基于SDL的图像显示技术SDL(simple directmedia layer)是一套基于C语言的跨平台多媒体开发库,提供了多种控制音视频输入与输出的函数㊂SDL显示YUV数据流程如下: (1)对SDL进行初始化㊂SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIM⁃ER);(2)利用控件创建SDL显示窗口㊂screen=SDL_CreateWindowForm(m_hwnd); (3)基于窗口创建渲染器㊂sdlRenderer=SDL_CreateRenderer(screen,-1,0);(4)创建纹理用于显示YUV数据㊂sdlTexture=SDL_CreateTexture(sdlRenderer,SDL_ PIXELFORMAT_IYUV,1,width,height); (5)设置纹理的像素数据㊂㊃391㊃ 第4期 郝 朝等:基于FFMPEG和SDL的遥测视频解析技术SDL _UpdateTexture (sdlTexture ,NULL ,pFram⁃eYUV →data [0],pFrameYUV →linesize [0]);(6)将纹理数据复制给渲染目标㊂SDL _RenderCopy (sdlRenderer ,sdlTexture ,NULL ,NULL );(7)显示画面㊂SDL _RenderPresent (sdlRenderer )㊂执行步骤1~4实现SDL 初始化显示设置,接收到解码线程发送的消息,调用步骤5~7即可实现对YUV 图像的显示㊂5 应用效果软件界面如图5所示㊂选择飞机与服务器,点击开始按钮,通过组播接收服务器视频数据包㊂应用效果表明,该软件能够实现多路遥测视频的解码与显示,时延满足实时监控需求㊂图5 实际应用效果6 结束语为了解决机载多路视频实时监控的问题,设计了基于FFMPEG 和SDL 的遥测视频实时解析软件㊂客户端通过UDP 组播接收服务器发送的PCM 视频数据包,通过解包并拼成完整的一帧视频数据,送给FFM⁃PEG 进行解码并通过SDL 进行显示㊂该软件已经应用于多个型号试飞视频实时监控中,正确性和可靠性得到验证,为保障型号试飞的高效顺利进行发挥了重要作用㊂参考文献:[1] 张 杰,邹 强,晏 晖.机载多路视频PCM 遥测传输技术[J ].计算机与数字工程,2013,41(5):805-807.[2] 于宏亮.TCP 与UDP 的原理及其在网络编程中的区别[J ].科技信息:学术版,2007(22):186.[3] 尹然然.基于UDP 协议的可靠性改进协议[J ].电脑知识与技术,2010,6(16):4379-4380.[4] ZENG Hao ,FANG Yuan.Implementation of video transcod⁃ing client based on FFMPEG [J ].Advanced Materials Re⁃search ,2013,756-759:1748-1752.[5] CHENG Yun ,LIU Qingtang ,ZHU Xiaoliang ,et al.Researchon digital content protection technology for video and audio based on FFmpeg [J ].International Journal of Advancements in Computing Technology ,2011,3(8):9-17.[6] 辛长春,娄小平,吕乃光.基于FFmpeg 的远程视频监控系统编解码[J ].电子技术,2013(1):3-5.[7] 孟 成,陈 亚.FFmpeg +SDL 实时播放摄像机视频设计[J ].产业与科技论坛,2017,16(17):57-58.[8] 李 科,李 璐,兰时勇.基于FFmpeg 和SDL 实现多路实时流变换及播放[J ].计算机技术与发展,2014,24(4):65-68.[9] 李亚南,杨 亮,李 波.一种基于YUV 颜色空间的匹配跟踪算法[J ].机电产品开发与创新,2017,30(4):89-90.[10]MUKHERJEE J ,LANGB M K ,MITRA S K.Demosaicingof images obtained from single -chip imaging sensors in YUV color space [J ].Pattern Recognition Letters ,2005,26(7):985-997.[11]朱明海.流媒体服务器TS 流封装的实现及流控的研究[D ].北京:北京邮电大学,2011.[12]李 叶.基于TS 流的数字电视播出系统的研究与实现[D ].长沙:中南大学,2014.[13]COURBIN P ,LUPU I ,GOOSSENS J.Scheduling of hard re⁃al -time multi -phase multi -thread (MPMT )periodic tasks[J ].Real -Time Systems ,2013,49(2):239-266.[14]郝文化.Windows 多线程编程技术与实例[M ].北京:中国水利水电出版社,2005.[15]李 晔,樊燕红,姜竞赛,等.多缓冲区技术在DSP 语音存储系统中的应用[J ].计算机应用与软件,2014,31(3):131-133.㊃491㊃ 计算机技术与发展 第29卷。

FFMPEG教程02指导2:输出到屏幕

FFMPEG教程02指导2:输出到屏幕

SDL和视频为了在屏幕上显示,我们将使用SDL.SDL是Simple Direct Layer的缩写。

它是一个出色的多媒体库,适用于多平台,并且被用在许多工程中。

你可以从它的官方网站的网址/上来得到这个库的源代码或者如果有可能的话你可以直接下载开发包到你的操作系统中。

按照这个指导,你将需要编译这个库。

(剩下的几个指导中也是一样)SDL库中有许多种方式来在屏幕上绘制图形,而且它有一个特殊的方式来在屏幕上显示图像――这种方式叫做YUV覆盖。

YUV(从技术上来讲并不叫YUV而是叫做YCbCr)是一种类似于RGB方式的存储原始图像的格式。

粗略的讲,Y是亮度分量,U和V是色度分量。

(这种格式比RGB复杂的多,因为很多的颜色信息被丢弃了,而且你可以每2个Y有1个U和1个V)。

SDL的YUV覆盖使用一组原始的YUV数据并且在屏幕上显示出他们。

它可以允许4种不同的YUV格式,但是其中的YV12是最快的一种。

还有一个叫做YUV420P的YUV格式,它和YV12是一样的,除了U和V分量的位置被调换了以外。

420意味着它以4:2:0的比例进行了二次抽样,基本上就意味着1个颜色分量对应着4个亮度分量。

所以它的色度信息只有原来的1/4。

这是一种节省带宽的好方式,因为人眼感觉不到这种变化。

在名称中的P表示这种格式是平面的――简单的说就是Y,U和V分量分别在不同的数组中。

FFMPEG可以把图像格式转换为YUV420P,但是现在很多视频流的格式已经是YUV420P的了或者可以被很容易的转换成YUV420P格式。

于是,我们现在计划把指导1中的SaveFrame()函数替换掉,让它直接输出我们的帧到屏幕上去。

但一开SDL_Init()函数告诉了SDL库,哪些特性我们将要用到。

当然SDL_GetError()是一个用来手工除错的函数。

创建一个显示这就创建了一个给定高度和宽度的屏幕。

下一个选项是屏幕的颜色深度――0表示使用和当前一样的深度。

FFmpeg命令行工具学习(二):播放媒体文件的工具ffplay

FFmpeg命令行工具学习(二):播放媒体文件的工具ffplay

FFmpeg命令⾏⼯具学习(⼆):播放媒体⽂件的⼯具ffplay ⼀、简述ffplay是以FFmpeg框架为基础,外加渲染⾳视频的库libSDL构建的媒体⽂件播放器。

⼆、命令格式在安装了在命令⾏中输⼊如下格式的命令:ffplay [选项] ['输⼊⽂件']1. 主要选项'-x width' 强制以 "width" 宽度显⽰'-y height' 强制以 "height" ⾼度显⽰'-an' 禁⽌⾳频'-vn' 禁⽌视频'-ss pos' 跳转到指定的位置(秒)'-t duration' 播放 "duration" 秒⾳/视频'-bytes' 按字节跳转'-nodisp' 禁⽌图像显⽰(只输出⾳频)'-f fmt' 强制使⽤ "fmt" 格式'-window_title title' 设置窗⼝标题(默认为输⼊⽂件名)'-loop number' 循环播放 "number" 次(0将⼀直循环)'-showmode mode' 设置显⽰模式可选的 mode :'0, video' 显⽰视频'1, waves' 显⽰⾳频波形'2, rdft' 显⽰⾳频频带默认值为 'video',你可以在播放进⾏时,按 "w" 键在这⼏种模式间切换'-i input_file' 指定输⼊⽂件2. ⼀些⾼级选项'-sync type' 设置主时钟为⾳频、视频、或者外部。

默认为⾳频。

主时钟⽤来进⾏⾳视频同步'-threads count' 设置线程个数'-autoexit' 播放完成后⾃动退出'-exitonkeydown' 任意键按下时退出'-exitonmousedown' 任意⿏标按键按下时退出'-acodec codec_name' 强制指定⾳频解码器为 "codec_name"'-vcodec codec_name' 强制指定视频解码器为 "codec_name"'-scodec codec_name' 强制指定字幕解码器为 "codec_name"3. ⼀些快捷键'q, ESC' 退出'f' 全屏'p, SPC' 暂停'w' 切换显⽰模式(视频/⾳频波形/⾳频频带)'s' 步进到下⼀帧'left/right' 快退/快进 10 秒'down/up' 快退/快进 1 分钟'page down/page up' 跳转到前⼀章/下⼀章(如果没有章节,快退/快进 10 分钟)'mouse click' 跳转到⿏标点击的位置(根据⿏标在显⽰窗⼝点击的位置计算百分⽐)三、ffplay 播放⾳频播放⾳频⽂件的命令:ffplay shy.mp3这时候就会弹出来⼀个窗⼝,⼀边播放MP3⽂件,⼀边将播放⾳频的图画到该窗⼝上。

FFmpeg学习3:播放音频

FFmpeg学习3:播放音频

FFmpeg学习3:播放⾳频参考,本⽂将介绍如何使⽤FFmpeg解码⾳频数据,并使⽤SDL将解码后的数据输出。

本⽂主要包含以下⼏⽅⾯的内容:关于播放⾳频的需要的⼀些基础知识介绍使⽤SDL2播放⾳频数据队列⾳频格式的转换确实⼊门FFmpeg⽐较好的教程,虽然作者在2015年的时候根据新版本的FFmpeg更新了,但是其中还是有不少API过时了。

特别是,教程中使⽤的是SDL1.0,和现在的SDL2的API也有很⼤的不同,并且不能兼容。

1. 关于⾳频的⼀些基础知识和视频⼀样,⾳频数据也会被打包到⼀个容器中,其⽂件的扩展名并不是其编码的⽅法,只是其打包⽂件的格式。

现实世界中,我们所听到的声⾳是⼀个个连续的波形,但是计算机⽆法存储和处理这种拥有⽆限点的波形数据。

所以通过重采样,按照⼀定的频率(1秒采集多少个点),将有⽆限个点的连续波形数据转换为有有限个点的离散数据,这就是通常说的A/D转换(模数转换,将模拟数据转换为数字数据)。

通过上⾯转换过程的描述可以知道,⼀个数字⾳频通常由以下三个部分组成:采样频率采样是在拥有⽆限个点的连续波形上选取有限个离散点,采集到的离散点越多,也就越能真实的波形。

由于声⾳是在时间上的连续波形,其采样点的间隔就是两次采样的时间间隔。

通俗来说,采样率指的是每秒钟采样的点数,单位为HZ。

采样率的倒数是采样周期,也就是两次采样的时间间隔。

采样率越⼤,则采集到的样本点数就越多,采样得到的数字⾳频也就更接近真实的声⾳波形,相应的其占⽤的存储空间也就越⼤。

常⽤的采样频率有:22k Hz ⽆限⼴播所⽤的采样率44.1k Hz CD⾳质48k Hz 数字电视,DVD96k Hz 192k Hz 蓝光盘,⾼清DVD采样精度采集到的点被称为样本(sample),每个样本占⽤的位数就是采样精度。

这点和图像的像素表⽰⽐较类似,可以使⽤8bit,16bit或者24bit来表⽰采集到的⼀个样本。

同样,⼀个样本占⽤的空间越⼤其表⽰的就越接近真实的声⾳。

ffmpeg 浮点运算

ffmpeg 浮点运算

ffmpeg 浮点运算英文回答:FFmpeg is a powerful multimedia framework that allows users to perform various operations on audio and videofiles. One of the key features of FFmpeg is its ability to perform floating-point calculations. Floating-point arithmetic is a method of representing and performing calculations with real numbers, including decimal fractions.In FFmpeg, floating-point calculations are commonlyused for tasks such as video scaling, color conversion, and audio processing. For example, when scaling a video, FFmpeg uses floating-point calculations to accurately resize the image and maintain its quality. Similarly, when converting the color space of a video, floating-point calculations are used to accurately map the color values from one colorspace to another.Floating-point calculations in FFmpeg are implementedusing the IEEE 754 standard, which defines the representation and operations of floating-point numbers. This standard ensures that the calculations are performed consistently across different platforms and programming languages.To illustrate the use of floating-point calculations in FFmpeg, let's consider the example of video scaling. Suppose we have a video that needs to be scaled down tohalf its original size. FFmpeg provides a command-line option `-vf scale` to perform this operation. We can specify the desired width and height of the output video using floating-point values. For instance, the following command scales the video to a width of 640 pixels and a height of 360 pixels:ffmpeg -i input.mp4 -vf scale=0.5:0.5 output.mp4。

ffmpeg解码流程

ffmpeg解码流程

ffmpeg解码流程FFmpeg是一款非常强大的开源多媒体框架,它可以将编码后的多媒体数据进行解码,从而进行音频或视频播放。

那么具体来说,FFmpeg的解码流程是怎样的呢?下面我们一步步地来看。

第一步:打开文件首先,我们需要使用FFmpeg提供的avformat_open_input函数打开要解码的文件,这个函数将会返回一个AVFormatContext结构体,它包含了打开文件的数据流,以及其他文件相关的信息。

第二步:查找流接着,我们需要使用FFmpeg提供的avformat_find_stream_info 函数,通过读取文件头信息来查找音频或视频数据流的信息,并且将这些信息存储在AVFormatContext结构体中。

第三步:查找解码器在确定了音频或视频数据流的类型之后,我们就需要确定使用哪个解码器来进行解码了。

FFmpeg提供了avcodec_find_decoder函数来查找可用于解码的解码器。

根据我们得到的音频或视频数据流信息,我们可以得知需要使用的解码器的类型。

第四步:打开解码器一旦我们找到了合适的解码器,我们就需要打开它了。

使用FFmpeg提供的avcodec_open2函数可以打开解码器,并将信息存储在AVCodecContext结构体中。

除此之外,我们还要检查一下是否解码器正常开启了。

第五步:读取数据帧现在我们已经准备好开始解码了。

使用FFmpeg提供的av_read_frame函数读取文件的一个数据帧,并将数据帧中的数据存储在AVPacket结构体中。

注意,一个数据帧可能包含多个音频或视频帧。

第六步:解码数据帧一旦我们获得了数据帧,我们就可以使用我们开启的解码器开始对数据帧进行解码了。

使用FFmpeg提供的avcodec_send_packet函数将之前存储的数据帧发送到解码器中。

之后,使用avcodec_receive_frame函数获取解码器解码后的数据帧。

第七步:处理解码后的数据最后,我们可以将解码后的数据帧用于音频或视频的处理了。

SDL+FFmpeg实现视频简单播放

SDL+FFmpeg实现视频简单播放

实现视频的基本播放1.新建工程,右击——属性:a)C/C++——常规——附加包含目录:引入ffmpeg和SDL的include目录b)C/C++——所有选项——附加包含目录:引入ffmpeg和SDL的include目录c)链接器——常规——附加库目录:引入ffmpeg和SDL的lib目录d)链接器——输入——附加依赖项:avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;postproc.lib;swresample.lib;swscale.lib;SDL.lib;2.新建头文件:stdafx.h包含一些常用的但不经常更改的头文件#pragma once#include<stdio.h>#include<tchar.h>#include<io.h>#include<direct.h>#include<SDKDDKVer.h>extern"C"{#include"libavcodec\avcodec.h"#include"libavformat\avformat.h"#include"libswscale\swscale.h"//新版里的图像转换结构需要引入的头文件#include"SDL.h"#include"SDL_thread.h"};3.新建源文件:main.cppA)包含头文件:#include"stdafx.h"B)创建主程序:int_tmain(int argc, _TCHAR* argv[]) {char filepath[]="D:\\TDDOWNLOAD\\123\\123.mkv";//文件路径//1.初始化av_register_all();//2.打开视频文件AVFormatContext *pFormatCtx;pFormatCtx =avformat_alloc_context();//分配一个AVFormatContext结构,负责申请一个AVFormatContext结构的内存,并进行简单初始化,从中得到我们想要的信息if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){return -1;}//3.获取视频信息if(avformat_find_stream_info(pFormatCtx,NULL)<0){return -1;}//av_dump_format();dump只是个调试函数,输出文件的音、视频流的基本信息,帧率、分辨率、音频采样等等av_dump_format(pFormatCtx, 0, filepath, false);//输出文件信息//4.查找第一个视频流,记录该流的编码序号int i,videoStream=-1;for(i=0;i<pFormatCtx->nb_streams;i++){if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){videoStream=i;break;}}if(videoStream==-1){return -1;}//5.得到视频流上下文编码的指针AVCodecContext *pCodecCtx;pCodecCtx=pFormatCtx->streams[videoStream]->codec;//6.寻找视频流的解码器AVCodec *pCodec;pCodec=avcodec_find_decoder(pCodecCtx->codec_id);if(pCodec==NULL){return -1;}//7.打开解码器if(avcodec_open2(pCodecCtx,pCodec,NULL)<0){return -1;}//8.为解码帧分配内存AVFrame *pFrame,*pFrameYUV;pFrame=av_frame_alloc();pFrameYUV=av_frame_alloc();//9.根据像素格式和分辨率获得图片所需空间大小uint8_t *out_buffer;out_buffer=newuint8_t[avpicture_get_size(PIX_FMT_YUV420P,pCodecCtx->width,pCodecCtx->height)];//为已经分配的空间的结构体AVPicture挂上一段用于保存数据的空间avpicture_fill((AVPicture*)pFrameYUV, out_buffer, PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);/*****************事件处理************************/bool quit=false;//确保程序一直等待quit,用它来跟踪用户是否想要退出程序SDL_Event event;//将要用到的事件结构体//10.启动SDLif(SDL_Init(SDL_INIT_EVERYTHING)){return -1;}//11.建立一个指定高度和宽度的窗口SDL_Surface *screen;screen=SDL_SetVideoMode(pCodecCtx->width,pCodecCtx->height,0,0);if(!screen){return -1;}//12.在屏幕上创建一个YUV覆盖,以便于我们输入视频上去SDL_Overlay *bmp;bmp=SDL_CreateYUVOverlay(pCodecCtx->width,pCodecCtx->height,SDL_YV12_OVERLAY,screen);//13.定义数据包int y_size=pCodecCtx->width *pCodecCtx->height;AVPacket *packet=(AVPacket *)av_malloc(sizeof(AVPacket));av_new_packet(packet,y_size);//创建指定大小的数据包用来作为缓冲区//14.根据编码信息设置渲染格式struct SwsContext *img_convert_ctx;img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);//15.不停地从码流中提取出帧数据,存放到数据包中,并且判断用户是否要退出!int ret,got_picture;while(av_read_frame(pFormatCtx,packet)>=0 && quit==false){//16.判断帧的类型,对于视频帧进行解码if(packet->stream_index==videoStream){ret=avcodec_decode_video2(pCodecCtx,pFrame,&got_picture,packet);if(ret<0){return -1;//解码失败}if(got_picture){//解码成功,获得图片,并输出到SDL窗口sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);SDL_LockYUVOverlay(bmp);bmp->pixels[0]=pFrameYUV->data[0];bmp->pixels[2]=pFrameYUV->data[1];bmp->pixels[1]=pFrameYUV->data[2];bmp->pitches[0]=pFrameYUV->linesize[0];bmp->pitches[2]=pFrameYUV->linesize[1];bmp->pitches[1]=pFrameYUV->linesize[2];SDL_UnlockYUVOverlay(bmp);SDL_Rect rect;//用于确定SDL_Overlay显示的位置。

ffmpeg的tutorial中文版

ffmpeg的tutorial中文版
这个函数只是检测了文件的头部,所以接着我们需要检查在文件中的流的信息:
// Retrieve stream information if(av_find_stream_info(pFormatCtx)<0)
return -1; // Couldn't find stream information
这个函数为 pFormatCtx->streams 填充上正确的信息。我们引进一个手工调试的 函数来看一下里面有什么:
} if(videoStream==-1)
return -1; // Didn't find a video stream
// Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec;
fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if(avcodec_open(pCodecCtx, pCodec)<0) return -1; // Could not open codec
// Is this a packet from the video stream? if(packet.stream_index==videoStreபைடு நூலகம்m) {
唯一的问题是它的文档基本上是没有的。有一个单独的指导讲了它的基本原理另 外还有一个使用 doxygen 生成的文档。这就是为什么当我决定研究 FFMPEG 来弄 清楚音视频应用程序是如何工作的过程中,我决定把这个过程用文档的形式记录 并且发布出来作为初学指导的原因。

FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

FFmpeg开发笔记(九):ffmpeg解码rtsp流并使用SDL同步播放

FFmpeg开发笔记(九):ffmpeg解码rtsp流并使⽤SDL同步播放前⾔ ffmpeg播放rtsp⽹络流和摄像头流。

Demo 使⽤ffmpeg播放局域⽹rtsp1080p海康摄像头:延迟0.2s,存在马赛克 使⽤ffmpeg播放⽹络rtsp⽂件流:偶尔卡顿,延迟看不出 使⽤vlc软件播放局域⽹rtsp1080p海康摄像头:演⽰2s,不存在马赛克 使⽤vlc软件播放⽹络rtsp⽂件流:不卡顿,延迟看不出FFmpeg基本播放流程ffmpeg解码流程 ffmpeg新增API的解码执⾏流程。

新api解码基本流程如下:步骤⼀:注册: 使⽤ffmpeg对应的库,都需要进⾏注册,可以注册⼦项也可以注册全部。

步骤⼆:打开⽂件: 打开⽂件,根据⽂件名信息获取对应的ffmpeg全局上下⽂。

步骤三:探测流信息: ⼀定要探测流信息,拿到流编码的编码格式,不探测流信息则其流编码器拿到的编码类型可能为空,后续进⾏数据转换的时候就⽆法知晓原始格式,导致错误。

步骤四:查找对应的解码器 依据流的格式查找解码器,软解码还是硬解码是在此处决定的,但是特别注意是否⽀持硬件,需要⾃⼰查找本地的硬件解码器对应的标识,并查询其是否⽀持。

普遍操作是,枚举⽀持⽂件后缀解码的所有解码器进⾏查找,查找到了就是可以硬解了(此处,不做过多的讨论,对应硬解码后续会有⽂章进⾏进⼀步研究)。

(注意:解码时查找解码器,编码时查找编码器,两者函数不同,不要弄错了,否则后续能打开但是数据是错的)步骤五:打开解码器 开打解码器的时候,播放的是rtsp流,需要设置⼀些参数,在ffmpeg中参数的设置是通过AVDictionary来设置的。

使⽤以上设置的参数,传⼊并打开获取到的解码器。

AVDictionary *pAVDictionary = 0// 设置缓存⼤⼩ 1024000byteav_dict_set(&pAVDictionary, "buffer_size", "1024000", 0);// 设置超时时间 20sav_dict_set(&pAVDictionary, "stimeout", "20000000", 0);// 设置最⼤延时 3sav_dict_set(&pAVDictionary, "max_delay", "30000000", 0);// 设置打开⽅式 tcp/udpav_dict_set(&pAVDictionary, "rtsp_transport", "tcp", 0);ret = avcodec_open2(pAVCodecContext, pAVCodec, &pAVDictionary);if(ret){LOG << "Failed to avcodec_open2(pAVCodecContext, pAVCodec, pAVDictionary)";return;}步骤六:申请缩放数据格式转换结构体 此处特别注意,基本上解码的数据都是yuv系列格式,但是我们显⽰的数据是rgb等相关颜⾊空间的数据,所以此处转换结构体就是进⾏转换前到转换后的描述,给后续转换函数提供转码依据,是很关键并且⾮常常⽤的结构体。

一种多格式视频插件在图书馆的应用

一种多格式视频插件在图书馆的应用

一种多格式视频插件在图书馆的应用【摘要】针对我校图书馆在利用发布视频资源中遇到的实际问题,提出一种多格式视频插件。

该插件利用ActiveX技术开发,使用了FFMPEG和SDL多媒体库函数,实现对各种格式的视频文件解码和播放,并能嵌入到网页浏览器中使用。

将此插件应用于图书馆的视频点播系统中,可简化视频发布系统的工作,解决了工作中的实际问题。

【关键词】视频插件;格式通用;图书馆The Application of A Multi-format Video Plug-in in the Library【Abstract】For my school library practical problems encountered in the use of Video resources,proposed a multi-format video plug-in.The plug-in using ActiveX technology for development,using FFMPEG and SDL multimedia library functions , to achieve a variety of formats for decoding and playback of video files,and can be embedded into web browser to ing this plug-in in the VOD system of library,can simplify the work of video distribution system,and solve the practical problem in work.【Key words】Video plug-in;General format;Library我校图书馆近年来采购和收集了大量的教学、培训、学习等视频资料,为方便读者使用并保护这些资源的版权,准备通过视频点播网站向读者提供这些视频资源,供读者在线观看。

嵌入式Linux系统中图片解码和显示的跨平台支持技术

嵌入式Linux系统中图片解码和显示的跨平台支持技术

嵌入式Linux系统中图片解码和显示的跨平台支持技术在嵌入式Linux系统中,图片解码和显示的跨平台支持技术是一个关键的问题。

由于嵌入式系统的资源有限,加上不同硬件平台和操作系统的差异,如何有效地实现图片的解码和显示成为了一个挑战。

本文将介绍几种常用的跨平台支持技术,包括FFmpeg、GStreamer和SDL。

一、FFmpegFFmpeg是一个开源的跨平台多媒体解决方案,它包含了一系列处理音视频的库和工具。

对于图片解码和显示,FFmpeg提供了libavcodec和libavformat两个库来支持各种格式的图片解码和编码。

同时,FFmpeg还提供了libswscale库用于图片的缩放和转换,以及libavutil库用于处理音视频数据。

在嵌入式Linux系统中,我们可以使用FFmpeg的库函数来实现图片的解码和显示。

首先,我们需要使用libavformat库中的avformat_open_input函数打开图片文件,并通过avformat_find_stream_info函数获取图像流信息。

然后,我们可以使用libavcodec库中的avcodec_find_decoder函数查找合适的解码器,并通过avcodec_open2函数打开解码器。

接下来,我们可以使用avcodec_decode_video2函数对图像进行解码,并将解码得到的图像数据进行显示。

最后,我们需要释放相关资源,如关闭文件和释放内存。

二、GStreamerGStreamer是一个开源的多媒体框架,可以用于构建多媒体应用程序。

它提供了丰富的插件和管道,可以支持多种媒体格式的解码和编码。

对于图片解码和显示,GStreamer提供了gst-launch-1.0命令行工具和GstPlayer库来实现。

在嵌入式Linux系统中,我们可以使用GstPlayer库中的功能来实现图片的解码和显示。

首先,我们可以使用gst_player_new函数创建一个GstPlayer对象,并使用gst_player_set_uri函数设置要播放的图片文件的路径。

ffmpeg常用命令汇总

ffmpeg常用命令汇总

ffmpeg常用命令汇总FFmpeg是一个开源的音视频处理工具,可以用于转码、剪辑、合并、提取音视频等操作。

下面是一些常用的FFmpeg命令汇总:1.转码命令:- 将视频转为H.264编码:ffmpeg -i input.mp4 -c:v libx264 output.mp4- 将视频转为H.265编码:ffmpeg -i input.mp4 -c:v libx265 output.mp4- 将视频转为VP9编码:ffmpeg -i input.mp4 -c:v libvpx-vp9 output.webm- 将视频转为AV1编码:ffmpeg -i input.mp4 -c:v libaom-av1 output.mp4- 将视频转为MPEG-4编码:ffmpeg -i input.mp4 -c:v mpeg4 output.mp4- 将视频转为VP8编码:ffmpeg -i input.mp4 -c:v libvpx output.webm- 将音频转为AAC编码:ffmpeg -i input.mp3 -c:a aacoutput.aac- 将音频转为MP3编码:ffmpeg -i input.wav -c:a libmp3lame output.mp32.合并命令:- 合并视频和音频文件:ffmpeg -i input.mp4 -i input.mp3 -c:v copy -c:a copy output.mp43.剪辑命令:- 剪辑视频片段:ffmpeg -ss 00:00:10 -i input.mp4 -t 00:00:20 -c:v copy -c:a copy output.mp4- 剪辑音频片段:ffmpeg -ss 00:00:10 -i input.mp3 -t 00:00:20 -c:a copy output.mp34.提取命令:- 提取视频的音频:ffmpeg -i input.mp4 -vn -c:a copyoutput.mp3- 提取视频的帧图片:ffmpeg -i input.mp4 -vf"select='eq(n,100)'" -vframes 1 output.jpg- 提取视频的音频和视频:ffmpeg -i input.mp4 -vn -c:a copy audio.mp3 -an -c:v copy video.mp45.调整命令:- 调整视频的分辨率:ffmpeg -i input.mp4 -vf "scale=640:480" output.mp4- 调整视频的帧率:ffmpeg -i input.mp4 -r 30 output.mp4- 调整视频的码率:ffmpeg -i input.mp4 -b:v 1M output.mp4- 调整音频的码率:ffmpeg -i input.mp3 -b:a 128k output.mp3 6.其他命令:- 视频截图:ffmpeg -i input.mp4 -vframes 1 output.jpg- 视频转GIF动图:ffmpeg -i input.mp4 -vf"fps=10,scale=320:-1:flags=lanczos" output.gif以上是一些常用的FFmpeg命令汇总,可以根据需求进行使用。

关于视频流化

关于视频流化

关于视频流化
⾸先使⽤师兄给的mp4creator,命令没错,mp4creator -hint=1 file.mp4,先给视频流化,但是报错MP4ERROR:
MP4File::FindIntegerProperty: no such property - moov.trak[2].mdia.min
f.stbl.stsd.*.esds.decConfigDescr.objectTypeId,多⽅查找⽆解
换个mp4box(搭建DSS的教程中提过),流化Dance.mp4,成功。

在DSS上⽤vlc点播成功。

现在考虑切换到linux下⽤实验室服务器(9036209增加到9392313)
转换到linux下⾯,⽤实验室的服务器和电影播放机来点播,成功
后来和庄⽼师借了真机,能播放(⼩插曲:⽆线路由没有连接到交换机上,所以最开始都没有得到ip地址)
流化之后的mp4 ,仍然可以⽤我之前写的东东去解码关键帧~
和庄⽼师聊完,考虑程序移植,哇塞,ffmpeg+sdl移植到⼿机平台,多么壮观。

Android使用FFmpeg--ffmpeg实现视频播放

Android使用FFmpeg--ffmpeg实现视频播放

Android使用FFmpeg--ffmpeg实现视频播放关于前言如果你已经准备好ffmpeg的开发环境,那么我们在这篇文章中实现对视频的一个播放,如果还没有准备好,请看前面的内容。

正文视频播放大概流程图.pngOk,上图就是使用ffmpeg实现了一个视频的播放的大概流程图,那么,我们将根据流程图来编写代码,这样子,代码的编写就会显得比较简单,比较好理解了。

1.注册各大组件,这一步很重要,如果不注册就无法使用后面的函数了。

av_register_all(;2.在解码之前我们得获取里面的内容吧,所以这一步就是打开地址并且获取里面的内容。

其中avFormatContext是内容的一个上下文,inputPath为输入的地址。

AVFormatContext *avFormatContext = avformat_alloc_context(;//获取上下文 avfor mat_open_input(&avFormatContext, inputPath, NULL, NULL)//解封装 avformat_fin d_stream_info(avFormatContext, NULL)3.我们在上面已经获取了内容,但是在一个音视频中包括了音频流,视频流和字幕流,所以在所有的内容当中,我们应当找出相对应的视频流。

int video_index=-1; for (int i = 0; i < avFormatContext->nb_streams; ++i) { if (a vFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { //如果是视频流,标记一哈 video_index = i; } }4.在第三步的时候已经找到了视频流,那么我们就对视频流进行解码、转换和绘制。

a.如果要进行解码,那么得有解码器并打开解码器。

SDL+FFmpeg实现音频简单播放

SDL+FFmpeg实现音频简单播放

实现音频的基本播放1.新建工程,右击——属性:a)C/C++——常规——附加包含目录:引入ffmpeg和SDL的include目录b)C/C++——所有选项——附加包含目录:引入ffmpeg和SDL的include目录c)链接器——常规——附加库目录:引入ffmpeg和SDL的lib目录d)链接器——输入——附加依赖项:avcodec.lib;avformat.lib;avutil.lib;avdevice.lib;avfilter.lib;postproc.lib;swresample.lib;swscale.lib;SDL.lib;SDLmain.lib;2.新建头文件:stdafx.h包含一些常用的但不经常更改的头文件#pragma once#include<sdkddkver.h>#include<stdlib.h>#include<string.h>#include<stdio.h>#include<tchar.h>extern"C"{#include"libavcodec\avcodec.h"#include"libavformat\avformat.h"#include"libswresample\swresample.h"#include"SDL.h"#include"SDL_thread.h"};3.新建源文件:main.cppA)包含头文件:#include"stdafx.h"B)创建主程序:#define MAX_AUDIO_FRAME_SIZE 192000static Uint8 *audio_chunk;//音频块static Uint32 audio_len;//音频长度static Uint8 *audio_pos;//播放到的位置//回调函数void fill_audio(void *udata,Uint8 *stream,int len){if(audio_len==0)return;//有数据剩余时播放len=(len>audio_len?audio_len:len);//混合尽可能多的数据SDL_MixAudio(stream,audio_pos,len,SDL_MIX_MAXVOLUME);//混合两个音频缓冲,最后一个参数为音量大小,范围从0—SDL_MIX_MAXVOLUMEaudio_pos+=len;audio_len-=len;}int_tmain(int argc,_TCHAR *argv[]){char filepath[]="S.H.E - 梦田.mp3";//文件路径//1.初始化av_register_all();//2.打开文件AVFormatContext *pFormatCtx;pFormatCtx=avformat_alloc_context();if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)){return -1;}//3.获取音频信息并输出if(avformat_find_stream_info(pFormatCtx,NULL)){return -1;}av_dump_format(pFormatCtx,0,filepath,false);//4.查找第一个音频流,并记录该流的编码序号int audioStream=-1;for(int i=0;i<pFormatCtx->nb_streams;i++){if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_AUDIO){audioStream=i;break;}}if(audioStream==-1){return -1;}//5.得到音频流上下文编码的指针AVCodecContext *aCodecCtx;aCodecCtx=pFormatCtx->streams[audioStream]->codec;//6.查找音频流解码器AVCodec *aCodec;aCodec=avcodec_find_decoder(aCodecCtx->codec_id);if(aCodec==NULL){return -1;}//7.打开解码器if(avcodec_open2(aCodecCtx,aCodec,NULL)){return -1;}//8.设置输出的音频参数uint64_t out_channel_layout=AV_CH_LAYOUT_STEREO;//设置输出通道,此处为立体声int out_nb_samples=1024;//音频缓存AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16;//采样格式int out_sample_rate=44100;//采样频率int out_channels=av_get_channel_layout_nb_channels(out_channel_layout);//返回通道数intout_buffer_size=av_samples_get_buffer_size(NULL,out_channels,out_nb_samples,out_sample_ fmt,1);//获得给定的音频参数所需的缓冲区大小。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

An ffmpeg and SDL TutorialPage 1 2 3 4 5 6 7 8 End Prev Home NextTutorial 01: Making ScreencapsCode: tutorial01.cOverviewMovie files have a few basic components. First, the file itself is called a container, and the type of container determines where the information in the file goes. Examples of containers are AVI and Quicktime. Next, you have a bunch of streams; for example, you usually have an audio stream and a video stream. (A "stream" is just a fancy word for "a succession of data elements made available over time".) The data elements in a stream are called frames. Each stream is encoded by a different kind of codec. The codec defines how the actual data is COded and DECoded - hence the name CODEC. Examples of codecs are DivX and MP3. Packets are then read from the stream. Packets are pieces of data that can contain bits of data that are decoded into raw frames that we can finally manipulate for our application. For our purposes, each packet contains complete frames, or multiple frames in the case of audio. At its very basic level, dealing with video and audio streams is very easy:10 OPEN video_stream FROM video.avi 20 READ packet FROM video_stream INTO frame 30 IF frame NOT COMPLETE GOTO 20 40 DO SOMETHING WITH frame 50 GOTO 20 Handling multimedia with ffmpeg is pretty much as simple as this program, although some programs might have a very complex "DO SOMETHING" step. So in this tutorial, we're going to open a file, read from the video stream inside it, and our DO SOMETHING is going to be writing the frame to a PPM file.Opening the FileFirst, let's see how we open a file in the first place. With ffmpeg, you have to first initialize the library. (Note that some systems might have to use <ffmpeg/avcodec.h> and <ffmpeg/avformat.h> instead.)#include <avcodec.h> #include <avformat.h> ...int main(int argc, charg *argv[]) { av_register_all(); This registers all available file formats and codecs with the library so they will be used automatically when a file with the corresponding format/codec is opened. Note that you only need to call av_register_all() once, so we do it here in main(). If you like, it's possible to register only certain individual file formats and codecs, but there's usually no reason why you would have to do that. Now we can actually open the file:AVFormatContext *pFormatCtx; // Open video file if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0) return -1; // Couldn't open file We get our filename from the first argument. This function reads the file header and stores information about the file format in the AVFormatContext structure we have given it. The last three arguments are used to specify the file format, buffer size, and format options, but by setting this to NULL or 0, libavformat will auto-detect these. This function only looks at the header, so next we need to check out the stream information in the file.:// Retrieve stream information if(av_find_stream_info(pFormatCtx)<0) return -1; // Couldn't find stream information This function populates pFormatCtx->streams with the proper information. We introduce a handy debugging function to show us what's inside: // Dump information about file onto standard error dump_format(pFormatCtx, 0, argv[1], 0); Now pFormatCtx->streams is just an array of pointers, of sizepFormatCtx->nb_streams, so let's walk through it until we find a video stream.int i; AVCodecContext *pCodecCtx;// Find the first video stream videoStream=-1; for(i=0; i<pFormatCtx->nb_streams; i++) if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO) { videoStream=i; break; }if(videoStream==-1) return -1; // Didn't find a video stream// Get a pointer to the codec context for the video stream pCodecCtx=pFormatCtx->streams[videoStream]->codec; The stream's information about the codec is in what we call the "codec context." This contains all the information about the codec that the stream is using, and now we have a pointer to it. But we still have to find the actual codec and open it: AVCodec *pCodec;// Find the decoder for the video stream pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL) { fprintf(stderr, "Unsupported codec!\n"); return -1; // Codec not found } // Open codec if(avcodec_open(pCodecCtx, pCodec)<0) return -1; // Could not open codec Some of you might remember from the old tutorial that there were two other parts to this code: adding CODEC_FLAG_TRUNCATED to pCodecCtx->flags and adding a hack to correct grossly incorrect frame rates. These two fixes aren't in ffplay.c anymore, so I have to assume that they are not necessary anymore. There's another difference to point out since we removed that code: pCodecCtx->time_base now holds the frame rate information.time_base is a struct that has the numerator and denominator (AVRational). We represent theframe rate as a fraction because many codecs have non-integer frame rates (like NTSC's 29.97fps).Storing the DataNow we need a place to actually store the frame:AVFrame *pFrame; // Allocate video frame pFrame=avcodec_alloc_frame(); Since we're planning to output PPM files, which are stored in 24-bit RGB, we're going to have to convert our frame from its native format to RGB. ffmpeg will do these conversions for us. For most projects (including ours) we're going to want to convert our initial frame to a specific format. Let's allocate a frame for the converted frame now. // Allocate an AVFrame structure pFrameRGB=avcodec_alloc_frame();if(pFrameRGB==NULL) return -1; Even though we've allocated the frame, we still need a place to put the raw data when we convert it. We use avpicture_get_size to get the size we need, and allocate the space manually: uint8_t *buffer; int numBytes; // Determine required buffer size and allocate buffer numBytes=avpicture_get_size(PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); buffer=(uint8_t *)av_malloc(numBytes*sizeof(uint8_t));av_malloc is ffmpeg's malloc that is just a simple wrapper around malloc that makes sure thememory addresses are aligned and such. It will not protect you from memory leaks, double freeing, or other malloc problems. Now we use avpicture_fill to associate the frame with our newly allocated buffer. About the AVPicture cast: the AVPicture struct is a subset of the AVFrame struct - the beginning of the AVFrame struct is identical to the AVPicture struct.// Assign appropriate parts of buffer to image planes in pFrameRGB // Note that pFrameRGB is an AVFrame, but AVFrame is a superset // of AVPicture avpicture_fill((AVPicture *)pFrameRGB, buffer, PIX_FMT_RGB24, pCodecCtx->width, pCodecCtx->height); Finally! Now we're ready to read from the stream!Reading the DataWhat we're going to do is read through the entire video stream by reading in the packet, decoding it into our frame, and once our frame is complete, we will convert and save it.int frameFinished; AVPacket packet;i=0; while(av_read_frame(pFormatCtx, &packet)>=0) { // Is this a packet from the video stream? if(packet.stream_index==videoStream) { // Decode video frame avcodec_decode_video(pCodecCtx, pFrame, &frameFinished, packet.data, packet.size);// Did we get a video frame?if(frameFinished) { // Convert the image from its native format to RGB img_convert((AVPicture *)pFrameRGB, PIX_FMT_RGB24, (AVPicture*)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);// Save the frame to disk if(++i<=5) SaveFrame(pFrameRGB, pCodecCtx->width, pCodecCtx->height, i); } }// Free the packet that was allocated by av_read_frame av_free_packet(&packet); }A note on packetsTechnically a packet can contain partial frames or other bits of data, but ffmpeg's parser ensures that the packets we get contain either complete or multiple frames.The process, again, is simple: av_read_frame() reads in a packet and stores it in theAVPacket struct. Note that we've only allocated the packet structure - ffmpeg allocates the internal data for us, which is pointed to by packet.data. This is freed by the av_free_packet() later. avcodec_decode_video() converts the packet to aframe for us. However, we might not have all the information we need for a frame after decoding a packet, so avcodec_decode_video() sets frameFinished for us when we have the next frame. Finally, we use img_convert() to convert from the native format (pCodecCtx->pix_fmt) to RGB. Remember that you can cast an AVFrame pointer to an AVPicture pointer. Finally, we pass the frame and height and width information to our SaveFrame function. Now all we need to do is make the SaveFrame function to write the RGB information to a file in PPM format. We're going to be kind of sketchy on the PPM format itself; trust us, it works.void SaveFrame(AVFrame *pFrame, int width, int height, int iFrame) { FILE *pFile; char szFilename[32]; int y;// Open filesprintf(szFilename, "frame%d.ppm", iFrame); pFile=fopen(szFilename, "wb"); if(pFile==NULL) return;// Write header fprintf(pFile, "P6\n%d %d\n255\n", width, height);// Write pixel data for(y=0; y<height; y++) fwrite(pFrame->data[0]+y*pFrame->linesize[0], 1, width*3, pFile);// Close file fclose(pFile); } We do a bit of standard file opening, etc., and then write the RGB data. We write the file one line at a time. A PPM file is simply a file that has RGB information laid out in a long string. If you know HTML colors, it would be like laying out the color of each pixel end to end like#ff0000#ff0000.... would be a red screen. (It's stored in binary and without the separator,but you get the idea.) The header indicated how wide and tall the image is, and the max size of the RGB values. Now, going back to our main() function. Once we're done reading from the video stream, we just have to clean everything up:// Free the RGB image av_free(buffer); av_free(pFrameRGB);// Free the YUV frame av_free(pFrame);// Close the codec avcodec_close(pCodecCtx);// Close the video file av_close_input_file(pFormatCtx);return 0; You'll notice we use av_free for the memory we allocated with avcode_alloc_frame and av_malloc. That's it for the code! Now, if you're on Linux or a similar platform, you'll run:gcc -o tutorial01 tutorial01.c -lavutil -lavformat -lavcodec -lz -lavutil -lm If you have an older version of ffmpeg, you may need to drop -lavutil: gcc -o tutorial01 tutorial01.c -lavformat -lavcodec -lz -lm Most image programs should be able to open PPM files. Test it on some movie files.>> Tutorial 2: Outputting to the ScreenTutorial 02: Outputting to the ScreenCode: tutorial02.cSDL and VideoTo draw to the screen, we're going to use SDL. SDL stands for Simple Direct Layer, and is an excellent library for multimedia, is cross-platform, and is used in several projects. You can get the library at the official website or you can download the development package for your operating system if there is one. You'll need the libraries to compile the code for this tutorial (and for the rest of them, too). SDL has many methods for drawing images to the screen, and it has one in particular that is meant for displaying movies on the screen - what it calls a YUV overlay. YUV (technically not YUV but YCbCr) * A note: There is a great deal of annoyance from some people at the convention of calling "YCbCr" "YUV". Generally speaking, YUV is an analog format and YCbCr is a digital format. ffmpeg and SDL both refer to YCbCr as YUV in their code and macros. is a way of storing raw image data like RGB. Roughly speaking, Y is the brightness (or "luma") component, and U and V are the color components. (It's more complicated than RGB because some of the color information is discarded, and you might have only 1 U and V sample for every 2 Y samples.) SDL's YUV overlay takes in a raw array of YUV data and displays it. It accepts 4 different kinds of YUV formats, but YV12 is the fastest. There is another YUV format called YUV420P that is the same as YV12, except the U and V arrays are switched. The 420 means it is subsampled at a ratio of 4:2:0, basically meaning there is 1 color sample for every 4 luma samples, so the color information is quartered. This is a good way of saving bandwidth, as the human eye does not percieve this change. The "P" in the name means that the format is "planar" — simply meaning that the Y, U, and V components are in separate arrays. ffmpeg can convert images to YUV420P, with the added bonus that many video streams are in that format already, or are easily converted to that format. So our current plan is to replace the SaveFrame() function from Tutorial 1, and instead output our frame to the screen. But first we have to start by seeing how to use the SDL Library. First we have to include the libraries and initalize SDL:#include <SDL.h>#include <SDL_thread.h> if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); exit(1); }SDL_Init() essentially tells the library what features we're going to use. SDL_GetError(), of course, is a handy debugging function.Creating a DisplayNow we need a place on the screen to put stuff. The basic area for displaying images with SDL is called a surface:SDL_Surface *screen;screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0); if(!screen) { fprintf(stderr, "SDL: could not set video mode - exiting\n"); exit(1); } This sets up a screen with the given width and height. The next option is the bit depth of the screen - 0 is a special value that means "same as the current display". (This does not work on OS X; see source.) Now we create a YUV overlay on that screen so we can input video to it:SDL_Overlay*bmp;bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen); As we said before, we are using YV12 to display the image.Displaying the ImageWell that was simple enough! Now we just need to display the image. Let's go all the way down to where we had our finished frame. We can get rid of all that stuff we had for the RGB frame, and we're going to replace the SaveFrame() with our display code. To display the image, we're going to make an AVPicture struct and set its data pointers and linesize to our YUV overlay:if(frameFinished) { SDL_LockYUVOverlay(bmp);AVPicture pict; pict.data[0] = bmp->pixels[0]; pict.data[1] = bmp->pixels[2]; pict.data[2] = bmp->pixels[1];pict.linesize[0] = bmp->pitches[0]; pict.linesize[1] = bmp->pitches[2]; pict.linesize[2] = bmp->pitches[1];// Convert the image into YUV format that SDL uses img_convert(&pict, PIX_FMT_YUV420P, (AVPicture *)pFrame, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);SDL_UnlockYUVOverlay(bmp); } First, we lock the overlay because we are going to be writing to it. This is a good habit to get into so you don't have problems later. The AVPicture struct, as shown before, has a data pointer that is an array of 4 pointers. Since we are dealing with YUV420P here, we only have 3 channels, and therefore only 3 sets of data. Other formats might have a fourth pointer for an alpha channel or something. linesize is what it sounds like. The analogous structures in our YUV overlay are the pixels and pitches variables. ("pitches" is the term SDL uses to refer to the width of a given line of data.) So what we do is point the three arrays of pict.data at our overlay, so when we write to pict, we're actually writing into our overlay, which of course already has the necessary space allocated. Similarly, we get the linesize information directly from our overlay. We change the conversion format to PIX_FMT_YUV420P, and we use img_convert just like before.Drawing the ImageBut we still need to tell SDL to actually show the data we've given it. We also pass this function a rectangle that says where the movie should go and what width and height it should be scaled to. This way, SDL does the scaling for us, and it can be assisted by your graphics processor for faster scaling:SDL_Rect rect;if(frameFinished) { /* ... code ... */ // Convert the image into YUV format that SDL uses img_convert(&pict, PIX_FMT_YUV420P, (AVPicture *)pFrame, pCodecCtx->pix_fmt,pCodecCtx->width, pCodecCtx->height); SDL_UnlockYUVOverlay(bmp); rect.x = 0; rect.y = 0; rect.w = pCodecCtx->width; rect.h = pCodecCtx->height; SDL_DisplayYUVOverlay(bmp, &rect); } Now our video is displayed! Let's take this time to show you another feature of SDL: its event system. SDL is set up so that when you type, or move the mouse in the SDL application, or send it a signal, it generates an event. Your program then checks for these events if it wants to handle user input. Your program can also make up events to send the SDL event system. This is especially useful when multithread programming with SDL, which we'll see in Tutorial 4. In our program, we're going to poll for events right after we finish processing a packet. For now, we're just going to handle the SDL_QUIT event so we can exit:SDL_Eventevent;av_free_packet(&packet); SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: SDL_Quit(); exit(0); break; default: break; } And there we go! Get rid of all the old cruft, and you're ready to compile. If you are using Linux or a variant, the best way to compile using the SDL libs is this: gcc -o tutorial02 tutorial02.c -lavutil -lavformat -lavcodec -lz -lm \ `sdl-config --cflags --libs` sdl-config just prints out the proper flags for gcc to include the SDL libraries properly. You may need to do something different to get it to compile on your system; please check the SDL documentation for your system. Once it compiles, go ahead and run it. What happens when you run this program? The video is going crazy! In fact, we're just displaying all the video frames as fast as we can extract them from the movie file. We don't have any coderight now for figuring out when we need to display video. Eventually (in Tutorial 5), we'll get around to syncing the video. But first we're missing something even more important: sound!>> Playing SoundTutorial 03: Playing SoundCode: tutorial03.cAudioSo now we want to play sound. SDL also gives us methods for outputting sound. TheSDL_OpenAudio() function is used to open the audio device itself. It takes as arguments an SDL_AudioSpec struct, which contains all the information about the audio we are going tooutput. Before we show how you set this up, let's explain first about how audio is handled by computers. Digital audio consists of a long stream of samples. Each sample represents a value of the audio waveform. Sounds are recorded at a certain sample rate, which simply says how fast to play each sample, and is measured in number of samples per second. Example sample rates are 22,050 and 44,100 samples per second, which are the rates used for radio and CD respectively. In addition, most audio can have more than one channel for stereo or surround, so for example, if the sample is in stereo, the samples will come 2 at a time. When we get data from a movie file, we don't know how many samples we will get, but ffmpeg will not give us partial samples - that also means that it will not split a stereo sample up, either. SDL's method for playing audio is this: you set up your audio options: the sample rate (called "freq" for frequency in the SDL struct), number of channels, and so forth, and we also set a callback function and userdata. When we begin playing audio, SDL will continually call this callback function and ask it to fill the audio buffer with a certain number of bytes. After we put this information in theSDL_AudioSpec struct, we call SDL_OpenAudio(), which will open the audio deviceand give us back another AudioSpec struct. These are the specs we will actually be using — we are not guaranteed to get what we asked for!Setting Up the AudioKeep that all in your head for the moment, because we don't actually have any information yet about the audio streams yet! Let's go back to the place in our code where we found the video stream and find which stream is the audio stream.// Find the first video stream videoStream=-1; audioStream=-1; for(i=0; i < pFormatCtx->nb_streams; i++) {if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO && videoStream < 0) { videoStream=i; } if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO && audioStream < 0) { audioStream=i; } } if(videoStream==-1) return -1; // Didn't find a video stream if(audioStream==-1) return -1; From here we can get all the info we want from the AVCodecContext from the stream, just like we did with the video stream: AVCodecContext *aCodecCtx;aCodecCtx=pFormatCtx->streams[audioStream]->codec;Contained within this codec context is all the information we need to set up our audio:wanted_spec.freq = aCodecCtx->sample_rate; wanted_spec.format = AUDIO_S16SYS; wanted_spec.channels = aCodecCtx->channels; wanted_spec.silence = 0; wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE; wanted_spec.callback = audio_callback; wanted_erdata = aCodecCtx;if(SDL_OpenAudio(&wanted_spec, &spec) < 0) { fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError()); return -1; } Let's go through these:• •freq: The sample rate, as explained earlier. format: This tells SDL what format we will be giving it. The "S" in "S16SYS" stands for"signed", the 16 says that each sample is 16 bits long, and "SYS" means that the endian-order will depend on the system you are on. This is the format that•avcodec_decode_audio2 will give us the audio in. channels: Number of audio channels.• • • •silence: This is the value that indicated silence. Since the audio is signed, 0 is ofcourse the usual value.samples: This is the size of the audio buffer that we would like SDL to give us when itasks for more audio. A good value here is between 512 and 8192; ffplay uses 1024.callback: Here's where we pass the actual callback function. We'll talk more about thecallback function later.userdata: SDL will give our callback a void pointer to any user data that we want ourcallback function to have. We want to let it know about our codec context; you'll see why.Finally, we open the audio with SDL_OpenAudio. If you remember from the previous tutorials, we still need to open the audio codec itself. This is straightforward:AVCodec*aCodec;aCodec = avcodec_find_decoder(aCodecCtx->codec_id); if(!aCodec) { fprintf(stderr, "Unsupported codec!\n"); return -1; } avcodec_open(aCodecCtx, aCodec);QueuesThere! Now we're ready to start pulling audio information from the stream. But what do we do with that information? We are going to be continuously getting packets from the movie file, but at the same time SDL is going to call the callback function! The solution is going to be to create some kind of global structure that we can stuff audio packets in so our audio_callback has something to get audio data from! So what we're going to do is to create a queue of packets. ffmpeg even comes with a structure to help us with this: AVPacketList, which is just a linked list for packets. Here's our queue structure:typedef struct PacketQueue { AVPacketList *first_pkt, *last_pkt; int nb_packets; int size; SDL_mutex *mutex; SDL_cond *cond; } PacketQueue; First, we should point out that nb_packets is not the same as size — size refers to a byte size that we get from packet->size. You'll notice that we have a mutex and a condtion variable in there. This is because SDL is running the audio process as a separate thread. If we don'tlock the queue properly, we could really mess up our data. We'll see how in the implementation of the queue. Every programmer should know how to make a queue, but we're including this so you can learn the SDL functions. First we make a function to initialize the queue:void packet_queue_init(PacketQueue *q) { memset(q, 0, sizeof(PacketQueue)); q->mutex = SDL_CreateMutex(); q->cond = SDL_CreateCond(); } Then we will make a function to put stuff in our queue: int packet_queue_put(PacketQueue *q, AVPacket *pkt) { AVPacketList *pkt1; if(av_dup_packet(pkt) < 0) { return -1; } pkt1 = av_malloc(sizeof(AVPacketList)); if (!pkt1) return -1; pkt1->pkt = *pkt; pkt1->next = NULL;SDL_LockMutex(q->mutex);if (!q->last_pkt) q->first_pkt = pkt1; else q->last_pkt->next = pkt1; q->last_pkt = pkt1; q->nb_packets++; q->size += pkt1->pkt.size; SDL_CondSignal(q->cond);SDL_UnlockMutex(q->mutex); return 0; }SDL_LockMutex() locks the mutex in the queue so we can add something to it, and then SDL_CondSignal() sends a signal to our get function (if it is waiting) through our conditionvariable to tell it that there is data and it can proceed, then unlocks the mutex to let it go.Here's the corresponding get function. Notice how SDL_CondWait() makes the function block (i.e. pause until we get data) if we tell it to.int quit = 0; static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block) { AVPacketList *pkt1; int ret;SDL_LockMutex(q->mutex);for(;;) {if(quit) { ret = -1; break; }pkt1 = q->first_pkt; if (pkt1) { q->first_pkt = pkt1->next; if (!q->first_pkt) q->last_pkt = NULL; q->nb_packets--; q->size -= pkt1->pkt.size; *pkt = pkt1->pkt; av_free(pkt1); ret = 1; break; } else if (!block) { ret = 0; break; } else { SDL_CondWait(q->cond, q->mutex); } } SDL_UnlockMutex(q->mutex); return ret; } As you can see, we've wrapped the function in a forever loop so we will be sure to get some data if we want to block. We avoid looping forever by making use of SDL's SDL_CondWait() function. Basically, all CondWait does is wait for a signal from SDL_CondSignal() (orSDL_CondBroadcast()) and then continue. However, it looks as though we've trapped itwithin our mutex — if we hold the lock, our put function can't put anything in the queue! However,what SDL_CondWait() also does for us is to unlock the mutex we give it and then attempt to lock it again once we get the signal.In Case of FireYou'll also notice that we have a global quit variable that we check to make sure that we haven't set the program a quit signal (SDL automatically handles TERM signals and the like). Otherwise, the thread will continue forever and we'll have to kill-9 the program. ffmpeg also has afunction for providing a callback to check and see if we need to quit some blocking function:url_set_interrupt_cb.int decode_interrupt_cb(void) { return quit; } ... main() { ... url_set_interrupt_cb(decode_interrupt_cb); ... SDL_PollEvent(&event); switch(event.type) { case SDL_QUIT: quit = 1; ... This only applies for ffmpeg functions that block, of course, not SDL ones. We make sure to set thequit flag to 1.Feeding PacketsThe only thing left is to set up our queue:PacketQueue audioq; main() { ... avcodec_open(aCodecCtx, aCodec);packet_queue_init(&audioq); SDL_PauseAudio(0);SDL_PauseAudio() finally starts the audio device. It plays silence if it doesn't get data;which it won't right away. So, we've got our queue set up, now we're ready to start feeding it packets. We go to our packet-reading loop:。

相关文档
最新文档