ffplay源码解析2 读线程_1

ffplay源码解析2 读线程_1

在主线程中使用 SDL 函数 SDL_CreateThread 创建一个名为 “read_thread” 的线程
读函数

static int read_thread(void *arg)

1 定义变量

AVPacket pkt1, *pkt = &pkt1;

pkt1 是一个 AVPacket 结构体,用于存储解码前的媒体数据包(比如编码的音频帧或视频帧)。pkt 是指向 pkt1 的指针,方便传递数据。

int64_t stream_start_time;

stream_start_time 是一个 64 位整数,表示媒体流的开始时间。它通常用来计算播放的时间戳,进行时间同步。

int64_t pkt_ts;

pkt_ts 是一个 64 位整数,代表包的时间戳。它可能用于视频帧或音频帧的同步,确保包按照正确的时间顺序解码和播放。

int pkt_in_play_range = 0;

pkt_in_play_range 是一个布尔变量(0 或 1),用于判断当前解码的包是否在播放范围内。例如,在快进或播放特定区段时,用于判断是否需要处理当前包。

SDL_mutex *wait_mutex = SDL_CreateMutex();

wait_mutex 是一个 SDL 库的互斥锁(mutex)。SDL_CreateMutex() 函数创建了一个新的互斥锁,用于多线程同步,避免多线程访问共享数据时发生竞态条件。

2 初始化流状态

首先检查互斥锁创建是否成功if (!wait_mutex)

// 初始化为-1,如果一直为-1说明没相应steam
    is->last_video_stream = is->video_stream = -1;
    is->last_audio_stream = is->audio_stream = -1;
    is->last_subtitle_stream = is->subtitle_stream = -1;
    is->eof = 0;    // =1是表明数据读取完毕

初始化三个流,初始化为 -1 表示还没有找到或设置相关的流

ic = avformat_alloc_context();
    if (!ic) {

3 创建IC结构

ic 指向新分配的 AVFormatContext 结构体,然后判断是否成功分配
AVFormatContext是一个巨大的结构体(好几百行),是 FFmpeg 中处理多媒体文件的核心结构,管理文件格式、流信息和元数据等。

ic->interrupt_callback.callback = decode_interrupt_cb;
ic->interrupt_callback.opaque = is;

4 设置中断回调函数

设置IC(AVFormatContext)中中断回调函数(interrupt_callback)的两个成员变量:
callback:指向中断回调函数的指针
opaque:一个指向用户数据的指针,赋值成了is(AVPacket)

5 打开输入文件流

err = avformat_open_input(&ic, is->filename, is->iformat, &format_opts);

avformat_open_input():这是 FFmpeg 中用于打开输入文件的函数。它会根据给定的文件名、输入格式和选项来初始化 AVFormatContext(ic)。

&ic:指向 AVFormatContext 的指针,用于存储文件的格式和流信息。
is->filename:输入文件的路径或 URL。
is->iformat:指定文件的格式。如果传入 NULL,FFmpeg 会自动探测文件格式。
&format_opts:字典选项,包含特定的选项,如我们之前看到的 scan_all_pmts

if (genpts)
        ic->flags |= AVFMT_FLAG_GENPTS;
//#define AVFMT_FLAG_GENPTS       0x0001 ///< Generate missing pts even if it requires parsing future frames.

添加时间戳标志,将 AVFMT_FLAG_GENPTS 标志添加到 flags 字段中,表示会生成 PTS

 if (find_stream_info) {
        AVDictionary **opts = setup_find_stream_info_opts(ic, codec_opts);
        int orig_nb_streams = ic->nb_streams;

find_stream_info为真时查找流信息,setup_find_stream_info_opts 函数的主要功能是为每个流分配内存并设置相应的编解码器选项,以便在查找流信息时使用。

err = avformat_find_stream_info(ic, opts);

探测媒体类型和流的信息。

if (ic->pb)
    ic->pb->eof_reached = 0; // FIXME hack, ffplay maybe should not use 

重置流状态:如果 ic->pb(指向 AVIOContext 的指针)存在,重置 eof_reached 标志为 0,表示尚未到达流的末尾。

6 跳转(seek)播放时间

if (start_time != AV_NOPTS_VALUE) 

检查 start_time 是否有效,表示有一个指定的播放起始时间,即有指定seek。

int64_t timestamp;
timestamp = start_time;

timestamp作为指定的播放起始时间

if (ic->start_time != AV_NOPTS_VALUE)
    timestamp += ic->start_time;

调整时间戳,ic->start_time是媒体流的实际开始时间 + 指定的偏移时间

ret = avformat_seek_file(ic, -1, INT64_MIN, timestamp, INT64_MAX, 0);

avformat_seek_file() 函数来进行媒体文件的跳转(seek)操作

ic: 输入的 AVFormatContext,表示当前打开的媒体文件或流。
-1: 表示不限制跳转到哪个流(stream_index = -1 表示对所有流进行跳转)。
INT64_MIN: 表示允许跳转的最小时间戳,即可以从最早的时间开始跳转。
timestamp: 目标跳转的时间戳(以微秒为单位)。这是你希望跳转到的媒体时间点,通常是用户或者程序指定的起始时间(可能已经调整过起始时间)。
INT64_MAX: 表示允许跳转的最大时间戳,即跳转时不设上限。
0: 这个参数表示没有额外的标志位(可以通过不同的标志控制跳转行为,比如 AVSEEK_FLAG_BACKWARD 允许向后跳转)

返回值:
ret: 如果跳转成功,avformat_seek_file() 会返回 0;如果跳转失败,则返回负数,表示错误。

7 查找AVStream

下面是根据用户指定来查找流,假设输入文件中有多个音频和视频流,用户只想选择特定语言的音频流以及某个分辨率的字幕流,那么 wanted_stream_spec 中会包含相关选择规则。代码略过

if (!video_disable)
    st_index[AVMEDIA_TYPE_VIDEO] =
            av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO,
                                st_index[AVMEDIA_TYPE_VIDEO], -1, NULL, 0);

if (!video_disable):如果没有禁用视频流(video_disable 为 0)
使用av_find_best_stream 根据流的类型(如视频、音频、字幕等)找到最佳的流。
别的流同理

8 从待处理流中获取相关参数,设置显示窗口的宽度、高度及宽高比

它首先检查是否有有效的视频流,如果存在,则获取该流的宽度、高度和宽高比信息。
然后根据这些信息设置显示窗口的大小,使视频能够正确显示,保持适当的宽高比,避免失真。

// 设置显示窗口的大小和宽高比
            set_default_window_size(codecpar->width, codecpar->height, sar);

9 打开视频、音频解码器。在此会打开相应解码器,并创建相应的解码线程

if (st_index[AVMEDIA_TYPE_AUDIO] >= 0) { // 如果有音频流则打开音频流
    stream_component_open(is, st_index[AVMEDIA_TYPE_AUDIO]);
}

先检查是否存在有效的音频流。如果 st_index[AVMEDIA_TYPE_AUDIO] 大于或等于 0,则调用 stream_component_open 函数打开该音频流。

然后打开视频流,字幕流

最后,检查是否成功打开了音频或视频流。如果两者都没有成功(is->video_stream < 0 和 is->audio_stream < 0),则输出错误日志,说明打开文件或配置滤镜图失败,并将 ret 设置为 -1,跳转到错误处理逻辑。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值