ffplay源码解析1 主线程

news2024/9/21 23:28:14

目录

    • 1.mian函数的参数
    • 2.初始化
      • 2.1 FFmpeg初始化
      • 2.2 对传递的参数进行初始化
      • 2.3 SDL的初始化
      • 2.4 创建窗口
      • 2.5 开启read_thread读取线程
      • 2.6 事件响应
      • 附1 VideoState 结构体

ffplay源码解析1 主线程
播放器常用功能包括:播放、停止、暂停、拖动、快进、快退、变速播放。

1.mian函数的参数

int main(int argc, char **argv)

argc: 这是一个整型参数,表示命令行中传递给程序的参数数量。
argv: 这是一个字符指针数组,存储了命令行参数的具体字符串值。**argv相当于 *argv[]。
argc 提供参数的数量,而 argv 提供每个参数的内容,便于程序根据用户输入进行相应操作。

2.初始化

包括对FFmpeg的初始化,对传递的参数进行初始化,SDL的初始化

2.1 FFmpeg初始化

#if CONFIG_AVDEVICE  //如果在编译时定义了 CONFIG_AVDEVICE,则条件为真,编译器将编译 #if 和 #endif 之间的代码;否则,将跳过这些代码。
    avdevice_register_all(); //通过调用FFmpeg的API遍历所有可用的设备驱动程序,并将它们注册到 FFmpeg 中。
#endif
    avformat_network_init(); //初始化网络
signal(SIGINT , sigterm_handler); /* Interrupt (ANSI).    */
signal(SIGTERM, sigterm_handler); /* Termination (ANSI).  */

sigterm_handler 函数是一个信号处理器,专门用于处理接收到的信号。当程序接收到中断或终止信号时(如 SIGINT 或 SIGTERM),这个函数将被调用,程序将以状态码 123 终止。

 show_banner(argc, argv, options);

show_banner 函数负责显示程序的启动信息,包括程序名称、版权和相关库的配置信息。

2.2 对传递的参数进行初始化

parse_options(NULL, argc, argv, options, opt_input_file);

首先parse_options对用户输入的参数进行解析

怎么知道解析出来的参数哪个是input_filename?
通常,-i 是一个选项,input.mp4是该选项的参数,表示输入文件名。解析时根据选项对应

对参数两个判断

  1. input_filename是否为空,空的话异常退出,exit(1);
  2. display_disable是否显示视频,显示的话audio_disable = 1

2.3 SDL的初始化

flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER;

其中

#define SDL_INIT_TIMER  0x00000001  // 二进制:0000 0001
#define SDL_INIT_AUDIO  0x00000010  // 二进制:0001 0000
#define SDL_INIT_VIDEO  0x00000020  // 二进制:0010 0000
所有可能的 flags 结果组合有8种
由于每个模块可以启用或关闭,这里是每种可能的组合:

无任何模块启用:
flags = 0(即所有位都为 0)
仅启用 SDL_INIT_TIMER:
flags = 0x000000010000 0001)
仅启用 SDL_INIT_AUDIO:
flags = 0x000000100001 0000)
仅启用 SDL_INIT_VIDEO:
flags = 0x000000200010 0000)
启用 SDL_INIT_TIMER 和 SDL_INIT_AUDIO:
flags = 0x000000110001 0001)
启用 SDL_INIT_TIMER 和 SDL_INIT_VIDEO:
flags = 0x000000210010 0001)
启用 SDL_INIT_AUDIO 和 SDL_INIT_VIDEO:
flags = 0x000000300011 0000)
启用 SDL_INIT_TIMER、SDL_INIT_AUDIO 和 SDL_INIT_VIDEO:
flags = 0x000000310011 0001

如果禁用音频display_disable == true,那么把音频标志从flags中去掉

if (display_disable)
        flags &= ~SDL_INIT_VIDEO;

如果禁用音频,那么把视频标志从flag中去掉

if (audio_disable)
        flags &= ~SDL_INIT_AUDIO;

Flush Packet:在 FFmpeg 中,flush_pkt 常用于刷新缓冲区(flush),特别是在处理完一个流后,发送一个这样的包可以让解码器知道流结束了,并清除其内部缓存。

av_init_packet(&flush_pkt);				// 初始化flush_packet
    flush_pkt.data = (uint8_t *)&flush_pkt; // 初始化为数据指向自己本身

2.4 创建窗口

如果没有禁用视频if (!display_disable)才往下执行

判断窗口有没有边框,把标志加到flags里面

if (borderless)
            flags |= SDL_WINDOW_BORDERLESS; //无边框,不能调整大小
        else
            flags |= SDL_WINDOW_RESIZABLE;

创建窗口

window = SDL_CreateWindow(program_name, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, default_width, default_height, flags);

参数解释:
program_name: 窗口的标题,通常是程序的名称。
SDL_WINDOWPOS_UNDEFINED:表示窗口的位置未定义,SDL 会自动选择一个位置。 default_width: 窗口的宽度,通常是一个预先定义的常量或变量。
default_height: 窗口的高度,通常是一个预先定义的常量或变量。
flags:用于指定窗口的属性(如边框、可调整大小等),这些标志在前面的代码中已根据 borderless 和其他选项设置。

设置渲染质量

SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");

如果窗口创建成功,使用SDL 库的一个函数,给窗口创建一个渲染器renderer

renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);

在 SDL (Simple DirectMedia Layer) 中,renderer 是一个用于渲染图形的对象,它负责将图像绘制到窗口上。具体来说,renderer 提供了一系列函数和功能,用于处理绘图、图像加载、纹理管理等。

2.5 开启read_thread读取线程

is = stream_open(input_filename, file_iformat);

stream_open 函数用于打开和初始化一个流(stream)
它接受两个参数:
input_filename:指定的输入文件名或路径。
file_iformat:文件格式,可能是一个指示要打开的文件类型的参数。
返回值 is 是一个指向 VideoState 结构体的指针,用于表示视频播放器的状态和控制。
VideoState 结构体是一个很大的结构体,在放在附1

stream_open函数做了很多初始化工作,例如初始化帧队列,初始化时钟,初始化音量,创建读线程
初始化结构体

	VideoState *is;//生命结构体指针
    is = av_mallocz(sizeof(VideoState));	/* 分配结构体并初始化 */
    if (!is)
        return NULL;
    is->filename = av_strdup(filename);
    if (!is->filename)
        goto fail;
    is->iformat = iformat; //成员变量赋值
    is->ytop    = 0;
    is->xleft   = 0;

初始化帧队列
init了音频,视频,字幕的包队列和帧队列,有一个创建不成功就返回fail

	if (frame_queue_init(&is->pictq, &is->videoq, VIDEO_PICTURE_QUEUE_SIZE, 1) < 0)//1:表示队列在初始化时会分配内存
        goto fail;
    if (frame_queue_init(&is->subpq, &is->subtitleq, SUBPICTURE_QUEUE_SIZE, 0) < 0)
        goto fail;
    if (frame_queue_init(&is->sampq, &is->audioq, SAMPLE_QUEUE_SIZE, 1) < 0)
        goto fail;

    if (packet_queue_init(&is->videoq) < 0 ||
            packet_queue_init(&is->audioq) < 0 ||
            packet_queue_init(&is->subtitleq) < 0)
        goto fail;

初始化时钟
初始化音频时钟

init_clock(&is->audclk, &is->audioq.serial);

初始化音频
限制启动音量的范围:

startup_volume = av_clip(startup_volume, 0, 100);

将启动音量转换为 SDL 的音量范围:

startup_volume = av_clip(SDL_MIX_MAXVOLUME * startup_volume / 100, 0, SDL_MIX_MAXVOLUME);

相关设置:

is->audio_volume 设置为经过调整后的启动音量值。
is->muted 设置为 0,表示未静音状态。
is->av_sync_type 设置为 av_sync_type,可能是与音视频同步相关的类型或设置。

创建读线程

is->read_tid     = SDL_CreateThread(read_thread, "read_thread", is);

使用 SDL 函数 SDL_CreateThread 创建一个名为 “read_thread” 的线程,并将该线程的标识符保存在 is->read_tid 中。
read_thread 是一个函数指针,用于指定线程的入口函数。

2.6 事件响应

事件循环监控,响应相关的键盘事件(f键,p键,空格等),鼠标事件,窗口事件等

event_loop(is);

附1 VideoState 结构体

typedef struct VideoState {
    SDL_Thread	*read_tid;      // 读线程句柄
    AVInputFormat	*iformat;   // 指向demuxer
    int		abort_request;      // =1时请求退出播放
    int		force_refresh;      // =1时需要刷新画面,请求立即刷新画面的意思
    int		paused;             // =1时暂停,=0时播放
    int		last_paused;        // 暂存“暂停”/“播放”状态
    int		queue_attachments_req;
    int		seek_req;           // 标识一次seek请求
    int		seek_flags;         // seek标志,诸如AVSEEK_FLAG_BYTE等
    int64_t		seek_pos;       // 请求seek的目标位置(当前位置+增量)
    int64_t		seek_rel;       // 本次seek的位置增量
    int		read_pause_return;
    AVFormatContext *ic;        // iformat的上下文
    int		realtime;           // =1为实时流

    Clock	audclk;             // 音频时钟
    Clock	vidclk;             // 视频时钟
    Clock	extclk;             // 外部时钟

    FrameQueue	pictq;          // 视频Frame队列
    FrameQueue	subpq;          // 字幕Frame队列
    FrameQueue	sampq;          // 采样Frame队列

    Decoder auddec;             // 音频解码器
    Decoder viddec;             // 视频解码器
    Decoder subdec;             // 字幕解码器

    int audio_stream ;          // 音频流索引

    int av_sync_type;           // 音视频同步类型, 默认audio master

    double			audio_clock;            // 当前音频帧的PTS+当前帧Duration
    int             audio_clock_serial;     // 播放序列,seek可改变此值
    // 以下4个参数 非audio master同步方式使用
    double			audio_diff_cum;         // used for AV difference average computation
    double			audio_diff_avg_coef;
    double			audio_diff_threshold;
    int			audio_diff_avg_count;
    // end

    AVStream		*audio_st;              // 音频流
    PacketQueue		audioq;                 // 音频packet队列
    int			audio_hw_buf_size;          // SDL音频缓冲区的大小(字节为单位)
    // 指向待播放的一帧音频数据,指向的数据区将被拷入SDL音频缓冲区。若经过重采样则指向audio_buf1,
    // 否则指向frame中的音频
    uint8_t			*audio_buf;             // 指向需要重采样的数据
    uint8_t			*audio_buf1;            // 指向重采样后的数据
    unsigned int		audio_buf_size;     // 待播放的一帧音频数据(audio_buf指向)的大小
    unsigned int		audio_buf1_size;    // 申请到的音频缓冲区audio_buf1的实际尺寸
    int			audio_buf_index;            // 更新拷贝位置 当前音频帧中已拷入SDL音频缓冲区
    // 的位置索引(指向第一个待拷贝字节)
    // 当前音频帧中尚未拷入SDL音频缓冲区的数据量:
    // audio_buf_size = audio_buf_index + audio_write_buf_size
    int			audio_write_buf_size;
    int			audio_volume;               // 音量
    int			muted;                      // =1静音,=0则正常
    struct AudioParams audio_src;           // 音频frame的参数
#if CONFIG_AVFILTER
    struct AudioParams audio_filter_src;
#endif
    struct AudioParams audio_tgt;       // SDL支持的音频参数,重采样转换:audio_src->audio_tgt
    struct SwrContext *swr_ctx;         // 音频重采样context
    int frame_drops_early;              // 丢弃视频packet计数
    int frame_drops_late;               // 丢弃视频frame计数

    enum ShowMode {
        SHOW_MODE_NONE = -1,    // 无显示
        SHOW_MODE_VIDEO = 0,    // 显示视频
        SHOW_MODE_WAVES,        // 显示波浪,音频
        SHOW_MODE_RDFT,         // 自适应滤波器
        SHOW_MODE_NB
    } show_mode;

    // 音频波形显示使用
    int16_t sample_array[SAMPLE_ARRAY_SIZE];    // 采样数组
    int sample_array_index;                     // 采样索引
    int last_i_start;                           // 上一开始
    RDFTContext *rdft;                          // 自适应滤波器上下文
    int rdft_bits;                              // 自使用比特率
    FFTSample *rdft_data;                       // 快速傅里叶采样

    int xpos;
    double last_vis_time;
    SDL_Texture *vis_texture;       // 音频Texture

    SDL_Texture *sub_texture;       // 字幕显示
    SDL_Texture *vid_texture;       // 视频显示

    int subtitle_stream;            // 字幕流索引
    AVStream *subtitle_st;          // 字幕流
    PacketQueue subtitleq;          // 字幕packet队列

    double frame_timer;             // 记录最后一帧播放的时刻
    double frame_last_returned_time;    // 上一次返回时间
    double frame_last_filter_delay;     // 上一个过滤器延时

    int video_stream;               // 视频流索引
    AVStream *video_st;             // 视频流
    PacketQueue videoq;             // 视频队列
    double max_frame_duration;      // 一帧最大间隔. above this, we consider the jump a timestamp discontinuity
    struct SwsContext *img_convert_ctx; // 视频尺寸格式变换
    struct SwsContext *sub_convert_ctx; // 字幕尺寸格式变换
    int eof;            // 是否读取结束

    char *filename;     // 文件名
    int width, height, xleft, ytop; // 宽、高,x起始坐标,y起始坐标
    int step;           // =1 步进播放模式, =0 其他模式

#if CONFIG_AVFILTER
    int vfilter_idx;
    AVFilterContext *in_video_filter;   // the first filter in the video chain
    AVFilterContext *out_video_filter;  // the last filter in the video chain
    AVFilterContext *in_audio_filter;   // the first filter in the audio chain
    AVFilterContext *out_audio_filter;  // the last filter in the audio chain
    AVFilterGraph *agraph;              // audio filter graph
#endif
    // 保留最近的相应audio、video、subtitle流的steam index
    int last_video_stream, last_audio_stream, last_subtitle_stream;

    SDL_cond *continue_read_thread; // 当读取数据队列满了后进入休眠时,可以通过该condition唤醒读线程
} VideoState;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2153588.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录一个英语听力网站的开发

背景 在当前全球经济衰退的背景下&#xff0c;国内IT相关工作的竞争日益激烈。为了获得更多的职业机会&#xff0c;学习英语或许能为程序员打开一扇新的窗户。尤其是在国际化背景的远程工作中&#xff0c;英语协作沟通是必不可少的。 尽管我们大多数人从小到大都在学习英语&a…

使用Renesas R7FA8D1BH (Cortex®-M85)和微信小程序App数据传输

目录 概述 1 系统架构 1.1 系统结构 1.2 系统硬件框架结构 1.3 蓝牙模块介绍 2 微信小程序实现 2.1 UI介绍 2.2 代码实现 3 上位机功能实现 3.1 通信协议 3.2 系统测试 4 下位机功能实现 4.1 功能介绍 4.2 代码实现 4.3 源代码文件 5 测试 5.1 编译和下载代码…

RNN的反向传播

目录 1.RNN网络&#xff1a;通过时间反向传播(through time back propagate TTBP) 2.RNN梯度分析 2.1隐藏状态和输出 2.2正向传播&#xff1a; 2.3反向传播&#xff1a; 2.4问题瓶颈&#xff1a; 3.截断时间步分类&#xff1a; 4.截断策略比较 5.反向传播的细节 ​编辑…

大数据新视界 --大数据大厂之JavaScript在大数据前端展示中的精彩应用

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

浙大数据结构:05-树8 File Transfer

数据结构MOOC PTA习题 这道题考察并查集的操作&#xff0c;合并以及找根结点 机翻&#xff1a; 1、条件准备 node是数组存放1-N结点的根节点的&#xff0c;n为总结点数 #include <iostream> using namespace std;const int N 1e4 5; int node[N]; int n; 先初始化…

众数信科AI智能体政务服务解决方案——寻知智能笔录系统

政务服务解决方案 寻知智能笔录方案 融合民警口供录入与笔录生成需求 2分钟内生成笔录并提醒错漏 助办案人员二次询问 提升笔录质量和效率 寻知智能笔录系统 众数信科AI智能体 产品亮点 分析、理解行业知识和校验规则 AI实时提醒用户文书需注意部分 全文校验格式、内…

【在Linux世界中追寻伟大的One Piece】进程间关系与守护进程

目录 1 -> 进程组 1.1 -> 什么是进程组 1.2 -> 组长进程 2 -> 会话 2.1 -> 什么是会话 2.2 -> 如何创建会话 2.3 -> 会话ID(SID) 3 -> 控制终端 4 -> 作业控制 4.1 -> 什么是作业(job)和作业控制(Job Control) 4.2 -> 作业号 4.3…

Spring:项目中的统一异常处理和自定义异常

介绍异常的处理方式。在项目中&#xff0c;都会进行自定义异常&#xff0c;并且都是需要配合统一结果返回进行使用。 1.背景引入 &#xff08;1&#xff09;背景介绍 为什么要处理异常&#xff1f;如果不处理项目中的异常信息&#xff0c;前端访问我们后端就是显示访问失败的…

20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令

console:/dev # cat ttyUSB1 & console:/dev # echo AT > ttyUSB1 20240921在友善之臂的NanoPC-T6开发板上确认宸芯的数传模块CX6602N的AT命令 2024/9/21 21:03 【必须】Android12/Linux&#xff08;Buildroot&#xff09;都必须要&#xff01; 4、【Android12默认打开U…

电脑硬件-机械硬盘

简介 机械硬盘是电脑的主要存储媒介之一&#xff0c;通常用于存储一些文件资料或者学习视频笔记等比较大的内容。 结构 采用磁盘存储数据&#xff0c;使用温彻斯特的结构&#xff0c;特有四个特点&#xff1a; 1.磁头、盘片和运动机构安装在一个密封的腔体内。 2.盘片告诉旋…

一图快速看懂flink source的设计实现

文章目录 整体来说多个处理流程是解偶的&#xff0c;这样可以在面对多数据源情况下&#xff0c;能更加的灵活。 下面只展示了&#xff0c;主要的一些流程 下面补充一点&#xff0c;读取文件状态的保存&#xff0c;切分信息用了一个 ListState 来保存。具体要保存的信息&#x…

day2-1 app端文章查看

首先一共三张表 然后大致过程就是三层架构 用mp实现 具体出现的问题 1 测试的时候后端代码启动不了 先在maven clean一下 具体流程 然后执行完之后建议把这三个模块的target文件删除一下再运行 最后的话 如果还是报错 也是正常的 因为后边的东西都没写有些文件没有用到 2…

常见的中间件漏洞

Tomcat CVE-2017-12615 访问主页进行抓包 修改传参方式为put 放包进行连接 后台弱⼝令部署war包 访问主页试用默认账号密码tomcat/tomcat进行登录后 将哥斯拉生成的jsp木马文件压缩城成zip文件&#xff0c;然后再修改zip后缀文war 然后进行上传 使用哥斯拉进行测试连接 CVE-…

基于SpringBoot+Vue的在线酒店预订系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

微服务——网关登录校验(一)

1.网关登录校验 微服务中的网关登录校验是微服务架构中常见的一种安全机制&#xff0c;用于在请求到达微服务之前&#xff0c;对用户的身份进行验证&#xff0c;确保只有合法的用户才能访问相应的服务。 在微服务架构中&#xff0c;每个微服务都是独立部署的&#xff0c;它们之…

Apipost IDEA插件新升级,Apipost Helper上架IDEA插件市场

大家好&#xff01;今天向大家介绍一个非常方便的IDEA插件——Apipost Helper&#xff01;相信很多使用过Apipost的朋友在开发过程中都希望能够直接将编写好的API同步至Apipost&#xff0c;而无需手动填写。前段时间&#xff0c;Apipost推出了Apipost IDEA插件的内测版&#xf…

macOS平台编译libidn2库给iOS及macOS用

1.克隆源码: git clone https://gitlab.com/libidn/libidn2.git --recursive 2.安装依赖库: pkg-config也要安装 3.启动bootstrap生成configure 配置成功 configure生成成功

概率论与数理统计(2)

第一节博客已经整理了求导的公式&#xff0c;一些常用的概念。链接如下&#xff1a;高等数学基础&#xff08;1&#xff09;-CSDN博客。 第二节博客整理了微积分的公式及其相关概念。链接如下&#xff1a;高等数学基础&#xff08;2&#xff09;——微积分-CSDN博客 第三节博客…

主流卷积神经网络CNN总结

ResNet&#xff08;2015&#xff09;残差神经网络 残差结构 ResNet50具体卷积结构图 ResNeXt&#xff08;2016&#xff09;加入了分组卷积的思想&#xff0c;将原ResNet网络中的block替换成由group分组的block&#xff0c;两者得到的feature map一致&#xff0c;只是参数量更少…

计算机网络(月考一知识点)

文章目录 计算机网络背诵默写版计算机网络知识点&#xff08;月考1版&#xff09; 计算机网络背诵默写版 为我自己留个印记&#xff0c;本来荧光笔画的是没记住的&#xff0c;但是后面用紫色的&#xff0c;结果扫描的时候就看不见了。 计算机网络知识点&#xff08;月考1版&a…