]> git.sesse.net Git - ffmpeg/blobdiff - libavformat/asfdec.c
Fix aspect ratio for files that have it stored in
[ffmpeg] / libavformat / asfdec.c
index 08f0d6586bf1481f77e83299eec873a736f79962..e1f33317740302c0cbc23ba595281467c0143b95 100644 (file)
@@ -35,6 +35,7 @@ void ff_mms_set_stream_selection(URLContext *h, AVFormatContext *format);
 #undef NDEBUG
 #include <assert.h>
 
+#define ASF_MAX_STREAMS 127
 #define FRAME_HEADER_SIZE 17
 // Fix Me! FRAME_HEADER_SIZE may be different.
 
@@ -82,6 +83,7 @@ static void print_guid(const ff_asf_guid *g)
     else PRINT_IF_GUID(g, ff_asf_ext_stream_embed_stream_header);
     else PRINT_IF_GUID(g, ff_asf_ext_stream_audio_stream);
     else PRINT_IF_GUID(g, ff_asf_metadata_header);
+    else PRINT_IF_GUID(g, ff_asf_marker_header);
     else PRINT_IF_GUID(g, stream_bitrate_guid);
     else PRINT_IF_GUID(g, ff_asf_language_guid);
     else
@@ -122,9 +124,12 @@ static void get_str16(ByteIOContext *pb, char *buf, int buf_size)
 static void get_str16_nolen(ByteIOContext *pb, int len, char *buf, int buf_size)
 {
     char* q = buf;
-    for (; len > 1; len -= 2) {
+    while (len > 1) {
         uint8_t tmp;
-        PUT_UTF8(get_le16(pb), tmp, if (q - buf < buf_size - 1) *q++ = tmp;)
+        uint32_t ch;
+
+        GET_UTF16(ch, (len -= 2) >= 0 ? get_le16(pb) : 0, break;)
+        PUT_UTF8(ch, tmp, if (q - buf < buf_size - 1) *q++ = tmp;)
     }
     if (len > 0)
         url_fskip(pb, len);
@@ -152,19 +157,28 @@ static int get_value(ByteIOContext *pb, int type){
 
 static void get_tag(AVFormatContext *s, const char *key, int type, int len)
 {
-    char value[1024];
-    if (type <= 1) {         // unicode or byte
-        get_str16_nolen(s->pb, len, value, sizeof(value));
-    } else if (type <= 5) {  // boolean or DWORD or QWORD or WORD
+    char *value;
+
+    if ((unsigned)len >= (UINT_MAX - 1)/2)
+        return;
+
+    value = av_malloc(2*len+1);
+    if (!value)
+        return;
+
+    if (type == 0) {         // UTF16-LE
+        get_str16_nolen(s->pb, len, value, 2*len + 1);
+    } else if (type > 1 && type <= 5) {  // boolean or DWORD or QWORD or WORD
         uint64_t num = get_value(s->pb, type);
-        snprintf(value, sizeof(value), "%"PRIu64, num);
+        snprintf(value, len, "%"PRIu64, num);
     } else {
         url_fskip(s->pb, len);
+        av_freep(&value);
+        av_log(s, AV_LOG_DEBUG, "Unsupported value type %d in tag %s.\n", type, key);
         return;
     }
-    if (!strncmp(key, "WM/", 3))
-        key += 3;
-    av_metadata_set(&s->metadata, key, value);
+    av_metadata_set2(&s->metadata, key, value, 0);
+    av_freep(&value);
 }
 
 static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
@@ -191,9 +205,10 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
     get_byte(pb);
     memset(&asf->asfid2avid, -1, sizeof(asf->asfid2avid));
     for(;;) {
+        uint64_t gpos= url_ftell(pb);
         get_guid(pb, &g);
         gsize = get_le64(pb);
-        dprintf(s, "%08"PRIx64": ", url_ftell(pb) - 24);
+        dprintf(s, "%08"PRIx64": ", gpos);
         print_guid(&g);
         dprintf(s, "  size=0x%"PRIx64"\n", gsize);
         if (!guidcmp(&g, &ff_asf_data_header)) {
@@ -223,13 +238,18 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
             asf->hdr.max_bitrate        = get_le32(pb);
             s->packet_size = asf->hdr.max_pktsize;
         } else if (!guidcmp(&g, &ff_asf_stream_header)) {
-            enum CodecType type;
+            enum AVMediaType type;
             int type_specific_size, sizeX;
             uint64_t total_size;
             unsigned int tag1;
             int64_t pos1, pos2, start_time;
             int test_for_ext_stream_audio, is_dvr_ms_audio=0;
 
+            if (s->nb_streams == ASF_MAX_STREAMS) {
+                av_log(s, AV_LOG_ERROR, "too many streams\n");
+                return AVERROR(EINVAL);
+            }
+
             pos1 = url_ftell(pb);
 
             st = av_new_stream(s, 0);
@@ -252,14 +272,17 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
 
             test_for_ext_stream_audio = 0;
             if (!guidcmp(&g, &ff_asf_audio_stream)) {
-                type = CODEC_TYPE_AUDIO;
+                type = AVMEDIA_TYPE_AUDIO;
             } else if (!guidcmp(&g, &ff_asf_video_stream)) {
-                type = CODEC_TYPE_VIDEO;
+                type = AVMEDIA_TYPE_VIDEO;
+            } else if (!guidcmp(&g, &ff_asf_jfif_media)) {
+                type = AVMEDIA_TYPE_VIDEO;
+                st->codec->codec_id = CODEC_ID_MJPEG;
             } else if (!guidcmp(&g, &ff_asf_command_stream)) {
-                type = CODEC_TYPE_DATA;
+                type = AVMEDIA_TYPE_DATA;
             } else if (!guidcmp(&g, &ff_asf_ext_stream_embed_stream_header)) {
                 test_for_ext_stream_audio = 1;
-                type = CODEC_TYPE_UNKNOWN;
+                type = AVMEDIA_TYPE_UNKNOWN;
             } else {
                 return -1;
             }
@@ -276,7 +299,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
             if (test_for_ext_stream_audio) {
                 get_guid(pb, &g);
                 if (!guidcmp(&g, &ff_asf_ext_stream_audio_stream)) {
-                    type = CODEC_TYPE_AUDIO;
+                    type = AVMEDIA_TYPE_AUDIO;
                     is_dvr_ms_audio=1;
                     get_guid(pb, &g);
                     get_le32(pb);
@@ -288,7 +311,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
             }
 
             st->codec->codec_type = type;
-            if (type == CODEC_TYPE_AUDIO) {
+            if (type == AVMEDIA_TYPE_AUDIO) {
                 ff_get_wav_header(pb, st->codec, type_specific_size);
                 if (is_dvr_ms_audio) {
                     // codec_id and codec_tag are unreliable in dvr_ms
@@ -338,7 +361,8 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
                     st->codec->frame_size = 1;
                     break;
                 }
-            } else if (type == CODEC_TYPE_VIDEO) {
+            } else if (type == AVMEDIA_TYPE_VIDEO &&
+                       gsize - (url_ftell(pb) - pos1 + 24) >= 51) {
                 get_le32(pb);
                 get_le32(pb);
                 get_byte(pb);
@@ -366,7 +390,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
                     st->codec->palctrl = av_mallocz(sizeof(AVPaletteControl));
 #if HAVE_BIGENDIAN
                     for (i = 0; i < FFMIN(st->codec->extradata_size, AVPALETTE_SIZE)/4; i++)
-                        st->codec->palctrl->palette[i] = bswap_32(((uint32_t*)st->codec->extradata)[i]);
+                        st->codec->palctrl->palette[i] = av_bswap32(((uint32_t*)st->codec->extradata)[i]);
 #else
                     memcpy(st->codec->palctrl->palette, st->codec->extradata,
                            FFMIN(st->codec->extradata_size, AVPALETTE_SIZE));
@@ -376,8 +400,16 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
 
                 st->codec->codec_tag = tag1;
                 st->codec->codec_id = ff_codec_get_id(ff_codec_bmp_tags, tag1);
-                if(tag1 == MKTAG('D', 'V', 'R', ' '))
+                if(tag1 == MKTAG('D', 'V', 'R', ' ')){
                     st->need_parsing = AVSTREAM_PARSE_FULL;
+                    // issue658 containse wrong w/h and MS even puts a fake seq header with wrong w/h in extradata while a correct one is in te stream. maximum lameness
+                    st->codec->width  =
+                    st->codec->height = 0;
+                    av_freep(&st->codec->extradata);
+                    st->codec->extradata_size=0;
+                }
+                if(st->codec->codec_id == CODEC_ID_H264)
+                    st->need_parsing = AVSTREAM_PARSE_FULL_ONCE;
             }
             pos2 = url_ftell(pb);
             url_fskip(pb, gsize - (pos2 - pos1 + 24));
@@ -428,10 +460,22 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
                     char name[1024];
 
                     name_len = get_le16(pb);
+                    if (name_len%2)     // must be even, broken lavf versions wrote len-1
+                        name_len += 1;
                     get_str16_nolen(pb, name_len, name, sizeof(name));
                     value_type = get_le16(pb);
                     value_len  = get_le16(pb);
-                    get_tag(s, name, value_type, value_len);
+                    if (!value_type && value_len%2)
+                        value_len += 1;
+                    /**
+                     * My sample has that stream set to 0 maybe that mean the container.
+                     * Asf stream count start at 1. I am using 0 to the container value since it's unused
+                     */
+                    if (!strcmp(name, "AspectRatioX")){
+                        dar[0].num= get_value(s->pb, value_type);
+                    } else if(!strcmp(name, "AspectRatioY")){
+                        dar[0].den= get_value(s->pb, value_type);
+                    } else get_tag(s, name, value_type, value_len);
             }
         } else if (!guidcmp(&g, &ff_asf_metadata_header)) {
             int n, stream_num, name_len, value_len, value_type, value_num;
@@ -499,11 +543,39 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
 
             // there could be a optional stream properties object to follow
             // if so the next iteration will pick it up
+            continue;
         } else if (!guidcmp(&g, &ff_asf_head1_guid)) {
             int v1, v2;
             get_guid(pb, &g);
             v1 = get_le32(pb);
             v2 = get_le16(pb);
+            continue;
+        } else if (!guidcmp(&g, &ff_asf_marker_header)) {
+            int i, count, name_len;
+            char name[1024];
+
+            get_le64(pb);            // reserved 16 bytes
+            get_le64(pb);            // ...
+            count = get_le32(pb);    // markers count
+            get_le16(pb);            // reserved 2 bytes
+            name_len = get_le16(pb); // name length
+            for(i=0;i<name_len;i++){
+                get_byte(pb); // skip the name
+            }
+
+            for(i=0;i<count;i++){
+                int64_t pres_time;
+                int name_len;
+
+                get_le64(pb);             // offset, 8 bytes
+                pres_time = get_le64(pb); // presentation time
+                get_le16(pb);             // entry length
+                get_le32(pb);             // send time
+                get_le32(pb);             // flags
+                name_len = get_le32(pb);  // name length
+                get_str16_nolen(pb, name_len * 2, name, sizeof(name));
+                ff_new_chapter(s, i, (AVRational){1, 10000000}, pres_time, AV_NOPTS_VALUE, name );
+            }
 #if 0
         } else if (!guidcmp(&g, &ff_asf_codec_comment_header)) {
             int len, v1, n, num;
@@ -532,8 +604,19 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
         } else if (url_feof(pb)) {
             return -1;
         } else {
-            url_fseek(pb, gsize - 24, SEEK_CUR);
+            if (!s->keylen) {
+                if (!guidcmp(&g, &ff_asf_content_encryption)) {
+                    av_log(s, AV_LOG_WARNING, "DRM protected stream detected, decoding will likely fail!\n");
+                } else if (!guidcmp(&g, &ff_asf_ext_content_encryption)) {
+                    av_log(s, AV_LOG_WARNING, "Ext DRM protected stream detected, decoding will likely fail!\n");
+                } else if (!guidcmp(&g, &ff_asf_digital_signature)) {
+                    av_log(s, AV_LOG_WARNING, "Digital signature detected, decoding will likely fail!\n");
+                }
+            }
         }
+        if(url_ftell(pb) != gpos + gsize)
+            av_log(s, AV_LOG_DEBUG, "gpos mismatch our pos=%"PRIu64", end=%"PRIu64"\n", url_ftell(pb)-gpos, gsize);
+        url_fseek(pb, gpos + gsize, SEEK_SET);
     }
     get_guid(pb, &g);
     get_le64(pb);
@@ -551,11 +634,16 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
             AVStream *st = s->streams[stream_num];
             if (!st->codec->bit_rate)
                 st->codec->bit_rate = bitrate[i];
-            if (dar[i].num > 0 && dar[i].den > 0)
+            if (dar[i].num > 0 && dar[i].den > 0){
                 av_reduce(&st->sample_aspect_ratio.num,
                           &st->sample_aspect_ratio.den,
                           dar[i].num, dar[i].den, INT_MAX);
-//av_log(s, AV_LOG_ERROR, "dar %d:%d sar=%d:%d\n", dar[i].num, dar[i].den, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
+            } else if ((dar[0].num > 0) && (dar[0].den > 0) && (st->codec->codec_type==CODEC_TYPE_VIDEO)) // Use ASF container value if the stream doesn't AR set.
+                av_reduce(&st->sample_aspect_ratio.num,
+                          &st->sample_aspect_ratio.den,
+                          dar[0].num, dar[0].den, INT_MAX);
+
+//av_log(s, AV_LOG_INFO, "i=%d, st->codec->codec_type:%d, dar %d:%d sar=%d:%d\n", i, st->codec->codec_type, dar[i].num, dar[i].den, st->sample_aspect_ratio.num, st->sample_aspect_ratio.den);
 
             // copy and convert language codes to the frontend
             if (asf->streams[i].stream_language_index < 128) {
@@ -564,7 +652,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
                     const char primary_tag[3] = { rfc1766[0], rfc1766[1], '\0' }; // ignore country code if any
                     const char *iso6392 = av_convert_lang_to(primary_tag, AV_LANG_ISO639_2_BIBL);
                     if (iso6392)
-                        av_metadata_set(&st->metadata, "language", iso6392);
+                        av_metadata_set2(&st->metadata, "language", iso6392, 0);
                 }
             }
         }
@@ -586,7 +674,7 @@ static int asf_read_header(AVFormatContext *s, AVFormatParameters *ap)
  * Load a single ASF packet into the demuxer.
  * @param s demux context
  * @param pb context to read data from
- * @returns 0 on success, <0 on error
+ * @return 0 on success, <0 on error
  */
 static int ff_asf_get_packet(AVFormatContext *s, ByteIOContext *pb)
 {
@@ -749,7 +837,7 @@ static int asf_read_frame_header(AVFormatContext *s, ByteIOContext *pb){
  * @param s demux context
  * @param pb context to read data from
  * @param pkt pointer to store packet data into
- * @returns 0 if data was stored in pkt, <0 on error or 1 if more ASF
+ * @return 0 if data was stored in pkt, <0 on error or 1 if more ASF
  *          packets need to be loaded (through asf_get_packet())
  */
 static int ff_asf_parse_packet(AVFormatContext *s, ByteIOContext *pb, AVPacket *pkt)
@@ -836,12 +924,12 @@ static int ff_asf_parse_packet(AVFormatContext *s, ByteIOContext *pb, AVPacket *
             asf_st->pkt.pos =
             asf_st->packet_pos= asf->packet_pos;
 //printf("new packet: stream:%d key:%d packet_key:%d audio:%d size:%d\n",
-//asf->stream_index, asf->packet_key_frame, asf_st->pkt.flags & PKT_FLAG_KEY,
-//s->streams[asf->stream_index]->codec->codec_type == CODEC_TYPE_AUDIO, asf->packet_obj_size);
-            if (s->streams[asf->stream_index]->codec->codec_type == CODEC_TYPE_AUDIO)
+//asf->stream_index, asf->packet_key_frame, asf_st->pkt.flags & AV_PKT_FLAG_KEY,
+//s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO, asf->packet_obj_size);
+            if (s->streams[asf->stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
                 asf->packet_key_frame = 1;
             if (asf->packet_key_frame)
-                asf_st->pkt.flags |= PKT_FLAG_KEY;
+                asf_st->pkt.flags |= AV_PKT_FLAG_KEY;
         }
 
         /* read data */
@@ -994,7 +1082,7 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos,
     int64_t pts;
     int64_t pos= *ppos;
     int i;
-    int64_t start_pos[s->nb_streams];
+    int64_t start_pos[ASF_MAX_STREAMS];
 
     for(i=0; i<s->nb_streams; i++){
         start_pos[i]= pos;
@@ -1016,7 +1104,7 @@ static int64_t asf_read_pts(AVFormatContext *s, int stream_index, int64_t *ppos,
         pts= pkt->pts;
 
         av_free_packet(pkt);
-        if(pkt->flags&PKT_FLAG_KEY){
+        if(pkt->flags&AV_PKT_FLAG_KEY){
             i= pkt->stream_index;
 
             asf_st= s->streams[i]->priv_data;
@@ -1048,7 +1136,7 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index)
     url_fseek(s->pb, asf->data_object_offset + asf->data_object_size, SEEK_SET);
     get_guid(s->pb, &g);
     if (!guidcmp(&g, &index_guid)) {
-        int64_t itime;
+        int64_t itime, last_pos=-1;
         int pct, ict;
         int64_t av_unused gsize= get_le64(s->pb);
         get_guid(s->pb, &g);
@@ -1063,8 +1151,11 @@ static void asf_build_simple_index(AVFormatContext *s, int stream_index)
             int64_t pos      = s->data_offset + s->packet_size*(int64_t)pktnum;
             int64_t index_pts= av_rescale(itime, i, 10000);
 
+            if(pos != last_pos){
             av_log(s, AV_LOG_DEBUG, "pktnum:%d, pktct:%d\n", pktnum, pktct);
             av_add_index_entry(s->streams[stream_index], pos, index_pts, s->packet_size, 0, AVINDEX_KEYFRAME);
+            last_pos=pos;
+            }
         }
         asf->index_read= 1;
     }