FFmpeg的tutorial 学习
一、前言:
这是一个学习 FFmpeg 的 tutorial 系列。
这个是一个对初学者比较友好的FFmpeg学习教程,作者一步步引导我们实现了一个音视频同步的播放器。
参考链接:
- 原文地址:
http://dranger.com/ffmpeg/ - 比较新的代码Github地址:
https://github.com/mpenkov/ffmpeg-tutorial - 中文博客参考地址:
http://blog.csdn.net/liujiakunit/article/details/46899229
建议可以打开代码+中文学习博客学习,我看了01~04篇的代码,然后就直接跳到了07的代码,结合中文博客+代码可以看懂。
#
二、源码分析
tutorial01.c 代码分析
这个代码很简单,就是解码一个视频文件,然后把前五帧视频保存为五个后缀格式 .ppm 图片视频
tutorial02.c 代码分析
这个代码是把读取视频文件,然后每解码一帧,马上就用SDL显示出来。
tutorial03.c 代码分析
跟02代码相比,这个添加了播放音频的部分。
FFmpeg负责把音频的从AVPacket解码为AVFrame得到原始数据,
然后SDL通过回调函数,有节奏的取出原始音频数据播放(这个是在一个独立的音频播放线程应该)
tutorial04.c 代码分析
tutorial07.c 代码分析
2.1 整体流程分析:
2.1 整体流程分析:
逻辑还是蛮复杂的 可以这样分析:
总共就两条主线音视频:
- 音频:SDL 通过这个audio_callback(void userdata, Uint8 stream, int len)不断回调获取音频数据,我们就从这里获取音频的AVPacket 并decode到一个audio buffer中,然后SDL中这个audio buffer取出数据播放
decode_thread :
死循环,不断读取AVPacket ,
并保存到 音频AVPacket队列, 视频AVPacket队列
- 视频:和音频不同,需要一个独立的video_thread 线程, ->在主线程 有一个死循环不断从VideoPicture 数据里面取出数据,然后通过SDL显示死循环不断的从视频的AVPacket取出数据 decode 获取AVFrame ,然后把AVFrame 保存到 一个 全局的 VideoPicture 数组里面
2.2 音视频同步细节详解
音视频同步方式有三种:
- 以音频播放为时钟轴,视频播放参考音频当前播放时间,调整下一帧的播放快慢实现视频同步到音频
参考tutorial05.c
关键点三个:
a.如何正确获取音频已播放时间
音频以一个恒定的速率不断的播放,获取准确的已播放音频播放时间
音频时间轴:0-》1-》2-》3-》4-》5-》6-》7-》8-》9
int audio_decode_frame(VideoState *is, double *pts_ptr) {//省略代码//方法1:通过每一次decode获得的音频数据的字节数除以每一秒音频数据的字节数获得这一次decode的音频时长//累加这个时长,得出decode的音频最新时间is->audio_clock += (double)data_size /(double)(n * is->audio_st->codec->sample_rate);//方法2:如果AVPacket的pts有值,则可以通过pts计算 is->audio_clockif(pkt->pts != AV_NOPTS_VALUE) {is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;}void audio_callback(void *userdata, Uint8 *stream, int len) {VideoState *is = (VideoState *)userdata;int len1, audio_size;double pts;while(len > 0) {if(is->audio_buf_index >= is->audio_buf_size) {/* We have already sent all our data; get more */audio_size = audio_decode_frame(is, &pts);if(audio_size < 0) {/* If error, output silence */is->audio_buf_size = 1024;memset(is->audio_buf, 0, is->audio_buf_size);} else {is->audio_buf_size = audio_size;}is->audio_buf_index = 0;}len1 = is->audio_buf_size - is->audio_buf_index;if(len1 > len)len1 = len;memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);len -= len1;stream += len1;is->audio_buf_index += len1;}}double get_audio_clock(VideoState *is) {double pts;int hw_buf_size, bytes_per_sec, n;//关键语句 is->audio_clock 代表已经解码的音频的数据最新时间pts = is->audio_clock; /* maintained in the audio thread */hw_buf_size = is->audio_buf_size - is->audio_buf_index;bytes_per_sec = 0;n = is->audio_st->codec->channels * 2;if(is->audio_st) {bytes_per_sec = is->audio_st->codec->sample_rate * n;}if(bytes_per_sec) {//关键语句,音频是解码到了一个buffer里面,其中一部分被SDL取走播放,还有一部分在缓冲区//已经解码的音频的数据最新时间 不能代表音频已经播放的时间,因为还有一部分解码的音频数据没有被播放//所以需要减去没有播放的音频的时间,得出准确的音频已经播放时间pts -= (double)hw_buf_size / bytes_per_sec;}return pts;}
b.如何正确设置每一帧视频的解码时间pts
c.如何调整视频播放速度快慢实现和音频的播放速度同步
以视频播放为时钟,音频播放参考视频播放时钟,调整下一帧的播放快慢实现视频同步到音频
以系统时间为时钟,音频视频同步到系统时钟
2.2.1
(未完待续~)
FFmpeg的tutorial 学习的更多相关文章
- Python Tutorial 学习(八)--Errors and Exceptions
Python Tutorial 学习(八)--Errors and Exceptions恢复 Errors and Exceptions 错误与异常 此前,我们还没有开始着眼于错误信息.不过如果你是一 ...
- FFmpeg 结构体学习(二): AVStream 分析
在上文FFmpeg 结构体学习(一): AVFormatContext 分析我们学习了AVFormatContext结构体的相关内容.本文,我们将讲述一下AVStream. AVStream是存储每一 ...
- FFmpeg 结构体学习(三): AVPacket 分析
在上文FFmpeg 结构体学习(二): AVStream 分析我们学习了AVStream结构体的相关内容.本文,我们将讲述一下AVPacket. AVPacket是存储压缩编码数据相关信息的结构体.下 ...
- FFmpeg 结构体学习(四): AVFrame 分析
在上文FFmpeg 结构体学习(三): AVPacket 分析我们学习了AVPacket结构体的相关内容.本文,我们将讲述一下AVFrame. AVFrame是包含码流参数较多的结构体.下面我们来分析 ...
- FFmpeg 结构体学习(五): AVCodec 分析
在上文FFmpeg 结构体学习(四): AVFrame 分析我们学习了AVFrame结构体的相关内容.本文,我们将讲述一下AVCodec. AVCodec是存储编解码器信息的结构体.下面我们来分析一下 ...
- FFmpeg 结构体学习(六): AVCodecContext 分析
在上文FFmpeg 结构体学习(五): AVCodec 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVCodecContext. AVCodecContext是包含变量较多的结 ...
- FFmpeg 结构体学习(七): AVIOContext 分析
在上文FFmpeg 结构体学习(六): AVCodecContext 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVIOContext. AVIOContext是FFMPEG管 ...
- Django 1.7 Tutorial 学习笔记
官方教程在这里 : Here 写在前面的废话:)) 以前学习新东西,第一想到的是找本入门教程,按照书上做一遍.现在看了各种网上的入门教程后,我觉得还是看官方Tutorial靠谱.书的弊端一说一大推 本 ...
- 深度学习 Deep Learning UFLDL 最新 Tutorial 学习笔记 1:Linear Regression
1 前言 Andrew Ng的UFLDL在2014年9月底更新了. 对于開始研究Deep Learning的童鞋们来说这真的是极大的好消息! 新的Tutorial相比旧的Tutorial添加了Conv ...
随机推荐
- [Maven实战-许晓斌]-[第二章]-2.3安装目录分析
bin boot conf settings.xml非常重要 这个是maven安装包自带的settings.xml 通常我们会放在习惯路径,C:\Users\admin\.m2\下面 即 用户路径\ ...
- scrapy框架基于CrawlSpider的全站数据爬取
引入 提问:如果想要通过爬虫程序去爬取”糗百“全站数据新闻数据的话,有几种实现方法? 方法一:基于Scrapy框架中的Spider的递归爬取进行实现(Request模块递归回调parse方法). 方法 ...
- ArchLinux 下 VirtualBox 增强设置
0.前言: 其实这个标题本来不是我的本意,因为我的ArchLinux开机启动报错了!. 原本是一个服务报错,解决就行了对不对.后来我各种搜索大法发现了一个常识错误. 1.报错: 2.过程(赶时间的朋友 ...
- 模块sys, os, glob, pickle, subprocess常见用法
参考python常用标准库 http://blog.51cto.com/lizhenliang/1872538 一. sys 1. sys.argv 脚本名1.py, 命令行中执行python 1 ...
- easy-ui grid里的toobar按钮隐藏与显示
//隐藏第一个按钮$('div.datagrid-toolbar a').eq(0).hide();//隐藏第一条分隔线$('div.datagrid-toolbar div').eq(0).hide ...
- 【随记】WPF中xaml输入中文乱码问题解决
在Visual Studio中开发WPF应用程序时,在XMAL文档编写界面输入中文时变为乱码.可能的原因之一是VS中安装了VAssistX插件,导致编码冲突,使中文输入乱码.解决方法是在VAssist ...
- weex 编译vue成js
cd 项目 1.开发 npm run web 开发过程中可以直接使用浏览器运行 npm run ios 2.打包 npm run build 会在 dist 文件夹 中生成js文件, 即可拖入ios ...
- linux的sed(增删改查)使用方法
sed的增删改查的基本操作 参考:https://www.cnblogs.com/0zcl/p/6855740.html 01:增(a) 2个sed命令,分别是:(这些操作都是在内存中进行的,所以不会 ...
- supervisor使用小记
最近使用supervisor部署爬虫,百度了很多,磕磕绊绊自己也算是用起来了,以下是整理的使用情况. 第一步: 下载安装supervisor 使用的ubuntu16.04,直接 sudo apt-ge ...
- readonly与disable
Readonly只针对input(text / password)和textarea有效,而disabled对于所有的表单元素都有效,但是表单元素在使用了disabled后,当我们将表单以POST或G ...