播放器是一个典型多线程工程,核心线程如读数据、视频解码、音频解码、视频渲染、音频输出等,此外还需要提供给内部或上层应用层接受各种事件的消息模块。该消息模块需要支持任意线程生产消息数据,同时也要支持任何时间终止等。下面我们来看下 ijkplayer 中消息的一个基本流程图
2.1 结构体对象定义
ff_ffmsg_queue.h 消息、队列结构体定义和功能函数
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
- // 消息结构体(节点)
- typedef struct AVMessage {
- int what; //消息类型
- int arg1; //整型可选参数
- int arg2; //整型可选参数
- void *obj; //无类型可选动态内存参数
- void (*free_l)(void *obj); //用于释放obj指针的内存
- } 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; //alloc次数
- } MessageQueue;
- // 消息初始化
- inline static void msg_init_msg(AVMessage *msg);
-
- // 便利生成消息并添加进队列函数
- inline static void msg_queue_put_simple1(MessageQueue *q, int what);
- inline static void msg_queue_put_simple2(MessageQueue *q, int what, int arg1);
- inline static void msg_queue_put_simple3(MessageQueue *q, int what, int arg1, int arg2);
- inline static void msg_queue_put_simple4(MessageQueue *q, int what, int arg1, int arg2, void *obj, int obj_len);
-
- // 队列中添加一条消息,发送信号量
- inline static int msg_queue_put(MessageQueue *q, AVMessage *msg);
-
- // 队列初始化,abort_request=1,初始化锁、信号量
- inline static void msg_queue_init(MessageQueue *q);
- // 清空消息队列,释放相关内存
- inline static void msg_queue_flush(MessageQueue *q);
- // 销毁队列,abort_request=1
- inline static void msg_queue_destroy(MessageQueue *q);
- // 终止队列,abort_request=1,发送信号量通知其他线程
- inline static void msg_queue_abort(MessageQueue *q);
- // 启动队列,abort_request=0,队列中添加一个what为0的消息,发送信号量
- inline static void msg_queue_start(MessageQueue *q);
- // 获取队头一条消息,block:无消息时是否阻塞
- inline static int msg_queue_get(MessageQueue *q, AVMessage *msg, int block)
- // 移除队列中what类型的消息
- inline static void msg_queue_remove(MessageQueue *q, int what);
2.3 核心函数分析
- // 队列中添加一条消息
- inline static 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;
- }
-
- inline static int msg_queue_put_private(MessageQueue *q, AVMessage *msg)
- {
- AVMessage *msg1;
-
- if (q->abort_request)
- return -1; // 队列中止,直接返回
-
- #ifdef FFP_MERGE
- msg1 = av_malloc(sizeof(AVMessage)); // 无重用下直接分配内存
- #else
- msg1 = q->recycle_msg;
- if (msg1) { // 存在重用对象:将重用对象的next赋值给队列重用对象,重用+1
- q->recycle_msg = msg1->next;
- q->recycle_count++;
- } else { // 不存在重用对象:分配一个内存空间,alloc+1
- q->alloc_count++;
- msg1 = av_malloc(sizeof(AVMessage));
- }
- #ifdef FFP_SHOW_MSG_RECYCLE
- int total_count = q->recycle_count + q->alloc_count;
- if (!(total_count % 10)) { // alloc及重用消息数量日志输出
- av_log(NULL, AV_LOG_DEBUG, "msg-recycle \t%d + \t%d = \t%d\n", q->recycle_count, q->alloc_count, total_count);
- }
- #endif
- #endif
- if (!msg1)
- return -1;
-
- *msg1 = *msg; // 拷贝msg(浅拷贝,obj内存不会拷贝)
- msg1->next = NULL; // 下一个NULL,最为最后一个节点
-
- if (!q->last_msg) // 无队首,放队首
- q->first_msg = msg1;
- else // 当前队尾的下一个指向新消息(即最后)
- q->last_msg->next = msg1;
- q->last_msg = msg1; // 改变对尾指针
- q->nb_messages++; // 队列数量加1
- SDL_CondSignal(q->cond); // 发送信号量
- return 0;
- }
-
- // 获取队头一条消息,block:无消息时是否阻塞
- /* return < 0 if aborted, 0 if no msg and > 0 if msg. */
- inline static 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--; // 队列数量减1
- *msg = *msg1; // 浅拷贝
- msg1->obj = NULL; // 原队首obj变量置空(内存不能释放,否则拷贝的msg对象参数受影响)
- #ifdef FFP_MERGE
- av_free(msg1); // 释放临时变量内存
- #else
- msg1->next = q->recycle_msg; // 将已取出的这个消息标记为重用对象,之前的设置为此对象的下一个
- q->recycle_msg = msg1;
- #endif
- ret = 1;
- break;
- } else if (!block) { // 队首不存在,不等待返回
- ret = 0;
- break;
- } else {
- // 该函数会先释放锁,等待信号量条件后,重新加锁
- SDL_CondWait(q->cond, q->mutex); // 队首不存在,等待信号量后继续执行(循环)
- }
- }
- SDL_UnlockMutex(q->mutex);
- return ret;
- }
-
- // 队列清空,释放内存或全部放入重用池
- inline static void msg_queue_flush(MessageQueue *q)
- {
- AVMessage *msg, *msg1;
-
- SDL_LockMutex(q->mutex);
- for (msg = q->first_msg; msg != NULL; msg = msg1) {
- msg1 = msg->next; //取出下一条消息
- #ifdef FFP_MERGE
- av_freep(&msg); //直接释放消息内存
- #else
- msg->next = q->recycle_msg; // 将msg设置q->recycle_msg,q->recycle_msg设置重用池中下一个
- q->recycle_msg = msg;
- #endif
- }
- q->last_msg = NULL;
- q->first_msg = NULL;
- q->nb_messages = 0;
- SDL_UnlockMutex(q->mutex);
- }
-
- // 队列销毁
- inline static 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);
- }
-
- inline static void msg_free_res(AVMessage *msg)
- {
- if (!msg || !msg->obj)
- return;
- assert(msg->free_l);
- msg->free_l(msg->obj);
- msg->obj = NULL;
- }
3.1 初始化
OC层 IJKFFMoviePlayerController
- - (id)initWithContentURLString:(NSString *)aUrlString
- withOptions:(IJKFFOptions *)options
- {
- self = [super init];
- if (self) {
- ......
- // init player
- _mediaPlayer = ijkmp_ios_create(media_player_msg_loop); //播放器初始化,传入消息函数指针
- _msgPool = [[IJKFFMoviePlayerMessagePool alloc] init]; //消息重用池
- ......
- }
- return self;
- }
OC-C层 ijkplayer_ios
- IjkMediaPlayer *ijkmp_ios_create(int (*msg_loop)(void*))
- {
- //创建IjkMediaPlayer对象(包括FFPlayer初始化),消息函数继续向下传递
- IjkMediaPlayer *mp = ijkmp_create(msg_loop);
- ......
- return mp;
- }
c层 ijkplayer
- IjkMediaPlayer *ijkmp_create(int (*msg_loop)(void*))
- {
- IjkMediaPlayer *mp = (IjkMediaPlayer *) mallocz(sizeof(IjkMediaPlayer));
- ......
-
- mp->ffplayer = ffp_create(); // 初始化FFPlayer,初始化消息队列
- ......
-
- mp->msg_loop = msg_loop; // 函数指针赋值给IjkMediaPlayer的msg_loop
-
- return mp;
- }
c层 ff_ffplay
- FFPlayer *ffp_create()
- {
- ......
- FFPlayer* ffp = (FFPlayer*) av_mallocz(sizeof(FFPlayer));
- ......
-
- msg_queue_init(&ffp->msg_queue); // 消息队列初始化
- ......
- return ffp;
- }
ijkplayer
在ijkmp_prepare_async_l中我们看到在ff_msg_loop线程中调用了 ijkmp_msg_loop函数,这个函数中调用了msg_loop 函数,并传递了自身对象作为参数,由初始化那里可知,msg_loop指向IJKFFMoviePlayerController中的media_player_msg_loop函数。
- // 播放器准备播放
- static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
- {
- ......
- ijkmp_change_state_l(mp, MP_STATE_ASYNC_PREPARING); // 添加一条消息,msg_queue_start未调用过之前无效
-
- msg_queue_start(&mp->ffplayer->msg_queue); // 消息队列启动
-
- // released in msg_loop
- ijkmp_inc_ref(mp);
- mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");
- // msg_thread is detached inside msg_loop
-
- return 0;
- }
-
- static int ijkmp_msg_loop(void *arg)
- {
- IjkMediaPlayer *mp = arg;
- int ret = mp->msg_loop(arg); // 调用msg_loop函数
- return ret;
- }
-
- void ijkmp_change_state_l(IjkMediaPlayer *mp, int new_state)
- {
- mp->mp_state = new_state;
- ffp_notify_msg1(mp->ffplayer, FFP_MSG_PLAYBACK_STATE_CHANGED);
- }
3.3 消息回调
IJKFFMoviePlayerController
- // 应用层播放器注册回调函数
- int media_player_msg_loop(void* arg)
- {
- @autoreleasepool {
- IjkMediaPlayer *mp = (IjkMediaPlayer*)arg;
- __weak IJKFFMoviePlayerController *ffpController = ffplayerRetain(ijkmp_set_weak_thiz(mp, NULL));
- while (ffpController) { //ff_msg_loop线程中保持循环,循环条件:当前控制器存在
- @autoreleasepool {
- IJKFFMoviePlayerMessage *msg = [ffpController obtainMessage]; // OC对象便于消息分发,obtainMessage获取oc重用池中一个对象,不存在时创建
- if (!msg)
- break;
-
- int retval = ijkmp_get_msg(mp, &msg->_msg, 1); //读取消息,无时阻塞等待
- if (retval < 0) // 消息队列终止
- break;
-
- // block-get should never return 0
- assert(retval > 0);
- [ffpController performSelectorOnMainThread:@selector(postEvent:) withObject:msg waitUntilDone:NO]; // 分发至主线程
- }
- }
-
- // retained in prepare_async, before SDL_CreateThreadEx
- ijkmp_dec_ref_p(&mp);
- return 0;
- }
- }
-
- // 应用层播放器实现
- - (void)postEvent: (IJKFFMoviePlayerMessage *)msg
- {
- if (!msg)
- return;
-
- AVMessage *avmsg = &msg->_msg; // 取出消息原始数据
- switch (avmsg->what) { // OC基础播放器业务处理或外抛
- case FFP_MSG_FLUSH:
- break;
- case FFP_MSG_ERROR: {
- NSLog(@"FFP_MSG_ERROR: %d\n", avmsg->arg1);
-
- [self setScreenOn:NO];
-
- [[NSNotificationCenter defaultCenter]
- postNotificationName:IJKMPMoviePlayerPlaybackStateDidChangeNotification
- object:self];
-
- [[NSNotificationCenter defaultCenter]
- postNotificationName:IJKMPMoviePlayerPlaybackDidFinishNotification
- object:self
- userInfo:@{
- IJKMPMoviePlayerPlaybackDidFinishReasonUserInfoKey: @(IJKMPMovieFinishReasonPlaybackError),
- @"error": @(avmsg->arg1)}];
- break;
- }
- ......
- default:
- // NSLog(@"unknown FFP_MSG_xxx(%d)\n", avmsg->what);
- break;
- }
-
- [_msgPool recycle:msg]; // 使用完毕,放入重用池
- }
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓