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