[vlc-devel] [RFC PATCH] mediacodec: parse SPS/PPS, fix H264 via live streaming

Thomas Guillem thomas at gllm.fr
Fri Apr 24 16:21:45 CEST 2015


Don't abort if H264 fmt doesn't have any valid size or extra. The SPS/PPS will
be parsed from DecodeVideo and MediaCodec will be (re)started if video size and
SPS/PPS are valid.
---
 modules/codec/omxil/android_mediacodec.c | 158 +++++++++++++++++++++++++++++--
 1 file changed, 149 insertions(+), 9 deletions(-)

diff --git a/modules/codec/omxil/android_mediacodec.c b/modules/codec/omxil/android_mediacodec.c
index 66f82bc..27d7a4c 100644
--- a/modules/codec/omxil/android_mediacodec.c
+++ b/modules/codec/omxil/android_mediacodec.c
@@ -142,6 +142,9 @@ struct decoder_sys_t
     int stride, slice_height;
     char *name;
 
+    int i_width;
+    int i_height;
+    int i_sps_id;
     void *p_extra_buffer;
     size_t i_extra_buffer;
 
@@ -149,6 +152,7 @@ struct decoder_sys_t
     bool started;
     bool decoded;
     bool error_state;
+    bool b_new_block;
 
     ArchitectureSpecificCopyData architecture_specific_data;
 
@@ -388,6 +392,32 @@ end:
     return ret;
 }
 
+static int H264GetSPSPPS(uint8_t *p_buf, size_t i_buf,
+                         uint8_t **pp_sps_buf, size_t *p_sps_size,
+                         uint8_t **pp_pps_buf, size_t *p_pps_size,
+                         struct nal_sps *p_sps)
+{
+    uint8_t *p_sps_buf, *p_pps_buf;
+    size_t i_sps_size, i_pps_size;
+
+    if (h264_get_spspps(p_buf, i_buf, &p_sps_buf, &i_sps_size,
+                        &p_pps_buf, &i_pps_size) == 0)
+    {
+        if (pp_sps_buf && p_sps_size )
+        {
+            *pp_sps_buf = p_sps_buf;
+            *p_sps_size = i_sps_size;
+        }
+        if (pp_pps_buf && p_pps_size )
+        {
+            *pp_pps_buf = p_pps_buf;
+            *p_pps_size = i_pps_size;
+        }
+        return h264_parse_sps(p_sps_buf, i_sps_size, p_sps);
+    } else
+        return -1;
+}
+
 /*****************************************************************************
  * OpenMediaCodec: Create the mediacodec instance
  *****************************************************************************/
@@ -526,10 +556,6 @@ loopclean:
     p_sys->allocated = true;
     p_sys->codec = (*env)->NewGlobalRef(env, p_sys->codec);
 
-    jobject format = (*env)->CallStaticObjectMethod(env, jfields.media_format_class,
-                         jfields.create_video_format, (*env)->NewStringUTF(env, mime),
-                         p_dec->fmt_in.video.i_width, p_dec->fmt_in.video.i_height);
-
     if (p_dec->fmt_in.i_extra && !p_sys->p_extra_buffer) {
         uint32_t size = p_dec->fmt_in.i_extra;
         int buf_size = p_dec->fmt_in.i_extra + 20;
@@ -555,7 +581,32 @@ loopclean:
             memcpy(p_sys->p_extra_buffer, p_dec->fmt_in.p_extra, size);
         }
         p_sys->i_extra_buffer = size;
+
+        if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
+        {
+            struct nal_sps sps;
+
+            if (H264GetSPSPPS(p_sys->p_extra_buffer, p_sys->i_extra_buffer,
+                              NULL, NULL, NULL, NULL, &sps) == 0)
+            {
+                msg_Warn(p_dec, "SPS found, id: %d size: %dx%d (vs %dx%d)",
+                         sps.i_id, sps.i_width, sps.i_height,
+                         p_sys->i_width, p_sys->i_height);
+                p_sys->i_sps_id = sps.i_id;
+                p_sys->i_width = sps.i_width;
+                p_sys->i_height = sps.i_height;
+            }
+        }
+    }
+    if (!p_sys->i_width && !p_sys->i_height)
+    {
+        msg_Err(p_dec, "invalid size, abort MediaCodec");
+        goto error;
     }
+    jobject format = (*env)->CallStaticObjectMethod(env, jfields.media_format_class,
+                         jfields.create_video_format, (*env)->NewStringUTF(env, mime),
+                         p_sys->i_width, p_sys->i_height);
+
     if (p_sys->p_extra_buffer)
     {
         jobject jextra_buffer;
@@ -731,6 +782,8 @@ static int OpenDecoder(vlc_object_t *p_this)
 
     switch (p_dec->fmt_in.i_codec) {
     case VLC_CODEC_H264:
+        /* We can handle h264 without a valid video size */
+        break;
     case VLC_CODEC_HEVC:
     case VLC_CODEC_H263:
     case VLC_CODEC_MP4V:
@@ -764,12 +817,25 @@ static int OpenDecoder(vlc_object_t *p_this)
     p_dec->fmt_out.audio = p_dec->fmt_in.audio;
     p_dec->b_need_packetized = true;
 
+    p_dec->p_sys->i_sps_id = -1;
+    p_dec->p_sys->i_width = p_dec->fmt_in.video.i_width;
+    p_dec->p_sys->i_height = p_dec->fmt_in.video.i_height;
+
     p_dec->p_sys->timestamp_fifo = timestamp_FifoNew(32);
     if (!p_dec->p_sys->timestamp_fifo)
         goto error;
 
+    p_dec->p_sys->b_new_block = true;
+
     switch (p_dec->fmt_in.i_codec)
     {
+    case VLC_CODEC_H264:
+        if (!p_dec->p_sys->i_width || !p_dec->p_sys->i_height)
+        {
+            msg_Warn(p_dec, "waiting for sps/pps for codec %4.4s",
+                     (const char *)&p_dec->fmt_in.i_codec);
+            return VLC_SUCCESS;
+        }
     case VLC_CODEC_VC1:
         if (!p_dec->fmt_in.i_extra)
         {
@@ -903,7 +969,6 @@ static int PutInput(decoder_t *p_dec, JNIEnv *env, block_t *p_block, jlong timeo
     jobject buf;
     jsize size;
     uint8_t *bufptr;
-    struct H264ConvertState convert_state = { 0, 0 };
 
     index = (*env)->CallIntMethod(env, p_sys->codec,
                                   jfields.dequeue_input_buffer, timeout);
@@ -928,8 +993,6 @@ static int PutInput(decoder_t *p_dec, JNIEnv *env, block_t *p_block, jlong timeo
         size = p_block->i_buffer;
     memcpy(bufptr, p_block->p_buffer, size);
 
-    convert_h264_to_annexb(bufptr, size, p_sys->nal_size, &convert_state);
-
     int64_t ts = p_block->i_pts;
     if (!ts && p_block->i_dts)
         ts = p_block->i_dts;
@@ -1108,6 +1171,71 @@ static int GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t *p_pic, jlong time
     return 0;
 }
 
+static void H264ProcessBlock(decoder_t *p_dec, JNIEnv *env, block_t *p_block,
+                             bool *p_delayed_open)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    uint8_t *p_sps_buf, *p_pps_buf;
+    size_t i_sps_size, i_pps_size;
+    struct nal_sps sps;
+    struct H264ConvertState convert_state = { 0, 0 };
+
+    assert(p_dec->fmt_in.i_codec == VLC_CODEC_H264 && p_block);
+
+    convert_h264_to_annexb(p_block->p_buffer, p_block->i_buffer,
+                           p_sys->nal_size, &convert_state);
+
+    if (H264GetSPSPPS(p_block->p_buffer, p_block->i_buffer,
+                      &p_sps_buf, &i_sps_size,
+                      &p_pps_buf, &i_pps_size, &sps) == 0)
+    {
+        if (sps.i_id != p_sys->i_sps_id && sps.i_width && sps.i_height)
+        {
+            msg_Warn(p_dec, "SPS found, id: %d size: %dx%d (vs %dx%d)"
+                     ", restart MediaCodec !",
+                     sps.i_id, sps.i_width, sps.i_height,
+                     p_sys->i_width, p_sys->i_height);
+
+            if (p_sys->codec)
+                CloseMediaCodec(p_dec, env);
+
+            if (p_sys->p_extra_buffer)
+                free(p_sys->p_extra_buffer);
+            p_sys->p_extra_buffer = malloc(i_sps_size + i_pps_size);
+            if (p_sys->p_extra_buffer)
+            {
+                if (p_sps_buf && i_sps_size)
+                    memcpy(p_sys->p_extra_buffer, p_sps_buf, i_sps_size);
+                if (p_pps_buf && i_pps_size)
+                    memcpy((uint8_t *)p_sys->p_extra_buffer + i_sps_size,
+                           p_pps_buf, i_pps_size);
+                p_sys->i_extra_buffer = i_sps_size + i_pps_size;
+            }
+
+            p_sys->i_sps_id = sps.i_id;
+            p_sys->i_width = sps.i_width;
+            p_sys->i_height = sps.i_height;
+            *p_delayed_open = true;
+        }
+    }
+}
+
+static void HEVCProcessBlock(decoder_t *p_dec, JNIEnv *env, block_t *p_block,
+                             bool *p_delayed_open)
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    struct H264ConvertState convert_state = { 0, 0 };
+
+    assert(p_dec->fmt_in.i_codec == VLC_CODEC_HEVC && p_block);
+
+    convert_h264_to_annexb(p_block->p_buffer, p_block->i_buffer,
+                           p_sys->nal_size, &convert_state);
+
+    /* TODO */
+    VLC_UNUSED(env);
+    VLC_UNUSED(p_delayed_open);
+}
+
 static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
 {
     decoder_sys_t *p_sys = p_dec->p_sys;
@@ -1119,10 +1247,15 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
     int i_output_ret = 0;
     int i_input_ret = 0;
     bool b_error = false;
+    bool b_delayed_open = false;
+    bool b_new_block = p_block ? p_sys->b_new_block : false;
 
     if (p_sys->error_state)
         goto endclean;
 
+    if (b_new_block)
+        p_sys->b_new_block = false;
+
     if (!(env = jni_get_env(THREAD_NAME)))
     {
         b_error = true;
@@ -1149,11 +1282,17 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         goto endclean;
     }
 
+    if (b_new_block)
+    {
+        if (p_dec->fmt_in.i_codec == VLC_CODEC_H264)
+            H264ProcessBlock(p_dec, env, p_block, &b_delayed_open);
+        else if (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC)
+            HEVCProcessBlock(p_dec, env, p_block, &b_delayed_open);
+    }
+
     /* try delayed opening if there is a new extra data */
     if (!p_sys->codec)
     {
-        bool b_delayed_open = false;
-
         switch (p_dec->fmt_in.i_codec)
         {
         case VLC_CODEC_VC1:
@@ -1242,6 +1381,7 @@ endclean:
     {
         block_Release(p_block);
         *pp_block = NULL;
+        p_sys->b_new_block = true;
     }
     if (b_error && !p_sys->error_state) {
         /* Signal the error to the Java. */
-- 
2.1.3




More information about the vlc-devel mailing list