Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  1. /*
  2.  * APE tag handling
  3.  * Copyright (c) 2007 Benjamin Zores <ben@geexbox.org>
  4.  *  based upon libdemac from Dave Chapman.
  5.  *
  6.  * This file is part of FFmpeg.
  7.  *
  8.  * FFmpeg is free software; you can redistribute it and/or
  9.  * modify it under the terms of the GNU Lesser General Public
  10.  * License as published by the Free Software Foundation; either
  11.  * version 2.1 of the License, or (at your option) any later version.
  12.  *
  13.  * FFmpeg is distributed in the hope that it will be useful,
  14.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  16.  * Lesser General Public License for more details.
  17.  *
  18.  * You should have received a copy of the GNU Lesser General Public
  19.  * License along with FFmpeg; if not, write to the Free Software
  20.  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  21.  */
  22.  
  23. #include "libavutil/intreadwrite.h"
  24. #include "libavutil/dict.h"
  25. #include "avformat.h"
  26. #include "avio_internal.h"
  27. #include "apetag.h"
  28. #include "internal.h"
  29.  
  30. #define APE_TAG_FLAG_CONTAINS_HEADER  (1 << 31)
  31. #define APE_TAG_FLAG_CONTAINS_FOOTER  (1 << 30)
  32. #define APE_TAG_FLAG_IS_HEADER        (1 << 29)
  33. #define APE_TAG_FLAG_IS_BINARY        (1 << 1)
  34.  
  35. static int ape_tag_read_field(AVFormatContext *s)
  36. {
  37.     AVIOContext *pb = s->pb;
  38.     uint8_t key[1024], *value;
  39.     uint32_t size, flags;
  40.     int i, c;
  41.  
  42.     size = avio_rl32(pb);  /* field size */
  43.     flags = avio_rl32(pb); /* field flags */
  44.     for (i = 0; i < sizeof(key) - 1; i++) {
  45.         c = avio_r8(pb);
  46.         if (c < 0x20 || c > 0x7E)
  47.             break;
  48.         else
  49.             key[i] = c;
  50.     }
  51.     key[i] = 0;
  52.     if (c != 0) {
  53.         av_log(s, AV_LOG_WARNING, "Invalid APE tag key '%s'.\n", key);
  54.         return -1;
  55.     }
  56.     if (size >= UINT_MAX)
  57.         return -1;
  58.     if (flags & APE_TAG_FLAG_IS_BINARY) {
  59.         uint8_t filename[1024];
  60.         enum AVCodecID id;
  61.         AVStream *st = avformat_new_stream(s, NULL);
  62.         if (!st)
  63.             return AVERROR(ENOMEM);
  64.  
  65.         size -= avio_get_str(pb, size, filename, sizeof(filename));
  66.         if (size <= 0) {
  67.             av_log(s, AV_LOG_WARNING, "Skipping binary tag '%s'.\n", key);
  68.             return 0;
  69.         }
  70.  
  71.         av_dict_set(&st->metadata, key, filename, 0);
  72.  
  73.         if ((id = ff_guess_image2_codec(filename)) != AV_CODEC_ID_NONE) {
  74.             AVPacket pkt;
  75.             int ret;
  76.  
  77.             ret = av_get_packet(s->pb, &pkt, size);
  78.             if (ret < 0) {
  79.                 av_log(s, AV_LOG_ERROR, "Error reading cover art.\n");
  80.                 return ret;
  81.             }
  82.  
  83.             st->disposition      |= AV_DISPOSITION_ATTACHED_PIC;
  84.             st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
  85.             st->codec->codec_id   = id;
  86.  
  87.             st->attached_pic              = pkt;
  88.             st->attached_pic.stream_index = st->index;
  89.             st->attached_pic.flags       |= AV_PKT_FLAG_KEY;
  90.         } else {
  91.             if (ff_alloc_extradata(st->codec, size))
  92.                 return AVERROR(ENOMEM);
  93.             if (avio_read(pb, st->codec->extradata, size) != size) {
  94.                 av_freep(&st->codec->extradata);
  95.                 st->codec->extradata_size = 0;
  96.                 return AVERROR(EIO);
  97.             }
  98.             st->codec->codec_type = AVMEDIA_TYPE_ATTACHMENT;
  99.         }
  100.     } else {
  101.         value = av_malloc(size+1);
  102.         if (!value)
  103.             return AVERROR(ENOMEM);
  104.         c = avio_read(pb, value, size);
  105.         if (c < 0) {
  106.             av_free(value);
  107.             return c;
  108.         }
  109.         value[c] = 0;
  110.         av_dict_set(&s->metadata, key, value, AV_DICT_DONT_STRDUP_VAL);
  111.     }
  112.     return 0;
  113. }
  114.  
  115. int64_t ff_ape_parse_tag(AVFormatContext *s)
  116. {
  117.     AVIOContext *pb = s->pb;
  118.     int64_t file_size = avio_size(pb);
  119.     uint32_t val, fields, tag_bytes;
  120.     uint8_t buf[8];
  121.     int64_t tag_start;
  122.     int i;
  123.  
  124.     if (file_size < APE_TAG_FOOTER_BYTES)
  125.         return 0;
  126.  
  127.     avio_seek(pb, file_size - APE_TAG_FOOTER_BYTES, SEEK_SET);
  128.  
  129.     avio_read(pb, buf, 8);     /* APETAGEX */
  130.     if (strncmp(buf, APE_TAG_PREAMBLE, 8)) {
  131.         return 0;
  132.     }
  133.  
  134.     val = avio_rl32(pb);       /* APE tag version */
  135.     if (val > APE_TAG_VERSION) {
  136.         av_log(s, AV_LOG_ERROR, "Unsupported tag version. (>=%d)\n", APE_TAG_VERSION);
  137.         return 0;
  138.     }
  139.  
  140.     tag_bytes = avio_rl32(pb); /* tag size */
  141.     if (tag_bytes - APE_TAG_FOOTER_BYTES > (1024 * 1024 * 16)) {
  142.         av_log(s, AV_LOG_ERROR, "Tag size is way too big\n");
  143.         return 0;
  144.     }
  145.  
  146.     if (tag_bytes > file_size - APE_TAG_FOOTER_BYTES) {
  147.         av_log(s, AV_LOG_ERROR, "Invalid tag size %u.\n", tag_bytes);
  148.         return 0;
  149.     }
  150.     tag_start = file_size - tag_bytes - APE_TAG_FOOTER_BYTES;
  151.  
  152.     fields = avio_rl32(pb);    /* number of fields */
  153.     if (fields > 65536) {
  154.         av_log(s, AV_LOG_ERROR, "Too many tag fields (%d)\n", fields);
  155.         return 0;
  156.     }
  157.  
  158.     val = avio_rl32(pb);       /* flags */
  159.     if (val & APE_TAG_FLAG_IS_HEADER) {
  160.         av_log(s, AV_LOG_ERROR, "APE Tag is a header\n");
  161.         return 0;
  162.     }
  163.  
  164.     avio_seek(pb, file_size - tag_bytes, SEEK_SET);
  165.  
  166.     for (i=0; i<fields; i++)
  167.         if (ape_tag_read_field(s) < 0) break;
  168.  
  169.     return tag_start;
  170. }
  171.  
  172. static int string_is_ascii(const uint8_t *str)
  173. {
  174.     while (*str && *str >= 0x20 && *str <= 0x7e ) str++;
  175.     return !*str;
  176. }
  177.  
  178. int ff_ape_write_tag(AVFormatContext *s)
  179. {
  180.     AVDictionaryEntry *e = NULL;
  181.     int size, ret, count = 0;
  182.     AVIOContext *dyn_bc = NULL;
  183.     uint8_t *dyn_buf = NULL;
  184.  
  185.     if ((ret = avio_open_dyn_buf(&dyn_bc)) < 0)
  186.         goto end;
  187.  
  188.     // flags
  189.     avio_wl32(dyn_bc, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER |
  190.                      APE_TAG_FLAG_IS_HEADER);
  191.     ffio_fill(dyn_bc, 0, 8);             // reserved
  192.  
  193.     while ((e = av_dict_get(s->metadata, "", e, AV_DICT_IGNORE_SUFFIX))) {
  194.         int val_len;
  195.  
  196.         if (!string_is_ascii(e->key)) {
  197.             av_log(s, AV_LOG_WARNING, "Non ASCII keys are not allowed\n");
  198.             continue;
  199.         }
  200.  
  201.         val_len = strlen(e->value);
  202.         avio_wl32(dyn_bc, val_len);            // value length
  203.         avio_wl32(dyn_bc, 0);                  // item flags
  204.         avio_put_str(dyn_bc, e->key);          // key
  205.         avio_write(dyn_bc, e->value, val_len); // value
  206.         count++;
  207.     }
  208.     if (!count)
  209.         goto end;
  210.  
  211.     size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
  212.     if (size <= 0)
  213.         goto end;
  214.     size += 20;
  215.  
  216.     // header
  217.     avio_write(s->pb, "APETAGEX", 8);   // id
  218.     avio_wl32(s->pb, APE_TAG_VERSION);  // version
  219.     avio_wl32(s->pb, size);
  220.     avio_wl32(s->pb, count);
  221.  
  222.     avio_write(s->pb, dyn_buf, size - 20);
  223.  
  224.     // footer
  225.     avio_write(s->pb, "APETAGEX", 8);   // id
  226.     avio_wl32(s->pb, APE_TAG_VERSION);  // version
  227.     avio_wl32(s->pb, size);             // size
  228.     avio_wl32(s->pb, count);            // tag count
  229.  
  230.     // flags
  231.     avio_wl32(s->pb, APE_TAG_FLAG_CONTAINS_HEADER | APE_TAG_FLAG_CONTAINS_FOOTER);
  232.     ffio_fill(s->pb, 0, 8);             // reserved
  233.  
  234. end:
  235.     if (dyn_bc && !dyn_buf)
  236.         avio_close_dyn_buf(dyn_bc, &dyn_buf);
  237.     av_freep(&dyn_buf);
  238.  
  239.     return ret;
  240. }
  241.