1 /*
2 ogmmerge -- utility for splicing together ogg bitstreams
3 from component media subtypes
4
5 generic.cpp
6 generic base classes, chapter file support
7
8 Written by Moritz Bunkus <moritz@bunkus.org>
9 Based on Xiph.org's 'oggmerge' found in their CVS repository
10 See http://www.xiph.org
11
12 Distributed under the GPL
13 see the file COPYING for details
14 or visit http://www.gnu.org/copyleft/gpl.html
15 */
16 #include <ctype.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include <vorbis/codec.h>
21
22 #include "common.h"
23 #include "ogmmerge.h"
24 #include "vorbis_header_utils.h"
25
generic_packetizer_c()26 generic_packetizer_c::generic_packetizer_c() {
27 comments = NULL;
28 }
29
serial_in_use(int serial)30 int generic_packetizer_c::serial_in_use(int serial) {
31 return (serial == serialno);
32 }
33
set_comments(vorbis_comment * ncomments)34 void generic_packetizer_c::set_comments(vorbis_comment *ncomments) {
35 if (comments != NULL) {
36 vorbis_comment_clear(comments);
37 free(comments);
38 }
39 if (ncomments == NULL) {
40 comments = NULL;
41 return;
42 }
43 comments = (vorbis_comment *)malloc(sizeof(vorbis_comment));
44 if (comments == NULL)
45 die("malloc");
46 memcpy(comments, ncomments, sizeof(vorbis_comment));
47 }
48
generic_reader_c()49 generic_reader_c::generic_reader_c() {
50 chapter_info = NULL;
51 }
52
~generic_reader_c()53 generic_reader_c::~generic_reader_c() {
54 if (chapter_info != NULL) {
55 vorbis_comment_clear(chapter_info);
56 free(chapter_info);
57 }
58 }
59
set_chapter_info(vorbis_comment * info)60 void generic_reader_c::set_chapter_info(vorbis_comment *info) {
61 if (chapter_info != NULL) {
62 vorbis_comment_clear(chapter_info);
63 free(chapter_info);
64 }
65 chapter_info = vorbis_comment_dup(info);
66 }
67
68 #define isequal(s) (*(s) == '=')
69 #define iscolon(s) (*(s) == ':')
70 #define isfullstop(s) (*(s) == '.')
71 #define istwodigits(s) (isdigit(*(s)) && isdigit(*(s + 1)))
72 #define isthreedigits(s) (isdigit(*(s)) && isdigit(*(s + 1)) && \
73 isdigit(*(s + 2)))
74 #define isarrow(s) (!strncmp((s), " --> ", 5))
75 #define istimestamp(s) (istwodigits(s) && iscolon(s + 2) && \
76 istwodigits(s + 3) && iscolon(s + 5) && \
77 istwodigits(s + 6) && isfullstop(s + 8) && \
78 isthreedigits(s + 9))
79 #define ischapter(s) ((strlen(s) == 22) && \
80 !strncmp(s, "CHAPTER", 7) && \
81 istwodigits(s + 7) && isequal(s + 9) && \
82 istimestamp(s + 10))
83 #define ischaptername(s) ((strlen(s) > 14) && \
84 !strncmp(s, "CHAPTER", 7) && \
85 istwodigits(s + 7) && \
86 !strncmp(s + 9, "NAME", 4) && isequal(s + 13))
87
chapter_information_probe(FILE * file,off_t size)88 int chapter_information_probe(FILE *file, off_t size) {
89 char buf[201];
90 int len;
91
92 if (size < 37)
93 return 0;
94 if (fseeko(file, 0, SEEK_SET) != 0)
95 return 0;
96 if (fgets(buf, 200, file) == NULL)
97 return 0;
98 len = strlen(buf);
99 if (len == 0)
100 return 0;
101 if (buf[len - 1] != '\n')
102 return 0;
103 if (strncmp(buf, "CHAPTER", 7))
104 return 0;
105 if (strncmp(&buf[9], "=", 1))
106 return 0;
107 if (!istimestamp(&buf[10]))
108 return 0;
109 if (fgets(buf, 200, file) == NULL)
110 return 0;
111 len = strlen(buf);
112 if (len == 0)
113 return 0;
114 if (buf[len - 1] != '\n')
115 return 0;
116 if (strncmp(buf, "CHAPTER", 7))
117 return 0;
118 if (strncmp(&buf[9], "NAME=", 5))
119 return 0;
120
121 return 1;
122 }
123
chapter_information_read(char * name)124 vorbis_comment *chapter_information_read(char *name) {
125 vorbis_comment *vc;
126 char buf[201];
127 char *s;
128 int len;
129 FILE *file;
130
131 if ((file = fopen(name, "r")) == NULL)
132 return NULL;
133
134 if (fseeko(file, 0, SEEK_SET) != 0)
135 return NULL;
136
137 if (verbose)
138 fprintf(stdout, "Using chapter information reader for %s.\n", name);
139
140 vc = (vorbis_comment *)malloc(sizeof(vorbis_comment));
141 if (vc == NULL)
142 die("malloc");
143
144 vc->vendor = strdup(VERSIONINFO);
145 vc->user_comments = (char **)mmalloc(4);
146 vc->comment_lengths = (int *)mmalloc(4);
147 vc->comments = 0;
148
149 while (!feof(file)) {
150 if (fgets(buf, 200, file) != NULL) {
151 len = strlen(buf);
152 if (len > 0) {
153 s = &buf[len - 1];
154 while ((s != buf) && ((*s == '\n') || (*s == '\r'))) {
155 *s = 0;
156 s--;
157 }
158 len = strlen(buf);
159 if (len > 0)
160 vorbis_comment_add(vc, buf);
161 }
162 }
163 }
164
165 return vc;
166 }
167
chapter_information_adjust(vorbis_comment * vc,double start,double end)168 vorbis_comment *chapter_information_adjust(vorbis_comment *vc, double start,
169 double end) {
170 vorbis_comment *nvc;
171 int i, chapter_sub, chapter, hour, min, sec, msec;
172 char *copy;
173 char *last;
174 char copy_chapters[100], new_chapter[24];
175 double cstart;
176
177 if (vc == NULL)
178 return NULL;
179
180 memset(copy_chapters, 0, 100);
181 nvc = (vorbis_comment *)malloc(sizeof(vorbis_comment));
182 if (nvc == NULL)
183 die("malloc");
184
185 nvc->vendor = strdup(VERSIONINFO);
186 nvc->user_comments = (char **)mmalloc(4);
187 nvc->comment_lengths = (int *)mmalloc(4);
188 nvc->comments = 0;
189 chapter_sub = -1;
190 last = NULL;
191
192 for (i = 0; i < vc->comments; i++) {
193 if (ischapter(vc->user_comments[i])) {
194 copy = strdup(vc->user_comments[i]);
195 if (copy == NULL)
196 die("malloc");
197 /*
198 * CHAPTER01=01:23:45.678
199 * 0123456789012345678901
200 * 1 2
201 */
202 copy[9] = 0; // =
203 copy[12] = 0; // :
204 copy[15] = 0; // :
205 copy[18] = 0; // .
206 chapter = strtol(©[7], NULL, 10);
207 hour= strtol(©[10], NULL, 10);
208 min = strtol(©[13], NULL, 10);
209 sec = strtol(©[16], NULL, 10);
210 msec = strtol(©[19], NULL, 10);
211 cstart = msec + (1000.0 * sec) + (60000.0 * min) + (3600000.0 * hour);
212 if ((cstart >= start) && (cstart < end)) {
213 copy_chapters[chapter] = 1;
214 if (chapter_sub == -1) {
215 chapter_sub = chapter - 1;
216 if (last && (cstart > start)) {
217 int last_length = strlen(last);
218 sprintf(new_chapter, "CHAPTER01=00:00:00.000");
219 vorbis_comment_add(nvc, new_chapter);
220 last = (char *)realloc(last, last_length + 12 + 1);
221 sprintf(&last[7], "01");
222 last[9] = 'N';
223 sprintf(&last[last_length], " (continued)");
224 last[last_length+12] = 0;
225 vorbis_comment_add(nvc, last);
226 free(last);
227 chapter_sub--;
228 }
229 }
230 chapter -= chapter_sub;
231 sprintf(new_chapter, "CHAPTER%02d=%02d:%02d:%02d.%03d", chapter,
232 (((int)(cstart - start)) / 1000 / 60 / 60),
233 (((int)(cstart - start)) / 1000 / 60) % 60,
234 (((int)(cstart - start)) / 1000) % 60,
235 ((int)(cstart - start)) % 1000);
236 vorbis_comment_add(nvc, new_chapter);
237 }
238 free(copy);
239 } else if (ischaptername(vc->user_comments[i])) {
240 memcpy(new_chapter, &vc->user_comments[i][7], 2);
241 new_chapter[2] = 0;
242 chapter = strtol(new_chapter, NULL, 10);
243 if (copy_chapters[chapter]) {
244 copy = strdup(vc->user_comments[i]);
245 if (copy == NULL)
246 die("malloc");
247 sprintf(©[7], "%02d", chapter - chapter_sub);
248 copy[9] = 'N';
249 vorbis_comment_add(nvc, copy);
250 free(copy);
251 } else if (chapter_sub == -1) {
252 if (last != NULL)
253 free(last);
254 last = strdup(vc->user_comments[i]);
255 if (last == NULL)
256 die("malloc");
257 }
258 } else
259 vorbis_comment_add(nvc, vc->user_comments[i]);
260 }
261
262 return nvc;
263 }
264