Merge commit 'c5560e72d0bb69f8a1ac9536570398f84388f396'
[ffmpeg.git] / libavformat / apetag.c
index bd8d0ed48554af64ffe899a71b9375f70c07e2f6..c8d1bdca5ad063350620b821fd6aaf846aa7ce65 100644 (file)
@@ -3,20 +3,20 @@
  * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>
  *  based upon libdemac from Dave Chapman.
  *
- * 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
  */
 
@@ -29,8 +29,6 @@
 #include "apetag.h"
 #include "internal.h"
 
-#define APE_TAG_VERSION               2000
-#define APE_TAG_FOOTER_BYTES          32
 #define APE_TAG_FLAG_CONTAINS_HEADER  (1 << 31)
 #define APE_TAG_FLAG_CONTAINS_FOOTER  (1 << 30)
 #define APE_TAG_FLAG_IS_HEADER        (1 << 29)
@@ -94,14 +92,8 @@ static int ape_tag_read_field(AVFormatContext *s)
             st->attached_pic.stream_index = st->index;
             st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
         } else {
-            st->codec->extradata = av_malloc(size + FF_INPUT_BUFFER_PADDING_SIZE);
-            if (!st->codec->extradata)
+            if (ff_get_extradata(st->codec, s->pb, size) < 0)
                 return AVERROR(ENOMEM);
-            if (avio_read(pb, st->codec->extradata, size) != size) {
-                av_freep(&st->codec->extradata);
-                return AVERROR(EIO);
-            }
-            st->codec->extradata_size = size;
             st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
         }
     } else {
@@ -134,7 +126,7 @@ int64_t ff_ape_parse_tag(AVFormatContext *s)
     avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET);
 
     avio_read(pb, buf, 8);     /* APETAGEX */
-    if (strncmp(buf, "APETAGEX", 8)) {
+    if (strncmp(buf, APE_TAG_PREAMBLE, 8)) {
         return 0;
     }
 
@@ -176,43 +168,61 @@ int64_t ff_ape_parse_tag(AVFormatContext *s)
     return tag_start;
 }
 
+static int string_is_ascii(const uint8_t *str)
+{
+    while (*str && *str >= 0x20 && *str <= 0x7e ) str++;
+    return !*str;
+}
+
 int ff_ape_write_tag(AVFormatContext *s)
 {
     AVDictionaryEntry *e = NULL;
-    int64_t start, end;
-    int size, count = 0;
-
-    if (!s->pb->seekable)
-        return 0;
+    int size, ret, count = 0;
+    AVIOContext *dyn_bc = NULL;
+    uint8_t *dyn_buf = NULL;
 
-    start = avio_tell(s->pb);
-
-    // header
-    avio_write(s->pb, "APETAGEX", 8);   // id
-    avio_wl32 (s->pb, APE_TAG_VERSION); // version
-    avio_wl32(s->pb, 0);                // reserve space for size
-    avio_wl32(s->pb, 0);                // reserve space for tag count
+    if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
+        goto end;
 
     // flags
-    avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER |
+    avio_wl32(dyn_bc, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER |
                      APE_TAG_FLAG_IS_HEADER);
-    ffio_fill(s->pb, 0, 8);             // reserved
+    ffio_fill(dyn_bc, 0, 8);             // reserved
 
     while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
-        int val_len = strlen(e->value);
+        int val_len;
 
-        avio_wl32(s->pb, val_len);            // value length
-        avio_wl32(s->pb, 0);                  // item flags
-        avio_put_str(s->pb, e->key);          // key
-        avio_write(s->pb, e->value, val_len); // value
+        if (!string_is_ascii(e->key)) {
+            av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n");
+            continue;
+        }
+
+        val_len = strlen(e->value);
+        avio_wl32(dyn_bc, val_len);            // value length
+        avio_wl32(dyn_bc, 0);                  // item flags
+        avio_put_str(dyn_bc, e->key);          // key
+        avio_write(dyn_bc, e->value, val_len); // value
         count++;
     }
+    if (!count)
+        goto end;
+
+    size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+    if (size <= 0)
+        goto end;
+    size += 20;
+
+    // header
+    avio_write(s->pb, "APETAGEX", 8);   // id
+    avio_wl32(s->pb, APE_TAG_VERSION);  // version
+    avio_wl32(s->pb, size);
+    avio_wl32(s->pb, count);
 
-    size = avio_tell(s->pb) - start;
+    avio_write(s->pb, dyn_buf, size - 20);
 
     // footer
     avio_write(s->pb, "APETAGEX", 8);   // id
-    avio_wl32 (s->pb, APE_TAG_VERSION); // version
+    avio_wl32(s->pb, APE_TAG_VERSION);  // version
     avio_wl32(s->pb, size);             // size
     avio_wl32(s->pb, count);            // tag count
 
@@ -220,12 +230,10 @@ int ff_ape_write_tag(AVFormatContext *s)
     avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER);
     ffio_fill(s->pb, 0, 8);             // reserved
 
-    // update values in the header
-    end = avio_tell(s->pb);
-    avio_seek(s->pb, start + 12, SEEK_SET);
-    avio_wl32(s->pb, size);
-    avio_wl32(s->pb, count);
-    avio_seek(s->pb, end, SEEK_SET);
+end:
+    if (dyn_bc && !dyn_buf)
+        avio_close_dyn_buf(dyn_bc, &dyn_buf);
+    av_freep(&dyn_buf);
 
-    return 0;
+    return ret;
 }