From cd58dd6b0be181c021b7ec88463a1ed9ccde6caa Mon Sep 17 00:00:00 2001 From: raymondzheng Date: Thu, 2 Mar 2017 14:09:22 +0800 Subject: [PATCH] ijkplayer: support accurate seek --- .../ijkplayer/android/ijkplayer_android_def.h | 2 + ijkmedia/ijkplayer/android/ijkplayer_jni.c | 4 + ijkmedia/ijkplayer/ff_ffmsg.h | 1 + ijkmedia/ijkplayer/ff_ffplay.c | 142 ++++++++++++++++++ ijkmedia/ijkplayer/ff_ffplay_def.h | 12 ++ ijkmedia/ijkplayer/ff_ffplay_options.h | 5 +- 6 files changed, 165 insertions(+), 1 deletion(-) diff --git a/ijkmedia/ijkplayer/android/ijkplayer_android_def.h b/ijkmedia/ijkplayer/android/ijkplayer_android_def.h index 671df364..63ffe845 100644 --- a/ijkmedia/ijkplayer/android/ijkplayer_android_def.h +++ b/ijkmedia/ijkplayer/android/ijkplayer_android_def.h @@ -133,6 +133,8 @@ enum media_info_type { //100xx MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001, MEDIA_INFO_AUDIO_RENDERING_START = 10002, + + MEDIA_INFO_MEDIA_ACCURATE_SEEK_COMPLETE = 10100, }; typedef struct ijkmp_mediacodecinfo_context diff --git a/ijkmedia/ijkplayer/android/ijkplayer_jni.c b/ijkmedia/ijkplayer/android/ijkplayer_jni.c index 95ac5b04..683f02f2 100755 --- a/ijkmedia/ijkplayer/android/ijkplayer_jni.c +++ b/ijkmedia/ijkplayer/android/ijkplayer_jni.c @@ -965,6 +965,10 @@ static void message_loop_n(JNIEnv *env, IjkMediaPlayer *mp) MPTRACE("FFP_MSG_SEEK_COMPLETE:\n"); post_event(env, weak_thiz, MEDIA_SEEK_COMPLETE, 0, 0); break; + case FFP_MSG_ACCURATE_SEEK_COMPLETE: + MPTRACE("FFP_MSG_ACCURATE_SEEK_COMPLETE:\n"); + post_event(env, weak_thiz, MEDIA_INFO, MEDIA_INFO_MEDIA_ACCURATE_SEEK_COMPLETE, msg.arg1); + break; case FFP_MSG_PLAYBACK_STATE_CHANGED: break; case FFP_MSG_TIMED_TEXT: diff --git a/ijkmedia/ijkplayer/ff_ffmsg.h b/ijkmedia/ijkplayer/ff_ffmsg.h index b205ece5..be145860 100755 --- a/ijkmedia/ijkplayer/ff_ffmsg.h +++ b/ijkmedia/ijkplayer/ff_ffmsg.h @@ -42,6 +42,7 @@ #define FFP_MSG_SEEK_COMPLETE 600 /* arg1 = seek position, arg2 = error */ #define FFP_MSG_PLAYBACK_STATE_CHANGED 700 #define FFP_MSG_TIMED_TEXT 800 +#define FFP_MSG_ACCURATE_SEEK_COMPLETE 900 /* arg1 = current position*/ #define FFP_MSG_VIDEO_DECODER_OPEN 10001 diff --git a/ijkmedia/ijkplayer/ff_ffplay.c b/ijkmedia/ijkplayer/ff_ffplay.c index e864efcf..d6b191b3 100755 --- a/ijkmedia/ijkplayer/ff_ffplay.c +++ b/ijkmedia/ijkplayer/ff_ffplay.c @@ -806,7 +806,10 @@ static void stream_close(FFPlayer *ffp) frame_queue_destory(&is->pictq); frame_queue_destory(&is->sampq); frame_queue_destory(&is->subpq); + SDL_DestroyCond(is->audio_accurate_seek_cond); + SDL_DestroyCond(is->video_accurate_seek_cond); SDL_DestroyCond(is->continue_read_thread); + SDL_DestroyMutex(is->accurate_seek_mutex); SDL_DestroyMutex(is->play_mutex); #if !CONFIG_AVFILTER sws_freeContext(is->img_convert_ctx); @@ -1268,6 +1271,59 @@ static int queue_picture(FFPlayer *ffp, AVFrame *src_frame, double pts, double d { VideoState *is = ffp->is; Frame *vp; + int video_accurate_seek_fail = 0; + int64_t video_seek_pos = 0; + + if (ffp->enable_accurate_seek && is->video_accurate_seek_req && !is->seek_req) { + if (!isnan(pts)) { + video_seek_pos = is->seek_pos; + if (pts * 1000 * 1000 < is->seek_pos) { + if (is->drop_vframe_count == 0) { + av_log(NULL, AV_LOG_INFO, "video accurate_seek start, is->seek_pos=%lld, pts=%lf\n", is->seek_pos, pts); + } + is->drop_vframe_count++; + if (is->drop_vframe_count < MAX_KEY_FRAME_INTERVAL) { + return 1; // drop some old frame when do accurate seek + } else { + av_log(NULL, AV_LOG_WARNING, "video accurate_seek is error, is->drop_vframe_count=%d\n", is->drop_vframe_count); + video_accurate_seek_fail = 1; // if KEY_FRAME interval too big, disable accurate seek + } + } else { + av_log(NULL, AV_LOG_INFO, "video accurate_seek is ok, is->drop_vframe_count =%d, is->seek_pos=%lld, pts=%lf\n", is->drop_vframe_count, is->seek_pos, pts); + if (video_seek_pos == is->seek_pos) { + is->drop_vframe_count = 0; + SDL_LockMutex(is->accurate_seek_mutex); + is->video_accurate_seek_req = 0; + SDL_CondSignal(is->audio_accurate_seek_cond); + if (video_seek_pos == is->seek_pos && is->audio_accurate_seek_req && !is->abort_request) + SDL_CondWait(is->video_accurate_seek_cond, is->accurate_seek_mutex); + + ffp_notify_msg2(ffp, FFP_MSG_ACCURATE_SEEK_COMPLETE, (int)(pts * 1000)); + if (video_seek_pos != is->seek_pos && !is->abort_request) { + is->video_accurate_seek_req = 1; + SDL_UnlockMutex(is->accurate_seek_mutex); + return 1; + } + + SDL_UnlockMutex(is->accurate_seek_mutex); + } + } + } else { + video_accurate_seek_fail = 1; + } + + if (video_accurate_seek_fail) { + if (!isnan(pts)) { + ffp_notify_msg2(ffp, FFP_MSG_ACCURATE_SEEK_COMPLETE, (int)(pts * 1000)); + } + ffp->enable_accurate_seek = 0; + is->drop_vframe_count = 0; + SDL_LockMutex(is->accurate_seek_mutex); + is->video_accurate_seek_req = 0; + SDL_CondSignal(is->audio_accurate_seek_cond); + SDL_UnlockMutex(is->accurate_seek_mutex); + } + } #if defined(DEBUG_SYNC) printf("frame_type=%c pts=%0.3f\n", @@ -1642,6 +1698,8 @@ static int audio_thread(void *arg) int got_frame = 0; AVRational tb; int ret = 0; + int audio_accurate_seek_fail = 0; + int64_t audio_seek_pos = 0; if (!frame) return AVERROR(ENOMEM); @@ -1653,6 +1711,58 @@ static int audio_thread(void *arg) if (got_frame) { tb = (AVRational){1, frame->sample_rate}; + if (ffp->enable_accurate_seek && is->audio_accurate_seek_req && !is->seek_req) { + double frame_pts = (frame->pts == AV_NOPTS_VALUE) ? NAN : frame->pts * av_q2d(tb); + double audio_clock = 0; + if (!isnan(frame_pts)) { + double samples_duration = (double) frame->nb_samples / frame->sample_rate; + audio_clock = frame_pts + samples_duration; + audio_seek_pos = is->seek_pos; + if (audio_clock * 1000 * 1000 < is->seek_pos) { + if (is->drop_aframe_count == 0) { + av_log(NULL, AV_LOG_INFO, "audio accurate_seek start, is->seek_pos=%lld, audio_clock=%lf\n", is->seek_pos, audio_clock); + } + is->drop_aframe_count++; + if (is->drop_aframe_count < MAX_KEY_FRAME_INTERVAL) { + continue; // drop some old frame when do accurate seek + } else { + av_log(NULL, AV_LOG_INFO, "audio accurate_seek is error, is->drop_aframe_count=%d\n", is->drop_aframe_count); + audio_accurate_seek_fail = 1; + } + } else { + if (audio_seek_pos == is->seek_pos) { + av_log(NULL, AV_LOG_INFO, "audio accurate_seek is ok, is->drop_aframe_count=%d\n", is->drop_aframe_count); + is->drop_aframe_count = 0; + SDL_LockMutex(is->accurate_seek_mutex); + is->audio_accurate_seek_req = 0; + SDL_CondSignal(is->video_accurate_seek_cond); + if (audio_seek_pos == is->seek_pos && is->video_accurate_seek_req && !is->abort_request) + SDL_CondWait(is->audio_accurate_seek_cond, is->accurate_seek_mutex); + + if (audio_seek_pos != is->seek_pos && !is->abort_request) { + is->audio_accurate_seek_req = 1; + SDL_UnlockMutex(is->accurate_seek_mutex); + continue; + } + + SDL_UnlockMutex(is->accurate_seek_mutex); + } + } + } else { + audio_accurate_seek_fail = 1; + } + if (audio_accurate_seek_fail) { + if (!isnan(frame_pts)) { + ffp_notify_msg2(ffp, FFP_MSG_ACCURATE_SEEK_COMPLETE, (int)(audio_clock * 1000)); + } + ffp->enable_accurate_seek = 0; + is->drop_aframe_count = 0; + SDL_LockMutex(is->accurate_seek_mutex); + is->audio_accurate_seek_req = 0; + SDL_CondSignal(is->video_accurate_seek_cond); + SDL_UnlockMutex(is->accurate_seek_mutex); + } + } #if CONFIG_AVFILTER dec_channel_layout = get_valid_channel_layout(frame->channel_layout, av_frame_get_channels(frame)); @@ -2837,6 +2947,18 @@ static int read_thread(void *arg) if (is->pause_req) step_to_next_frame_l(ffp); SDL_UnlockMutex(ffp->is->play_mutex); + + if (ffp->enable_accurate_seek) { + is->drop_aframe_count = 0; + is->drop_vframe_count = 0; + SDL_LockMutex(is->accurate_seek_mutex); + is->audio_accurate_seek_req = 1; + is->video_accurate_seek_req = 1; + SDL_CondSignal(is->audio_accurate_seek_cond); + SDL_CondSignal(is->video_accurate_seek_cond); + SDL_UnlockMutex(is->accurate_seek_mutex); + } + ffp_notify_msg3(ffp, FFP_MSG_SEEK_COMPLETE, (int)fftime_to_milliseconds(seek_target), ret); ffp_toggle_buffering(ffp, 1); } @@ -3054,6 +3176,16 @@ static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputForma goto fail; } + if (!(is->video_accurate_seek_cond = SDL_CreateCond())) { + av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError()); + ffp->enable_accurate_seek = 0; + } + + if (!(is->audio_accurate_seek_cond = SDL_CreateCond())) { + av_log(NULL, AV_LOG_FATAL, "SDL_CreateCond(): %s\n", SDL_GetError()); + ffp->enable_accurate_seek = 0; + } + init_clock(&is->vidclk, &is->videoq.serial); init_clock(&is->audclk, &is->audioq.serial); init_clock(&is->extclk, &is->extclk.serial); @@ -3063,6 +3195,7 @@ static VideoState *stream_open(FFPlayer *ffp, const char *filename, AVInputForma is->av_sync_type = ffp->av_sync_type; is->play_mutex = SDL_CreateMutex(); + is->accurate_seek_mutex = SDL_CreateMutex(); ffp->is = is; is->pause_req = !ffp->start_on_prepared; @@ -3681,6 +3814,15 @@ int ffp_stop_l(FFPlayer *ffp) } msg_queue_abort(&ffp->msg_queue); + if (ffp->enable_accurate_seek && is && is->accurate_seek_mutex + && is->audio_accurate_seek_cond && is->video_accurate_seek_cond) { + SDL_LockMutex(is->accurate_seek_mutex); + is->audio_accurate_seek_req = 0; + is->video_accurate_seek_req = 0; + SDL_CondSignal(is->audio_accurate_seek_cond); + SDL_CondSignal(is->video_accurate_seek_cond); + SDL_UnlockMutex(is->accurate_seek_mutex); + } return 0; } diff --git a/ijkmedia/ijkplayer/ff_ffplay_def.h b/ijkmedia/ijkplayer/ff_ffplay_def.h index 093b14b2..57977d65 100755 --- a/ijkmedia/ijkplayer/ff_ffplay_def.h +++ b/ijkmedia/ijkplayer/ff_ffplay_def.h @@ -128,6 +128,8 @@ #define MIN_PKT_DURATION 15 +#define MAX_KEY_FRAME_INTERVAL 1000 // max key frame interval is 1000 + #ifdef FFP_MERGE #define CURSOR_HIDE_DELAY 1000000 @@ -381,6 +383,14 @@ typedef struct VideoState { volatile int latest_seek_load_serial; volatile int64_t latest_seek_load_start_at; + + int drop_aframe_count; + int drop_vframe_count; + int audio_accurate_seek_req; + int video_accurate_seek_req; + SDL_mutex *accurate_seek_mutex; + SDL_cond *video_accurate_seek_cond; + SDL_cond *audio_accurate_seek_cond; } VideoState; /* options specified by the user */ @@ -665,6 +675,7 @@ typedef struct FFPlayer { AVApplicationContext *app_ctx; IjkIOManagerContext *ijkio_manager_ctx; + int enable_accurate_seek; } FFPlayer; #define fftime_to_milliseconds(ts) (av_rescale(ts, 1000, AV_TIME_BASE)) @@ -741,6 +752,7 @@ inline static void ffp_reset_internal(FFPlayer *ffp) ffp->start_on_prepared = 1; ffp->first_video_frame_rendered = 0; ffp->sync_av_start = 1; + ffp->enable_accurate_seek = 0; ffp->playable_duration_ms = 0; diff --git a/ijkmedia/ijkplayer/ff_ffplay_options.h b/ijkmedia/ijkplayer/ff_ffplay_options.h index ebda766a..d83f3f7d 100644 --- a/ijkmedia/ijkplayer/ff_ffplay_options.h +++ b/ijkmedia/ijkplayer/ff_ffplay_options.h @@ -147,6 +147,9 @@ static const AVOption ffp_context_options[] = { { "preset-5-1-center-mix-level", "preset center-mix-level for 5.1 channel", OPTION_OFFSET(preset_5_1_center_mix_level), OPTION_DOUBLE(M_SQRT1_2, -32, 32) }, + { "enable-accurate-seek", "enable accurate seek", + OPTION_OFFSET(enable_accurate_seek), OPTION_INT(0, 0, 1) }, + // iOS only options { "videotoolbox", "VideoToolbox: enable", OPTION_OFFSET(videotoolbox), OPTION_INT(0, 0, 1) }, @@ -178,7 +181,7 @@ static const AVOption ffp_context_options[] = { OPTION_OFFSET(mediacodec_handle_resolution_change), OPTION_INT(0, 0, 1) }, { "opensles", "OpenSL ES: enable", OPTION_OFFSET(opensles), OPTION_INT(0, 0, 1) }, - + { NULL } }; -- GitLab