1 /*******************************************************************************
2 lqt_remux.c
3
4 libquicktime - A library for reading and writing quicktime/avi/mp4 files.
5 http://libquicktime.sourceforge.net
6
7 Copyright (C) 2002 Heroine Virtual Ltd.
8 Copyright (C) 2002-2011 Members of the libquicktime project.
9
10 This library is free software; you can redistribute it and/or modify it under
11 the terms of the GNU Lesser General Public License as published by the Free
12 Software Foundation; either version 2.1 of the License, or (at your option)
13 any later version.
14
15 This library is distributed in the hope that it will be useful, but WITHOUT
16 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
18 details.
19
20 You should have received a copy of the GNU Lesser General Public License along
21 with this library; if not, write to the Free Software Foundation, Inc., 51
22 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 *******************************************************************************/
24
25 #include <quicktime/lqt.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <stdio.h>
29
print_usage()30 static void print_usage()
31 {
32 fprintf(stderr, "Usage: lqtremux [-pre <outprefix>] infile\n");
33 fprintf(stderr, " lqtremux infile1 infile2 ... outfile\n");
34 }
35
36 typedef struct
37 {
38 const lqt_compression_info_t * ci;
39 quicktime_t * in_file;
40 quicktime_t * out_file;
41 double time;
42 int timescale;
43
44 int in_index;
45 int out_index;
46 int eof;
47 } track_t;
48
49 typedef struct
50 {
51 quicktime_t * file;
52 int num_audio_tracks;
53 int num_video_tracks;
54 } file_t;
55
56 /* Global variables */
57 track_t * audio_tracks = NULL;
58 track_t * video_tracks = NULL;
59 int total_tracks = 0;
60 file_t * files;
61 int num_files;
62
63 int nfiles;
64 int num_audio_tracks, num_video_tracks;
65 quicktime_t * file;
66
67 int prefix_len;
68 char * prefix = NULL;
69
70 lqt_codec_info_t ** audio_encoders;
71 lqt_codec_info_t ** video_encoders;
72
find_audio_encoder(lqt_compression_id_t id)73 static lqt_codec_info_t * find_audio_encoder(lqt_compression_id_t id)
74 {
75 int i = 0;
76
77 while(audio_encoders[i])
78 {
79 if(audio_encoders[i]->compression_id == id)
80 return audio_encoders[i];
81 i++;
82 }
83 return NULL;
84 }
85
find_video_encoder(lqt_compression_id_t id)86 static lqt_codec_info_t * find_video_encoder(lqt_compression_id_t id)
87 {
88 int i = 0;
89
90 while(video_encoders[i])
91 {
92 // fprintf(stderr, "find_video_encoder %s\n", video_encoders[i]->name);
93 if(video_encoders[i]->compression_id == id)
94 return video_encoders[i];
95 i++;
96 }
97 return NULL;
98 }
99
audio_iteration(track_t * track,lqt_packet_t * p)100 static void audio_iteration(track_t * track, lqt_packet_t * p)
101 {
102 int samples_read = 0;
103 int samples_to_read = track->ci->samplerate / 2; /* We make half second audio chunks */
104
105 while(samples_read < samples_to_read)
106 {
107 if(!lqt_read_audio_packet(track->in_file, p, track->in_index))
108 {
109 track->eof = 1;
110 return;
111 }
112
113 // fprintf(stderr, "Got audio packet\n");
114 // lqt_packet_dump(p);
115
116 lqt_write_audio_packet(track->out_file, p, track->out_index);
117
118 samples_read += p->duration;
119 }
120
121 track->time = (double)(p->timestamp + p->duration) / (double)(track->ci->samplerate);
122
123 }
124
video_iteration(track_t * track,lqt_packet_t * p)125 static void video_iteration(track_t * track, lqt_packet_t * p)
126 {
127 double test_time;
128
129 if(!lqt_read_video_packet(track->in_file, p, track->in_index))
130 {
131 track->eof = 1;
132 return;
133 }
134
135 // fprintf(stderr, "Got video packet\n");
136 // lqt_packet_dump(p);
137
138 lqt_write_video_packet(track->out_file, p, track->out_index);
139
140 test_time = (double)(p->timestamp + p->duration) / (double)(track->ci->video_timescale);
141 if(test_time > track->time)
142 track->time = test_time;
143
144 }
145
tracks_min_time(track_t * tracks,int num)146 static track_t * tracks_min_time(track_t * tracks, int num)
147 {
148 int i;
149 track_t * ret = NULL;
150 double min_time = 0.0;
151
152 for(i = 0; i < num; i++)
153 {
154 if(tracks[i].in_file && !tracks[i].eof &&
155 (!ret || (tracks[i].time < min_time)))
156 {
157 min_time = tracks[i].time;
158 ret = &tracks[i];
159 }
160 }
161 return ret;
162 }
163
init_demultiplex(char * filename)164 static int init_demultiplex(char * filename)
165 {
166 int i;
167 char * tmp_string;
168
169 lqt_codec_info_t * codec_info;
170
171 file = lqt_open_read(filename);
172 if(!file)
173 {
174 fprintf(stderr, "Couldn't open file %s\n", filename);
175 return -1;
176 }
177
178 num_audio_tracks = quicktime_audio_tracks(file);
179 num_video_tracks = quicktime_video_tracks(file);
180
181 if(!prefix) /* Create default prefix */
182 {
183 char * pos;
184 prefix = strdup(filename);
185
186 /* Put into current directory */
187 pos = strrchr(prefix, '/');
188 if(pos)
189 {
190 pos++;
191 memmove(prefix, pos, strlen(pos)+1);
192 }
193
194 /* Strip off extension */
195 pos = strrchr(prefix, '.');
196 if(pos)
197 *pos = '\0';
198 }
199
200 prefix_len = strlen(prefix);
201
202 tmp_string = malloc(prefix_len + 128);
203
204 if(num_audio_tracks)
205 {
206 audio_tracks = calloc(num_audio_tracks, sizeof(*audio_tracks));
207 for(i = 0; i < num_audio_tracks; i++)
208 {
209 audio_tracks[i].ci = lqt_get_audio_compression_info(file, i);
210
211 if(!audio_tracks[i].ci)
212 {
213 fprintf(stderr, "Audio track %d cannot be read compressed\n", i+1);
214 continue;
215 }
216 lqt_compression_info_dump(audio_tracks[i].ci);
217
218 codec_info = find_audio_encoder(audio_tracks[i].ci->id);
219 if(!codec_info)
220 {
221 fprintf(stderr, "No audio encoder found for compressed writing\n");
222 continue;
223 }
224
225 sprintf(tmp_string, "%s_audio_%02d.mov", prefix, i+1);
226 audio_tracks[i].in_file = file;
227 audio_tracks[i].out_file = lqt_open_write(tmp_string, LQT_FILE_QT);
228
229 if(!lqt_writes_compressed(lqt_get_file_type(audio_tracks[i].out_file),
230 audio_tracks[i].ci, codec_info))
231 {
232 fprintf(stderr, "Audio track %d cannot be written compressed\n", i+1);
233 quicktime_close(audio_tracks[i].out_file);
234 remove(tmp_string);
235 audio_tracks[i].out_file = NULL;
236 audio_tracks[i].in_file = NULL;
237 continue;
238 }
239 total_tracks++;
240
241 lqt_add_audio_track_compressed(audio_tracks[i].out_file,
242 audio_tracks[i].ci, codec_info);
243 audio_tracks[i].in_index = i;
244 audio_tracks[i].out_index = 0;
245 }
246 }
247 if(num_video_tracks)
248 {
249 video_tracks = calloc(num_video_tracks, sizeof(*video_tracks));
250 for(i = 0; i < num_video_tracks; i++)
251 {
252 video_tracks[i].ci = lqt_get_video_compression_info(file, i);
253
254 if(!video_tracks[i].ci)
255 {
256 fprintf(stderr, "Video track %d cannot be read compressed\n", i+1);
257 continue;
258 }
259 lqt_compression_info_dump(video_tracks[i].ci);
260
261 codec_info = find_video_encoder(video_tracks[i].ci->id);
262 if(!codec_info)
263 {
264 fprintf(stderr, "No video encoder found for compressed writing of %s\n",
265 lqt_compression_id_to_string(video_tracks[i].ci->id));
266 continue;
267 }
268
269 sprintf(tmp_string, "%s_video_%02d.mov", prefix, i+1);
270 video_tracks[i].in_file = file;
271 video_tracks[i].out_file = lqt_open_write(tmp_string, LQT_FILE_QT);
272
273 if(!lqt_writes_compressed(lqt_get_file_type(video_tracks[i].out_file),
274 video_tracks[i].ci, codec_info))
275 {
276 fprintf(stderr, "Video track %d cannot be written compressed\n", i+1);
277 quicktime_close(video_tracks[i].out_file);
278 remove(tmp_string);
279 video_tracks[i].out_file = NULL;
280 video_tracks[i].in_file = NULL;
281 continue;
282 }
283 total_tracks++;
284
285 lqt_add_video_track_compressed(video_tracks[i].out_file,
286 video_tracks[i].ci, codec_info);
287 video_tracks[i].in_index = i;
288 video_tracks[i].out_index = 0;
289 }
290 }
291
292 free(tmp_string);
293
294 return 1;
295 }
296
cleanup_demultiplex()297 static void cleanup_demultiplex()
298 {
299 int i;
300 for(i = 0; i < num_audio_tracks; i++)
301 {
302 if(audio_tracks[i].out_file)
303 quicktime_close(audio_tracks[i].out_file);
304 }
305 for(i = 0; i < num_video_tracks; i++)
306 {
307 if(video_tracks[i].out_file)
308 quicktime_close(video_tracks[i].out_file);
309 }
310 quicktime_close(file);
311 }
312
init_multiplex(char ** in_files,int num_in_files,char * out_file)313 static int init_multiplex(char ** in_files, int num_in_files, char * out_file)
314 {
315 int i, j;
316 int audio_index;
317 int video_index;
318 char * pos;
319 lqt_file_type_t type = LQT_FILE_QT;
320 lqt_codec_info_t * codec_info;
321
322 files = calloc(num_in_files, sizeof(*files));
323 num_files = num_in_files;
324 /* Open input files */
325
326 for(i = 0; i < num_in_files; i++)
327 {
328 files[i].file = lqt_open_read(in_files[i]);
329 if(!files[i].file)
330 {
331 fprintf(stderr, "Opening %s failed\n", in_files[i]);
332 return 0;
333 }
334 files[i].num_audio_tracks = quicktime_audio_tracks(files[i].file);
335 files[i].num_video_tracks = quicktime_video_tracks(files[i].file);
336 num_audio_tracks += files[i].num_audio_tracks;
337 num_video_tracks += files[i].num_video_tracks;
338 }
339
340 /* Open output file */
341
342 pos = strrchr(out_file, '.');
343 if(!pos)
344 {
345 fprintf(stderr, "Unknown file type for file %s\n", out_file);
346 return 0;
347 }
348
349 pos++;
350 if(!strcmp(pos, "mov"))
351 type = LQT_FILE_QT;
352 else if(!strcmp(pos, "avi"))
353 type = LQT_FILE_AVI_ODML;
354 else if(!strcmp(pos, "mp4"))
355 type = LQT_FILE_MP4;
356 else if(!strcmp(pos, "m4a"))
357 type = LQT_FILE_M4A;
358 else
359 {
360 fprintf(stderr, "Unknown file type for file %s\n", out_file);
361 return 0;
362 }
363
364 file = lqt_open_write(out_file, type);
365
366 audio_tracks = calloc(num_audio_tracks, sizeof(*audio_tracks));
367 video_tracks = calloc(num_video_tracks, sizeof(*video_tracks));
368
369 audio_index = 0;
370 video_index = 0;
371
372 for(i = 0; i < num_in_files; i++)
373 {
374 for(j = 0; j < files[i].num_audio_tracks; j++)
375 {
376 audio_tracks[audio_index].ci = lqt_get_audio_compression_info(files[i].file, j);
377 if(!audio_tracks[audio_index].ci)
378 {
379 fprintf(stderr, "Audio track %d of file %s cannot be read compressed\n",
380 j+1, in_files[i]);
381 audio_index++;
382 continue;
383 }
384
385 codec_info = find_audio_encoder(audio_tracks[i].ci->id);
386 if(!codec_info)
387 {
388 fprintf(stderr, "No audio encoder found for compressed writing\n");
389 continue;
390 }
391
392 if(!lqt_writes_compressed(lqt_get_file_type(file),
393 audio_tracks[audio_index].ci, codec_info))
394 {
395 fprintf(stderr, "Audio track %d of file %s cannot be written compressed\n",
396 j+1, in_files[i]);
397 audio_index++;
398 continue;
399 }
400
401 lqt_compression_info_dump(audio_tracks[i].ci);
402
403 audio_tracks[audio_index].in_index = j;
404 audio_tracks[audio_index].out_index = audio_index;
405 audio_tracks[audio_index].in_file = files[i].file;
406 audio_tracks[audio_index].out_file = file;
407
408 lqt_add_audio_track_compressed(audio_tracks[audio_index].out_file,
409 audio_tracks[audio_index].ci, codec_info);
410
411 audio_index++;
412 total_tracks++;
413 }
414 for(j = 0; j < files[i].num_video_tracks; j++)
415 {
416 video_tracks[video_index].ci = lqt_get_video_compression_info(files[i].file, j);
417 if(!video_tracks[video_index].ci)
418 {
419 fprintf(stderr, "Video track %d of file %s cannot be read compressed\n",
420 j+1, in_files[i]);
421 video_index++;
422 continue;
423 }
424
425 codec_info = find_video_encoder(video_tracks[i].ci->id);
426 if(!codec_info)
427 {
428 fprintf(stderr, "No video encoder found for compressed writing of %s\n",
429 lqt_compression_id_to_string(video_tracks[i].ci->id));
430 continue;
431 }
432
433 if(!lqt_writes_compressed(lqt_get_file_type(file),
434 video_tracks[video_index].ci, codec_info))
435 {
436 fprintf(stderr, "Video track %d of file %s cannot be written compressed\n",
437 j+1, in_files[i]);
438 video_index++;
439 continue;
440 }
441 lqt_compression_info_dump(video_tracks[i].ci);
442 video_tracks[video_index].in_index = j;
443 video_tracks[video_index].out_index = video_index;
444 video_tracks[video_index].in_file = files[i].file;
445 video_tracks[video_index].out_file = file;
446
447 lqt_add_video_track_compressed(video_tracks[video_index].out_file,
448 video_tracks[video_index].ci, codec_info);
449
450 video_index++;
451 total_tracks++;
452
453 }
454
455 }
456 return 1;
457 }
458
cleanup_multiplex()459 static void cleanup_multiplex()
460 {
461 int i;
462 for(i = 0; i < num_files; i++)
463 {
464 if(files[i].file)
465 quicktime_close(files[i].file);
466 }
467 if(file)
468 quicktime_close(file);
469 }
470
main(int argc,char ** argv)471 int main(int argc, char ** argv)
472 {
473 int i;
474 int first_file;
475 int nfiles;
476
477 track_t * atrack, *vtrack;
478 lqt_packet_t p;
479
480 memset(&p, 0, sizeof(p));
481
482 if(argc < 2)
483 {
484 print_usage();
485 return 0;
486 }
487
488 /* Parse agruments */
489
490 nfiles = argc - 1;
491
492 i = 1;
493 while(i < argc)
494 {
495 if(*(argv[i]) != '-')
496 {
497 first_file = i;
498 break;
499 }
500
501 if(!strcmp(argv[i], "-pre"))
502 {
503 if(i == argc-1)
504 {
505 fprintf(stderr, "-pre requires an argument\n");
506 return -1;
507 }
508 prefix = strdup(argv[i+1]);
509 i++;
510 nfiles -= 2;
511 }
512 i++;
513 }
514
515 if(!nfiles)
516 {
517 print_usage();
518 return -1;
519 }
520
521 /* Query registry */
522
523 audio_encoders = lqt_query_registry(1, 0, 1, 0);
524 video_encoders = lqt_query_registry(0, 1, 1, 0);
525
526 if(nfiles == 1)
527 {
528 /* Demultiplex */
529 if(!init_demultiplex(argv[argc-1]))
530 return -1;
531 }
532 else
533 {
534 /* Multiplex */
535
536 if(!init_multiplex(&argv[first_file], nfiles-1, argv[argc-1]))
537 return -1;
538 }
539
540 if(!total_tracks)
541 {
542 fprintf(stderr, "No tracks to demultiplex\n");
543 return -1;
544 }
545
546 /* Transmultiplex */
547
548 while(1)
549 {
550 atrack = tracks_min_time(audio_tracks, num_audio_tracks);
551 vtrack = tracks_min_time(video_tracks, num_video_tracks);
552
553 if(atrack && vtrack)
554 {
555 fprintf(stderr, "A: %f, V: %f\n", atrack->time, vtrack->time);
556
557 if(atrack->time < vtrack->time)
558 audio_iteration(atrack, &p);
559 else
560 video_iteration(vtrack, &p);
561 }
562 else if(atrack)
563 audio_iteration(atrack, &p);
564 else if(vtrack)
565 video_iteration(vtrack, &p);
566 else
567 break;
568 }
569
570 /* Cleanup */
571
572 if(nfiles == 1)
573 cleanup_demultiplex();
574 else
575 cleanup_multiplex();
576
577 if(files)
578 free(files);
579 if(audio_tracks)
580 free(audio_tracks);
581 if(video_tracks)
582 free(video_tracks);
583
584 lqt_destroy_codec_info(audio_encoders);
585 lqt_destroy_codec_info(video_encoders);
586
587 return 0;
588 }
589