• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * VorbisComment writer
3  * Copyright (c) 2009 James Darnley
4  *
5  * This file is part of FFmpeg.
6  *
7  * FFmpeg is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * FFmpeg is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with FFmpeg; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "avio.h"
23 #include "avformat.h"
24 #include "metadata.h"
25 #include "vorbiscomment.h"
26 #include "libavutil/dict.h"
27 
28 /**
29  * VorbisComment metadata conversion mapping.
30  * from Ogg Vorbis I format specification: comment field and header specification
31  * http://xiph.org/vorbis/doc/v-comment.html
32  */
33 const AVMetadataConv ff_vorbiscomment_metadata_conv[] = {
34     { "ALBUMARTIST", "album_artist"},
35     { "TRACKNUMBER", "track"  },
36     { "DISCNUMBER",  "disc"   },
37     { "DESCRIPTION", "comment" },
38     { 0 }
39 };
40 
ff_vorbiscomment_length(const AVDictionary * m,const char * vendor_string,AVChapter ** chapters,unsigned int nb_chapters)41 int64_t ff_vorbiscomment_length(const AVDictionary *m, const char *vendor_string,
42                                 AVChapter **chapters, unsigned int nb_chapters)
43 {
44     int64_t len = 8;
45     len += strlen(vendor_string);
46     if (chapters && nb_chapters) {
47         for (int i = 0; i < nb_chapters; i++) {
48             AVDictionaryEntry *tag = NULL;
49             len += 4 + 12 + 1 + 10;
50             while ((tag = av_dict_get(chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
51                 int64_t len1 = !strcmp(tag->key, "title") ? 4 : strlen(tag->key);
52                 len += 4 + 10 + len1 + 1 + strlen(tag->value);
53             }
54         }
55     }
56     if (m) {
57         AVDictionaryEntry *tag = NULL;
58         while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
59             len += 4 +strlen(tag->key) + 1 + strlen(tag->value);
60         }
61     }
62     return len;
63 }
64 
ff_vorbiscomment_write(AVIOContext * pb,const AVDictionary * m,const char * vendor_string,AVChapter ** chapters,unsigned int nb_chapters)65 int ff_vorbiscomment_write(AVIOContext *pb, const AVDictionary *m,
66                            const char *vendor_string,
67                            AVChapter **chapters, unsigned int nb_chapters)
68 {
69     int cm_count = 0;
70     avio_wl32(pb, strlen(vendor_string));
71     avio_write(pb, vendor_string, strlen(vendor_string));
72     if (chapters && nb_chapters) {
73         for (int i = 0; i < nb_chapters; i++) {
74             cm_count += av_dict_count(chapters[i]->metadata) + 1;
75         }
76     }
77     if (m) {
78         int count = av_dict_count(m) + cm_count;
79         AVDictionaryEntry *tag = NULL;
80         avio_wl32(pb, count);
81         while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) {
82             int64_t len1 = strlen(tag->key);
83             int64_t len2 = strlen(tag->value);
84             if (len1+1+len2 > UINT32_MAX)
85                 return AVERROR(EINVAL);
86             avio_wl32(pb, len1 + 1 + len2);
87             avio_write(pb, tag->key, len1);
88             avio_w8(pb, '=');
89             avio_write(pb, tag->value, len2);
90         }
91         for (int i = 0; i < nb_chapters; i++) {
92             AVChapter *chp = chapters[i];
93             char chapter_time[13];
94             char chapter_number[4];
95             int h, m, s, ms;
96 
97             s  = av_rescale(chp->start, chp->time_base.num, chp->time_base.den);
98             h  = s / 3600;
99             m  = (s / 60) % 60;
100             ms = av_rescale_q(chp->start, chp->time_base, av_make_q(   1, 1000)) % 1000;
101             s  = s % 60;
102             snprintf(chapter_number, sizeof(chapter_number), "%03d", i);
103             snprintf(chapter_time, sizeof(chapter_time), "%02d:%02d:%02d.%03d", h, m, s, ms);
104             avio_wl32(pb, 10 + 1 + 12);
105             avio_write(pb, "CHAPTER", 7);
106             avio_write(pb, chapter_number, 3);
107             avio_w8(pb, '=');
108             avio_write(pb, chapter_time, 12);
109 
110             tag = NULL;
111             while ((tag = av_dict_get(chapters[i]->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
112                 int64_t len1 = !strcmp(tag->key, "title") ? 4 : strlen(tag->key);
113                 int64_t len2 = strlen(tag->value);
114                 if (len1+1+len2+10 > UINT32_MAX)
115                     return AVERROR(EINVAL);
116                 avio_wl32(pb, 10 + len1 + 1 + len2);
117                 avio_write(pb, "CHAPTER", 7);
118                 avio_write(pb, chapter_number, 3);
119                 if (!strcmp(tag->key, "title"))
120                     avio_write(pb, "NAME", 4);
121                 else
122                     avio_write(pb, tag->key, len1);
123                 avio_w8(pb, '=');
124                 avio_write(pb, tag->value, len2);
125             }
126         }
127     } else
128         avio_wl32(pb, 0);
129     return 0;
130 }
131