vlc | branch: master | Felix Abecassis <felix.abecas...@gmail.com> | Wed Mar 12 23:29:22 2014 +0100| [5e4e1fa495ed991022914f0f460f50f84d6dafa0] | committer: Felix Abecassis
mediacodec: use a circular buffer of timestamps as a workaround for DTS only samples > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=5e4e1fa495ed991022914f0f460f50f84d6dafa0 --- modules/codec/omxil/android_mediacodec.c | 97 +++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c index d9c47b5..951d879 100644 --- a/modules/codec/omxil/android_mediacodec.c +++ b/modules/codec/omxil/android_mediacodec.c @@ -54,6 +54,80 @@ extern void jni_SetAndroidSurfaceSizeEnv(JNIEnv *p_env, int width, int height, i extern void jni_EventHardwareAccelerationError(); extern bool jni_IsVideoPlayerActivityCreated(); +/* Implementation of a circular buffer of timestamps with overwriting + * of older values. MediaCodec has only one type of timestamp, if a + * block has no PTS, we send the DTS instead. Some hardware decoders + * cannot cope with this situation and output the frames in the wrong + * order. As a workaround in this case, we use a FIFO of timestamps in + * order to remember which input packets had no PTS. Since an + * hardware decoder can silently drop frames, this might cause a + * growing desynchronization with the actual timestamp. Thus the + * circular buffer has a limited size and will overwrite older values. + */ +typedef struct +{ + uint32_t begin; + uint32_t size; + uint32_t capacity; + int64_t *buffer; +} timestamp_fifo_t; + +static timestamp_fifo_t *timestamp_FifoNew(uint32_t capacity) +{ + timestamp_fifo_t *fifo = calloc(1, sizeof(*fifo)); + if (!fifo) + return NULL; + fifo->buffer = malloc(capacity * sizeof(*fifo->buffer)); + if (!fifo->buffer) { + free(fifo); + return NULL; + } + fifo->capacity = capacity; + return fifo; +} + +static void timestamp_FifoRelease(timestamp_fifo_t *fifo) +{ + free(fifo->buffer); + free(fifo); +} + +static bool timestamp_FifoIsEmpty(timestamp_fifo_t *fifo) +{ + return fifo->size == 0; +} + +static bool timestamp_FifoIsFull(timestamp_fifo_t *fifo) +{ + return fifo->size == fifo->capacity; +} + +static void timestamp_FifoEmpty(timestamp_fifo_t *fifo) +{ + fifo->size = 0; +} + +static void timestamp_FifoPut(timestamp_fifo_t *fifo, int64_t ts) +{ + uint32_t end = (fifo->begin + fifo->size) % fifo->capacity; + fifo->buffer[end] = ts; + if (!timestamp_FifoIsFull(fifo)) + fifo->size += 1; + else + fifo->begin = (fifo->begin + 1) % fifo->capacity; +} + +static int64_t timestamp_FifoGet(timestamp_fifo_t *fifo) +{ + if (timestamp_FifoIsEmpty(fifo)) + return VLC_TS_INVALID; + + int64_t result = fifo->buffer[fifo->begin]; + fifo->begin = (fifo->begin + 1) % fifo->capacity; + fifo->size -= 1; + return result; +} + struct decoder_sys_t { jclass media_codec_list_class, media_codec_class, media_format_class; @@ -92,6 +166,8 @@ struct decoder_sys_t bool direct_rendering; int i_output_buffers; /**< number of MediaCodec output buffers */ picture_t** inflight_picture; /**< stores the inflight picture for each output buffer or NULL */ + + timestamp_fifo_t *timestamp_fifo; }; enum Types @@ -451,6 +527,12 @@ static int OpenDecoder(vlc_object_t *p_this) (*env)->DeleteLocalRef(env, format); (*myVm)->DetachCurrentThread(myVm); + + const int timestamp_fifo_size = 32; + p_sys->timestamp_fifo = timestamp_FifoNew(timestamp_fifo_size); + if (!p_sys->timestamp_fifo) + goto error; + return VLC_SUCCESS; error: @@ -490,6 +572,8 @@ static void CloseDecoder(vlc_object_t *p_this) free(p_sys->name); ArchitectureSpecificCopyHooksDestroy(p_sys->pixel_format, &p_sys->architecture_specific_data); free(p_sys->inflight_picture); + if (p_sys->timestamp_fifo) + timestamp_FifoRelease(p_sys->timestamp_fifo); free(p_sys); } @@ -591,7 +675,16 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t // TODO: Use crop_top/crop_left as well? Or is that already taken into account? // On OMX_TI_COLOR_FormatYUV420PackedSemiPlanar the offset already incldues // the cropping, so the top/left cropping params should just be ignored. - p_pic->date = (*env)->GetLongField(env, p_sys->buffer_info, p_sys->pts_field); + + /* If the oldest input block had no PTS, the timestamp + * of the frame returned by MediaCodec might be wrong + * so we overwrite it with the corresponding dts. */ + int64_t forced_ts = timestamp_FifoGet(p_sys->timestamp_fifo); + if (forced_ts == VLC_TS_INVALID) + p_pic->date = (*env)->GetLongField(env, p_sys->buffer_info, p_sys->pts_field); + else + p_pic->date = forced_ts; + if (p_sys->direct_rendering) { picture_sys_t *p_picsys = p_pic->p_sys; p_picsys->pf_display_callback = DisplayCallback; @@ -741,6 +834,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) { block_Release(p_block); + timestamp_FifoEmpty(p_sys->timestamp_fifo); if (p_sys->decoded) { /* Invalidate all pictures that are currently in flight * since flushing make all previous indices returned by @@ -831,6 +925,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block) int64_t ts = p_block->i_pts; if (!ts && p_block->i_dts) ts = p_block->i_dts; + timestamp_FifoPut(p_sys->timestamp_fifo, p_block->i_pts ? VLC_TS_INVALID : p_block->i_dts); (*env)->CallVoidMethod(env, p_sys->codec, p_sys->queue_input_buffer, index, 0, size, ts, 0); (*env)->DeleteLocalRef(env, buf); p_sys->decoded = true; _______________________________________________ vlc-commits mailing list vlc-commits@videolan.org https://mailman.videolan.org/listinfo/vlc-commits