zoukankan      html  css  js  c++  java
  • FFmpeg的tutorial 学习

    一、前言:

    这是一个学习 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

    (未完待续~)





  • 相关阅读:
    清除右键图形属性--图形选项
    Hibernate整合Struts2时报错
    shell基础篇(十)shell脚本的包含
    shell基础篇(九)函数
    浅谈sql的字符分割
    shell基础(八)-循环语句
    shell基础(七)-条件语句
    vue ---- Object的一些常用的方法
    vuex的应用和解决的实际问题
    webpack2代码分割
  • 原文地址:https://www.cnblogs.com/bylijian/p/7151668.html
Copyright © 2011-2022 走看看