FFmpeg: 自实现ijkplayer播放器--04消息队列设计

news2024/12/26 22:16:24

文章目录

      • 播放器状态转换图
        • 播放器状态对应的消息:
      • 消息对象
      • 消息队列
      • 消息队列api
        • 插入消息
        • 获取消息
        • 初始化消息
        • 插入消息加锁
        • 初始化消息
        • 设置消息参数
        • 消息队列初始化
        • 清空消息
        • 销毁消息
        • 启动消息队列
        • 终止消息队列
        • 删除消息

消息队列,用于发送,设置播放器的状态,实现ui界面,jikpalyer以及ffplay之间的通信

播放器状态转换图

实线箭头连接的状态变化通过 API 调⽤完成
虚线箭头连接的状态变化是通过 播放器内部执⾏完特定任务或者发⽣错误 ⽽⾃动发⽣的状态
变化
请添加图片描述

播放器状态对应的消息:
  • idle: MP_STATE_IDLE 闲置状态,刚完成构造的 FijkPlaye
  • initialized: MP_STATE_INITIALIZED 初始化完成状态,和 idle 状态相⽐,仅是多了输⼊媒体
    数据源的信息
  • async_preparing:MP_STATE_ASYNC_PREPARING 异步准备状态,进行打开媒体⽂件,打开解码器以及新建解码线程,新建数据 read 线程,打开⾳频输出设备,新建视频输出线程等
  • prepared:MP_STATE_PREPARED,完成指定任务后⾃动转化为此状态。此状态下已经缓冲并解码了⼀部分⾳视频数据,可以随时进⾏播放
  • started:MP_STATE_STARTED 媒体(视频、⾳频)正在播放中
  • paused:MP_STATE_PAUSED 媒体(视频、⾳频)播放暂停
  • completed:MP_STATE_COMPLETED 媒体(视频、⾳频)播放完成。 可重新从头开始播
    放。
  • stop: MP_STATE_STOPPED 播放器各种线程占⽤资源都已经释放。 ⾳频设备关闭
  • error: MP_STATE_ERROR 播放器出现错误

消息对象

typedef struct AVMessage {
    int what;           // 消息类型
    int arg1;           // 参数1
    int arg2;           // 参数2
    void *obj;          // 如果arg1 arg2还不够存储消息则使⽤该参数
    void (*free_l)(void *obj);  // 释放obj指向的函数
    struct AVMessage *next; // 下⼀个消息
} AVMessage;

消息队列

typedef struct MessageQueue {   // 消息队列
    AVMessage *first_msg, *last_msg;    // 消息头,消息尾部
    int nb_messages;    // 有多少个消息
    int abort_request;  // 请求终⽌消息队列
    SDL_mutex *mutex;   // 互斥量
    SDL_cond *cond;     // 条件变量
    AVMessage *recycle_msg; // 消息循环使⽤
    int recycle_count;  // 循环的次数,利⽤局部性原理
    int alloc_count;    // 分配的次数
} MessageQueue;

recycle_msg:
用于回收消息,消息使用链表进行存储,当消息取出时,通过recycle_msg链接该消息,重新用做新消息使用
作用:节省了对新消息申请空间,以及对取出的消息释放内存操作

消息队列api

插入消息
// 消息队列内部重新去构建 AVMessage(重新申请AVMessage,或者来自于recycle_msg)
// 新的消息插入到尾部
int msg_queue_put_private(MessageQueue *q, AVMessage *msg)
{
    AVMessage *msg1;

    if(q->abort_request)
        return -1;

    //1. 消息体使用回收的资源还是重新malloc
    msg1 = q->recycle_msg;
    if(msg1) {
        q->recycle_msg = msg1->next;
        q->recycle_count++;
    } else {
        q->alloc_count++;
        msg1 = (AVMessage *)av_malloc(sizeof(AVMessage));
    }

    *msg1 = *msg;
    msg1->next = NULL;

    if(!q->first_msg) {
        q->first_msg = msg1;
    } else {
        q->last_msg->next = msg1;
    }

    q->last_msg = msg1;
    q->nb_messages++;
    SDL_CondSignal(q->cond);
    return 0;
}
获取消息
int msg_queue_get(MessageQueue *q, AVMessage *msg, int block)
{
    AVMessage *msg1;
    int ret;

    SDL_LockMutex(q->mutex);

    for(;;) {
        if(q->abort_request) {
            ret = -1;
            break;
        }
        //获取消息
        msg1 = q->first_msg;
        if(msg1) {
            q->first_msg = msg1->next;
            if(!q->first_msg)
                q->last_msg = NULL;
            q->nb_messages--;
            *msg = *msg1;
            msg1->obj = NULL;
            msg1->next = q->recycle_msg;
            q->recycle_msg = msg1;
            ret =1;
            break;      // 记得这里有个break的
        } else if (!block) {
            ret = 0;
            break;
        } else {
            SDL_CondWait(q->cond, q->mutex);
        }
    }
    SDL_UnlockMutex(q->mutex);
    return ret;
}
初始化消息
// 消息队列初始化
void msg_queue_init(MessageQueue *q)
{
    memset(q, 0, sizeof(MessageQueue));
    q->mutex = SDL_CreateMutex();
    q->cond = SDL_CreateCond();
    q->abort_request = 1;
}
插入消息加锁
int msg_queue_put(MessageQueue *q, AVMessage *msg)
{
    int ret;
    SDL_LockMutex(q->mutex);
    ret = msg_queue_put_private(q, msg);
    SDL_UnlockMutex(q->mutex);
    return ret;
}

初始化消息
void msg_init_msg(AVMessage *msg)
{
    memset(msg, 0, sizeof(AVMessage));
}
设置消息参数
void msg_queue_put_simple1(MessageQueue *q, int what)
{
    AVMessage msg;
    msg_init_msg(&msg);
    msg.what = what;
    msg_queue_put(q, &msg);
}

// 释放msg的obj资源
void msg_obj_free_l(void *obj)
{
    av_free(obj);
}
//插入消息,带消息类型,带2个参数,带obj
void msg_queue_put_simple4(MessageQueue *q, int what, int arg1, int arg2, void *obj, int obj_len)
{
    AVMessage msg;
    msg_init_msg(&msg);
    msg.what = what;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    msg.obj = av_malloc(obj_len);
    memcpy(msg.obj, obj, obj_len);
    msg.free_l = msg_obj_free_l;
    msg_queue_put(q, &msg);
}
消息队列初始化
void msg_queue_init(MessageQueue *q)
{
    memset(q, 0, sizeof(MessageQueue));
    q->mutex = SDL_CreateMutex();
    q->cond = SDL_CreateCond();
    q->abort_request = 1;
}
清空消息
 // 消息队列flush,清空所有的消息
void msg_queue_flush(MessageQueue *q)
{
    AVMessage *msg, *msg1;

    SDL_LockMutex(q->mutex);
    for (msg = q->first_msg; msg != NULL; msg = msg1) { // 这个时候的obj没有清空?那会导致泄漏,实际是把消息对象暂存到了recycle_msg
        msg1 = msg->next;
        msg->next = q->recycle_msg;
        q->recycle_msg = msg;
    }
    q->last_msg = NULL;
    q->first_msg = NULL;
    q->nb_messages = 0;
    SDL_UnlockMutex(q->mutex);

}
销毁消息
void msg_queue_destroy(MessageQueue *q)
{
    msg_queue_flush(q);

   SDL_LockMutex(q->mutex);
   while(q->recycle_msg) {
       AVMessage *msg = q->recycle_msg;
       if (msg)
           q->recycle_msg = msg->next;
       msg_free_res(msg);
       av_freep(&msg);
   }
   SDL_UnlockMutex(q->mutex);

   SDL_DestroyMutex(q->mutex);
   SDL_DestroyCond(q->cond);
}
启动消息队列
void msg_queue_start(MessageQueue *q)
{
    SDL_LockMutex(q->mutex);
    q->abort_request = 0;
    // 插入一个消息
    AVMessage msg;
    msg_init_msg(&msg);
    msg.what = FFP_MSG_FLUSH;
    msg_queue_put_private(q, &msg);
    SDL_UnlockMutex(q->mutex);
}
终止消息队列
void msg_queue_abort(MessageQueue *q)
{
    SDL_LockMutex(q->mutex);
    q->abort_request = 1;
    SDL_CondSignal(q->cond);
    SDL_UnlockMutex(q->mutex);
}
删除消息
// 消息删除 把队列里同一消息类型的消息全删除掉
void msg_queue_remove(MessageQueue *q, int what)
{
    AVMessage **p_msg, *msg, *last_msg;
        SDL_LockMutex(q->mutex);

        last_msg = q->first_msg;

        if (!q->abort_request && q->first_msg) {
            p_msg = &q->first_msg;
            while (*p_msg) {
                msg = *p_msg;
                if (msg->what == what) {        // 同类型的消息全部删除
                    *p_msg = msg->next;
                    msg_free_res(msg);
                    msg->next = q->recycle_msg;     // 消息体回收
                    q->recycle_msg = msg;
                    q->nb_messages--;
                } else {
                    last_msg = msg;
                    p_msg = &msg->next;
                }
            }

            if (q->first_msg) {
                q->last_msg = last_msg;
            } else {
                q->last_msg = NULL;
            }
        }

        SDL_UnlockMutex(q->mutex);
}

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

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

相关文章

docker-compose yaml指定具体容器网桥ip网段subnet;docker创建即指定subnet;docker取消自启动

1、docker-compose yaml指定具体容器网桥ip网段subnet docker-compose 启动yaml有时可能的容器网段与宿主机的ip冲突导致宿主机上不了网,这时候可以更改yaml指定subnet 宿主机内网一般是192**,这时候容器可以指定172* version: 3.9 services:coredns:…

内网渗透-域环境的搭建

域环境的搭建 文章目录 域环境的搭建前言一、什么是域环境 什么是域内网基础知识点 二、域环境的搭建 1. 部署域结构2.如何加入域3.SRV出错及解决办法4.SRV记录注册不成功的可能原因 禁用域中的账户将计算机退出域添加域用户总结 前言 一、什么是域环境 什么是域 域是一种管…

盘点2024年最新可用免费云服务器

随着云计算技术的快速发展,越来越多的企业和个人开始使用云服务器来满足各种业务需求。云服务器作为云计算的核心服务之一,以其弹性扩展、按需付费等特点受到广泛关注。本文将为大家盘点2024年最新可用免费云服务器,助力大家轻松上云&#xf…

浏览器工作原理与实践--CSRF攻击:陌生链接不要随便点

在上一篇文章中我们讲到了XSS攻击,XSS 的攻击方式是黑客往用户的页面中注入恶意脚本,然后再通过恶意脚本将用户页面的数据上传到黑客的服务器上,最后黑客再利用这些数据进行一些恶意操作。XSS攻击能够带来很大的破坏性,不过另外一…

量子飞跃:从根本上改变复杂问题的解决方式

内容来源:量子前哨(ID:Qforepost) 编辑丨王珩 编译/排版丨沛贤 深度好文:1000字丨5分钟阅读 利用多功能量子比特的量子计算机已处于解决复杂优化问题的最前沿,例如旅行商问题,这是一个典型的…

Python实现时间序列ARIMA模型(附带超详细理论知识和完整代码实现)

文章目录 0 结果1 介绍2 建模2.1 预备知识2.1.1 ADF检验结果(单位根检验统计量)2.1.2 差分序列的白噪声检验(这里使用Ljung-Box检验)2.1.3 ARIMA模型(差分整合移动平均自回归模型)的三个参数:p,…

探索分布式技术--------------注册中心zookeeper

目录 一、ZooKeeper是什么 二、ZooKeeper的工作机制 三、ZooKeeper特点 四、ZooKeeper数据结构 五、ZooKeeper应用场景 5.1统一命名服务 5.2统一配置管理 5.3统一集群管理 5.4服务器动态上下线 5.5软负载均衡 六、ZooKeeper的选举机制 6.1第一次启动选举机制 6.2非…

家居网购项目(Ajax验证用户名+上传图片)

文章目录 1.Ajax验证用户名1.程序框架图2.修改MemberServlet3.修改login.jsp4.结果展示 2.Ajax判断验证码是否输入正确1.修改MemberServlet2.修改login.jsp3.结果展示 3.Ajax添加购物车1.程序框架图2.修改CartServlet2.修改index.jsp3.解决问题—未登录直接添加购物车&#xff…

Excel文件解析

在此模块的学习中,我们需要一个新的开源类库---Apahche POI开源类库。这个类库的用途是:解析并生成Excel文件(Word、ppt)。Apahche POI基于DOM方式进行解析,将文件直接加载到内存,所以速度比较快,适合Excel文件数据量不…

没有算法大佬,都是草台班子

没有算法大佬,都是草台班子。 最近除了工作之外,还有一些时间在和加我微信的小伙伴沟通,聊的内容大部分集中在如何快速有效的学习人工智能、入门人工智能的技巧。 其中,一个知乎过来加我微信的小伙伴的经历更是让我感触很深。 …

eclipse导入maven项目与配置使用本地仓库

前言 本人润国外了,发现不能用收费软件IDEA了,需要使用eclipse,这个免费。 但是早忘了怎么用了,在此总结下。 一、eclipse导入本地项目 1.选这个:open projects from file system… 2.找到项目文件夹,…

树莓派点亮双色LED

双色LED灯准确来说叫双基色LED灯,是指模块只能显示2种颜色,一般是红色和绿色,可以有三种状态 :灭,颜色1亮,颜色2亮,根据颜色组合的不同,分为红蓝双色,黄蓝双色,红绿双色等等。 接线:将引脚S(绿色)和中间引脚(红色)连接到Raspberry Pi的GPIO接口上,对Raspberry…

《QT实用小工具·二十六》运行时间记录

1、概述 源码放在文章末尾 运行时间记录,包含如下功能: 可以启动和停止服务,在需要的时候启动。 可以指定日志文件存放目录。 可以指定时间日志输出间隔。 可以单独追加一条记录到日志文件。 日志为文本格式,清晰明了。 软…

JavaScript排序大揭秘:手绘图解6大常见排序算法,一网打尽

前言 📫 大家好,我是南木元元,热爱技术和分享,欢迎大家交流,一起学习进步! 🍅 个人主页:南木元元 本文用图解总结梳理了6种常见的排序算法 ,如下👇&#xff1…

【热门话题】PyTorch:深度学习领域的强大工具

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 PyTorch:深度学习领域的强大工具一、PyTorch概述二、PyTorch核心特性…

WGCNA分析

目录 基本概念 基本原理 下游分析 基本概念 WGCNA其译为加权基因共表达网络分析。该分析方法旨在寻找协同表达的基因模块(module),并探索基因网络与关注的表型之间的关联关系,以及网络中的核心基因。 适用于复杂的数据模式(推荐5组(或者…

JavaEE企业开发新技术5

目录 2.18 综合应用-1 2.19 综合应用-2 2.20 综合应用-3 2.21 综合应用-4 2.22 综合应用-5 Synchronized : 2.18 综合应用-1 反射的高级应用 DAO开发中,实体类对应DAO的实现类中有很多方法的代码具有高度相似性,为了提供代码的复用性,降低…

Zookeeper与Kafka消息队列

目录 一、Zookeeper 1、zookeeper简介 2、zookeeper的特点 3、zookeeper的工作模式跟工作机制 3.1 工作模式: 3.2工作机制:​编辑 4、zookeeper应用场景及选举机制 4.1 应用场景: 4.2 选举机制: 4.2.1第一次启动选举机制…

<计算机网络自顶向下> CDN

视频服务挑战 规模性异构性:不同用户有不同的能力(比如有线接入和移动用户;贷款丰富和受限用户)解决方法是:分布式的应用层面的基础设施CDN 多媒体:视频 视频是固定速度显示的一系列图像的序列&#xff…

MySQL 使用C语言

一般使用MySQL很少用命令行,一般都是通过程序内部使用,MySQL也为不同的语言定制了不同的头文件和库函数,可以在自己的程序中通过包含头文件和编译时候链接库函数来使用MySQL。 现在一般安装MySQL的时候就会自动给你安装库函数和头文件。 可…