[vlc-commits] mediacodec: use a circular buffer of timestamps as a workaround for DTS only samples

Felix Abecassis git at videolan.org
Thu Mar 13 19:31:46 CET 2014


vlc | branch: master | Felix Abecassis <felix.abecassis at 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;



More information about the vlc-commits mailing list