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

news2025/2/27 21:11:26

文章目录

      • 播放器状态转换图
        • 播放器状态对应的消息:
      • 消息对象
      • 消息队列
      • 消息队列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/1585201.html

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

相关文章

Spring 如何组织项目-ApiHug准备-工具篇-007

🤗 ApiHug {Postman|Swagger|Api...} 快↑ 准√ 省↓ GitHub - apihug/apihug.com: All abou the Apihug apihug.com: 有爱,有温度,有质量,有信任ApiHug - API design Copilot - IntelliJ IDEs Plugin | Marketplace ApiHug …

贪心算法|452.用最少数量的箭引爆气球

力扣题目链接 class Solution { private:static bool cmp(const vector<int>& a, const vector<int>& b) {return a[0] < b[0];} public:int findMinArrowShots(vector<vector<int>>& points) {if (points.size() 0) return 0;sort(p…

【JavaEE】_Spring MVC项目获取Session

目录 1. 使用servlet原生方法获取Session 1.1 错误获取方法 1.2 正确获取方法 2. 使用Spring注解获取Session 3. 使用Spring内置对象获取Session 1. 使用servlet原生方法获取Session .java文件内容如下&#xff1a; setSession方法用于设置Session对象的内容&#xff1b;…

文件输入/输出流(I/O)

文章目录 前言一、文件输入\输出流是什么&#xff1f;二、使用方法 1.FileInputStream与FileOutputStream类2.FileReader与FileWriter类总结 前言 对于文章I/O(输入/输出流的概述)&#xff0c;有了下文。这篇文章将具体详细展述如何向磁盘文件中输入数据&#xff0c;或者读取磁…

面试八股——数据库——事务

概述 事务的特性&#xff08;ACID&#xff09; 原子性&#xff1a;事务是不可分割的操作单元&#xff0c;要么全部成功&#xff0c;要么全部失败。 一致性&#xff1a;事务结束时&#xff0c;所有数据都必须保证一致状态。 隔离性&#xff1a;事务在独立环境运行&#xff0c;…

Burp Suite Professional 2024.3.1 for Windows x64 - 领先的 Web 渗透测试软件

Burp Suite Professional 2024.3.1 for Windows x64 - 领先的 Web 渗透测试软件 世界排名第一的 Web 渗透测试工具包 请访问原文链接&#xff1a;Burp Suite Professional 2024.3.1 for Windows x64 - 领先的 Web 渗透测试软件&#xff0c;查看最新版。原创作品&#xff0c;转…

OpenGL Assimp 加载3D模型介绍

OpenGL Assimp 加载3D模型介绍 Assimp对应模型结构体解说 所有的模型、场景数据都包含在scene对象中&#xff0c;如所有的材质和Mesh。同样&#xff0c;场景的根节点引用也包含在这个scene对象中 场景的Root node&#xff08;根节点&#xff09;可能也会包含很多子节点和一个…

微服务面试题一

1.SOA、分布式、微服务之间有什么关系和区别&#xff1f; 分布式架构是指将单体架构中的各个部分拆分&#xff0c;然后部署不同的机器或进程中去&#xff0c;SOA和微服务基 本上都是分布式架构的SOA是⼀种⾯向服务的架构&#xff0c;系统的所有服务都注册在总线上&#xff0c;…

“植物大战僵尸:机器学习引领游戏AI新革命“

上期回顾&#xff1a; 今天给大家推荐一个Gtihub开源项目&#xff1a;PythonPlantsVsZombies&#xff0c;翻译成中就是植物大战僵尸。 《植物大战僵尸》是一款极富策略性的小游戏。可怕的僵尸即将入侵&#xff0c;每种僵尸都有不同的特点&#xff0c;例如铁桶僵尸拥有极强的抗…

开源推荐榜【Casbin.NET 一个为.NET(C#)项目设计的高效且强大的访问控制库】

Casbin 是一个强大高效的开源访问控制库&#xff0c;支持各种 访问控制模型 , 如 ACL, RBAC, ABAC 等。 我们希望可以让指定的主体 subject&#xff0c;可以访问指定的资源 object&#xff0c;访问可以是读和写。这就是 Casbin 使用最广泛的方式。也称为 { subject, object, ac…

【MATLAB源码-第20期】基于matlab的短波通信多径信道仿真,多径数目为3,用QPSK来测试误码率效果输出误码率对比图。

1、算法描述 短波通信是一种使用短波频段的无线电通信技术&#xff0c;它具有以下特点&#xff1a; 1. 频段范围&#xff1a;短波通信通常使用3MHz到30MHz之间的频段。这个频段之所以称为“短波”&#xff0c;是因为它的波长相对较短&#xff0c;适合进行长距离通信。 2. 长距…

大语言模型的指令调优:综述

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 论文标题&#xff1a;Instruction Tuning for Large Language Models: A Survey 论文地址&#xff1a;https://arxiv.org/abs/2308.10792 指令调优是提升大语言模型&#xff08;LLMs&#xff09;性能…

前端招聘:几招识别注水简历,不懂技术也能挤出水分。

现在的前端开发市场鱼目混珠&#xff0c;尤其是某些培训机构经常鼓动学院美&#xff08;造&#xff09;化&#xff08; 假&#xff09;简历&#xff0c;搞得很多前端招聘经验缺乏&#xff0c;又急需用人单位公司损失不小&#xff0c;满以为找到合适的人员了&#xff0c;其实是频…

倍思和南卡开放式耳机哪个好?深度测评剖析实力王!

​开放式耳机凭借其新颖的佩戴设计和优秀的音质体验&#xff0c;正逐渐赢得越来越多消费者的喜爱。在众多品牌中&#xff0c;南卡、倍思等业界巨头推出的开放式耳机尤为引人注目。这些耳机是否真的名副其实&#xff1f;它们又有哪些特色和优缺点&#xff1f;为了解答这些问题&a…

Canal扩展篇

1.Canal介绍 Canal官网&#xff1a;https://github.com/alibaba/canal Canal 主要用途是基于 MySQL 数据库增量日志解析&#xff0c;提供增量数据订阅和消费&#xff0c;工作原理如下&#xff1a; Canal 模拟 MySQL slave 的交互协议&#xff0c;伪装自己为 MySQL slave &…

FPGA - 以太网UDP通信(一)

一&#xff0c;简述以太网 以太网简介 ​以太网是一种计算机局域网技术。IEEE组织的IEEE 802.3标准制定了以太网的技术标准&#xff0c;它规定了包括物理层的连线、电子信号和介质访问层协议的内容。 ​ 以太网类型介绍 以太网是现实世界中最普遍的一种计算机网络。以太网有…

流程图高亮跟踪 可自定义渲染图片

【流程跟踪】获取流程定义图 获取代码如下&#xff1a; ProcessDefinition processDefinition repositoryService.createProcessDefinitionQuery().processDefinitionKey("leave").singleResult();String diagramResourceName processDefinition.getDiagramResour…

恒流电源驱动惠斯通电桥输出电压计算

笔记 并联分流/串联分压贯穿始终 先通过式子计算每条路流经的电流大小 &#xff08;1-(支路电阻)/(所有电阻) &#xff09;* 电流源I 每条之路电流大小知道了。就可以知道每条路中间点输出 支路电流*电阻。 输出即&#xff1a;中间点输出电位差。同理可以通过阻值变化推…

力扣HOT100 - 240. 搜索二维矩阵 II

解题思路&#xff1a; 从左下角开始&#xff0c;根据条件删除行和列。 class Solution {public boolean searchMatrix(int[][] matrix, int target) {int row matrix.length - 1;int col matrix[0].length - 1;int l 0;while (row > 0 && l < col) {if (targ…

深入理解java虚拟机-类的生命周期-初始化阶段

针对P2的字节码解释&#xff1a; 操作数栈里面放1&#xff0c;然后对Demo1.value的static变量进行赋值操作&#xff0c;从栈里面拿1赋值给Demo1.value的static变量 P3的情况由于源码是把 public static int value 1; static {value 2; }颠倒为&#xff1a; static {value …