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(&copy[7], NULL, 10);
207       hour= strtol(&copy[10], NULL, 10);
208       min = strtol(&copy[13], NULL, 10);
209       sec = strtol(&copy[16], NULL, 10);
210       msec = strtol(&copy[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(&copy[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