一、前言:

这是一个学习 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. 搭建sftp并设置不同权限的多个用户

    一, 设置相关用户,用户组,ssh配置文件 mkdir -pv /opt/ftpsite/{admin,user1,user2} groupadd sftpadmins groupadd sftpus ...

  2. String 源码浅析(一)

    前言 相信作为 JAVAER,平时编码时使用最多的必然是 String 字符串,而相信应该存在不少人对于 String 的 api 很熟悉了,但没有看过其源码实现,其实我个人觉得对于 api 的使用, ...

  3. luogu5282 【模板】快速阶乘算法

    由于巨佬 shadowice1984 卡时限,本代码已经 T 请不要粘上去交 退役之后再写一个常数小的多项式取模吧 一句话题意:NP问题,求N!%P 吐槽:出题人太毒瘤...必须写任意模数NTT,而且 ...

  4. js数组的常用操作

    数组合并 var arr=[1,"abc","张三","122"]; var b=["今天天气不错","适合学 ...

  5. Eclipse MarketPlace 打不开,对话框闪退

    原文地址: https://blog.csdn.net/wonder_boy869/article/details/81031222 Eclipse的版本更新到了4.8.0(photon版),点击he ...

  6. Python3 scrapy 新手命令

    基本命令 建立项目 scrapy startproject projectname #在CMD命令框内执行,路径是你需要保存的位置 建立爬虫 cd projectname #在CMD命令框内执行,目的 ...

  7. PIE SDK影像坏线修复

    1.算法功能简介 坏条带的由来:2003年5月31日,Landsat-7ETM+机载扫描行校正器(SLC) 故障,导致此后获取的影像出现了数据条带丢失,严重影响了Landsat ETM遥感影像的使用. ...

  8. NEXIQ 125032 USB Link Diesel Truck Diagnose Interface Introduction

    What are the features of nexiq usb link? 1.Compatible with applications that diagnose engines, trans ...

  9. iOS开发苹果内购的介绍与实现

    1.iOS开发苹果内购的介绍 1.1 介绍 苹果规定,凡是虚拟的物品(例如:QQ音乐的乐币)进行交易时,都必须走苹果的内购通道,苹果要收取大约30%的抽成,所以不允许接入第三方的支付方式(微信.支付宝 ...

  10. Unity5 2D Animation

    1. 所有的动画保存在 .anim 后缀的文件里.2. Animation 标签用来编辑一堆 Animation clip,每一个clip是一个图片序列,也就是动图.动画的最小控制单位就是clip,一 ...