Merge commit 'f5fac6f77752931347ab302563802dcaa49c2419'
[ffmpeg.git] / libavformat / asfdec.c
index 0a315d4..8c6aaf4 100644 (file)
@@ -2,20 +2,20 @@
  * ASF compatible demuxer
  * Copyright (c) 2000, 2001 Fabrice Bellard
  *
- * This file is part of Libav.
+ * This file is part of FFmpeg.
  *
- * Libav is free software; you can redistribute it and/or
+ * FFmpeg is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License as published by the Free Software Foundation; either
  * version 2.1 of the License, or (at your option) any later version.
  *
- * Libav is distributed in the hope that it will be useful,
+ * FFmpeg is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
- * License along with Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
@@ -96,12 +96,8 @@ static const AVClass asf_class = {
 #include <assert.h>
 
 #define ASF_MAX_STREAMS 127
-#define FRAME_HEADER_SIZE 17
-// Fix Me! FRAME_HEADER_SIZE may be different.
-
-static const ff_asf_guid index_guid = {
-    0x90, 0x08, 0x00, 0x33, 0xb1, 0xe5, 0xcf, 0x11, 0x89, 0xf4, 0x00, 0xa0, 0xc9, 0x03, 0x49, 0xcb
-};
+#define FRAME_HEADER_SIZE 16
+// Fix Me! FRAME_HEADER_SIZE may be different. (17 is known to be too large)
 
 #ifdef DEBUG
 static const ff_asf_guid stream_bitrate_guid = { /* (http://get.to/sdp) */
@@ -127,7 +123,7 @@ static void print_guid(ff_asf_guid *g)
     else PRINT_IF_GUID(g, ff_asf_codec_comment_header);
     else PRINT_IF_GUID(g, ff_asf_codec_comment1_header);
     else PRINT_IF_GUID(g, ff_asf_data_header);
-    else PRINT_IF_GUID(g, index_guid);
+    else PRINT_IF_GUID(g, ff_asf_simple_index_header);
     else PRINT_IF_GUID(g, ff_asf_head1_guid);
     else PRINT_IF_GUID(g, ff_asf_head2_guid);
     else PRINT_IF_GUID(g, ff_asf_my_guid);
@@ -151,12 +147,6 @@ static void print_guid(ff_asf_guid *g)
 #define print_guid(g)
 #endif
 
-void ff_get_guid(AVIOContext *s, ff_asf_guid *g)
-{
-    assert(sizeof(*g) == 16);
-    avio_read(s, *g, sizeof(*g));
-}
-
 static int asf_probe(AVProbeData *pd)
 {
     /* check file header */
@@ -301,6 +291,9 @@ static void get_tag(AVFormatContext *s, const char *key, int type, int len, int
 
     if (type == 0) {         // UTF16-LE
         avio_get_str16le(s->pb, len, value, 2 * len + 1);
+    } else if (type == -1) { // ASCII
+        avio_read(s->pb, value, len);
+        value[len]=0;
     } else if (type == 1) {  // byte array
         if (!strcmp(key, "WM/Picture")) { // handle cover art
             asf_read_picture(s, len);
@@ -381,13 +374,14 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
     if (!asf_st)
         return AVERROR(ENOMEM);
     st->priv_data  = asf_st;
-    st->start_time = 0;
     start_time     = asf->hdr.preroll;
 
     asf_st->stream_language_index = 128; // invalid stream index means no language info
 
     if (!(asf->hdr.flags & 0x01)) { // if we aren't streaming...
-        st->duration = asf->hdr.play_time /
+        int64_t fsize = avio_size(pb);
+        if (fsize <= 0 || (int64_t)asf->hdr.file_size <= 0 || FFABS(fsize - (int64_t)asf->hdr.file_size) < 10000)
+            st->duration = asf->hdr.play_time /
                        (10000000 / 1000) - start_time;
     }
     ff_get_guid(pb, &g);
@@ -440,7 +434,7 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
         if (is_dvr_ms_audio) {
             // codec_id and codec_tag are unreliable in dvr_ms
             // files. Set them later by probing stream.
-            st->codec->codec_id  = AV_CODEC_ID_PROBE;
+            st->request_probe    = 1;
             st->codec->codec_tag = 0;
         }
         if (st->codec->codec_id == AV_CODEC_ID_AAC)
@@ -477,7 +471,7 @@ static int asf_read_stream_properties(AVFormatContext *s, int64_t size)
         tag1                             = avio_rl32(pb);
         avio_skip(pb, 20);
         if (sizeX > 40) {
-            st->codec->extradata_size = sizeX - 40;
+            st->codec->extradata_size = ffio_limit(pb, sizeX - 40);
             st->codec->extradata      = av_mallocz(st->codec->extradata_size +
                                                    FF_INPUT_BUFFER_PADDING_SIZE);
             avio_read(pb, st->codec->extradata, st->codec->extradata_size);
@@ -548,8 +542,10 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
     stream_ct      = avio_rl16(pb); // stream-name-count
     payload_ext_ct = avio_rl16(pb); // payload-extension-system-count
 
-    if (stream_num < 128)
+    if (stream_num < 128) {
         asf->stream_bitrates[stream_num] = leak_rate;
+        asf->streams[stream_num].payload_ext_ct = 0;
+    }
 
     for (i = 0; i < stream_ct; i++) {
         avio_rl16(pb);
@@ -558,10 +554,18 @@ static int asf_read_ext_stream_properties(AVFormatContext *s, int64_t size)
     }
 
     for (i = 0; i < payload_ext_ct; i++) {
+        int size;
         ff_get_guid(pb, &g);
-        avio_skip(pb, 2);
+        size = avio_rl16(pb);
         ext_len = avio_rl32(pb);
         avio_skip(pb, ext_len);
+        if (stream_num < 128 && i < FF_ARRAY_ELEMS(asf->streams[stream_num].payload)) {
+            ASFPayload *p = &asf->streams[stream_num].payload[i];
+            p->type = g[0];
+            p->size = size;
+            av_log(s, AV_LOG_DEBUG, "Payload extension %x %d\n", g[0], p->size );
+            asf->streams[stream_num].payload_ext_ct ++;
+        }
     }
 
     return 0;
@@ -723,7 +727,7 @@ static int asf_read_header(AVFormatContext *s)
 
     ff_get_guid(pb, &g);
     if (ff_guidcmp(&g, &ff_asf_header))
-        return -1;
+        return AVERROR_INVALIDDATA;
     avio_rl64(pb);
     avio_rl32(pb);
     avio_r8(pb);
@@ -745,7 +749,7 @@ static int asf_read_header(AVFormatContext *s)
             break;
         }
         if (gsize < 24)
-            return -1;
+            return AVERROR_INVALIDDATA;
         if (!ff_guidcmp(&g, &ff_asf_file_header)) {
             int ret = asf_read_file_properties(s, gsize);
             if (ret < 0)
@@ -774,19 +778,30 @@ static int asf_read_header(AVFormatContext *s)
             continue;
         } else if (!ff_guidcmp(&g, &ff_asf_marker_header)) {
             asf_read_marker(s, gsize);
-        } else if (pb->eof_reached) {
-            return -1;
+        } else if (url_feof(pb)) {
+            return AVERROR_EOF;
         } else {
             if (!s->keylen) {
                 if (!ff_guidcmp(&g, &ff_asf_content_encryption)) {
+                    unsigned int len;
+                    AVPacket pkt;
                     av_log(s, AV_LOG_WARNING,
                            "DRM protected stream detected, decoding will likely fail!\n");
+                    len= avio_rl32(pb);
+                    av_log(s, AV_LOG_DEBUG, "Secret data:\n");
+                    av_get_packet(pb, &pkt, len); av_hex_dump_log(s, AV_LOG_DEBUG, pkt.data, pkt.size); av_free_packet(&pkt);
+                    len= avio_rl32(pb);
+                    get_tag(s, "ASF_Protection_Type", -1, len, 32);
+                    len= avio_rl32(pb);
+                    get_tag(s, "ASF_Key_ID", -1, len, 32);
+                    len= avio_rl32(pb);
+                    get_tag(s, "ASF_License_URL", -1, len, 32);
                 } else if (!ff_guidcmp(&g, &ff_asf_ext_content_encryption)) {
                     av_log(s, AV_LOG_WARNING,
                            "Ext DRM protected stream detected, decoding will likely fail!\n");
+                    av_dict_set(&s->metadata, "encryption", "ASF Extended Content Encryption", 0);
                 } else if (!ff_guidcmp(&g, &ff_asf_digital_signature)) {
-                    av_log(s, AV_LOG_WARNING,
-                           "Digital signature detected, decoding will likely fail!\n");
+                    av_log(s, AV_LOG_INFO, "Digital signature detected!\n");
                 }
             }
         }
@@ -800,8 +815,8 @@ static int asf_read_header(AVFormatContext *s)
     avio_rl64(pb);
     avio_r8(pb);
     avio_r8(pb);
-    if (pb->eof_reached)
-        return -1;
+    if (url_feof(pb))
+        return AVERROR_EOF;
     asf->data_offset      = avio_tell(pb);
     asf->packet_size_left = 0;
 
@@ -900,20 +915,20 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb)
          * the stream. */
         if (pb->error == AVERROR(EAGAIN))
             return AVERROR(EAGAIN);
-        if (!pb->eof_reached)
+        if (!url_feof(pb))
             av_log(s, AV_LOG_ERROR,
                    "ff asf bad header %x  at:%"PRId64"\n", c, avio_tell(pb));
     }
     if ((c & 0x8f) == 0x82) {
         if (d || e) {
-            if (!pb->eof_reached)
+            if (!url_feof(pb))
                 av_log(s, AV_LOG_ERROR, "ff asf bad non zero\n");
-            return -1;
+            return AVERROR_INVALIDDATA;
         }
         c      = avio_r8(pb);
         d      = avio_r8(pb);
         rsize += 3;
-    } else if (!pb->eof_reached) {
+    } else if(!url_feof(pb)) {
         avio_seek(pb, -1, SEEK_CUR); // FIXME
     }
 
@@ -929,12 +944,12 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb)
         av_log(s, AV_LOG_ERROR,
                "invalid packet_length %d at:%"PRId64"\n",
                packet_length, avio_tell(pb));
-        return -1;
+        return AVERROR_INVALIDDATA;
     }
     if (padsize >= packet_length) {
         av_log(s, AV_LOG_ERROR,
                "invalid padsize %d at:%"PRId64"\n", padsize, avio_tell(pb));
-        return -1;
+        return AVERROR_INVALIDDATA;
     }
 
     asf->packet_timestamp = avio_rl32(pb);
@@ -954,7 +969,7 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb)
         av_log(s, AV_LOG_ERROR,
                "invalid packet header length %d for pktlen %d-%d at %"PRId64"\n",
                rsize, packet_length, padsize, avio_tell(pb));
-        return -1;
+        return AVERROR_INVALIDDATA;
     }
     asf->packet_size_left = packet_length - padsize - rsize;
     if (packet_length < asf->hdr.min_pktsize)
@@ -972,13 +987,16 @@ static int ff_asf_get_packet(AVFormatContext *s, AVIOContext *pb)
 static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
 {
     ASFContext *asf = s->priv_data;
+    ASFStream *asfst;
     int rsize       = 1;
     int num         = avio_r8(pb);
-    int64_t ts0;
+    int i;
+    int64_t ts0, ts1 av_unused;
 
     asf->packet_segments--;
     asf->packet_key_frame = num >> 7;
     asf->stream_index     = asf->asfid2avid[num & 0x7f];
+    asfst                 = &asf->streams[num & 0x7f];
     // sequence should be ignored!
     DO_2BITS(asf->packet_property >> 4, asf->packet_seq, 0);
     DO_2BITS(asf->packet_property >> 2, asf->packet_frag_offset, 0);
@@ -986,26 +1004,61 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
     av_dlog(asf, "key:%d stream:%d seq:%d offset:%d replic_size:%d\n",
             asf->packet_key_frame, asf->stream_index, asf->packet_seq,
             asf->packet_frag_offset, asf->packet_replic_size);
+    if (rsize+(int64_t)asf->packet_replic_size > asf->packet_size_left) {
+        av_log(s, AV_LOG_ERROR, "packet_replic_size %d is invalid\n", asf->packet_replic_size);
+        return AVERROR_INVALIDDATA;
+    }
     if (asf->packet_replic_size >= 8) {
+        int64_t end = avio_tell(pb) + asf->packet_replic_size;
+        AVRational aspect;
         asf->packet_obj_size = avio_rl32(pb);
         if (asf->packet_obj_size >= (1 << 24) || asf->packet_obj_size <= 0) {
             av_log(s, AV_LOG_ERROR, "packet_obj_size invalid\n");
-            return -1;
+            return AVERROR_INVALIDDATA;
         }
         asf->packet_frag_timestamp = avio_rl32(pb); // timestamp
-        if (asf->packet_replic_size >= 8 + 38 + 4) {
-            avio_skip(pb, 10);
-            ts0 = avio_rl64(pb);
-            avio_skip(pb, 8);
-            avio_skip(pb, 12);
-            avio_rl32(pb);
-            avio_skip(pb, asf->packet_replic_size - 8 - 38 - 4);
-            if (ts0 != -1)
-                asf->packet_frag_timestamp = ts0 / 10000;
-            else
-                asf->packet_frag_timestamp = AV_NOPTS_VALUE;
-        } else
-            avio_skip(pb, asf->packet_replic_size - 8);
+
+        for (i = 0; i < asfst->payload_ext_ct; i++) {
+            ASFPayload *p = &asfst->payload[i];
+            int size = p->size;
+            int64_t payend;
+            if (size == 0xFFFF)
+                size = avio_rl16(pb);
+            payend = avio_tell(pb) + size;
+            if (payend > end) {
+                av_log(s, AV_LOG_ERROR, "too long payload\n");
+                break;
+            }
+            switch (p->type) {
+            case 0x50:
+//              duration = avio_rl16(pb);
+                break;
+            case 0x54:
+                aspect.num = avio_r8(pb);
+                aspect.den = avio_r8(pb);
+                if (aspect.num > 0 && aspect.den > 0 && asf->stream_index >= 0) {
+                    s->streams[asf->stream_index]->sample_aspect_ratio = aspect;
+                }
+                break;
+            case 0x2A:
+                avio_skip(pb, 8);
+                ts0 = avio_rl64(pb);
+                ts1 = avio_rl64(pb);
+                if (ts0!= -1) asf->packet_frag_timestamp = ts0/10000;
+                else          asf->packet_frag_timestamp = AV_NOPTS_VALUE;
+                break;
+            case 0x5B:
+            case 0xB7:
+            case 0xCC:
+            case 0xC0:
+            case 0xA0:
+                //unknown
+                break;
+            }
+            avio_seek(pb, payend, SEEK_SET);
+        }
+
+        avio_seek(pb, end, SEEK_SET);
         rsize += asf->packet_replic_size; // FIXME - check validity
     } else if (asf->packet_replic_size == 1) {
         // multipacket - frag_offset is beginning timestamp
@@ -1018,18 +1071,18 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
     } else if (asf->packet_replic_size != 0) {
         av_log(s, AV_LOG_ERROR, "unexpected packet_replic_size of %d\n",
                asf->packet_replic_size);
-        return -1;
+        return AVERROR_INVALIDDATA;
     }
     if (asf->packet_flags & 0x01) {
         DO_2BITS(asf->packet_segsizetype >> 6, asf->packet_frag_size, 0); // 0 is illegal
         if (rsize > asf->packet_size_left) {
             av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n");
-            return -1;
+            return AVERROR_INVALIDDATA;
         } else if (asf->packet_frag_size > asf->packet_size_left - rsize) {
             if (asf->packet_frag_size > asf->packet_size_left - rsize + asf->packet_padsize) {
                 av_log(s, AV_LOG_ERROR, "packet_frag_size is invalid (%d-%d)\n",
                        asf->packet_size_left, rsize);
-                return -1;
+                return AVERROR_INVALIDDATA;
             } else {
                 int diff = asf->packet_frag_size - (asf->packet_size_left - rsize);
                 asf->packet_size_left += diff;
@@ -1037,16 +1090,12 @@ static int asf_read_frame_header(AVFormatContext *s, AVIOContext *pb)
             }
         }
     } else {
-        if (rsize > asf->packet_size_left) {
-            av_log(s, AV_LOG_ERROR, "packet_replic_size is invalid\n");
-            return -1;
-        }
         asf->packet_frag_size = asf->packet_size_left - rsize;
     }
     if (asf->packet_replic_size == 1) {
         asf->packet_multi_size = asf->packet_frag_size;
         if (asf->packet_multi_size > asf->packet_size_left)
-            return -1;
+            return AVERROR_INVALIDDATA;
     }
     asf->packet_size_left -= rsize;
 
@@ -1068,8 +1117,7 @@ static int ff_asf_parse_packet(AVFormatContext *s, AVIOContext *pb, AVPacket *pk
     ASFStream *asf_st = 0;
     for (;;) {
         int ret;
-
-        if (pb->eof_reached)
+        if (url_feof(pb))
             return AVERROR_EOF;
 
         if (asf->packet_size_left < FRAME_HEADER_SIZE ||
@@ -1348,11 +1396,12 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index,
               s->packet_size * s->packet_size +
               s->data_offset;
     *ppos = pos;
-    avio_seek(s->pb, pos, SEEK_SET);
+    if (avio_seek(s->pb, pos, SEEK_SET) < 0)
+        return AV_NOPTS_VALUE;
 
     asf_reset_header(s);
     for (;;) {
-        if (asf_read_packet(s, pkt) < 0) {
+        if (av_read_frame(s, pkt) < 0) {
             av_log(s, AV_LOG_INFO, "asf_read_pts failed\n");
             return AV_NOPTS_VALUE;
         }
@@ -1386,17 +1435,21 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index)
     ff_asf_guid g;
     ASFContext *asf     = s->priv_data;
     int64_t current_pos = avio_tell(s->pb);
-    int i;
 
-    avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET);
+    if(avio_seek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET) < 0) {
+        asf->index_read= -1;
+        return;
+    }
+
     ff_get_guid(s->pb, &g);
 
     /* the data object can be followed by other top-level objects,
      * skip them until the simple index object is reached */
-    while (ff_guidcmp(&g, &index_guid)) {
+    while (ff_guidcmp(&g, &ff_asf_simple_index_header)) {
         int64_t gsize = avio_rl64(s->pb);
-        if (gsize < 24 || s->pb->eof_reached) {
+        if (gsize < 24 || url_feof(s->pb)) {
             avio_seek(s->pb, current_pos, SEEK_SET);
+            asf->index_read= -1;
             return;
         }
         avio_skip(s->pb, gsize - 24);
@@ -1406,6 +1459,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index)
     {
         int64_t itime, last_pos = -1;
         int pct, ict;
+        int i;
         int64_t av_unused gsize = avio_rl64(s->pb);
         ff_get_guid(s->pb, &g);
         itime = avio_rl64(s->pb);
@@ -1428,7 +1482,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index)
                 last_pos = pos;
             }
         }
-        asf->index_read = ict > 0;
+        asf->index_read = ict > 1;
     }
     avio_seek(s->pb, current_pos, SEEK_SET);
 }
@@ -1438,8 +1492,6 @@ static int asf_read_seek(AVFormatContext *s, int stream_index,
 {
     ASFContext *asf = s->priv_data;
     AVStream *st    = s->streams[stream_index];
-    int64_t pos;
-    int index;
 
     if (s->packet_size <= 0)
         return -1;
@@ -1456,15 +1508,16 @@ static int asf_read_seek(AVFormatContext *s, int stream_index,
     if (!asf->index_read)
         asf_build_simple_index(s, stream_index);
 
-    if ((asf->index_read && st->index_entries)) {
-        index = av_index_search_timestamp(st, pts, flags);
+    if ((asf->index_read > 0 && st->index_entries)) {
+        int index = av_index_search_timestamp(st, pts, flags);
         if (index >= 0) {
             /* find the position */
-            pos = st->index_entries[index].pos;
+            uint64_t pos = st->index_entries[index].pos;
 
             /* do the seek */
             av_log(s, AV_LOG_DEBUG, "SEEKTO: %"PRId64"\n", pos);
-            avio_seek(s->pb, pos, SEEK_SET);
+            if(avio_seek(s->pb, pos, SEEK_SET) < 0)
+                return -1;
             asf_reset_header(s);
             return 0;
         }