]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/flvdec.c
librtmp: append the correct field to the string
[ffmpeg] / libavformat / flvdec.c
index 8ed724b0ff19b49ca894f8e35fb45cc25ed671e4..2d5e50e2573fad3579a31226132c337b34064a90 100644 (file)
@@ -438,45 +438,47 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
         return -1;
     }
 
-    // only look for metadata values when we are not nested and key != NULL
-    if (depth == 1 && key) {
-        acodec = astream ? astream->codec : NULL;
-        vcodec = vstream ? vstream->codec : NULL;
-
-        if (amf_type == AMF_DATA_TYPE_NUMBER ||
-            amf_type == AMF_DATA_TYPE_BOOL) {
-            if (!strcmp(key, "duration"))
-                s->duration = num_val * AV_TIME_BASE;
-            else if (!strcmp(key, "videodatarate") && vcodec &&
-                     0 <= (int)(num_val * 1024.0))
-                vcodec->bit_rate = num_val * 1024.0;
-            else if (!strcmp(key, "audiodatarate") && acodec &&
-                     0 <= (int)(num_val * 1024.0))
-                acodec->bit_rate = num_val * 1024.0;
-            else if (!strcmp(key, "datastream")) {
-                AVStream *st = create_stream(s, AVMEDIA_TYPE_DATA);
-                if (!st)
-                    return AVERROR(ENOMEM);
-                st->codec->codec_id = AV_CODEC_ID_TEXT;
-            } else if (flv->trust_metadata) {
-                if (!strcmp(key, "videocodecid") && vcodec) {
-                    flv_set_video_codec(s, vstream, num_val, 0);
-                } else if (!strcmp(key, "audiocodecid") && acodec) {
-                    int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
-                    flv_set_audio_codec(s, astream, acodec, id);
-                } else if (!strcmp(key, "audiosamplerate") && acodec) {
-                    acodec->sample_rate = num_val;
-                } else if (!strcmp(key, "audiosamplesize") && acodec) {
-                    acodec->bits_per_coded_sample = num_val;
-                } else if (!strcmp(key, "stereo") && acodec) {
-                    acodec->channels       = num_val + 1;
-                    acodec->channel_layout = acodec->channels == 2 ?
-                                             AV_CH_LAYOUT_STEREO :
-                                             AV_CH_LAYOUT_MONO;
-                } else if (!strcmp(key, "width") && vcodec) {
-                    vcodec->width = num_val;
-                } else if (!strcmp(key, "height") && vcodec) {
-                    vcodec->height = num_val;
+    if (key) {
+        // stream info doesn't live any deeper than the first object
+        if (depth == 1) {
+            acodec = astream ? astream->codec : NULL;
+            vcodec = vstream ? vstream->codec : NULL;
+
+            if (amf_type == AMF_DATA_TYPE_NUMBER ||
+                amf_type == AMF_DATA_TYPE_BOOL) {
+                if (!strcmp(key, "duration"))
+                    s->duration = num_val * AV_TIME_BASE;
+                else if (!strcmp(key, "videodatarate") && vcodec &&
+                         0 <= (int)(num_val * 1024.0))
+                    vcodec->bit_rate = num_val * 1024.0;
+                else if (!strcmp(key, "audiodatarate") && acodec &&
+                         0 <= (int)(num_val * 1024.0))
+                    acodec->bit_rate = num_val * 1024.0;
+                else if (!strcmp(key, "datastream")) {
+                    AVStream *st = create_stream(s, AVMEDIA_TYPE_DATA);
+                    if (!st)
+                        return AVERROR(ENOMEM);
+                    st->codec->codec_id = AV_CODEC_ID_TEXT;
+                } else if (flv->trust_metadata) {
+                    if (!strcmp(key, "videocodecid") && vcodec) {
+                        flv_set_video_codec(s, vstream, num_val, 0);
+                    } else if (!strcmp(key, "audiocodecid") && acodec) {
+                        int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
+                        flv_set_audio_codec(s, astream, acodec, id);
+                    } else if (!strcmp(key, "audiosamplerate") && acodec) {
+                        acodec->sample_rate = num_val;
+                    } else if (!strcmp(key, "audiosamplesize") && acodec) {
+                        acodec->bits_per_coded_sample = num_val;
+                    } else if (!strcmp(key, "stereo") && acodec) {
+                        acodec->channels       = num_val + 1;
+                        acodec->channel_layout = acodec->channels == 2 ?
+                                                 AV_CH_LAYOUT_STEREO :
+                                                 AV_CH_LAYOUT_MONO;
+                    } else if (!strcmp(key, "width") && vcodec) {
+                        vcodec->width = num_val;
+                    } else if (!strcmp(key, "height") && vcodec) {
+                        vcodec->height = num_val;
+                    }
                 }
             }
         }
@@ -492,9 +494,11 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream,
             !strcmp(key, "audiosamplerate") ||
             !strcmp(key, "audiosamplesize") ||
             !strcmp(key, "stereo")          ||
-            !strcmp(key, "audiocodecid"))
+            !strcmp(key, "audiocodecid")    ||
+            !strcmp(key, "datastream"))
             return 0;
 
+        s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
         if (amf_type == AMF_DATA_TYPE_BOOL) {
             av_strlcpy(str_val, num_val > 0 ? "true" : "false",
                        sizeof(str_val));
@@ -532,7 +536,7 @@ static int flv_read_metabody(AVFormatContext *s, int64_t next_pos)
     if (!strcmp(buffer, "onTextData"))
         return 1;
 
-    if (strcmp(buffer, "onMetaData"))
+    if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint"))
         return -1;
 
     // find the streams now so that amf_parse_object doesn't need to do
@@ -558,14 +562,6 @@ static int flv_read_header(AVFormatContext *s)
 
     avio_skip(s->pb, 4);
     flags = avio_r8(s->pb);
-    /* old flvtool cleared this field */
-    /* FIXME: better fix needed */
-    if (!flags) {
-        flags = FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO;
-        av_log(s, AV_LOG_WARNING,
-               "Broken FLV file, which says no streams present, "
-               "this might fail\n");
-    }
 
     s->ctx_flags |= AVFMTCTX_NOHEADER;
 
@@ -633,35 +629,84 @@ static void clear_index_entries(AVFormatContext *s, int64_t pos)
     }
 }
 
+static int amf_skip_tag(AVIOContext *pb, AMFDataType type)
+{
+    int nb = -1, ret, parse_name = 1;
+
+    switch (type) {
+    case AMF_DATA_TYPE_NUMBER:
+        avio_skip(pb, 8);
+        break;
+    case AMF_DATA_TYPE_BOOL:
+        avio_skip(pb, 1);
+        break;
+    case AMF_DATA_TYPE_STRING:
+        avio_skip(pb, avio_rb16(pb));
+        break;
+    case AMF_DATA_TYPE_ARRAY:
+        parse_name = 0;
+    case AMF_DATA_TYPE_MIXEDARRAY:
+        nb = avio_rb32(pb);
+    case AMF_DATA_TYPE_OBJECT:
+        while(!pb->eof_reached && (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY)) {
+            if (parse_name) {
+                int size = avio_rb16(pb);
+                if (!size) {
+                    avio_skip(pb, 1);
+                    break;
+                }
+                avio_skip(pb, size);
+            }
+            if ((ret = amf_skip_tag(pb, avio_r8(pb))) < 0)
+                return ret;
+        }
+        break;
+    case AMF_DATA_TYPE_NULL:
+    case AMF_DATA_TYPE_OBJECT_END:
+        break;
+    default:
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
 static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
                            int64_t dts, int64_t next)
 {
     AVIOContext *pb = s->pb;
     AVStream *st    = NULL;
-    AMFDataType type;
     char buf[20];
-    int ret, i, length;
+    int ret = AVERROR_INVALIDDATA;
+    int i, length = -1;
 
-    type = avio_r8(pb);
-    if (type == AMF_DATA_TYPE_MIXEDARRAY)
+    switch (avio_r8(pb)) {
+    case AMF_DATA_TYPE_MIXEDARRAY:
         avio_seek(pb, 4, SEEK_CUR);
-    else if (type != AMF_DATA_TYPE_OBJECT)
-        return AVERROR_INVALIDDATA;
-
-    amf_get_string(pb, buf, sizeof(buf));
-    if (strcmp(buf, "type") || avio_r8(pb) != AMF_DATA_TYPE_STRING)
-        return AVERROR_INVALIDDATA;
+    case AMF_DATA_TYPE_OBJECT:
+        break;
+    default:
+        goto skip;
+    }
 
-    amf_get_string(pb, buf, sizeof(buf));
-    // FIXME parse it as codec_id
-    amf_get_string(pb, buf, sizeof(buf));
-    if (strcmp(buf, "text") || avio_r8(pb) != AMF_DATA_TYPE_STRING)
-        return AVERROR_INVALIDDATA;
+    while ((ret = amf_get_string(pb, buf, sizeof(buf))) > 0) {
+        AMFDataType type = avio_r8(pb);
+        if (type == AMF_DATA_TYPE_STRING && !strcmp(buf, "text")) {
+            length = avio_rb16(pb);
+            ret    = av_get_packet(pb, pkt, length);
+            if (ret < 0)
+                goto skip;
+            else
+                break;
+        } else {
+            if ((ret = amf_skip_tag(pb, type)) < 0)
+                goto skip;
+        }
+    }
 
-    length = avio_rb16(pb);
-    ret    = av_get_packet(s->pb, pkt, length);
-    if (ret < 0)
-        return AVERROR(EIO);
+    if (length < 0) {
+        ret = AVERROR_INVALIDDATA;
+        goto skip;
+    }
 
     for (i = 0; i < s->nb_streams; i++) {
         st = s->streams[i];
@@ -672,7 +717,7 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
     if (i == s->nb_streams) {
         st = create_stream(s, AVMEDIA_TYPE_DATA);
         if (!st)
-            return AVERROR_INVALIDDATA;
+            return AVERROR(ENOMEM);
         st->codec->codec_id = AV_CODEC_ID_TEXT;
     }
 
@@ -683,6 +728,7 @@ static int flv_data_packet(AVFormatContext *s, AVPacket *pkt,
     pkt->stream_index = st->index;
     pkt->flags       |= AV_PKT_FLAG_KEY;
 
+skip:
     avio_seek(s->pb, next + 4, SEEK_SET);
 
     return ret;
@@ -747,7 +793,7 @@ static int flv_read_packet(AVFormatContext *s, AVPacket *pkt)
                     return flv_data_packet(s, pkt, dts, next);
                 } else /* skip packet */
                     av_log(s, AV_LOG_DEBUG,
-                           "skipping flv packet: type %d, size %d, flags %d\n",
+                           "Skipping flv packet: type %d, size %d, flags %d.\n",
                            type, size, flags);
 
 skip:
@@ -771,10 +817,18 @@ skip:
                     break;
             }
         }
-        if (i == s->nb_streams)
+        if (i == s->nb_streams) {
             st = create_stream(s, is_audio ? AVMEDIA_TYPE_AUDIO
                                            : AVMEDIA_TYPE_VIDEO);
+            if (!st)
+                return AVERROR(ENOMEM);
+        }
         av_dlog(s, "%d %X %d \n", is_audio, flags, st->discard);
+
+        if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+            is_audio)
+            av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
+
         if ((st->discard >= AVDISCARD_NONKEY &&
              !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || is_audio)) ||
             (st->discard >= AVDISCARD_BIDIR &&
@@ -783,8 +837,6 @@ skip:
             avio_seek(s->pb, next, SEEK_SET);
             continue;
         }
-        if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY)
-            av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
         break;
     }
 
@@ -830,6 +882,7 @@ skip:
         } else {
             AVCodecContext ctx;
             ctx.sample_rate = sample_rate;
+            ctx.bits_per_coded_sample = bits_per_coded_sample;
             flv_set_audio_codec(s, st, &ctx, flags & FLV_AUDIO_CODECID_MASK);
             sample_rate = ctx.sample_rate;
         }
@@ -845,13 +898,11 @@ skip:
             // sign extension
             int32_t cts = (avio_rb24(s->pb) + 0xff800000) ^ 0xff800000;
             pts = dts + cts;
-            if (cts < 0) { // dts are wrong
+            if (cts < 0 && !flv->wrong_dts) { // dts might be wrong
                 flv->wrong_dts = 1;
                 av_log(s, AV_LOG_WARNING,
-                       "negative cts, previous timestamps might be wrong\n");
+                       "Negative cts, previous timestamps might be wrong.\n");
             }
-            if (flv->wrong_dts)
-                dts = AV_NOPTS_VALUE;
         }
         if (type == 0) {
             if (st->codec->extradata) {
@@ -864,6 +915,12 @@ skip:
                 return ret;
             if (st->codec->codec_id == AV_CODEC_ID_AAC) {
                 MPEG4AudioConfig cfg;
+
+                /* Workaround for buggy Omnia A/XE encoder */
+                AVDictionaryEntry *t = av_dict_get(s->metadata, "Encoder", NULL, 0);
+                if (t && !strcmp(t->value, "Omnia A/XE"))
+                    st->codec->extradata_size = 2;
+
                 avpriv_mpeg4audio_get_config(&cfg, st->codec->extradata,
                                              st->codec->extradata_size * 8, 1);
                 st->codec->channels       = cfg.channels;