added support for audio adjustment.
[swftools.git] / avi2swf / avi2swf.cc
1 /* avi2swf.cc
2    Convert avi movie files into swf.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2001,2002,2003 Matthias Kramm <kramm@quiss.org>
7
8    This file is distributed under the GPL, see file COPYING for details */
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <fcntl.h>
14
15 #include "../config.h"
16
17 #ifdef HAVE_SIGNAL_H
18 #ifdef HAVE_PTHREAD_H
19 #include <pthread.h>
20 #include <signal.h>
21 #define DO_SIGNALS
22 #endif
23 #endif
24
25 extern "C" {
26 #include "../lib/args.h"
27 }
28 #include "v2swf.h"
29 #include "../lib/q.h"
30
31 #undef HAVE_CONFIG_H
32 #include <avifile/version.h>
33 #if (AVIFILE_MAJOR_VERSION == 0) && (AVIFILE_MINOR_VERSION>=6) 
34    #include <avifile.h>
35    #include <aviplay.h>
36    #include <fourcc.h>
37    #include <creators.h>
38    #include <StreamInfo.h>
39    #define VERSION6
40 #else
41    #include <avifile.h>
42    #include <aviplay.h>
43    #include <aviutil.h>
44    #define Width width
45    #define Height height
46    #define Data data
47    #define Bpp bpp
48 #endif
49
50 static char * filename = 0;
51 static char * outputfilename = "output.swf";
52 int verbose = 0;
53
54 static int quality = 80;
55 static double scale = 1.0;
56 static int flip = 0;
57 static int expensive = 0;
58 static int flashversion = 6;
59 static int keyframe_interval = -1;
60 static int skip = 0;
61 static float audio_adjust = 0;
62
63 struct options_t options[] =
64 {
65  {"v","verbose"},
66  {"A","adjust"},
67  {"o","output"},
68  {"p","flip"},
69  {"q","quality"},
70  {"s","scale"},
71  {"S","skip"},
72  {"x","extragood"},
73  {"T","flashversion"},
74  {"V","version"},
75  {0,0}
76 };
77
78 int args_callback_option(char*name,char*val)
79 {
80     if(!strcmp(name, "V")) {
81         printf("avi2swf-ng - part of %s %s\n", PACKAGE, VERSION);
82         exit(0);
83     } 
84     else if(!strcmp(name, "o")) {
85         outputfilename = val;
86         return 1;
87     }
88     else if(!strcmp(name, "q")) {
89         quality = atoi(val);
90         if(quality<0)
91             quality = 0;
92         if(quality>100)
93             quality = 100;
94         return 1;
95     }
96     else if(!strcmp(name, "p")) {
97         flip = 1;
98         return 0;
99     }
100     else if(!strcmp(name, "A")) {
101         audio_adjust = atof(val);
102         return 1;
103     }
104     else if(!strcmp(name, "v")) {
105         verbose = 1;
106         return 0;
107     }
108     else if(!strcmp(name, "T")) {
109         flashversion = atoi(val);
110         return 1;
111     }
112     else if(!strcmp(name, "x")) {
113         expensive = 1;
114         return 0;
115     }
116     else if(!strcmp(name, "S")) {
117         skip = atoi(val);
118         return 1;
119     }
120     else if(!strcmp(name, "s")) {
121         scale = atoi(val)/100.0;
122         if(scale>1.0 || scale<=0) {
123             fprintf(stderr, "Scale must be in the range 1-100!\n");
124             exit(1);
125         }
126         return 1;
127     }
128     fprintf(stderr, "Unknown option: -%s\n", name);
129     exit(1);
130 }
131 int args_callback_longoption(char*name,char*val)
132 {
133     return args_long2shortoption(options, name, val);
134 }
135 void args_callback_usage(char*name)
136 {    
137     printf("\nUsage: %s file.avi\n", name);
138     printf("\t-h , --help\t\t Print help and exit\n");
139     printf("\t-o , --output filename\t Specify output filename\n"); 
140     printf("\t-A , --adjust seconds\t Audio adjust: Shift sound -seconds to the future or +seconds into the past.\n"); 
141     printf("\t-n , --num frames\t Number of frames to encode\n");
142     printf("\t-d , --scale <val>\t Scale down to factor <val>. (in %, e.g. 100 = original size)\n");
143     printf("\t-p , --flip\t\t Turn movie upside down\n");
144     printf("\t-q , --quality <val>\t Set the quality to <val>. (0-100, 0=worst, 100=best, default:80)\n");
145     printf("\t-x , --extragood\t Enable some *very* expensive compression strategies. You may\n");
146     printf("\t                \t want to let this run overnight.\n");
147     printf("\t-T , --flashversion <n>\t Set output flash version to <n>. Notice: H.263 compression will only be\n");
148     printf("\t                       \t used for n >= 6.\n");
149     printf("\t-V , --version\t\t Print program version and exit\n");
150     exit(0);
151 }
152 int args_callback_command(char*name,char*val)
153 {
154     if(filename) {
155         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
156                  filename, name);
157     }
158     filename = name;
159     return 0;
160 }
161
162 static char toabuf[128];
163 static char*ftoa(double a)
164 {
165     sprintf(toabuf, "%f", a);
166     return toabuf;
167 }
168 static char*itoa(int a)
169 {
170     sprintf(toabuf, "%d", a);
171     return toabuf;
172 }
173
174 typedef struct _videoreader_avifile_internal
175 {
176     IAviReadFile* player;
177     IAviReadStream* astream;
178     IAviReadStream* vstream;
179     int do_audio;
180     int do_video;
181     int eof;
182     int frame;
183     int soundbits;
184     ringbuffer_t audio_buffer;
185 } videoreader_avifile_internal;
186
187 static int shutdown_avi2swf = 0;
188 static int frameno = 0;
189
190 #ifdef DO_SIGNALS
191 pthread_t main_thread;
192 static void sigterm(int sig)
193 {
194     if(pthread_equal (pthread_self(), main_thread))
195     {
196         if(frameno>0 && !shutdown_avi2swf) {
197             if(verbose)
198                 printf("Thread [%08x] got sigterm %d\n", pthread_self(), sig);
199             shutdown_avi2swf++;
200         } else {
201             exit(1);
202         }
203     }
204 }
205 #endif
206
207 static void readSamples(videoreader_avifile_internal*i, void*buffer, int buffer_size, int numsamples)
208 {
209     int ret;
210     while(i->audio_buffer.available < buffer_size) {
211         unsigned int samples_read = 0, bytes_read = 0;
212         ret = i->astream->ReadFrames(buffer, buffer_size, numsamples, samples_read, bytes_read);
213         if(samples_read<=0)
214             return;
215         ringbuffer_put(&i->audio_buffer, buffer, bytes_read);
216     }
217     ringbuffer_read(&i->audio_buffer, buffer, buffer_size);
218 }
219 int videoreader_avifile_getsamples(videoreader_t* v, void*buffer, int num)
220 {
221     if(verbose) {
222         printf("videoreader_getsamples(%d)\n", num);fflush(stdout);
223     }
224     videoreader_avifile_internal*i = (videoreader_avifile_internal*)v->internal;
225     if(i->soundbits == 8) {
226         readSamples(i, buffer, num/2, num/(v->channels*2));
227         unsigned char*b = (unsigned char*)buffer;
228         int t;
229         for(t=num-2;t>=0;t-=2) {
230             unsigned char x = b[t/2];
231             b[t] = 0;
232             b[t+1] = x-128;
233         }
234         return num;
235     }
236     if(i->soundbits == 16) {
237         readSamples(i, buffer, num, num/(v->channels*2));
238         return num;
239     }
240     return 0;
241 }
242 int videoreader_avifile_getimage(videoreader_t* v, void*buffer)
243 {
244     videoreader_avifile_internal*i = (videoreader_avifile_internal*)v->internal;
245     if(verbose) {
246         printf("videoreader_getimage()\n");fflush(stdout);
247     }
248
249     if(shutdown_avi2swf)
250         i->eof = 1;
251     
252     if(i->eof)
253         return 0;
254
255     if(i->vstream->ReadFrame() < 0) {
256         if(verbose) printf("vstream->ReadFrame() returned value < 0, shutting down...\n");
257         i->eof = 1;
258         return 0;
259     }
260     CImage*img2 = 0;
261     CImage*img = i->vstream->GetFrame();
262     if(!img) {
263         if(verbose) printf("vstream->GetFrame() returned NULL, shutting down...\n");
264         i->eof = 1;
265         return 0;
266     }
267     /* we convert the image to YUV first, because we can convert to RGB from YUV only */
268     img->ToYUV();
269     img->ToRGB();
270     if(img->Bpp() != 3) {
271         /* TODO: this doesn't work yet */
272         if(verbose) printf("Can't handle Bpp %d, shutting down...\n", img->Bpp());
273         return 0;
274         BitmapInfo tmp(v->width, v->height, 24);
275         img2 = new CImage(img, &tmp);
276         img = img2;
277     }
278
279
280     frameno++;
281     i->frame++;
282     unsigned char*data = img->Data();
283     int bpp = img->Bpp();
284     if(bpp == 3) {
285         int x,y;
286         for(y=0;y<v->height;y++) {
287             unsigned char*from,*to;
288             to = &((unsigned char*)buffer)[y*v->width*4];
289             if(flip)
290                 from = img->At(v->height-y-1);
291             else
292                 from = img->At(y);
293             for(x=0;x<v->width;x++) {
294                 to[x*4+0] = 0;
295                 to[x*4+1] = from[x*3+2];
296                 to[x*4+2] = from[x*3+1];
297                 to[x*4+3] = from[x*3+0];
298             }
299         }
300         if(img2) delete img2;
301         return v->width*v->height*4;
302     } else {
303         if(img2) delete img2;
304         if(verbose) printf("Can't handle bpp %d, shutting down...\n", bpp);
305         return 0;
306     }
307 }
308 bool videoreader_avifile_eof(videoreader_t* v)
309 {
310     videoreader_avifile_internal*i = (videoreader_avifile_internal*)v->internal;
311     if(verbose) {
312         printf("videoreader_eof()\n");fflush(stdout);
313     }
314     return i->eof;
315 }
316 void videoreader_avifile_close(videoreader_t* v)
317 {
318     videoreader_avifile_internal*i = (videoreader_avifile_internal*)v->internal;
319     ringbuffer_clear(&i->audio_buffer);
320     if(verbose) {
321         printf("videoreader_close()\n");fflush(stdout);
322     }
323 }
324 void* videoreader_avifile_getinfo(videoreader_t* v, char* name)
325 {
326     return 0;
327 }
328 void videoreader_avifile_setparameter(videoreader_t*v, char*name, char*value)
329 {
330     if(verbose) {
331         printf("videoreader_setparameter(%s, %s)\n", name, value);fflush(stdout);
332     }
333 }
334
335 int videoreader_avifile_open(videoreader_t* v, char* filename)
336 {
337     videoreader_avifile_internal* i;
338     i = (videoreader_avifile_internal*)malloc(sizeof(videoreader_avifile_internal));
339     memset(i, 0, sizeof(videoreader_avifile_internal));
340     memset(v, 0, sizeof(videoreader_t));
341     v->getsamples = videoreader_avifile_getsamples;
342     v->getinfo = videoreader_avifile_getinfo;
343     v->close = videoreader_avifile_close;
344     v->eof = videoreader_avifile_eof;
345     v->getimage = videoreader_avifile_getimage;
346     v->getsamples = videoreader_avifile_getsamples;
347     v->setparameter = videoreader_avifile_setparameter;
348     v->internal = i;
349     
350     i->do_video = 1;
351     i->do_audio = 1;
352
353     i->player = CreateIAviReadFile(filename);    
354     if(verbose) {
355         printf("%d streams (%d video, %d audio)\n", 
356                 i->player->StreamCount(),
357                 i->player->VideoStreamCount(),
358                 i->player->AudioStreamCount());
359     }
360     i->astream = i->player->GetStream(0, AviStream::Audio);
361     i->vstream = i->player->GetStream(0, AviStream::Video);
362     if(!i->vstream) {
363         printf("Couldn't open video stream\n");
364         i->do_video = 0;
365     }
366     if(!i->astream) {
367         printf("Couldn't open video stream\n");
368         i->do_audio = 0;
369     }
370
371     if(!i->do_video && !i->do_audio) {
372         printf("File has neither audio nor video streams.(?)\n");
373         return 0;
374     }
375
376 #ifndef VERSION6
377     MainAVIHeader head;
378     int dwMicroSecPerFrame = 0;
379     player->GetFileHeader(&head);
380     printf("fps: %d\n", 1000000/head.dwMicroSecPerFrame);
381     printf("frames: %d\n", head.dwTotalFrames);
382     printf("streams: %d\n", head.dwStreams);
383     printf("width: %d\n", head.dwWidth);
384     printf("height: %d\n", head.dwHeight);
385     printf("sound: %u samples (%f seconds)\n", i->astream->GetEndPos(), i->astream->GetEndTime());
386     v->width = head.dwWidth;
387     v->height = head.dwHeight;
388     dwMicroSecPerFrame = head.dwMicroSecPerFrame;
389     samplesperframe = astream->GetEndPos()/astream->GetEndTime()*head.dwMicroSecPerFrame/1000000;
390     v->rate = (int)(astream->GetEndPos()/astream->GetEndTime());
391     v->fps = 1000000.0/dwMicroSecPerFrame;
392     i->soundbits = 16;
393 #else
394     if(i->do_video)
395     {
396         StreamInfo*videoinfo;
397         videoinfo = i->vstream->GetStreamInfo();
398         v->width = videoinfo->GetVideoWidth();
399         v->height = videoinfo->GetVideoHeight();
400         v->fps = (double)(videoinfo->GetFps());
401     }
402     if(i->do_audio)
403     {
404         WAVEFORMATEX wave;
405         StreamInfo*audioinfo;
406
407         i->astream->GetAudioFormatInfo(&wave,0);
408         audioinfo = i->astream->GetStreamInfo();
409
410         v->channels = wave.nChannels;
411         v->rate = wave.nSamplesPerSec;
412         i->soundbits = wave.wBitsPerSample;
413
414         if(v->channels==0 || v->rate==0 || i->soundbits==0 || wave.wFormatTag!=1) {
415             v->rate = audioinfo->GetAudioSamplesPerSec();
416             v->channels = audioinfo->GetAudioChannels();
417             i->soundbits = audioinfo->GetAudioBitsPerSample();
418         }
419
420         if(verbose) {
421             printf("formatinfo: format %d, %d channels, %d bits/sample, rate %d, blockalign %d\n", wave.wFormatTag, wave.nChannels, wave.wBitsPerSample, wave.nSamplesPerSec, wave.nBlockAlign);
422             printf("audioinfo: %d channels, %d bits/sample, rate %d\n", audioinfo->GetAudioChannels(), audioinfo->GetAudioBitsPerSample(), audioinfo->GetAudioSamplesPerSec());
423         }
424         if(i->soundbits != 8 && i->soundbits != 16) {
425             printf("Can't handle %d bit audio, disabling sound\n", wave.wBitsPerSample);
426             i->do_audio = 0;
427             i->soundbits = 0;
428             v->channels = 0;
429             v->rate = 0;
430         }
431     }
432 #endif
433     i->vstream -> StartStreaming();
434     if(i->do_audio) {
435         i->astream -> StartStreaming();
436         ringbuffer_init(&i->audio_buffer);
437 #ifdef VERSION6
438         WAVEFORMATEX wave;
439         i->astream -> GetOutputFormat(&wave, sizeof(wave));
440         printf("formatinfo: format %d, %d channels, %d bits/sample, rate %d, blockalign %d\n", wave.wFormatTag, wave.nChannels, wave.wBitsPerSample, wave.nSamplesPerSec, wave.nBlockAlign);
441 #endif
442     }
443
444     return 1;
445 }
446
447 int main (int argc,char ** argv)
448
449     videoreader_t video;
450     v2swf_t v2swf;
451     int ret;
452     FILE*fi;
453
454 #ifdef DO_SIGNALS
455     signal(SIGTERM, sigterm);
456     signal(SIGINT , sigterm);
457     signal(SIGQUIT, sigterm);
458     main_thread = pthread_self();
459 #endif
460
461     processargs(argc, argv);
462     if(!filename)
463         exit(0);
464     if(keyframe_interval<0) {
465         if(flashversion>=6)
466             keyframe_interval=200;
467         else
468             keyframe_interval=5;
469     }
470     
471     fi = fopen(outputfilename, "wb");
472     if(!fi) {
473         fflush(stdout); fflush(stderr);
474         fprintf(stderr, "Couldn't open %s\n", outputfilename);
475         exit(1);
476     }
477
478     ret = videoreader_avifile_open(&video, filename);
479
480     if(!ret) {
481         printf("Error opening %s\n", filename);
482         exit(1);
483     }
484
485     if(verbose) {
486         printf("| video framerate: %f\n", video.fps);
487         printf("| video size: %dx%d\n", video.width, video.height);
488         printf("| audio rate: %d\n", video.rate);
489         printf("| audio channels: %d\n", video.channels);
490     }
491
492     ret = v2swf_init(&v2swf, &video);
493     if(verbose)
494         v2swf_setparameter(&v2swf, "verbose", "1");
495     v2swf_setparameter(&v2swf, "quality", itoa(quality));
496     v2swf_setparameter(&v2swf, "blockdiff", "0");
497     v2swf_setparameter(&v2swf, "blockdiff_mode", "exact");
498     v2swf_setparameter(&v2swf, "mp3_bitrate", "32");
499     //v2swf_setparameter(&v2swf, "fixheader", "1");
500     //v2swf_setparameter(&v2swf, "framerate", "15");
501     v2swf_setparameter(&v2swf, "scale", ftoa(scale));
502     v2swf_setparameter(&v2swf, "prescale", "1");
503     v2swf_setparameter(&v2swf, "flash_version", itoa(flashversion));
504     v2swf_setparameter(&v2swf, "keyframe_interval", itoa(keyframe_interval));
505     if(expensive)
506         v2swf_setparameter(&v2swf, "motioncompensation", "1");
507
508     if(!verbose)
509         printf("\n");
510
511     if(audio_adjust>0) {
512         int num = ((int)(audio_adjust*video.rate))*video.channels*2;
513         void*buf = malloc(num);
514         video.getsamples(&video, buf, num);
515         free(buf);
516     } else if(audio_adjust<0) {
517         int num = (int)(-audio_adjust*video.fps);
518         void*buf = malloc(video.width*video.height*4);
519         int t;
520         for(t=0;t<num;t++) {
521             video.getimage(&video, buf);
522         }
523         free(buf);
524     }
525
526     if(skip) {
527         int t;
528         void*buf = malloc(video.width*video.height*4);
529         for(t=0;t<skip;t++) {
530             video.getimage(&video, buf);
531             video.getsamples(&video, buf, (int)((video.rate/video.fps)*video.channels*2));
532             if(!verbose) {
533                 printf("\rSkipping frame %d", frameno);fflush(stdout);
534             }
535         }
536         free(buf);
537     }
538
539     char buffer[4096];
540     while(1) {
541         int l=v2swf_read(&v2swf, buffer, 4096);
542         fwrite(buffer, l, 1, fi);
543         if(!l)
544             break;
545         if(!verbose) {
546             printf("\rConverting frame %d", frameno);fflush(stdout);
547         }
548     }
549     if(!verbose)
550         printf("\n");
551     fclose(fi);
552     v2swf_backpatch(&v2swf, outputfilename);
553     v2swf_close(&v2swf);
554 }
555