一、前言:

这是一个学习 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. maven+eclipse+ssm 环境搭建和启动

    该类工程环境搭建和启动方法 ------------------------------------------------------------------------------- 配置 jdk ...

  2. python的数据结构分类,以及数字的处理函数,类型判断

    python的数据结构分类: 数值型 int:python3中都是长整形,没有大小限制,受限内存区域的大小 float:只有双精度型 complex:实数和虚数部分都是浮点型,1+1.2J bool: ...

  3. Objective-C中的meta-class

    讨论Objective-C的一个奇怪的概念 meta-class 在Objective-C中的每个类,都有它自己相关的meta-class,但因为你很少直接使用meta-class,所以显得很神秘.  ...

  4. WinServer下DNS随笔

    关于DNS:#hosts文件优先于dns解析.(同VIM注释)#SOA(权威记录)区域所有者包含NS(域名服务器记录)权威解析.关于SOA(权威记录)和NS(域名服务器记录)栗子:SOA爲什麼是dns ...

  5. android开发如何获取res/raw和assets文件夹的路径

    ---恢复内容开始--- android开发如何获取res/raw和assets文件夹的路径,主要分为两种情况: 1.如果你只是拷贝动作,那么你只需要得到res/raw和assets文件输入流就可以, ...

  6. suid, sgid,粘滞位,chattr

    一. 基本含义和作用 1. SUID:当设置了SUID 位的文件被执行时,该文件将以所有者的身份运行,也就是说无论谁来执行这个文件,他都有文件所有者的特权. 2. SGID:与上面的内容类似.用于设置 ...

  7. 队列优化dijsktra(SPFA)的玄学优化

    转载:大佬博客 最近想到了许多优化spfa的方法,这里想写个日报与大家探讨下 前置知识:spfa(不带任何优化) 由于使用较多 STLSTL ,本文中所有代码的评测均开启 O_2O2​ 优化 对一些数 ...

  8. editplus 编辑 php双击选中变量问题

    windows下,在很多地方双击鼠标左键可以选中一个连续的英文字符串. 在editplus 编辑器里可以双击选中一个变量,方便了编程,但是使用phptools(php.stx)增强语法插件后,在一个变 ...

  9. 【Python】子域名查询脚本

    脚本学习,多写写就会啦,来一发个人编写的超级无敌low的子域名查询脚本 #coding:utf-8 import re import requests import urllib import url ...

  10. oracle12c之二 控制PDB中SGA 与 PGA 内存使用

    oracle12c之 控制pdb中sga 与 pga 内存使用 Memory Management using Resource Manager Oracle数据库资源管理器(资源管理器)现在可以在多 ...