rtpdec_hevc: Parse out of band vps/sps/pps/sei from fmtp lines
authorMartin Storsjö <martin@martin.st>
Fri, 3 Oct 2014 18:40:13 +0000 (21:40 +0300)
committerMartin Storsjö <martin@martin.st>
Wed, 15 Oct 2014 09:12:02 +0000 (12:12 +0300)
These are assembled into extradata in the order vps/sps/pps/sei.

Signed-off-by: Martin Storsjö <martin@martin.st>
libavformat/rtpdec_hevc.c

index 0ff30e2..1bf3c1a 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include "libavutil/avstring.h"
+#include "libavutil/base64.h"
 
 #include "avformat.h"
 #include "rtpdec.h"
@@ -34,6 +35,8 @@
 struct PayloadContext {
     int using_donl_field;
     int profile_id;
+    uint8_t *sps, *pps, *vps, *sei;
+    int sps_size, pps_size, vps_size, sei_size;
 };
 
 static const uint8_t start_sequence[] = { 0x00, 0x00, 0x00, 0x01 };
@@ -85,6 +88,65 @@ static av_cold int hevc_sdp_parse_fmtp_config(AVFormatContext *s,
     /* sprop-sps: [base64] */
     /* sprop-pps: [base64] */
     /* sprop-sei: [base64] */
+    if (!strcmp(attr, "sprop-vps") || !strcmp(attr, "sprop-sps") ||
+        !strcmp(attr, "sprop-pps") || !strcmp(attr, "sprop-sei")) {
+        uint8_t **data_ptr;
+        int *size_ptr;
+        if (!strcmp(attr, "sprop-vps")) {
+            data_ptr = &hevc_data->vps;
+            size_ptr = &hevc_data->vps_size;
+        } else if (!strcmp(attr, "sprop-sps")) {
+            data_ptr = &hevc_data->sps;
+            size_ptr = &hevc_data->sps_size;
+        } else if (!strcmp(attr, "sprop-pps")) {
+            data_ptr = &hevc_data->pps;
+            size_ptr = &hevc_data->pps_size;
+        } else if (!strcmp(attr, "sprop-sei")) {
+            data_ptr = &hevc_data->sei;
+            size_ptr = &hevc_data->sei_size;
+        }
+
+        while (*value) {
+            char base64packet[1024];
+            uint8_t decoded_packet[1024];
+            int packet_size;
+            char *dst = base64packet;
+
+            while (*value && *value != ',' &&
+                   (dst - base64packet) < sizeof(base64packet) - 1) {
+                *dst++ = *value++;
+            }
+            *dst++ = '\0';
+
+            if (*value == ',')
+                value++;
+
+            packet_size = av_base64_decode(decoded_packet, base64packet,
+                                           sizeof(decoded_packet));
+            if (packet_size > 0) {
+                uint8_t *dest = av_malloc(packet_size + sizeof(start_sequence) +
+                                          *size_ptr);
+                if (!dest) {
+                    av_log(s, AV_LOG_ERROR,
+                           "Unable to allocate memory for extradata!\n");
+                    return AVERROR(ENOMEM);
+                }
+                if (*size_ptr) {
+                    memcpy(dest, *data_ptr, *size_ptr);
+                    av_free(*data_ptr);
+                }
+
+                memcpy(dest + *size_ptr, start_sequence,
+                       sizeof(start_sequence));
+                memcpy(dest + *size_ptr + sizeof(start_sequence),
+                       decoded_packet, packet_size);
+
+                *data_ptr  = dest;
+                *size_ptr += sizeof(start_sequence) + packet_size;
+            }
+        }
+    }
+
     /* max-lsr, max-lps, max-cpb, max-dpb, max-br, max-tr, max-tc */
     /* max-fps */
 
@@ -162,8 +224,41 @@ static av_cold int hevc_parse_sdp_line(AVFormatContext *ctx, int st_index,
         /* jump beyond the "-" and determine the height value */
         codec->height  = atoi(sdp_line_ptr + 1);
     } else if (av_strstart(sdp_line_ptr, "fmtp:", &sdp_line_ptr)) {
-        return ff_parse_fmtp(ctx, current_stream, hevc_data, sdp_line_ptr,
-                             hevc_sdp_parse_fmtp_config);
+        int ret = ff_parse_fmtp(ctx, current_stream, hevc_data, sdp_line_ptr,
+                                hevc_sdp_parse_fmtp_config);
+        if (hevc_data->vps_size || hevc_data->sps_size ||
+            hevc_data->pps_size || hevc_data->sei_size) {
+            av_freep(&codec->extradata);
+            codec->extradata_size = hevc_data->vps_size + hevc_data->sps_size +
+                                    hevc_data->pps_size + hevc_data->sei_size;
+            codec->extradata = av_malloc(codec->extradata_size +
+                                         FF_INPUT_BUFFER_PADDING_SIZE);
+            if (!codec->extradata) {
+                ret = AVERROR(ENOMEM);
+                codec->extradata_size = 0;
+            } else {
+                int pos = 0;
+                memcpy(codec->extradata + pos, hevc_data->vps, hevc_data->vps_size);
+                pos += hevc_data->vps_size;
+                memcpy(codec->extradata + pos, hevc_data->sps, hevc_data->sps_size);
+                pos += hevc_data->sps_size;
+                memcpy(codec->extradata + pos, hevc_data->pps, hevc_data->pps_size);
+                pos += hevc_data->pps_size;
+                memcpy(codec->extradata + pos, hevc_data->sei, hevc_data->sei_size);
+                pos += hevc_data->sei_size;
+                memset(codec->extradata + pos, 0, FF_INPUT_BUFFER_PADDING_SIZE);
+            }
+
+            av_freep(&hevc_data->vps);
+            av_freep(&hevc_data->sps);
+            av_freep(&hevc_data->pps);
+            av_freep(&hevc_data->sei);
+            hevc_data->vps_size = 0;
+            hevc_data->sps_size = 0;
+            hevc_data->pps_size = 0;
+            hevc_data->sei_size = 0;
+        }
+        return ret;
     }
 
     return 0;