1 /*
2  *  import_avi.c
3  *
4  *  Copyright (C) Thomas Oestreich - June 2001
5  *
6  *  This file is part of transcode, a video stream processing tool
7  *
8  *  transcode 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, or (at your option)
11  *  any later version.
12  *
13  *  transcode 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 GNU Make; see the file COPYING.  If not, write to
20  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  *
22  */
23 
24 #define MOD_NAME    "import_avi.so"
25 #define MOD_VERSION "v0.5.0 (2008-01-15)"
26 #define MOD_CODEC   "(video) * | (audio) *"
27 
28 #include "transcode.h"
29 
30 static int verbose_flag = TC_QUIET;
31 static int capability_flag = TC_CAP_PCM | TC_CAP_RGB | TC_CAP_AUD |
32                              TC_CAP_VID | TC_CAP_YUV | TC_CAP_YUV422;
33 
34 #define MOD_PRE avi
35 #include "import_def.h"
36 
37 #include "libtc/xio.h"
38 #include "libtc/tccodecs.h"
39 #include "libtcvideo/tcvideo.h"
40 
41 
42 static avi_t *avifile_aud = NULL;
43 static avi_t *avifile_vid = NULL;
44 
45 static int audio_codec;
46 static int aframe_count = 0, vframe_count = 0;
47 static int width = 0, height = 0;
48 
49 static TCVHandle tcvhandle = NULL;
50 static ImageFormat srcfmt = IMG_NONE, dstfmt = IMG_NONE;
51 static int destsize = 0;
52 
53 static const struct {
54     const char *name;  // fourcc
55     ImageFormat format;
56     int bpp;
57 } formats[] = {
58     { "I420", IMG_YUV420P, 12 },
59     { "YV12", IMG_YV12,    12 },
60     { "YUY2", IMG_YUY2,    16 },
61     { "UYVY", IMG_UYVY,    16 },
62     { "YVYU", IMG_YVYU,    16 },
63     { "Y800", IMG_Y8,       8 },
64     { "RGB",  IMG_RGB24,   24 },
65     { NULL,   IMG_NONE,     0 }
66 };
67 
tc_csp_translate(TCCodecID id)68 static ImageFormat tc_csp_translate(TCCodecID id)
69 {
70     switch (id) {
71       case CODEC_RGB:        /* fallthrough */
72       case TC_CODEC_RGB:
73         return IMG_RGB24;
74       case CODEC_YUV:        /* fallthrough */
75       case TC_CODEC_YUV420P:
76         return IMG_YUV420P;
77       case CODEC_YUV422:     /* fallthrough */
78       case TC_CODEC_YUV422P:
79         return IMG_YUV422P;
80       default: /* cannot happen */
81         return IMG_NONE;
82     }
83     return IMG_NONE; /*cannot happen */
84 }
85 
tc_cdc_translate(int id)86 static TCCodecID tc_cdc_translate(int id)
87 {
88     switch(id) {
89       case CODEC_YUV:
90         return TC_CODEC_YUV420P;
91       case CODEC_YUV422:
92         return TC_CODEC_YUV422P;
93       case CODEC_RGB:
94         return TC_CODEC_RGB;
95       default: /*cannot happen */
96         return TC_CODEC_UNKNOWN;
97     }
98     return TC_CODEC_ERROR; /* cannot happen */
99 }
100 
101 /* ------------------------------------------------------------
102  *
103  * open stream
104  *
105  * ------------------------------------------------------------*/
106 
107 MOD_open
108 {
109     double fps=0;
110     char *codec=NULL;
111     long rate=0, bitrate=0;
112     int format=0, chan=0, bits=0;
113     struct stat fbuf;
114     char import_cmd_buf[TC_BUF_MAX];
115     long sret;
116 
117     param->fd = NULL;
118 
119     if (param->flag == TC_AUDIO) {
120         // Is the input file actually a directory - if so use
121         // tccat to dump out the audio. N.B. This isn't going
122         // to work if a particular track is needed
123         /* directory content should really be handled by upper levels... -- FR */
124         if ((xio_stat(vob->audio_in_file, &fbuf)) == 0 && S_ISDIR(fbuf.st_mode)) {
125             sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX,
126                                 "tccat -a -i \"%s\" -d %d",
127                                 vob->video_in_file, vob->verbose);
128             if (sret < 0)
129                 return TC_ERROR;
130             if (verbose_flag)
131                 tc_log_info(MOD_NAME, "%s", import_cmd_buf);
132             param->fd = popen(import_cmd_buf, "r");
133             if (param->fd == NULL) {
134                 return TC_ERROR;
135             }
136             return TC_OK;
137         }
138 
139         // Otherwise proceed to open the file directly and decode here
140         if (avifile_aud == NULL) {
141             if (vob->nav_seek_file) {
142                 avifile_aud = AVI_open_input_indexfile(vob->audio_in_file,
143                                                        0, vob->nav_seek_file);
144             } else {
145                 avifile_aud = AVI_open_input_file(vob->audio_in_file, 1);
146             }
147             if (avifile_aud == NULL) {
148                 AVI_print_error("avi open error");
149                 return TC_ERROR;
150             }
151         }
152 
153         // set selected for multi-audio AVI-files
154         AVI_set_audio_track(avifile_aud, vob->a_track);
155 
156         rate   =  AVI_audio_rate(avifile_aud);
157         chan   =  AVI_audio_channels(avifile_aud);
158 
159         if (!chan) {
160             tc_log_warn(MOD_NAME, "error: no audio track found");
161             return TC_ERROR;
162         }
163 
164         bits   =  AVI_audio_bits(avifile_aud);
165         bits   =  (!bits) ?16 :bits;
166 
167         format =  AVI_audio_format(avifile_aud);
168         bitrate=  AVI_audio_mp3rate(avifile_aud);
169 
170         if (verbose_flag)
171             tc_log_info(MOD_NAME, "format=0x%x, rate=%ld Hz, bits=%d, "
172                         "channels=%d, bitrate=%ld",
173                         format, rate, bits, chan, bitrate);
174 
175         if (vob->im_a_codec == CODEC_PCM && format != CODEC_PCM) {
176             tc_log_info(MOD_NAME, "error: invalid AVI audio format '0x%x'"
177                         " for PCM processing", format);
178             return TC_ERROR;
179         }
180         // go to a specific byte for seeking
181         AVI_set_audio_position(avifile_aud,
182                                vob->vob_offset * vob->im_a_size);
183 
184         audio_codec = vob->im_a_codec;
185         return TC_OK;
186     }
187 
188     if (param->flag == TC_VIDEO) {
189     	int i = 0;
190 
191         if(avifile_vid==NULL) {
192             if (vob->nav_seek_file) {
193                 avifile_vid = AVI_open_input_indexfile(vob->video_in_file,
194                                                        0, vob->nav_seek_file);
195             } else {
196                 avifile_vid = AVI_open_input_file(vob->video_in_file, 1);
197             }
198             if (avifile_vid == NULL) {
199                 AVI_print_error("avi open error");
200                 return TC_ERROR;
201             }
202         }
203 
204         if (vob->vob_offset > 0)
205             AVI_set_video_position(avifile_vid, vob->vob_offset);
206 
207         // read all video parameter from input file
208         width  =  AVI_video_width(avifile_vid);
209         height =  AVI_video_height(avifile_vid);
210         fps    =  AVI_frame_rate(avifile_vid);
211         codec  =  AVI_video_compressor(avifile_vid);
212 
213         tc_log_info(MOD_NAME, "codec=%s, fps=%6.3f, width=%d, height=%d",
214                     codec, fps, width, height);
215 
216         if (AVI_max_video_chunk(avifile_vid) > SIZE_RGB_FRAME) {
217             tc_log_error(MOD_NAME, "invalid AVI video frame chunk size detected");
218             return TC_ERROR;
219         }
220 
221 	    for (i = 0; formats[i].name != NULL; i++) {
222 	        if (strcasecmp(formats[i].name, codec) == 0) {
223     	        srcfmt = formats[i].format;
224     	        dstfmt = tc_csp_translate(vob->im_v_codec);
225         	    destsize = vob->im_v_width * vob->im_v_height * formats[i].bpp / 8;
226     		    break;
227             }
228 	    }
229     	if ((srcfmt && dstfmt) && (srcfmt != dstfmt)) {
230             TCCodecID tc_id = tc_cdc_translate(vob->im_v_codec);
231             tcvhandle = tcv_init();
232             if (!tcvhandle) {
233 	            tc_log_error(MOD_NAME, "tcv_convert_init failed");
234                 return TC_ERROR;
235             }
236             tc_log_info(MOD_NAME, "raw source, "
237                                   "converting colorspace: %s -> %s",
238                         formats[i].name,
239                         tc_codec_to_string(tc_id));
240         }
241         return TC_OK;
242     }
243     return TC_ERROR;
244 }
245 
246 
247 /* ------------------------------------------------------------
248  *
249  * decode  stream
250  *
251  * ------------------------------------------------------------*/
252 
253 #define RETURN_IF_READ_ERROR(RET, MSG) do { \
254     if ((RET) < 0) {                        \
255         if (verbose & TC_DEBUG)             \
256             AVI_print_error((MSG));         \
257         return TC_ERROR;                    \
258     }                                       \
259 } while (0)
260 
261 MOD_decode
262 {
263     int key;
264     long bytes_read = 0;
265 
266     if (param->flag == TC_VIDEO) {
267         int i, mod = width % 4;
268 
269         // If we are using tccat, then do nothing here
270         if (param->fd != NULL) {
271             return TC_OK;
272         }
273 
274         param->size = AVI_read_frame(avifile_vid, param->buffer, &key);
275 
276         // Fixup: For uncompressed AVIs, it must be aligned at
277         // a 4-byte boundary
278         if (mod && vob->im_v_codec == CODEC_RGB) {
279             for (i = 0; i < height; i++) {
280                 memmove(param->buffer+(i*width*3),
281                         param->buffer+(i*width*3) + (mod)*i,
282                         width*3);
283             }
284         }
285 
286         if (verbose & TC_STATS && key)
287             tc_log_info(MOD_NAME, "keyframe %d", vframe_count);
288 
289         if (param->size < 0) {
290             if (verbose & TC_DEBUG)
291                 AVI_print_error("AVI read video frame");
292             return TC_ERROR;
293         }
294 
295     	if ((srcfmt && dstfmt) && (srcfmt != dstfmt)) {
296             int ret = tcv_convert(tcvhandle,
297                                   param->buffer, param->buffer,
298 		                          width, height,
299                                   srcfmt, dstfmt);
300             if (!ret) {
301                 tc_log_error(MOD_NAME, "image conversion failed");
302                 return TC_ERROR;
303             }
304             if (destsize)
305                 param->size = destsize;
306         }
307 
308         if (key)
309             param->attributes |= TC_FRAME_IS_KEYFRAME;
310 
311         vframe_count++;
312 
313         return TC_IMPORT_OK;
314     }
315 
316     if (param->flag == TC_AUDIO) {
317         if (audio_codec == CODEC_RAW) {
318             int r = 0;
319 
320             bytes_read = AVI_audio_size(avifile_aud, aframe_count);
321             RETURN_IF_READ_ERROR(bytes_read, "AVI audio size frame");
322 
323             r = AVI_read_audio(avifile_aud, param->buffer, bytes_read);
324             RETURN_IF_READ_ERROR(bytes_read, "AVI audio read frame");
325 
326             aframe_count++; // XXX ?? -- FR
327         } else {
328             bytes_read = AVI_read_audio(avifile_aud, param->buffer, param->size);
329             RETURN_IF_READ_ERROR(bytes_read, "AVI audio read frame");
330         }
331         param->size = bytes_read;
332         return TC_OK;
333     }
334     return TC_ERROR;
335 }
336 
337 /* ------------------------------------------------------------
338  *
339  * close stream
340  *
341  * ------------------------------------------------------------*/
342 
343 #define CLOSE_AVIFILE(AF) do { \
344    if ((AF) != NULL) {         \
345         AVI_close((AF));       \
346         (AF) = NULL;           \
347    }                           \
348 } while (0)
349 
350 MOD_close
351 {
352     if (param->fd != NULL)
353         pclose(param->fd);
354 
355     if (param->flag == TC_AUDIO) {
356         CLOSE_AVIFILE(avifile_aud);
357         return TC_OK;
358     }
359 
360     if (param->flag == TC_VIDEO) {
361         CLOSE_AVIFILE(avifile_vid);
362         return TC_OK;
363     }
364 
365     if (tcvhandle) {
366         tcv_free(tcvhandle);
367         tcvhandle = NULL;
368     }
369     return TC_ERROR;
370 }
371 
372