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

文章目录

消息队列,用于发送,设置播放器的状态,实现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 播放器出现错误

消息对象

cpp 复制代码
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;

消息队列

cpp 复制代码
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

插入消息
cpp 复制代码
// 消息队列内部重新去构建 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;
}
获取消息
cpp 复制代码
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;
}
初始化消息
cpp 复制代码
// 消息队列初始化
void msg_queue_init(MessageQueue *q)
{
    memset(q, 0, sizeof(MessageQueue));
    q->mutex = SDL_CreateMutex();
    q->cond = SDL_CreateCond();
    q->abort_request = 1;
}
插入消息加锁
cpp 复制代码
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;
}
初始化消息
cpp 复制代码
void msg_init_msg(AVMessage *msg)
{
    memset(msg, 0, sizeof(AVMessage));
}
设置消息参数
cpp 复制代码
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);
}
消息队列初始化
cpp 复制代码
void msg_queue_init(MessageQueue *q)
{
    memset(q, 0, sizeof(MessageQueue));
    q->mutex = SDL_CreateMutex();
    q->cond = SDL_CreateCond();
    q->abort_request = 1;
}
清空消息
cpp 复制代码
 // 消息队列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);

}
销毁消息
cpp 复制代码
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);
}
启动消息队列
cpp 复制代码
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);
}
终止消息队列
cpp 复制代码
void msg_queue_abort(MessageQueue *q)
{
    SDL_LockMutex(q->mutex);
    q->abort_request = 1;
    SDL_CondSignal(q->cond);
    SDL_UnlockMutex(q->mutex);
}
删除消息
cpp 复制代码
// 消息删除 把队列里同一消息类型的消息全删除掉
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);
}
相关推荐
神仙别闹3 小时前
基于C#实现的(WinForm)模拟操作系统文件管理系统
java·git·ffmpeg
Fre丸子_16 小时前
ffmpeg之播放一个yuv视频
ffmpeg·音视频
yinqinggong18 小时前
从源码编译支持FFmpeg的OpenCV
opencv·ffmpeg
冰山一脚201319 小时前
ffmpeg添加sps,pps
ffmpeg
嘟嘟实验室2 天前
微信小程序xr-frame透明视频实现
微信小程序·ffmpeg·音视频·xr
泰勒朗斯2 天前
如何编译Opencv +ffmpeg linux 明明安装了ffmpeg但是opencv就是找不到
linux·opencv·ffmpeg
-Mr_X-2 天前
windows下srs流媒体服务器使用ffmpeg推流
ffmpeg
dvlinker2 天前
C++开源项目 VLC 源代码的交叉编译以及库的裁剪方法详解
ffmpeg·mingw-w64·msys2·cygwin·开源vlc·vlc编译·vlc裁剪
因我你好久不见3 天前
springboot java ffmpeg 视频压缩、提取视频帧图片、获取视频分辨率
java·spring boot·ffmpeg
cuijiecheng20183 天前
音视频入门基础:MPEG2-TS专题(21)——FFmpeg源码中,获取TS流的视频信息的实现
ffmpeg·音视频