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