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

文章目录

      • 播放器状态转换图
        • 播放器状态对应的消息:
      • 消息对象
      • 消息队列
      • 消息队列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);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

探探各个微前端框架

本文作者为 360 奇舞团前端开发工程师 微前端架构是为了在解决单体应用在一个相对长的时间跨度下,由于参与的人员、团队的增多、变迁,从一个普通应用演变成一个巨石应用(Frontend Monolith)后,随之而来的应用不可维护的问题。这类问题在企业级…

全新华为MateBook X Pro发布,将Ultra9放入980g超轻薄机身

2024年4月11日,在华为鸿蒙生态春季沟通会上全新的华为MateBook X Pro正式发布。该机以美学设计、创新科技以及智慧体验,追求重新定义Pro、重新定义旗舰,将颠覆消费者对传统轻薄本的认知。 华为MateBook X Pro追求极致轻薄与强大性能的完美结合…

怎样将PDF转成PPT,有免费的工具吗?

PDF转换为PPT的需求在现代办公和学习中越来越常见。很多人可能遇到过需要将PDF文件中的内容转移到PPT中以方便编辑和展示的情况。幸运的是,现在市面上有许多工具可以帮助我们实现这一目标,而且其中不乏一些免费的选项。本文将详细介绍如何使用这些免费工…

linux学习:栈

目录 顺序栈 结构 初始化一个空顺序栈 压栈 出栈 例子 十进制转八进制 链式栈 管理结构体的定义 初始化 压栈 出栈 顺序栈 顺序栈的实现,主要就是定义一块连续的内存来存放这些栈元素,同时为了方便管理, 再定义一个整数变量来代表…

计算机基础知识-第9章-存储的本质(2)——硬盘和文件系统基础知识

一、机械硬盘的原理 概括来说,硬盘的工作原理是利用特定的磁粒子的极性来记录数据。磁头在读取数据时,将磁力子的不同极性转换成不同的电脉冲信号,再利用数据转换器将这些原始信号变成电脑可以使用的数据,写的操作正好与此相反。…

前端docker jenkins nginx CI/CD持续集成持续部署-实战

最近用go react ts开发了一个todolist后端基本开发完了,前端采用CI/CD方式去部署。 步骤总结 先安装docker 和 docker-compose。安装jenkins镜像,跑容器的时候要配好数据卷。配置gitee或github(我这里使用gitee)在服务器上一定要创建好dokcer的数据卷,以便持久保存jenkin…

Transformer模型-decoder解码器,target mask目标掩码的简明介绍

今天介绍transformer模型的decoder解码器,target mask目标掩码 背景 解码器层是对前面文章中提到的子层的包装器。它接受位置嵌入的目标序列,并将它们通过带掩码的多头注意力机制传递。使用掩码是为了防止解码器查看序列中的下一个标记。它迫使模型仅使用…

pytorch实现胶囊网络(capsulenet)

胶囊网络在hinton刚提出来的时候小热过一段时间,之后热度并没有维持多久。vision transformer之后基本少有人问津了。不过这个模型思路挺独特的,值得研究一下。 这个模型的提出是为了解决CNN模型学习到的特征之间没有空间上的关系,从而对于各…

Sketch3D:用于草图到3D生成的样式一致性指南

Sketch3D: Style-Consistent Guidance for Sketch-to-3D Generation Sketch3D:用于草图到3D生成的样式一致性指南 Wangguandong Zheng 重试 错误原因 Southeast UniversityChina 重试 错误原因 wgdzhengseu.edu.cnHaifeng Xia 重试 错误原因 Southeast Universit…

CSS - 盒子模型、图片模糊、过渡效果、2D图移动、放大缩小、CSS动画、flex布局

盒子模型 CSS盒子模型是指在网页布局中,每个元素都被看作是一个矩形的盒子,这个盒子由内容区域、内边距、边框和外边距组成。盒子模型在CSS中用于确定元素在页面中的尺寸、位置和边距。 盒子模型由以下几个部分组成: 内容区域(…

行云堡垒国密算法应用与信创支持

一、 国密算法和信创的介绍 1.1 什么是国密算法 国密算法是国家密码管理局制定颁布的一系列的密码标准,即已经被国家密码局认定的国产密码算法,又称商用密码(是指能够实现商用密码算法的加密,解密和认证等功能的技术)…

Qlik Sense : Crosstable在数据加载脚本中使用交叉表

什么是Crosstable? 交叉表是常见的表格类型,特点是在两个标题数据正交列表之间显示值矩阵。如果要将数据关联到其他数据表格,交叉表通常不是最佳数据格式。 本主题介绍了如何逆透视交叉表,即,在数据加载脚本中使用 L…

批归一化(BN)在神经网络中的作用与原理

文章目录 1. 批归一化(BN)在神经网络中的作用与原理1.1 作用与优势1.2 原理与推导 2. 将BN应用于神经网络的方法2.1 训练时的BN 2. 将BN应用于神经网络的方法2.1 训练时的BN2.2 测试时的BN代码示例(Python): 3. BN的优…

机器学习-09-图像处理01-理论

总结 本系列是机器学习课程的系列课程,主要介绍机器学习中图像处理技术。 参考 02图像知识 色彩基础知识整理-色相、饱和度、明度、色调 图像特征提取(VGG和Resnet特征提取卷积过程详解) Python图像处理入门 【人工智能】PythonOpenCV…

基于python的天气数据可视化系统、Flask框架,爬虫采集天气数据,可视化分析

系统介绍 基于Python的天气预测可视化分析系统,该项目的主要流程和功能包括: 数据获取: 使用Python的pandas库从2345天气网(http://tianqi.2345.com/Pc/GetHistory)抓取山东省各市区县在2021年至2023年间的天气历史数…

【方法】PDF密码如何取消?

对于重要的PDF文件,很多人会设置密码保护,那后续不需要保护了,如何取消密码呢? 今天我们来看看,PDF的两种密码,即“限制密码”和“打开密码”,是如何取消的,以及忘记密码的情况要怎…

文献学习-33-一个用于生成手术视频摘要的python库

VideoSum: A Python Library for Surgical Video Summarization Authors: Luis C. Garcia-Peraza-Herrera, Sebastien Ourselin, and Tom Vercauteren Source: https://arxiv.org/pdf/2303.10173.pdf 这篇文章主要关注的是如何通过视频摘要来简化和可视化手术视频&#xff0c…

计算机基础知识-第4章-真值表和逻辑运算、位运算

一、真值表与逻辑运算 真值表 真值表是什么呢?我们来看百度百科的定义。表征逻辑事件输入和输出之间全部可能状态的表格。列出命题公式真假值的表。通常以1表示真,0 表示假。命题公式的取值由组成命题公式的命题变元的取值和命题联结词决定,…

开源监控zabbix对接可视化工具grafana教程

今天要给大家介绍的是开源监控工具zabbix对接可视化工具grafana问题。 有一定运维经验的小伙伴大抵都或多或少使用过、至少也听说过开源监控工具zabbix,更进一步的小伙伴可能知道zabbix在数据呈现方面有着明显的短板,因此需要搭配使用第三方的可视化工具…

Qlik Sense :use Peek function to Group by and Get Rowno

Question Row number based on groups of data Calculate row number for groups 有时候我们需要基于分组来对数据进行内部排序,例如一个iddate,把不同的属性的记录标记为123,又或者把重复记录标记出来 Solved: Calculate row number for…