热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ijkplayeriOS初始化

本文对应的ijkplayer版本为:k0.7.9ijkplayeriOS初始化初始化代码idplayer[[IJKFFMoviePlayerControlleralloc]in

本文对应的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");

ifdef DEBUG

[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];

else

[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];

endif

// 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的渲染视图

ifdef DEBUG

[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_DEBUG];

else

[IJKFFMoviePlayerController setLogLevel:k_IJK_LOG_SILENT];

endif

这里是设置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)setLogLevel:(IJKLogLevel)logLevel
    {
    ijkmp_global_set_log_level(logLevel);
    }

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.
*/

define AV_LOG_QUIET -8

/**
* Something went really wrong and we will crash now.
*/

define AV_LOG_PANIC 0

/**
* 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.
*/

define AV_LOG_FATAL 8

/**
* Something went wrong and cannot losslessly be recovered.
* However, not all future data is affected.
*/

define AV_LOG_ERROR 16

/**
* Something somehow does not look correct. This may or may not
* lead to problems. An example would be the use of ‘-vstrict -2’.
*/

define AV_LOG_WARNING 24

/**
* Standard information.
*/

define AV_LOG_INFO 32

/**
* Detailed information.
*/

define AV_LOG_VERBOSE 40

/**
* Stuff which is only useful for libav* developers.
*/

define AV_LOG_DEBUG 48

/**
* Extremely verbose debugging, useful for libav* development.
*/

define AV_LOG_TRACE 56

[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;

ifdef FFP_MERGE

msg1 = av_malloc(sizeof(AVMessage));

else

msg1 = q->recycle_msg;
if (msg1) {q->recycle_msg = msg1->next;q->recycle_count++;
} else {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)) {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;
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 CONFIG_AVFILTER

if (ffp->vfilter0) {GROW_ARRAY(ffp->vfilters_list, ffp->nb_vfilters);ffp->vfilters_list[ffp->nb_vfilters - 1] &#61; ffp->vfilter0;
}

endif

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 0 && fps_den > 0) {_fpsInMeta &#61; ((CGFloat)(fps_num)) / fps_den;NSLog(&#64;"fps in meta %f\n", _fpsInMeta);}}} else if (0 &#61;&#61; strcmp(type, IJKM_VAL_TYPE__AUDIO)) {fillMetaInternal(streamMeta, streamRawMeta, IJKM_KEY_SAMPLE_RATE, nil);fillMetaInternal(streamMeta, streamRawMeta, IJKM_KEY_CHANNEL_LAYOUT, nil);if (audio_stream &#61;&#61; i) {_monitor.audioMeta &#61; streamMeta;}}}}[streams addObject:streamMeta];}[newMediaMeta setObject:streams forKey:kk_IJKM_KEY_STREAMS];ijkmeta_unlock(rawMeta);_monitor.mediaMeta &#61; newMediaMeta;}ijkmp_set_playback_rate(_mediaPlayer, [self playbackRate]);ijkmp_set_playback_volume(_mediaPlayer, [self playbackVolume]);[self startHudTimer];_isPreparedToPlay &#61; YES;[[NSNotificationCenter defaultCenter] postNotificationName:IJKMPMediaPlaybackIsPreparedToPlayDidChangeNotification object:self];_loadState &#61; IJKMPMovieLoadStatePlayable | IJKMPMovieLoadStatePlaythroughOK;[[NSNotificationCenter defaultCenter]postNotificationName:IJKMPMoviePlayerLoadStateDidChangeNotificationobject:self];break;}

会设置
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播放器的通知


推荐阅读
  • iOS Swift中如何实现自动登录?
    本文介绍了在iOS Swift中如何实现自动登录的方法,包括使用故事板、SWRevealViewController等技术,以及解决用户注销后重新登录自动跳转到主页的问题。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了一个题目的解法,通过二分答案来解决问题,但困难在于如何进行检查。文章提供了一种逃逸方式,通过移动最慢的宿管来锁门时跑到更居中的位置,从而使所有合格的寝室都居中。文章还提到可以分开判断两边的情况,并使用前缀和的方式来求出在任意时刻能够到达宿管即将锁门的寝室的人数。最后,文章提到可以改成O(n)的直接枚举来解决问题。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
author-avatar
书友49916066
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有