一、前言:

这是一个学习 FFmpeg 的 tutorial 系列。

这个是一个对初学者比较友好的FFmpeg学习教程,作者一步步引导我们实现了一个音视频同步的播放器。

参考链接:

建议可以打开代码+中文学习博客学习,我看了01~04篇的代码,然后就直接跳到了07的代码,结合中文博客+代码可以看懂。

#

二、源码分析

tutorial01.c 代码分析

这个代码很简单,就是解码一个视频文件,然后把前五帧视频保存为五个后缀格式 .ppm 图片视频

tutorial02.c 代码分析

这个代码是把读取视频文件,然后每解码一帧,马上就用SDL显示出来。

tutorial03.c 代码分析

跟02代码相比,这个添加了播放音频的部分。

FFmpeg负责把音频的从AVPacket解码为AVFrame得到原始数据,

然后SDL通过回调函数,有节奏的取出原始音频数据播放(这个是在一个独立的音频播放线程应该)

tutorial04.c 代码分析

tutorial07.c 代码分析

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

  1. int audio_decode_frame(VideoState *is, double *pts_ptr) {
  2. //省略代码
  3. //方法1:通过每一次decode获得的音频数据的字节数除以每一秒音频数据的字节数获得这一次decode的音频时长
  4. //累加这个时长,得出decode的音频最新时间
  5. is->audio_clock += (double)data_size /
  6. (double)(n * is->audio_st->codec->sample_rate);
  7. //方法2:如果AVPacket的pts有值,则可以通过pts计算 is->audio_clock
  8. if(pkt->pts != AV_NOPTS_VALUE) {
  9. is->audio_clock = av_q2d(is->audio_st->time_base)*pkt->pts;
  10. }
  11. void audio_callback(void *userdata, Uint8 *stream, int len) {
  12. VideoState *is = (VideoState *)userdata;
  13. int len1, audio_size;
  14. double pts;
  15. while(len > 0) {
  16. if(is->audio_buf_index >= is->audio_buf_size) {
  17. /* We have already sent all our data; get more */
  18. audio_size = audio_decode_frame(is, &pts);
  19. if(audio_size < 0) {
  20. /* If error, output silence */
  21. is->audio_buf_size = 1024;
  22. memset(is->audio_buf, 0, is->audio_buf_size);
  23. } else {
  24. is->audio_buf_size = audio_size;
  25. }
  26. is->audio_buf_index = 0;
  27. }
  28. len1 = is->audio_buf_size - is->audio_buf_index;
  29. if(len1 > len)
  30. len1 = len;
  31. memcpy(stream, (uint8_t *)is->audio_buf + is->audio_buf_index, len1);
  32. len -= len1;
  33. stream += len1;
  34. is->audio_buf_index += len1;
  35. }
  36. }
  37. double get_audio_clock(VideoState *is) {
  38. double pts;
  39. int hw_buf_size, bytes_per_sec, n;
  40. //关键语句 is->audio_clock 代表已经解码的音频的数据最新时间
  41. pts = is->audio_clock; /* maintained in the audio thread */
  42. hw_buf_size = is->audio_buf_size - is->audio_buf_index;
  43. bytes_per_sec = 0;
  44. n = is->audio_st->codec->channels * 2;
  45. if(is->audio_st) {
  46. bytes_per_sec = is->audio_st->codec->sample_rate * n;
  47. }
  48. if(bytes_per_sec) {
  49. //关键语句,音频是解码到了一个buffer里面,其中一部分被SDL取走播放,还有一部分在缓冲区
  50. //已经解码的音频的数据最新时间 不能代表音频已经播放的时间,因为还有一部分解码的音频数据没有被播放
  51. //所以需要减去没有播放的音频的时间,得出准确的音频已经播放时间
  52. pts -= (double)hw_buf_size / bytes_per_sec;
  53. }
  54. return pts;
  55. }

b.如何正确设置每一帧视频的解码时间pts

c.如何调整视频播放速度快慢实现和音频的播放速度同步

  • 以视频播放为时钟,音频播放参考视频播放时钟,调整下一帧的播放快慢实现视频同步到音频

  • 以系统时间为时钟,音频视频同步到系统时钟

2.2.1

(未完待续~)

FFmpeg的tutorial 学习的更多相关文章

  1. Python Tutorial 学习(八)--Errors and Exceptions

    Python Tutorial 学习(八)--Errors and Exceptions恢复 Errors and Exceptions 错误与异常 此前,我们还没有开始着眼于错误信息.不过如果你是一 ...

  2. FFmpeg 结构体学习(二): AVStream 分析

    在上文FFmpeg 结构体学习(一): AVFormatContext 分析我们学习了AVFormatContext结构体的相关内容.本文,我们将讲述一下AVStream. AVStream是存储每一 ...

  3. FFmpeg 结构体学习(三): AVPacket 分析

    在上文FFmpeg 结构体学习(二): AVStream 分析我们学习了AVStream结构体的相关内容.本文,我们将讲述一下AVPacket. AVPacket是存储压缩编码数据相关信息的结构体.下 ...

  4. FFmpeg 结构体学习(四): AVFrame 分析

    在上文FFmpeg 结构体学习(三): AVPacket 分析我们学习了AVPacket结构体的相关内容.本文,我们将讲述一下AVFrame. AVFrame是包含码流参数较多的结构体.下面我们来分析 ...

  5. FFmpeg 结构体学习(五): AVCodec 分析

    在上文FFmpeg 结构体学习(四): AVFrame 分析我们学习了AVFrame结构体的相关内容.本文,我们将讲述一下AVCodec. AVCodec是存储编解码器信息的结构体.下面我们来分析一下 ...

  6. FFmpeg 结构体学习(六): AVCodecContext 分析

    在上文FFmpeg 结构体学习(五): AVCodec 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVCodecContext. AVCodecContext是包含变量较多的结 ...

  7. FFmpeg 结构体学习(七): AVIOContext 分析

    在上文FFmpeg 结构体学习(六): AVCodecContext 分析我们学习了AVCodec结构体的相关内容.本文,我们将讲述一下AVIOContext. AVIOContext是FFMPEG管 ...

  8. Django 1.7 Tutorial 学习笔记

    官方教程在这里 : Here 写在前面的废话:)) 以前学习新东西,第一想到的是找本入门教程,按照书上做一遍.现在看了各种网上的入门教程后,我觉得还是看官方Tutorial靠谱.书的弊端一说一大推 本 ...

  9. 深度学习 Deep Learning UFLDL 最新 Tutorial 学习笔记 1:Linear Regression

    1 前言 Andrew Ng的UFLDL在2014年9月底更新了. 对于開始研究Deep Learning的童鞋们来说这真的是极大的好消息! 新的Tutorial相比旧的Tutorial添加了Conv ...

随机推荐

  1. 多继承c3算法

    一.mro mro即 method resolution order (方法解释顺序),主要用于在多继承时判断属性的路径(来自于哪个类). print(类.mro)得到属性路径按继承顺序 在pytho ...

  2. MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk. Commands that may modify the data set are disabled

    初试redis,删除或者修改值的时候报的错,解决方式是运行命令: 127.0.0.1:6379> config set stop-writes-on-bgsave-error no

  3. UIScreen和UIWindow

    UIScreen 和UIWindow UIScreen object defines the properties associated with a hardware-based display 就 ...

  4. Linux系统查找清理磁盘大文件方法

    本文主要介绍Linux系统磁盘使用空间不足时,如何查找大文件并进行清理的方法. 下午使用df-h检查一台服务器磁盘使用空间,发现磁盘已经使用了100%,其中/dev/mapper/vg_iavp-lv ...

  5. CF960G Bandit Blues 分治+NTT(第一类斯特林数)

    $ \color{#0066ff}{ 题目描述 }$ 给你三个正整数 \(n\),\(a\),\(b\),定义 \(A\) 为一个排列中是前缀最大值的数的个数,定义 \(B\) 为一个排列中是后缀最大 ...

  6. vue关闭令人抓狂的ESlint 语法检测配置方法

    随便改个vue 一堆报错 其实我并不反对这些语法检测,但是像许多反个人意愿的那就真的不得不吐槽了,比如vue-cli脚手架创建的默认eslint规则: 代码末尾不能加分号 ; 代码中不能存在多行空行 ...

  7. 免费观看vip/要劵的电影

    免费观看vip/要劵的电影 1.在爱奇艺/腾讯视频中复制电影的连接 2.复制连接到这个网站中(http://www.qmaile.com/) 3.粘贴路径到这个网站相应的位置 4.点击go ,等待解析 ...

  8. AHB协议

    AHB2 支持多个Bus Master,例如有三个Master,有四个slave,但是同时只有一个Mater可以拿到Bus的访问权.所以,总线的使用权就需要Master去申请,也就需要一个仲裁器(Ar ...

  9. apache的应用(发布目录,黑白名单,虚拟主机,PHP-cgi支持,正向代理,https加密,)

    [root@apache1 ~]# yum install httpd -y [root@apache1 ~]# cd /var/www/html/   进入默认发布目录 [root@apache1 ...

  10. C#文件上传编码乱码

    又遇到文件编码乱码的事情,这回稍微有些头绪,但是还是花了很多时间去解决. 场景:上传csv文件,导入到数据库.上传文件的编码不定,需要转成unicode存储. 问题点:需要解决判断上传文件的编码. 关 ...