• ffplay-主要数据结构分析


    数据结构分析

    VideoState

    其实主要作用和800行代码写一个播放器作用中一样 就是一个主要封装类

    保存 其中各项的主要内容

    个人觉得比较需要注意到的 几个非重点常用对象

    1 abort_request 就是判断这个时候有没有 退出播放 如果退出了 那就一系列退出操作

    2 force_refresh 视频播放时候用的

    3 av_sync_type ffplay 支持3种同步方式

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

    1. typedef struct VideoState {
    2. SDL_Thread *read_tid; // 读线程句柄
    3. AVInputFormat *iformat; // 指向demuxer
    4. int abort_request; // =1时请求退出播放
    5. int force_refresh; // =1时需要刷新画面,请求立即刷新画面的意思
    6. int paused; // =1时暂停,=0时播放
    7. int last_paused; // 暂存“暂停”/“播放”状态
    8. int queue_attachments_req;
    9. int seek_req; // 标识一次seek请求
    10. int seek_flags; // seek标志,诸如AVSEEK_FLAG_BYTE等
    11. int64_t seek_pos; // 请求seek的目标位置(当前位置+增量)
    12. int64_t seek_rel; // 本次seek的位置增量
    13. int read_pause_return;
    14. AVFormatContext *ic; // iformat的上下文
    15. int realtime; // =1为实时流
    16. Clock audclk; // 音频时钟
    17. Clock vidclk; // 视频时钟
    18. Clock extclk; // 外部时钟
    19. FrameQueue pictq; // 视频Frame队列
    20. FrameQueue subpq; // 字幕Frame队列
    21. FrameQueue sampq; // 采样Frame队列
    22. Decoder auddec; // 音频解码器
    23. Decoder viddec; // 视频解码器
    24. Decoder subdec; // 字幕解码器
    25. int audio_stream ; // 音频流索引
    26. int av_sync_type; // 音视频同步类型, 默认audio master
    27. double audio_clock; // 当前音频帧的PTS+当前帧Duration
    28. int audio_clock_serial; // 播放序列,seek可改变此值
    29. // 以下4个参数 非audio master同步方式使用
    30. double audio_diff_cum; // used for AV difference average computation
    31. double audio_diff_avg_coef;
    32. double audio_diff_threshold;
    33. int audio_diff_avg_count;
    34. // end
    35. AVStream *audio_st; // 音频流
    36. PacketQueue audioq; // 音频packet队列
    37. int audio_hw_buf_size; // SDL音频缓冲区的大小(字节为单位)
    38. // 指向待播放的一帧音频数据,指向的数据区将被拷入SDL音频缓冲区。若经过重采样则指向audio_buf1
    39. // 否则指向frame中的音频
    40. uint8_t *audio_buf; // 指向需要重采样的数据
    41. uint8_t *audio_buf1; // 指向重采样后的数据
    42. unsigned int audio_buf_size; // 待播放的一帧音频数据(audio_buf指向)的大小
    43. unsigned int audio_buf1_size; // 申请到的音频缓冲区audio_buf1的实际尺寸
    44. int audio_buf_index; // 更新拷贝位置 当前音频帧中已拷入SDL音频缓冲区
    45. // 的位置索引(指向第一个待拷贝字节)
    46. // 当前音频帧中尚未拷入SDL音频缓冲区的数据量:
    47. // audio_buf_size = audio_buf_index + audio_write_buf_size
    48. int audio_write_buf_size;
    49. int audio_volume; // 音量
    50. int muted; // =1静音,=0则正常
    51. struct AudioParams audio_src; // 音频frame的参数
    52. #if CONFIG_AVFILTER
    53. struct AudioParams audio_filter_src;
    54. #endif
    55. struct AudioParams audio_tgt; // SDL支持的音频参数,重采样转换:audio_src->audio_tgt
    56. struct SwrContext *swr_ctx; // 音频重采样context
    57. int frame_drops_early; // 丢弃视频packet计数
    58. int frame_drops_late; // 丢弃视频frame计数
    59. enum ShowMode {
    60. SHOW_MODE_NONE = -1, // 无显示
    61. SHOW_MODE_VIDEO = 0, // 显示视频
    62. SHOW_MODE_WAVES, // 显示波浪,音频
    63. SHOW_MODE_RDFT, // 自适应滤波器
    64. SHOW_MODE_NB
    65. } show_mode;
    66. // 音频波形显示使用
    67. int16_t sample_array[SAMPLE_ARRAY_SIZE]; // 采样数组
    68. int sample_array_index; // 采样索引
    69. int last_i_start; // 上一开始
    70. RDFTContext *rdft; // 自适应滤波器上下文
    71. int rdft_bits; // 自使用比特率
    72. FFTSample *rdft_data; // 快速傅里叶采样
    73. int xpos;
    74. double last_vis_time;
    75. SDL_Texture *vis_texture; // 音频Texture
    76. SDL_Texture *sub_texture; // 字幕显示
    77. SDL_Texture *vid_texture; // 视频显示
    78. int subtitle_stream; // 字幕流索引
    79. AVStream *subtitle_st; // 字幕流
    80. PacketQueue subtitleq; // 字幕packet队列
    81. double frame_timer; // 记录最后一帧播放的时刻
    82. double frame_last_returned_time; // 上一次返回时间
    83. double frame_last_filter_delay; // 上一个过滤器延时
    84. int video_stream; // 视频流索引
    85. AVStream *video_st; // 视频流
    86. PacketQueue videoq; // 视频队列
    87. double max_frame_duration; // 一帧最大间隔. above this, we consider the jump a timestamp discontinuity
    88. struct SwsContext *img_convert_ctx; // 视频尺寸格式变换
    89. struct SwsContext *sub_convert_ctx; // 字幕尺寸格式变换
    90. int eof; // 是否读取结束
    91. char *filename; // 文件名
    92. int width, height, xleft, ytop; // 宽、高,x起始坐标,y起始坐标
    93. int step; // =1 步进播放模式, =0 其他模式
    94. #if CONFIG_AVFILTER
    95. int vfilter_idx;
    96. AVFilterContext *in_video_filter; // the first filter in the video chain
    97. AVFilterContext *out_video_filter; // the last filter in the video chain
    98. AVFilterContext *in_audio_filter; // the first filter in the audio chain
    99. AVFilterContext *out_audio_filter; // the last filter in the audio chain
    100. AVFilterGraph *agraph; // audio filter graph
    101. #endif
    102. // 保留最近的相应audio、video、subtitle流的steam index
    103. int last_video_stream, last_audio_stream, last_subtitle_stream;
    104. SDL_cond *continue_read_thread; // 当读取数据队列满了后进入休眠时,可以通过该condition唤醒读线程
    105. } VideoState;

    MyAVPacketList

    和名字一样 就是 解封装出来的packet 用链表的形式开始保存 啦啦啦啦

    可以理解为是队列的⼀个节点。可以通过其 next 字段访问下一个节点

    重点!!! 1 serial

    ffplay中多处用到serial的概念,主要用来区分是否连续 数据,每做一次seek,该serial都会做+1的递增,以区分不同的播放序列。

    说人话就是: 就是 seek 一次以后 以前的队列中的东西当然就不能用了呗 应该放弃的放弃 要free 的 free

    1. typedef struct MyAVPacketList {
    2. AVPacket pkt; //解封装后的数据
    3. struct MyAVPacketList *next; //下一个节点
    4. int serial; //播放序列
    5. } MyAVPacketList;

    PacketQueue

    这里这个packeQueue 逻辑和前面800行代码播放器 逻辑一样

    保存一些基本信息

    这个packetqueue 是 同时作用于 视频 音频 和 字幕的

    音频、视频、字幕流都有自己独立的PacketQueue

    这里也看到了serial字段, MyAVPacketList的serial字段的赋值来自PacketQueue的serial,每个PacketQueue的serial是独立的。

    1. typedef struct PacketQueue {
    2. MyAVPacketList *first_pkt, *last_pkt; // 队首,队尾指针
    3. int nb_packets; // 包数量,也就是队列元素数量
    4. int size; // 队列所有元素的数据大小总和
    5. int64_t duration; // 队列所有元素的数据播放持续时间
    6. int abort_request; // 用户退出请求标志
    7. int serial; // 播放序列号,和MyAVPacketList的serial作用相同,但改变的时序稍微有点不同
    8. SDL_mutex *mutex; // 用于维持PacketQueue的多线程安全(SDL_mutex可以按pthread_mutex_t理解)
    9. SDL_cond *cond; // 用于读、写线程相互通知(SDL_cond可以按pthread_cond_t理解)
    10. } PacketQueue;

    Frame

    真正存储解码后音视频数据的结构体为AVFrame ,存储字幕则使用AVSubtitle,该Frame的设计是为了音频、视频、字幕帧通用,所以Frame结构体的设计类似AVFrame,部分成员变量只对不同类型有作用,比如sar只对视频有作用。 里面也包含了serial播放序列(每次seek时都切换serial),sar(图像的宽高比(16:9,4:3…),该值来 自AVFrame的sample_aspect_ratio变量。

    1. typedef struct Frame {
    2. AVFrame *frame; // 指向数据帧
    3. AVSubtitle sub; // 用于字幕
    4. int serial; // 帧序列,在seek的操作时serial会变化
    5. double pts; // 时间戳,单位为秒
    6. double duration; // 该帧持续时间,单位为秒
    7. int64_t pos; // 该帧在输入文件中的字节位置
    8. int width; // 图像宽度
    9. int height; // 图像高读
    10. int format; // 对于图像为(enum AVPixelFormat),
    11. // 对于声音则为(enum AVSampleFormat)
    12. AVRational sar; // 图像的宽高比(16:94:3...),如果未知或未指定则为0/1
    13. int uploaded; // 用来记录该帧是否已经显示过?
    14. int flip_v; // =1则垂直翻转, = 0则正常播放
    15. } Frame;

    FrameQueue

    FrameQueue是一个环形缓冲区(ring buffer),是用数组实现的一个FIFO。数组方式的环形缓冲区适合于 事先明确了缓冲区的最大容量的情形。 ffplay中创建了三个frame_queue:音频frame_queue,视频frame_queue,字幕frame_queue。每一个frame_queue一个写端一个读端,写端位于解码线程,读端位于播放线程。

    1. typedef struct FrameQueue {
    2. Frame queue[FRAME_QUEUE_SIZE]; // FRAME_QUEUE_SIZE 最大size, 数字太大时会占用大量的内存,需要注意该值的设置
    3. int rindex; // 读索引。待播放时读取此帧进行播放,播放后此帧成为上一帧
    4. int windex; // 写索引
    5. int size; // 当前总帧数
    6. int max_size; // 可存储最大帧数
    7. int keep_last; // = 1说明要在队列里面保持最后一帧的数据不释放,只在销毁队列的时候才将其真正释放
    8. int rindex_shown; // 初始化为0,配合keep_last=1使用
    9. SDL_mutex *mutex; // 互斥量
    10. SDL_cond *cond; // 条件变量
    11. PacketQueue *pktq; // 数据包缓冲队列
    12. } FrameQueue;

    FrameQueue的设计比如PacketQueue复杂,引入了读取节点但节点不出队列的操作、读取下一节点也不出队列等等的操作,FrameQueue具体操作函数见连接xxx

    在这里插入代码片

    AudioParams

    int			bytes_per_sec;          // 一秒时间的字节数,比如采样率48Khz,2 channel,16bit,则一秒48000*2*16/8=192000
    
    1. typedef struct AudioParams {
    2. int freq; // 采样率
    3. int channels; // 通道数
    4. int64_t channel_layout; // 通道布局,比如2.1声道,5.1声道等
    5. enum AVSampleFormat fmt; // 音频采样格式,比如AV_SAMPLE_FMT_S16表示为有符号16bit深度,交错排列模式。
    6. int frame_size; // 一个采样单元占用的字节数(比如2通道时,则左右通道各采样一次合成一个采样单元)
    7. int bytes_per_sec; // 一秒时间的字节数,比如采样率48Khz,2 channel,16bit,则一秒48000*2*16/8=192000
    8. } AudioParams;

    struct Decoder解码器封装 

    1. typedef struct Decoder {
    2. AVPacket pkt;
    3. PacketQueue *queue; // 数据包队列
    4. AVCodecContext *avctx; // 解码器上下文
    5. int pkt_serial; // 包序列
    6. int finished; // =0,解码器处于工作状态;=0,解码器处于空闲状态
    7. int packet_pending; // =0,解码器处于异常状态,需要考虑重置解码器;=1,解码器处于正常状态
    8. SDL_cond *empty_queue_cond; // 检查到packet队列空时发送 signal缓存read_thread读取数据
    9. int64_t start_pts; // 初始化时是stream的start time
    10. AVRational start_pts_tb; // 初始化时是stream的time_base
    11. int64_t next_pts; // 记录最近一次解码后的frame的pts,当解出来的部分帧没有有效的pts时则使用next_pts进行推算
    12. AVRational next_pts_tb; // next_pts的单位
    13. SDL_Thread *decoder_tid; // 线程句柄
    14. } Decoder;

    本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 相关阅读:
    Linux 基础指令汇总大全
    self xss+csrf dvwa靶机复现
    WEE指令和ROHS指令的区别
    蓝桥杯打卡Day9
    Spring AOP+Redis实现接口访问限制
    lotus-local-net 8MiB v1.17.0 本地测试环境
    深度学习-卷积神经网络
    Shiro授权
    【树形权限】树形列表权限互斥选择、el-tree设置禁用等等
    Promise的使用与async/await的使用
  • 原文地址:https://blog.csdn.net/m0_60259116/article/details/126786257