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