本文对应的ijkplayer版本为: k0.7.9
ijkplayer iOS初始化
初始化代码
id player = [[IJKFFMoviePlayerController alloc] initWithContentURL:url withOptions:options];
会调用
- (id)initWithContentURLString:(NSString *)aUrlString
withOptions:(IJKFFOptions *)options
{
if (aUrlString == nil)
return nil;
self = [super init];
if (self) {ijkmp_global_init();//主要是ffmpeg的初始化工作, ijkplayer是对ffmpeg的封装ijkmp_global_set_inject_callback(ijkff_inject_callback);//注册ijkplayer事件相关的回调函数[IJKFFMoviePlayerController checkIfFFmpegVersionMatch:NO];//检查ijkplayer版本号是否匹配if (options == nil)options = [IJKFFOptions optionsByDefault];// IJKFFIOStatRegister(IJKFFIOStatDebugCallback);// IJKFFIOStatCompleteRegister(IJKFFIOStatCompleteDebugCallback);// init fields_scalingMode = IJKMPMovieScalingModeAspectFit;_shouldAutoplay = YES;memset(&_asyncStat, 0, sizeof(_asyncStat));memset(&_cacheStat, 0, sizeof(_cacheStat));_monitor = [[IJKFFMonitor alloc] init];// init media resource_urlString = aUrlString;// init player 此处很重要 设置iOS的ijkplayer使用的FFmpeg播放器 _mediaPlayer = ijkmp_ios_create(media_player_msg_loop);_msgPool = [[IJKFFMoviePlayerMessagePool alloc] init];IJKWeakHolder *weakHolder = [IJKWeakHolder new];weakHolder.object = self;//此处向FFmpeg注册IJKFFMoviePlayerControllerijkmp_set_weak_thiz(_mediaPlayer, (__bridge_retained void *) self);ijkmp_set_inject_opaque(_mediaPlayer, (__bridge_retained void *) weakHolder);ijkmp_set_ijkio_inject_opaque(_mediaPlayer, (__bridge_retained void *)weakHolder);ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "start-on-prepared", _shouldAutoplay ? 1 : 0);// init video sink_glView = [[IJKSDLGLView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];//此处生成openGL画布_glView.shouldShowHudView = NO;_view = _glView;[_glView setHudValue:nil forKey:@"scheme"];[_glView setHudValue:nil forKey:@"host"];[_glView setHudValue:nil forKey:@"path"];[_glView setHudValue:nil forKey:@"ip"];[_glView setHudValue:nil forKey:@"tcp-info"];[_glView setHudValue:nil forKey:@"http"];[_glView setHudValue:nil forKey:@"tcp-spd"];[_glView setHudValue:nil forKey:@"t-prepared"];[_glView setHudValue:nil forKey:@"t-render"];[_glView setHudValue:nil forKey:@"t-preroll"];[_glView setHudValue:nil forKey:@"t-http-open"];[_glView setHudValue:nil forKey:@"t-http-seek"];self.shouldShowHudView = options.showHudView;ijkmp_ios_set_glview(_mediaPlayer, _glView);ijkmp_set_option(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "overlay-format", "fcc-_es2");
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];
// init audio sink[[IJKAudioKit sharedInstance] setupAudioSession];[options applyTo:_mediaPlayer];_pauseInBackground = NO;// init extra_keepScreenOnWhilePlaying = YES;[self setScreenOn:YES];_notificationManager = [[IJKNotificationManager alloc] init];[self registerApplicationObservers];
}
return self;
}
其中重要的事件有 当下列事件发生时就会回调对应的函数
AVAPP_EVENT_WILL_HTTP_OPEN http通道 打开事件
AVAPP_CTRL_WILL_TCP_OPEN tcp通道打开事件
AVAPP_CTRL_DID_TCP_OPEN tcp控制通道的打开事件
// NOTE: could be called from multiple thread
static int ijkff_inject_callback(void *opaque, int message, void *data, size_t data_size)
{
IJKWeakHolder weakHolder = (__bridge IJKWeakHolder)opaque;
IJKFFMoviePlayerController *mpc = weakHolder.object;
if (!mpc)
return 0;
switch (message) {case AVAPP_CTRL_WILL_CONCAT_SEGMENT_OPEN:return onInjectIOControl(mpc, mpc.segmentOpenDelegate, message, data, data_size);case AVAPP_CTRL_WILL_TCP_OPEN:return onInjectTcpIOControl(mpc, mpc.tcpOpenDelegate, message, data, data_size);case AVAPP_CTRL_WILL_HTTP_OPEN:return onInjectIOControl(mpc, mpc.httpOpenDelegate, message, data, data_size);case AVAPP_CTRL_WILL_LIVE_OPEN:return onInjectIOControl(mpc, mpc.liveOpenDelegate, message, data, data_size);case AVAPP_EVENT_ASYNC_STATISTIC:return onInjectAsyncStatistic(mpc, message, data, data_size);case IJKIOAPP_EVENT_CACHE_STATISTIC:return onInectIJKIOStatistic(mpc, message, data, data_size);case AVAPP_CTRL_DID_TCP_OPEN:return onInjectTcpIOControl(mpc, mpc.tcpOpenDelegate, message, data, data_size);case AVAPP_EVENT_WILL_HTTP_OPEN:case AVAPP_EVENT_DID_HTTP_OPEN:case AVAPP_EVENT_WILL_HTTP_SEEK:case AVAPP_EVENT_DID_HTTP_SEEK:return onInjectOnHttpEvent(mpc, message, data, data_size);default: {return 0;}
}
}
//生成默认的ijkplayer配置
+ (IJKFFOptions *)optionsByDefault
{
IJKFFOptions *options = [[IJKFFOptions alloc] init];
[options setPlayerOptionIntValue:30 forKey:@"max-fps"];
[options setPlayerOptionIntValue:0 forKey:@"framedrop"];
[options setPlayerOptionIntValue:3 forKey:@"video-pictq-size"];
[options setPlayerOptionIntValue:0 forKey:@"videotoolbox"];
[options setPlayerOptionIntValue:960 forKey:@"videotoolbox-max-frame-width"];[options setFormatOptionIntValue:0 forKey:@"auto_convert"];
[options setFormatOptionIntValue:1 forKey:@"reconnect"];
[options setFormatOptionIntValue:30 * 1000 * 1000 forKey:@"timeout"];
[options setFormatOptionValue:@"ijkplayer" forKey:@"user-agent"];options.showHudView = NO;return options;
}
// init player
_mediaPlayer = ijkmp_ios_create(media_player_msg_loop); //此处很重要 设置iOS的ijkplayer使用的FFmpeg播放器
_msgPool = [[IJKFFMoviePlayerMessagePool alloc] init];//创建ijkplayer的消息循环
IJKWeakHolder *weakHolder = [IJKWeakHolder new];
weakHolder.object = self;
ijkmp_set_weak_thiz(_mediaPlayer, (__bridge_retained void *) self);ijkmp_set_inject_opaque(_mediaPlayer, (__bridge_retained void *) weakHolder);ijkmp_set_ijkio_inject_opaque(_mediaPlayer, (__bridge_retained void *)weakHolder);ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, "start-on-prepared", _shouldAutoplay ? 1 : 0);
看一下
IjkMediaPlayer ijkmp_ios_create(int (*msg_loop)(void))
{
IjkMediaPlayer *mp = ijkmp_create(msg_loop);
if (!mp)
goto fail;
mp->ffplayer->vout = SDL_VoutIos_CreateForGLES2();//并设置视频通过OpenGLES2.0来处理
if (!mp->ffplayer->vout)goto fail;mp->ffplayer->pipeline = ffpipeline_create_from_ios(mp->ffplayer);//设置iOS的音频
if (!mp->ffplayer->pipeline)goto fail;return mp;
fail:
ijkmp_dec_ref_p(&mp);
return NULL;
}
视频部分的创建比较单一 就是通过openGL来处理
音频部分单独看一下:
IJKFF_Pipeline *ffpipeline_create_from_ios(FFPlayer *ffp)
{
IJKFF_Pipeline *pipeline = ffpipeline_alloc(&g_pipeline_class, sizeof(IJKFF_Pipeline_Opaque));
if (!pipeline)
return pipeline;
IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
opaque->ffp = ffp;
pipeline->func_destroy = func_destroy;
//注意这里音视频有点区别 对于视频来说创建的是视频解码器 对于音频来说创建的是音频播放器 这里音视频是不同的这一点一定要注意
pipeline->func_open_video_decoder = func_open_video_decoder;//这里创建视频解码器
pipeline->func_open_audio_output = func_open_audio_output;//这里创建音频播放器
return pipeline;
}
pipeline->func_open_video_decoder = func_open_video_decoder;
这里创建视频的解码器 这里分为是否使用硬解码
ffp->videotoolbox需要在播前通过如下方法配置:
ijkmp_set_option_int(_mediaPlayer, IJKMP_OPT_CATEGORY_PLAYER, “videotoolbox”, 1);
static IJKFF_Pipenode *func_open_video_decoder(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
IJKFF_Pipenode* node = NULL;
IJKFF_Pipeline_Opaque *opaque = pipeline->opaque;
if (ffp->videotoolbox) {
//这里如果是采用硬解码 使用videotoolbox来硬解码
node = ffpipenode_create_video_decoder_from_ios_videotoolbox(ffp);
if (!node)
ALOGE(“vtb fail!!! switch to ffmpeg decode!!!! \n”);
}
if (node == NULL) {
//如果没有设置硬解码 采用FFmpeg来软解
node = ffpipenode_create_video_decoder_from_ffplay(ffp);
ffp->stat.vdec_type = FFP_PROPV_DECODER_AVCODEC;
opaque->is_videotoolbox_open = false;
} else {
ffp->stat.vdec_type = FFP_PROPV_DECODER_VIDEOTOOLBOX;
opaque->is_videotoolbox_open = true;
}
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_DECODER_OPEN, opaque->is_videotoolbox_open);
return node;
}
这里创建iOS的音频播放器
pipeline->func_open_audio_output = func_open_audio_output;//这里创建iOS的音频播放器
static SDL_Aout *func_open_audio_output(IJKFF_Pipeline *pipeline, FFPlayer *ffp)
{
return SDL_AoutIos_CreateForAudioUnit();
}
通过以上调用 音频播放器这样创建
SDL_Aout *SDL_AoutIos_CreateForAudioUnit()
{
SDL_Aout *aout = SDL_Aout_CreateInternal(sizeof(SDL_Aout_Opaque));
if (!aout)
return NULL;
// SDL_Aout_Opaque *opaque = aout->opaque;aout->free_l = aout_free_l;
aout->open_audio = aout_open_audio;
aout->pause_audio = aout_pause_audio;
aout->flush_audio = aout_flush_audio;
aout->close_audio = aout_close_audio;aout->func_set_playback_rate = aout_set_playback_rate;
aout->func_set_playback_volume = aout_set_playback_volume;
aout->func_get_latency_seconds = auout_get_latency_seconds;
aout->func_get_audio_persecond_callbacks = aout_get_persecond_callbacks;
return aout;
}
//注意这句
aout->open_audio = aout_open_audio; 这里创建音频播放器
这个函数这个通过IJKSDLAudioQueueController 来创建音频播放器
static int aout_open_audio(SDL_Aout *aout, const SDL_AudioSpec *desired, SDL_AudioSpec *obtained)
{
assert(desired);
SDLTRACE(“aout_open_audio()\n”);
SDL_Aout_Opaque *opaque = aout->opaque;
opaque->aoutController = [[IJKSDLAudioQueueController alloc] initWithAudioSpec:desired];
// opaque->aoutController = [[IJKSDLAudioUnitController alloc] initWithAudioSpec:desired];
if (!opaque->aoutController) {
ALOGE(“aout_open_audio_n: failed to new AudioTrcak()\n”);
return -1;
}
if (obtained)*obtained = opaque->aoutController.spec;return 0;
}
ijkplayer默认创建一个基于Audio Queue的音频播放器
音频和视频部分后续单独讲解!
这里我们可以知道在ijkplayer初始化部分视频通过openGL来渲染,音频通过Audio Queue来播放
然后继续向下看
// init video sink
_glView = [[IJKSDLGLView alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_glView.shouldShowHudView = NO;
_view = _glView;
这里是设置openGL的渲染视图
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];
[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];
这里是设置ijkplayer的log输出等级
log等级为
typedef enum IJKLogLevel {
k_IJK_LOG_UNKNOWN = 0,
k_IJK_LOG_DEFAULT = 1,
k_IJK_LOG_VERBOSE = 2,//较为啰嗦的log等级
k_IJK_LOG_DEBUG = 3,
k_IJK_LOG_INFO = 4,
k_IJK_LOG_WARN = 5,
k_IJK_LOG_ERROR = 6,//普通错误
k_IJK_LOG_FATAL = 7,//毁灭性的错误
k_IJK_LOG_SILENT = 8,//静默 什么log都不输出
} IJKLogLevel;
void ffp_global_set_log_level(int log_level)
{
int av_level = log_level_ijk_to_av(log_level);
av_log_set_level(av_level);
}
这里需要特别说明一下
ijkplayer设置log的等级最终会处理为FFmpeg的log等级
FFmpeg的log等级为
/**
* Print no output.
*/
/**
* Something went really wrong and we will crash now.
*/
/**
* Something went wrong and recovery is not possible.
* For example, no header was found for a format which depends
* on headers or an illegal combination of parameters is used.
*/
/**
* Something went wrong and cannot losslessly be recovered.
* However, not all future data is affected.
*/
/**
* Something somehow does not look correct. This may or may not
* lead to problems. An example would be the use of ‘-vstrict -2’.
*/
/**
* Standard information.
*/
/**
* Detailed information.
*/
/**
* Stuff which is only useful for libav* developers.
*/
/**
* Extremely verbose debugging, useful for libav* development.
*/
[self registerApplicationObservers];
//这里是注册系统的一些通知
到这里ijkplayer的初始化还没有结束
因为iOS端的ijkplayer在播放器需要调用
- (void)prepareToPlay;//接口才能播放
那这个prepareToPlay函数都干了那些事情呢?
- (void)prepareToPlay
{
if (!_mediaPlayer)
return;
[self setScreenOn:_keepScreenOnWhilePlaying];
//这里设置视频的视频源
ijkmp_set_data_source(_mediaPlayer, [_urlString UTF8String]);
ijkmp_set_option(_mediaPlayer, IJKMP_OPT_CATEGORY_FORMAT, “safe”, “0”); // for concat demuxer
// 获取从SDL库初始化(定时器模块初始化)开始到当前的运行时间(ms);
_monitor.prepareStartTick = (int64_t)SDL_GetTickHR();
ijkmp_prepare_async(_mediaPlayer);
}
int ijkmp_set_data_source(IjkMediaPlayer *mp, const char *url)
{
assert(mp);
assert(url);
MPTRACE(“ijkmp_set_data_source(url=\”%s\”)\n”, url);
pthread_mutex_lock(&mp->mutex);
int retval = ijkmp_set_data_source_l(mp, url);
pthread_mutex_unlock(&mp->mutex);
MPTRACE(“ijkmp_set_data_source(url=\”%s\”)=%d\n”, url, retval);
return retval;
}
static int ijkmp_set_data_source_l(IjkMediaPlayer *mp, const char *url)
{
assert(mp);
assert(url);
// MPST_RET_IF_EQ(mp->mp_state, MP_STATE_IDLE);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_INITIALIZED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ASYNC_PREPARING);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PREPARED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STARTED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PAUSED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_COMPLETED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STOPPED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ERROR);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_END);freep((void**)&mp->data_source);
mp->data_source = strdup(url);
if (!mp->data_source)return EIJK_OUT_OF_MEMORY;ijkmp_change_state_l(mp, MP_STATE_INITIALIZED);//这里发送MP_STATE_INITIALIZED信号 通过pthread_cond_signal(&cond->id);发送
return 0;
}
ijkmp_change_state_l(mp, MP_STATE_INITIALIZED);
在ijkmp_change_state_l里面封装了一个what为MP_STATE_INITIALIZED的AVMessage,最后把其放入ijkplayer->ffplayer->msg_queue中,然后调用:
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);//和之前一样,ijkmp_change_state_l()向msg_queue发送一个FFP_MSG_PLAYBACK_STATE_CHANGED状态
}
inline static void ffp_notify_msg1(FFPlayer *ffp, int what) {
msg_queue_put_simple3(&ffp->msg_queue, what, 0, 0);
}
inline static void msg_queue_put_simple3(MessageQueue *q, int what, int arg1, int arg2)
{
AVMessage msg;
msg_init_msg(&msg);
msg.what = what;
msg.arg1 = arg1;
msg.arg2 = arg2;
msg_queue_put(q, &msg);
}
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;
msg1 = av_malloc(sizeof(AVMessage));
msg1 = q->recycle_msg;
if (msg1) {q->recycle_msg = msg1->next;q->recycle_count++;
} else {q->alloc_count++;msg1 = av_malloc(sizeof(AVMessage));
}
int total_count = q->recycle_count + q->alloc_count;
if (!(total_count % 10)) {av_log(NULL, AV_LOG_DEBUG, "msg-recycle \t%d + \t%d = \t%d\n", q->recycle_count, q->alloc_count, total_count);
}
if (!msg1)return -1;*msg1 = *msg;
msg1->next = NULL;if (!q->last_msg)q->first_msg = msg1;
elseq->last_msg->next = msg1;
q->last_msg = msg1;
q->nb_messages++;
SDL_CondSignal(q->cond);
return 0;
}
int SDL_CondSignal(SDL_cond *cond)
{
assert(cond);
if (!cond)
return -1;
return pthread_cond_signal(&cond->id);//这里使用了一个条件锁
}
cond->id其实是ijkplayer->ffplayer->msg_queue->cond->id,类型为pthread_cond_t。
那么pthread_cond_signal这个函数是干嘛的呢?
其实在liunx里面,pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行,当某个线程继续执行的时候发现msg_queue中有msg的时候会有相应操作。到底这里把是发给谁呢?请继续往下面读。
这个函数最终调用了
ijkmp_prepare_async(_mediaPlayer);
int ijkmp_prepare_async(IjkMediaPlayer *mp)
{
assert(mp);
MPTRACE(“ijkmp_prepare_async()\n”);
pthread_mutex_lock(&mp->mutex);
int retval = ijkmp_prepare_async_l(mp);
pthread_mutex_unlock(&mp->mutex);
MPTRACE(“ijkmp_prepare_async()=%d\n”, retval);
return retval;
}
static int ijkmp_prepare_async_l(IjkMediaPlayer *mp)
{
assert(mp);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_IDLE);
// MPST_RET_IF_EQ(mp->mp_state, MP_STATE_INITIALIZED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ASYNC_PREPARING);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PREPARED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STARTED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_PAUSED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_COMPLETED);
// MPST_RET_IF_EQ(mp->mp_state, MP_STATE_STOPPED);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_ERROR);
MPST_RET_IF_EQ(mp->mp_state, MP_STATE_END);assert(mp->data_source);//发送个准备消息
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);
//这里创建一个ijkmp_msg_loop消息循环线程
//这里创建了一个msg_loop的消息循环子线程,线程入口为ijkmp_msg_loop
//ijkplayer的消息在ijkmp_msg_loop函数里面进行处理
//ijkmp_msg_loop方法中调用的即是mp->msg_loop
mp->msg_thread = SDL_CreateThreadEx(&mp->_msg_thread, ijkmp_msg_loop, mp, "ff_msg_loop");
// msg_thread is detached inside msg_loop
// TODO: 9 release weak_thiz if pthread_create() failed;int retval = ffp_prepare_async_l(mp->ffplayer, mp->data_source);
if (retval <0) {ijkmp_change_state_l(mp, MP_STATE_ERROR);return retval;
}return 0;
}
inline static void msg_queue_start(MessageQueue *q)
{
SDL_LockMutex(q->mutex);
q->abort_request &#61; 0;
AVMessage msg;
msg_init_msg(&msg);
msg.what &#61; FFP_MSG_FLUSH;//这里又发送了一个状态FFP_MSG_FLUSH&#xff0c;然而函数名是msg_queue_start(),这是什么意思呢&#xff1f;
msg_queue_put_private(q, &msg);
SDL_UnlockMutex(q->mutex);
}
int ffp_prepare_async_l(FFPlayer *ffp, const char *file_name)
{
assert(ffp);
assert(!ffp->is);
assert(file_name);
if (av_stristart(file_name, "rtmp", NULL) ||av_stristart(file_name, "rtsp", NULL)) {// There is total different meaning for &#39;timeout&#39; option in rtmpav_log(ffp, AV_LOG_WARNING, "remove &#39;timeout&#39; option for rtmp.\n");av_dict_set(&ffp->format_opts, "timeout", NULL, 0);
}/* there is a length limit in avformat */
if (strlen(file_name) &#43; 1 > 1024) {av_log(ffp, AV_LOG_ERROR, "%s too long url\n", __func__);if (avio_find_protocol_name("ijklongurl:")) {av_dict_set(&ffp->format_opts, "ijklongurl-url", file_name, 0);file_name &#61; "ijklongurl:";}
}av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61; versions &#61;&#61;&#61;&#61;&#61;\n");
ffp_show_version_str(ffp, "ijkplayer", ijk_version_info());
ffp_show_version_str(ffp, "FFmpeg", av_version_info());
ffp_show_version_int(ffp, "libavutil", avutil_version());
ffp_show_version_int(ffp, "libavcodec", avcodec_version());
ffp_show_version_int(ffp, "libavformat", avformat_version());
ffp_show_version_int(ffp, "libswscale", swscale_version());
ffp_show_version_int(ffp, "libswresample", swresample_version());
av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61; options &#61;&#61;&#61;&#61;&#61;\n");
ffp_show_dict(ffp, "player-opts", ffp->player_opts);
ffp_show_dict(ffp, "format-opts", ffp->format_opts);
ffp_show_dict(ffp, "codec-opts ", ffp->codec_opts);
ffp_show_dict(ffp, "sws-opts ", ffp->sws_dict);
ffp_show_dict(ffp, "swr-opts ", ffp->swr_opts);
av_log(NULL, AV_LOG_INFO, "&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;&#61;\n");av_opt_set_dict(ffp, &ffp->player_opts);
if (!ffp->aout) {ffp->aout &#61; ffpipeline_open_audio_output(ffp->pipeline, ffp);if (!ffp->aout)return -1;
}
if (ffp->vfilter0) {GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);ffp->vfilters_list[ffp->nb_vfilters - 1] &#61; ffp->vfilter0;
}
VideoState *is &#61; stream_open(ffp, file_name, NULL);
if (!is) {av_log(NULL, AV_LOG_WARNING, "ffp_prepare_async_l: stream_open failed OOM");return EIJK_OUT_OF_MEMORY;
}ffp->is &#61; is;
ffp->input_filename &#61; av_strdup(file_name);
return 0;
}
ffp->aout &#61; ffpipeline_open_audio_output(ffp->pipeline, ffp);//打开音频输出
VideoState *is &#61; stream_open(ffp, file_name, NULL);
is->video_refresh_tid &#61; SDL_CreateThreadEx(&is->_video_refresh_tid, video_refresh_thread, ffp, “ff_vout”);//创建视频刷新线程 视频显示线程
is->read_tid &#61; SDL_CreateThreadEx(&is->_read_tid, read_thread, ffp, “ff_read”);//创建读线程 read_thread为入口的网络数据或者本地文件的数据读取线程。
其实在read_thread里面还向msg_queue发送了一个消息&#xff0c;那就是ffp_notify_msg1(ffp, FFP_MSG_PREPARED);&#xff0c;
消息循环线程收到这个消息后&#xff0c;
//这里是消息循环处理函数
int media_player_msg_loop(void* arg)
{
&#64;autoreleasepool {
IjkMediaPlayer mp &#61; (IjkMediaPlayer)arg;
__weak IJKFFMoviePlayerController *ffpController &#61; ffplayerRetain(ijkmp_set_weak_thiz(mp, NULL));
while (ffpController) {
&#64;autoreleasepool {
IJKFFMoviePlayerMessage *msg &#61; [ffpController obtainMessage];
if (!msg)
break;
int retval &#61; ijkmp_get_msg(mp, &msg->_msg, 1);if (retval <0)break;// block-get should never return 0assert(retval > 0);[ffpController performSelectorOnMainThread:&#64;selector(postEvent:) withObject:msg waitUntilDone:NO];}}// retained in prepare_async, before SDL_CreateThreadExijkmp_dec_ref_p(&mp);return 0;
}
}
FFP_MSG_PREPARED消息先在ijkmp_get_msg函数中被处理
int retval &#61; ijkmp_get_msg(mp, &msg->_msg, 1);
case FFP_MSG_PREPARED:
MPTRACE(“ijkmp_get_msg: FFP_MSG_PREPARED\n”);
pthread_mutex_lock(&mp->mutex);
if (mp->mp_state &#61;&#61; MP_STATE_ASYNC_PREPARING) {
ijkmp_change_state_l(mp, MP_STATE_PREPARED);
} else {
// FIXME: 1: onError() ?
av_log(mp->ffplayer, AV_LOG_DEBUG, “FFP_MSG_PREPARED: expecting mp_state&#61;&#61;MP_STATE_ASYNC_PREPARING\n”);
}
if (ffp_is_paused_l(mp->ffplayer)) {
ijkmp_change_state_l(mp, MP_STATE_PAUSED);
}
pthread_mutex_unlock(&mp->mutex);
break;
//如果mp MediaPlayer 当前状态是MP_STATE_ASYNC_PREPARING 就发送MP_STATE_PREPARED消息
ijkmp_change_state_l(mp, MP_STATE_PREPARED);
MP_STATE_PREPARED
消息又会被处理
- (IJKMPMoviePlaybackState)playbackState
{
if (!_mediaPlayer)
return NO;
IJKMPMoviePlaybackState mpState &#61; IJKMPMoviePlaybackStateStopped;
int state &#61; ijkmp_get_state(_mediaPlayer);
switch (state) {case MP_STATE_STOPPED:case MP_STATE_COMPLETED:case MP_STATE_ERROR:case MP_STATE_END:mpState &#61; IJKMPMoviePlaybackStateStopped;break;case MP_STATE_IDLE:case MP_STATE_INITIALIZED:case MP_STATE_ASYNC_PREPARING:case MP_STATE_PAUSED:mpState &#61; IJKMPMoviePlaybackStatePaused;break;case MP_STATE_PREPARED:case MP_STATE_STARTED: {if (_seeking)mpState &#61; IJKMPMoviePlaybackStateSeekingForward;elsempState &#61; IJKMPMoviePlaybackStatePlaying;break;}
}
// IJKMPMoviePlaybackStatePlaying,
// IJKMPMoviePlaybackStatePaused,
// IJKMPMoviePlaybackStateStopped,
// IJKMPMoviePlaybackStateInterrupted,
// IJKMPMoviePlaybackStateSeekingForward,
// IJKMPMoviePlaybackStateSeekingBackward
return mpState;
}
//当ijkmp_get_msg函数处理过MP_STATE_PREPARED消息后此消息被送到
- (void)postEvent: (IJKFFMoviePlayerMessage *)msg函数进行处理
在postEvent函数内部 这样处理MP_STATE_PREPARED消息
case FFP_MSG_PREPARED: {
NSLog(&#64;”FFP_MSG_PREPARED:\n”);
_monitor.prepareDuration &#61; (int64_t)SDL_GetTickHR() - _monitor.prepareStartTick;int64_t vdec &#61; ijkmp_get_property_int64(_mediaPlayer, FFP_PROP_INT64_VIDEO_DECODER, FFP_PROPV_DECODER_UNKNOWN);switch (vdec) {case FFP_PROPV_DECODER_VIDEOTOOLBOX:_monitor.vdecoder &#61; &#64;"VideoToolbox";break;case FFP_PROPV_DECODER_AVCODEC:_monitor.vdecoder &#61; [NSString stringWithFormat:&#64;"avcodec %d.%d.%d",LIBAVCODEC_VERSION_MAJOR,LIBAVCODEC_VERSION_MINOR,LIBAVCODEC_VERSION_MICRO];break;default:_monitor.vdecoder &#61; &#64;"Unknown";break;}IjkMediaMeta *rawMeta &#61; ijkmp_get_meta_l(_mediaPlayer);if (rawMeta) {ijkmeta_lock(rawMeta);NSMutableDictionary *newMediaMeta &#61; [[NSMutableDictionary alloc] init];fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_FORMAT, nil);fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_DURATION_US, nil);fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_START_US, nil);fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_BITRATE, nil);fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_VIDEO_STREAM, nil);fillMetaInternal(newMediaMeta, rawMeta, IJKM_KEY_AUDIO_STREAM, nil);int64_t video_stream &#61; ijkmeta_get_int64_l(rawMeta, IJKM_KEY_VIDEO_STREAM, -1);int64_t audio_stream &#61; ijkmeta_get_int64_l(rawMeta, IJKM_KEY_AUDIO_STREAM, -1);NSMutableArray *streams &#61; [[NSMutableArray alloc] init];size_t count &#61; ijkmeta_get_children_count_l(rawMeta);for(size_t i &#61; 0; i
会设置
ijkmp_set_playback_rate(_mediaPlayer, [self playbackRate]);
ijkmp_set_playback_volume(_mediaPlayer, [self playbackVolume]);
并开启hud的定时器
[self startHudTimer];
同时发出
[[NSNotificationCenter defaultCenter] postNotificationName:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification object:self];
_loadState &#61; IJKMPMovieLoadStatePlayable | IJKMPMovieLoadStatePlaythroughOK;
[[NSNotificationCenter defaultCenter]postNotificationName:IJKMPMoviePlayerLoadStateDidChangeNotificationobject:self];
IJKFFMoviePlayerController播放器的通知