1 /*******************************************************************************
2 lqt_transcode.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 /*
26 * Simple quicktime->quicktime transcoder
27 * Used mainly for testing the encoder capabilities
28 * of libquicktime
29 */
30
31 /* Limitation: Handles only 1 audio- and one video stream per file */
32
33 #include <config.h> // ONLY for the PACKAGE macro. Usually, applications never need
34 // to include config.h
35
36 #define _(str) dgettext(PACKAGE, str)
37 #include <libintl.h>
38 #include <locale.h>
39
40 #include <quicktime/lqt.h>
41 #include <quicktime/colormodels.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <string.h>
45
46
47
48 /* Supported colormodels */
49
50 int colormodels[] =
51 {
52 BC_RGB565,
53 BC_BGR565,
54 BC_BGR888,
55 BC_BGR8888,
56 BC_RGB888,
57 BC_RGBA8888,
58 BC_RGB161616,
59 BC_RGBA16161616,
60 BC_YUVA8888,
61 BC_YUV422,
62 BC_YUV420P,
63 BC_YUV422P,
64 BC_YUV444P,
65 BC_YUV411P,
66 LQT_COLORMODEL_NONE
67 };
68
69 static struct
70 {
71 char * name;
72 lqt_file_type_t type;
73 char * extension;
74 char * description;
75 char * default_audio_codec;
76 char * default_video_codec;
77 }
78 formats[] =
79 {
80 { "qt", LQT_FILE_QT, "mov", "Quicktime (QT7 compatible)", "faac", "ffmpeg_mpg4" },
81 { "qtold", LQT_FILE_QT_OLD, "mov", "Quicktime (qt4l and old lqt)", "twos", "mjpa" },
82 { "avi", LQT_FILE_AVI, "avi", "AVI (< 2G)", "lame", "ffmpeg_msmpeg4v3" },
83 { "avi_odml", LQT_FILE_AVI_ODML, "avi", "AVI (> 2G)", "lame", "ffmpeg_msmpeg4v3" },
84 { "mp4", LQT_FILE_MP4, "mp4", "ISO MPEG-4", "faac", "ffmpeg_mpg4" },
85 { "m4a", LQT_FILE_M4A, "m4a", "m4a (iTunes compatible)", "faac", "ffmpeg_mpg4" },
86 };
87
list_formats()88 static void list_formats()
89 {
90 int i;
91 printf(_("Supported formats\n"));
92 for(i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
93 {
94 printf(_("%8s: %s (default codecs: %s/%s)\n"), formats[i].name, formats[i].description,
95 formats[i].default_audio_codec, formats[i].default_video_codec);
96 }
97 }
98
99 typedef struct
100 {
101 quicktime_t * in_file;
102 quicktime_t * out_file;
103
104 int64_t num_video_frames;
105 int64_t video_duration;
106
107 int64_t num_audio_samples;
108
109 int64_t audio_samples_written;
110 int64_t video_frames_written;
111
112 unsigned char ** video_buffer;
113
114 float ** audio_buffer_f;
115 int16_t ** audio_buffer_i;
116 int samples_per_frame;
117
118 int do_audio;
119 int do_video;
120
121 /* Format information */
122
123 int colormodel;
124 int width;
125 int height;
126 int rowspan;
127 int rowspan_uv;
128
129 int frame_duration;
130 int timescale;
131 int samplerate;
132 int num_channels;
133 int audio_bits;
134
135 /* Progress (0..1) */
136
137 float progress;
138 } transcode_handle;
139
print_usage()140 static void print_usage()
141 {
142 printf(_("Usage: lqt_transcode [[-avi]|[-f <format>]] [-floataudio] [-qtvr <obj|pano>] [-qtvr_columns <columns>] [-qtvr_rows <rows>] [-ac <audio_codec>] [-vc <video_codec>] <in_file> <out_file>\n"));
143 printf(_(" Transcode <in_file> to <out_file> using <audio_codec> and <video_codec>\n\n"));
144 printf(_(" lqt_transcode -lv\n"));
145 printf(_(" List video encoders\n\n"));
146 printf(_(" lqt_transcode -la\n"));
147 printf(_(" List audio encoders\n"));
148 printf(_(" lqt_transcode -lf\n"));
149 printf(_(" List output formats\n"));
150 }
151
list_info(lqt_codec_info_t ** info)152 static void list_info(lqt_codec_info_t ** info)
153 {
154 int i, j;
155 int max_len;
156 int len;
157 max_len = 0;
158 i = 0;
159 while(info[i])
160 {
161 len = strlen(info[i]->name);
162 if(len > max_len)
163 max_len = len;
164 i++;
165 }
166 max_len++;
167 i = 0;
168
169 while(info[i])
170 {
171 len = strlen(info[i]->name);
172
173 printf("%s:", info[i]->name);
174 len = strlen(info[i]->name);
175
176 for(j = 0; j < max_len - len; j++)
177 printf(" ");
178
179 printf("%s\n", info[i]->long_name);
180
181 i++;
182 }
183
184 }
185
list_video_codecs()186 static void list_video_codecs()
187 {
188 lqt_codec_info_t ** info;
189 info = lqt_query_registry(0, 1, 1, 0);
190 list_info(info);
191 lqt_destroy_codec_info(info);
192 }
193
list_audio_codecs()194 static void list_audio_codecs()
195 {
196 lqt_codec_info_t ** info;
197 info = lqt_query_registry(1, 0, 1, 0);
198 list_info(info);
199 lqt_destroy_codec_info(info);
200 }
201
transcode_init(transcode_handle * h,char * in_file,char * out_file,char * video_codec,char * audio_codec,int floataudio,lqt_file_type_t type,char * qtvr,int qtvr_rows,int qtvr_columns)202 static int transcode_init(transcode_handle * h,
203 char * in_file,
204 char * out_file,
205 char * video_codec,
206 char * audio_codec,
207 int floataudio,
208 lqt_file_type_t type,
209 char * qtvr,
210 int qtvr_rows,
211 int qtvr_columns)
212 {
213 lqt_codec_info_t ** codec_info;
214 int i;
215 int in_cmodel, out_cmodel;
216 char * extension;
217
218 h->in_file = quicktime_open(in_file, 1, 0);
219 if(!h->in_file)
220 {
221 fprintf(stderr, _("Cannot open input file %s\n"), in_file);
222 return 0;
223 }
224
225 /* Get the output format */
226
227 if(type == LQT_FILE_NONE)
228 {
229 extension = strrchr(out_file, '.');
230 if(!extension)
231 {
232 fprintf(stderr, _("Need a file extension when autoguessing output format\n"));
233 return 0;
234 }
235 extension++;
236
237 for(i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
238 {
239 if(!strcasecmp(extension, formats[i].extension))
240 {
241 type = formats[i].type;
242 break;
243 }
244 }
245 }
246 if(type == LQT_FILE_NONE)
247 {
248 fprintf(stderr, _("Cannot detect output format. Specify a valid extension or use -f <format>\n"));
249 return 0;
250 }
251
252 if(!audio_codec || !video_codec)
253 {
254 for(i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
255 {
256 if(type == formats[i].type)
257 {
258 if(!audio_codec) audio_codec = formats[i].default_audio_codec;
259 if(!video_codec) video_codec = formats[i].default_video_codec;
260 }
261 }
262
263 }
264
265 h->out_file = lqt_open_write(out_file, type);
266 if(!h->out_file)
267 {
268 fprintf(stderr, _("Cannot open output file %s\n"), out_file);
269 return 0;
270 }
271
272 /* Check for video */
273
274 if(quicktime_video_tracks(h->in_file) &&
275 quicktime_supported_video(h->in_file, 0))
276 h->do_video = 1;
277
278 if(h->do_video)
279 {
280 h->width = quicktime_video_width(h->in_file, 0);
281 h->height = quicktime_video_height(h->in_file, 0);
282
283 h->timescale = lqt_video_time_scale(h->in_file, 0);
284 h->frame_duration = lqt_frame_duration(h->in_file, 0, NULL);
285
286 /* Codec info for encoding */
287
288 codec_info = lqt_find_video_codec_by_name(video_codec);
289 if(!codec_info)
290 {
291 fprintf(stderr, _("Unsupported video cocec %s, try -lv\n"), video_codec);
292 return 0;
293 }
294
295 /* Set up the output track */
296
297 lqt_set_video(h->out_file, 1, h->width, h->height, h->frame_duration, h->timescale, codec_info[0]);
298
299 /* Get colormodel */
300 in_cmodel = lqt_get_cmodel(h->in_file, 0);
301 out_cmodel = lqt_get_cmodel(h->out_file, 0);
302
303 if(quicktime_reads_cmodel(h->in_file, out_cmodel, 0))
304 {
305 h->colormodel = out_cmodel;
306 }
307 else if(quicktime_writes_cmodel(h->out_file, in_cmodel, 0))
308 {
309 h->colormodel = in_cmodel;
310 }
311 else
312 {
313 h->colormodel = BC_RGB888;
314 }
315
316 h->video_buffer = lqt_rows_alloc(h->width, h->height, h->colormodel, &(h->rowspan), &(h->rowspan_uv));
317
318 quicktime_set_cmodel(h->in_file, h->colormodel);
319 quicktime_set_cmodel(h->out_file, h->colormodel);
320
321 lqt_destroy_codec_info(codec_info);
322
323 h->num_video_frames = quicktime_video_length(h->in_file, 0);
324 h->video_duration = lqt_video_duration(h->in_file, 0);
325 }
326 /* Check for audio */
327
328 if(quicktime_audio_tracks(h->in_file) &&
329 quicktime_supported_audio(h->in_file, 0))
330 h->do_audio = 1;
331
332 if(h->do_audio)
333 {
334 h->audio_bits = quicktime_audio_bits(h->in_file, 0);
335 h->samplerate = quicktime_sample_rate(h->in_file, 0);
336 h->num_channels = lqt_total_channels(h->in_file);
337
338 /* Codec info for encoding */
339
340 codec_info = lqt_find_audio_codec_by_name(audio_codec);
341 if(!codec_info)
342 {
343 fprintf(stderr, _("Unsupported audio codec %s, try -la\n"), audio_codec);
344 return 0;
345 }
346
347 /* Set up audio track */
348
349 lqt_set_audio(h->out_file, h->num_channels,
350 h->samplerate, h->audio_bits,
351 codec_info[0]);
352 lqt_destroy_codec_info(codec_info);
353
354 /* Decide about audio frame size */
355
356 /* Ok, we must take care about the audio frame size.
357 The sample count, we pass to encode_audio() directly affects interleaving.
358 Many small audio chunks make decoding inefficient, few large chunks make seeking
359 slower because many samples inside a chunk have to be skipped.
360
361 Ok, then lets just take half a second and see how it works :-)
362
363 On a 25 fps system this means, that one audio chunks comes after an average of
364 12.5 video frames. This is roughly what we see in files created with other
365 Software
366 */
367
368 h->samples_per_frame = h->samplerate / 2;
369 /* Avoid too odd numbers */
370 h->samples_per_frame = 16 * ((h->samples_per_frame + 15) / 16);
371
372 /* Allocate output buffer */
373
374 if(floataudio)
375 {
376 h->audio_buffer_f = malloc(h->num_channels * sizeof(float*));
377 h->audio_buffer_f[0] = malloc(h->num_channels * h->samples_per_frame * sizeof(float));
378 for(i = 1; i < h->num_channels; i++)
379 h->audio_buffer_f[i] = &(h->audio_buffer_f[0][i*h->samples_per_frame]);
380 }
381 else
382 {
383 h->audio_buffer_i = malloc(h->num_channels * sizeof(int16_t*));
384 h->audio_buffer_i[0] = malloc(h->num_channels * h->samples_per_frame * sizeof(int16_t));
385 for(i = 1; i < h->num_channels; i++)
386 h->audio_buffer_i[i] = &(h->audio_buffer_i[0][i*h->samples_per_frame]);
387 }
388 h->num_audio_samples = quicktime_audio_length(h->in_file, 0);
389 }
390
391 if (qtvr) {
392 if(strncmp(qtvr,"obj", 3) == 0) {
393 lqt_qtvr_set_type(h->out_file, QTVR_OBJ, h->width , h->height, h->frame_duration, h->timescale, 0);
394 }
395
396 if(strncmp(qtvr,"pano", 4) == 0) {
397 lqt_qtvr_set_type(h->out_file, QTVR_PAN, h->width/2, h->width, h->frame_duration, h->timescale, 0);
398 }
399
400 if(qtvr_columns && qtvr_rows) {
401 lqt_qtvr_set_rows(h->out_file, qtvr_rows);
402 lqt_qtvr_set_columns(h->out_file, qtvr_columns);
403 }
404 }
405
406 return 1;
407 }
408
transcode_iteration(transcode_handle * h)409 static int transcode_iteration(transcode_handle * h)
410 {
411 double audio_time;
412 double video_time;
413 int num_samples;
414 int do_audio = 0;
415 float progress;
416 int64_t frame_time;
417
418 if(h->do_audio && h->do_video)
419 {
420 audio_time = (float)(h->audio_samples_written)/(float)(h->samplerate);
421 video_time = (float)(h->video_frames_written * h->frame_duration)/h->timescale;
422
423 if(audio_time < video_time)
424 do_audio = 1;
425 }
426 else if(h->do_audio)
427 {
428 do_audio = 1;
429 }
430
431 /* Audio Iteration */
432
433 if(do_audio)
434 {
435 // lqt_decode_audio(h->in_file, h->audio_buffer_i, h->audio_buffer_f, h->samples_per_frame);
436 lqt_decode_audio_track(h->in_file, h->audio_buffer_i, h->audio_buffer_f, h->samples_per_frame, 0);
437 num_samples = lqt_last_audio_position(h->in_file, 0) - h->audio_samples_written;
438 // fprintf(stderr, "Num samples: %d\n",num_samples);
439 quicktime_encode_audio(h->out_file, h->audio_buffer_i, h->audio_buffer_f, num_samples);
440 h->audio_samples_written += num_samples;
441
442 if(num_samples < h->samples_per_frame)
443 h->do_audio = 0;
444 progress = (float)(h->audio_samples_written)/(float)(h->num_audio_samples);
445
446 }
447 /* Video Iteration */
448 else
449 {
450 frame_time = lqt_frame_time(h->in_file, 0);
451 lqt_decode_video(h->in_file, h->video_buffer, 0);
452 lqt_encode_video(h->out_file, h->video_buffer, 0, frame_time);
453
454 h->video_frames_written++;
455 if(h->video_frames_written >= h->num_video_frames)
456 h->do_video = 0;
457 progress = (float)(h->video_frames_written)/(float)(h->num_video_frames);
458 }
459 if(!h->do_audio && !h->do_video)
460 return 0;
461
462 /* Calculate the progress */
463
464 if(progress > h->progress)
465 h->progress = progress;
466 return 1;
467 }
468
transcode_cleanup(transcode_handle * h)469 static void transcode_cleanup(transcode_handle * h)
470 {
471 quicktime_close(h->in_file);
472 quicktime_close(h->out_file);
473 if(h->video_buffer)
474 lqt_rows_free(h->video_buffer);
475
476 if(h->audio_buffer_i)
477 {
478 free(h->audio_buffer_i[0]);
479 free(h->audio_buffer_i);
480 }
481 if(h->audio_buffer_f)
482 {
483 free(h->audio_buffer_f[0]);
484 free(h->audio_buffer_f);
485 }
486 }
487
488
main(int argc,char ** argv)489 int main(int argc, char ** argv)
490 {
491 char * in_file = (char*)0;
492 char * out_file = (char*)0;
493 char * video_codec = (char*)0;
494 char * audio_codec = (char*)0;
495 char * format = (char*)0;
496 char * qtvr = (char*)0;
497 unsigned short qtvr_rows = 0;
498 unsigned short qtvr_columns = 0;
499 int i;
500 lqt_file_type_t type = LQT_FILE_NONE, floataudio = 0;
501 transcode_handle handle;
502 int progress_written = 0;
503
504 memset(&handle, 0, sizeof(handle));
505
506 switch(argc)
507 {
508 case 1:
509 print_usage();
510 exit(0);
511 break;
512 case 2:
513 if(!strcmp(argv[1], "-lv"))
514 list_video_codecs();
515 else if(!strcmp(argv[1], "-la"))
516 list_audio_codecs();
517 else if(!strcmp(argv[1], "-lf"))
518 list_formats();
519 else
520 print_usage();
521 exit(0);
522 break;
523 default:
524 for(i = 1; i < argc - 2; i++)
525 {
526 if(!strcmp(argv[i], "-vc"))
527 {
528 video_codec = argv[i+1];
529 i++;
530 }
531 else if(!strcmp(argv[i], "-ac"))
532 {
533 audio_codec = argv[i+1];
534 i++;
535 }
536 else if(!strcmp(argv[i], "-f"))
537 {
538 format = argv[i+1];
539 i++;
540 }
541 else if(!strcmp(argv[i], "-avi"))
542 format = "avi";
543 else if(!strcmp(argv[i], "-floataudio"))
544 floataudio = 1;
545 else if(!strcmp(argv[i], "-qtvr")) {
546 qtvr = argv[i+1];
547 i++;
548 }
549 else if(!strcmp(argv[i], "-qtvr_rows")) {
550 qtvr_rows = atoi(argv[i+1]);
551 i++;
552 }
553 else if(!strcmp(argv[i], "-qtvr_columns")) {
554 qtvr_columns = atoi(argv[i+1]);
555 i++;
556 }
557 }
558 in_file = argv[argc-2];
559 out_file = argv[argc-1];
560 }
561
562 /* Get file type */
563 if(format)
564 {
565 for(i = 0; i < sizeof(formats)/sizeof(formats[0]); i++)
566 {
567 if(!strcasecmp(format, formats[i].name))
568 {
569 type = formats[i].type;
570 break;
571 }
572 }
573 if(type == LQT_FILE_NONE)
574 {
575 fprintf(stderr, _("Unsupported format %s, try -lf"), format);
576 return -1;
577 }
578 }
579
580 if(!transcode_init(&handle, in_file, out_file, video_codec, audio_codec,
581 floataudio, type, qtvr, qtvr_rows, qtvr_columns))
582 {
583 return -1;
584 }
585
586 i = 10;
587
588 while(transcode_iteration(&handle))
589 {
590 if(i == 10)
591 {
592 if(progress_written)
593 putchar('\r');
594 printf(_("%6.2f%% Completed"), handle.progress*100.0);
595 fflush(stdout);
596 i = 0;
597 progress_written = 1;
598 }
599 i++;
600 }
601 printf(_("%6.2f%% Completed\n"), 100.0);
602
603 if(handle.audio_samples_written)
604 printf("Transcoded %"PRId64" audio samples\n", handle.audio_samples_written);
605
606 if(handle.video_frames_written)
607 printf("Transcoded %"PRId64" video frames\n", handle.video_frames_written);
608
609 transcode_cleanup(&handle);
610 return 0;
611 }
612