ffplay播放器-数据读取线程

在这里插入图片描述

数据读取线程

从ffplay框架分析我们可以看到,ffplay有专⻔的线程read_thread()读取数据,且在调⽤av_read_frame 读取数据包之前需要做例如打开⽂件,查找配置解码器,初始化⾳视频输出等准备阶段,主要包括三⼤步骤:

  • 准备⼯作
  • For循环读取数据
  • 退出线程处理

⼀ 准备⼯作

  1. avformat_alloc_context 创建上下⽂
  2. ic->interrupt_callback.callback = decode_interrupt_cb;
  3. avformat_open_input打开媒体⽂件
  4. avformat_find_stream_info 读取媒体⽂件的包获取更多的stream信息
  5. 检测是否指定播放起始时间,如果指定时间则seek到指定位置avformat_seek_file
  6. 查找查找AVStream,讲对应的index值记录到st_index[AVMEDIA_TYPE_NB];
  • a. 根据⽤户指定来查找avformat_match_stream_specifier
  • b. 使⽤av_find_best_stream查找流
  1. 从待处理流中获取相关参数,设置显示窗⼝的宽度、⾼度及宽⾼⽐
  2. stream_component_open打开⾳频、视频、字幕解码器,并创建相应的解码线程以及进⾏对应输出参 数的初始化。

⼆ For循环读取数据

  1. 检测是否退出
  2. 检测是否暂停/继续
  3. 检测是否需要seek
  4. 检测video是否为attached_pic
  5. 检测队列是否已经有⾜够数据
  6. 检测码流是否已经播放结束
  • a. 是否循环播放
  • b. 是否⾃动退出
  1. 使⽤av_read_frame读取数据包
  2. 检测数据是否读取完毕
  3. 检测是否在播放范围内
  4. 到这步才将数据插⼊对应的队列

三 退出线程处理

  1. 如果解复⽤器有打开则关闭avformat_close_input
  2. 调⽤SDL_PushEvent发送退出事件FF_QUIT_EVENT
  3. 消耗互斥量wait_mutex

准备⼯作

准备⼯作主要包括以下步骤:

  1. avformat_alloc_context 创建上下⽂
  2. ic->interrupt_callback.callback = decode_interrupt_cb;
  3. avformat_open_input打开媒体⽂件
  4. avformat_find_stream_info 读取媒体⽂件的包获取更多的stream信息
  5. 检测是否指定播放起始时间,如果指定时间则seek到指定位置avformat_seek_file
  6. 查找查找AVStream,讲对应的index值记录到st_index[AVMEDIA_TYPE_NB];
  • a. 根据⽤户指定来查找流avformat_match_stream_specifier
  • b. 使⽤av_find_best_stream查找流
  1. 通过AVCodecParameters和av_guess_sample_aspect_ratio计算出显示窗⼝的宽、⾼
  2. stream_component_open打开⾳频、视频、字幕解码器,并创建相应的解码线程以及进⾏对应输出参 数的初始化。
avformat_alloc_context 创建上下⽂

调⽤avformat_alloc_context创建解复⽤器上下⽂

// 1. 创建上下⽂结构体,这个结构体是最上层的结构体,表示输⼊上下⽂
ic = avformat_alloc_context();
is->ic = ic; // videoState的ic指向分配的ic
ic->interrupt_callback
 /* 2.设置中断回调函数,如果出错或者退出,就根据⽬前程序设置的状态选择继续check或者直 接退出 */
 /* 当执⾏耗时操作时(⼀般是在执⾏while或者for循环的数据读取时),会调⽤interrupt_ callback.callback
 * 回调函数中返回1则代表ffmpeg结束耗时操作退出当前函数的调⽤
 * * 回调函数中返回0则代表ffmpeg内部继续执⾏耗时操作,直到完成既定的任务(⽐如读取到既定 的数据包)
 * */
ic->interrupt_callback.callback = decode_interrupt_cb;
ic->interrupt_callback.opaque = is;

interrupt_callback⽤于ffmpeg内部在执⾏耗时操作时检查调⽤者是否有退出请求,避免⽤户退出请求没 有及时响应。
怎么去测试在哪⾥触发?
在ubuntu使⽤gdb进⾏调试:然后在decode_interrupt_cb打断点。
avformat_open_input的触发

#0 decode_interrupt_cb (ctx=0x7ffff7e36040) at fftools/ffplay.c:271 5 
#1 0x00000000007d99b7 in ff_check_interrupt (cb=0x7fffd00014b0) at libavformat/avio.c:667 
#2 retry_transfer_wrapper (transfer_func=0x7dd950 <file_read>, size _min=1, size=32768, buf=0x7fffd0001700 "", h=0x7fffd0001480) at libavformat/avio.c:374 
#3 ffurl_read (h=0x7fffd0001480, buf=0x7fffd0001700 "", size=32768) at libavformat/avio.c:411
#4 0x000000000068cd9c in read_packet_wrapper (size=<optimized out>, buf=<optimized out>, s=0x7fffd00011c0) at libavformat/aviobuf.c: 535 
#5 fill_buffer (s=0x7fffd00011c0) at libavformat/aviobuf.c:584 
#6 avio_read (s=s@entry=0x7fffd00011c0, buf=0x7fffd0009710 "", size=size@entry=2048) at libavformat/aviobuf.c:677 
#7 0x00000000006b7780 in av_probe_input_buffer2 (pb=0x7fffd00011c0, fmt=0x7fffd0000948, filename=filename@entry=0x31d50e0 "source.200kbps.768x320.flv", logctx=logctx@entry=0x7fffd0000940, offset=offset@entry=0, max_probe_size=1048576) at libavformat/format.c:262 
#8 0x00000000007b631d in init_input (options=0x7fffdd9bcb50, filename=0x31d50e0 "source.200kbps.768x320.flv", s=0x7fffd000094 0) at libavformat/utils.c:443 
#9 avformat_open_input (ps=ps@entry=0x7fffdd9bcbf8, filename=0x31d50e0 "source.200kbps.768x320.flv", fmt=<optimized out>,

可以看到是在libavformat/avio.c:374⾏有触发到
在这里插入图片描述
avformat_find_stream_info的触发

 #0 decode_interrupt_cb (ctx=0x7ffff7e36040) at fftools/ffplay.c:2715 
 #1 0x00000000007b25bc in avformat_find_stream_info (ic=0x7fffd000094 0, options=0x0) at libavformat/utils.c:3693 
 #2 0x00000000004a6ea9 in read_thread (arg=0x7ffff7e36040)

从该调⽤栈可以看出来 avformat_find_stream_info也会触发ic->interrupt_callback的调⽤,具体可以 看代码(libavformat/utils.c:3693⾏)
在这里插入图片描述
av_read_frame的触发

#0 decode_interrupt_cb (ctx=0x7ffff7e36040) at fftools/ffplay.c:271 5 
#1 0x00000000007d99b7 in ff_check_interrupt (cb=0x7fffd00014b0) at libavformat/avio.c:667 
#2 retry_transfer_wrapper (transfer_func=0x7dd950 <file_read>, size _min=1, size=32768, buf=0x7fffd0009710 "FLV\001\005", h=0x7fffd0001480) at libavformat/avio.c:374 
#3 ffurl_read (h=0x7fffd0001480, buf=0x7fffd0009710 "FLV\001\005", size=32768) at libavformat/avio.c:411 
#4 0x000000000068cd9c in read_packet_wrapper (size=<optimized out>, buf=<optimized out>, s=0x7fffd00011c0) at libavformat/aviobuf.c: 535  
#5 fill_buffer (s=0x7fffd00011c0) at libavformat/aviobuf.c:584 
#6 avio_read (s=s@entry=0x7fffd00011c0, buf=0x7fffd00dbf6d "\177", size=45, size@entry=90) at libavformat/aviobuf.c:677 
#7 0x00000000007a99d5 in append_packet_chunked (s=0x7fffd00011c0, pkt=pkt@entry=0x7fffdd9bca00, size=size@entry=90) at libavformat/utils.c:293 
#8 0x00000000007aa969 in av_get_packet (s=<optimized out>, pkt=pkt@entry=0x7fffdd9bca00, size=size@entry=90) at libavformat/utils.c:317 
#9 0x00000000006b350a in flv_read_packet (s=0x7fffd0000940, pkt=0x7fffdd9bca00) at libavformat/flvdec.c:1295 
#10 0x00000000007aad6d in ff_read_packet (s=s@entry=0x7fffd0000940,pkt=pkt@entry=0x7fffdd9bca00) at libavformat/utils.c:856 ---Type <return> to continue, or q <return> to quit---
#11 0x00000000007ae291 in read_frame_internal (s=0x7fffd0000940, pkt=0x7fffdd9bcc00) at libavformat/utils.c:1582 
#12 0x00000000007af422 in av_read_frame (s=0x7fffd0000940, pkt=pkt@entry=0x7fffdd9bcc00) at libavformat/utils.c:1779 
#13 0x00000000004a68b1 in read_thread (arg=0x7ffff7e36040) at fftools/ffplay.c:3008

这⾥的触发和avformat_open_input⼀致,⼤家可以⾃⾏跟踪调⽤栈。

avformat_open_input()打开媒体文件

函数原型:

/** * Open an input stream and read the header. The codecs are not opened. 
* The stream must be closed with avformat_close_input(). 
*
* @param ps Pointer to user-supplied AVFormatContext (allocated by avformat_alloc_context). 
* May be a pointer to NULL, in which case an AVFormatContext is allocated by this 
* function and written into ps. 
* Note that a user-supplied AVFormatContext will be freed on failure. 
* @param url URL of the stream to open.
* @param fmt If non-NULL, this parameter forces a specific input format. 
* Otherwise the format is autodetected. 
* @param options A dictionary filled with AVFormatContext and demuxer-private options. 
* On return this parameter will be destroyed and replaced with a dict containing 
* options that were not found. May be NULL. 
*
* @return 0 on success, a negative AVERROR on failure. 
*
* @note If you want to use custom IO, preallocate the format context and set its pb field. */
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options);

avformat_open_input⽤于打开输⼊⽂件(对于RTMP/RTSP/HTTP⽹络流也是⼀样,在ffmpeg内部都 抽象为URLProtocol,这⾥描述为⽂件是为了⽅便与后续提到的AVStream的流作区分),读取视频⽂件 的基本信息。

需要提到的两个参数是fmt和options。通过fmt可以强制指定视频⽂件的封装,options可以传递额外参数 给封装(AVInputFormat)。
主要代码:

if (!av_dict_get(format_opts, "scan_all_pmts", NULL, AV_DICT_MATCH_CASE)) {
   
   
        av_dict_set(&format_opts, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);
        scan_all_pmts_set = 1;
    }
     /* 3.打开文件,主要是探测协议类型,如果是网络文件则创建网络链接等 */
    err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);
    if (err < 0) {
   
   
        print_error(is->filename
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值