/* * import_avi.c * * Copyright (C) Thomas Oestreich - June 2001 * * This file is part of transcode, a video stream processing tool * * transcode is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * transcode is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Make; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * */ #define MOD_NAME "import_avi.so" #define MOD_VERSION "v0.5.0 (2008-01-15)" #define MOD_CODEC "(video) * | (audio) *" #include "transcode.h" static int verbose_flag = TC_QUIET; static int capability_flag = TC_CAP_PCM | TC_CAP_RGB | TC_CAP_AUD | TC_CAP_VID | TC_CAP_YUV | TC_CAP_YUV422; #define MOD_PRE avi #include "import_def.h" #include "libtc/xio.h" #include "libtc/tccodecs.h" #include "libtcvideo/tcvideo.h" static avi_t *avifile_aud = NULL; static avi_t *avifile_vid = NULL; static int audio_codec; static int aframe_count = 0, vframe_count = 0; static int width = 0, height = 0; static TCVHandle tcvhandle = NULL; static ImageFormat srcfmt = IMG_NONE, dstfmt = IMG_NONE; static int destsize = 0; static const struct { const char *name; // fourcc ImageFormat format; int bpp; } formats[] = { { "I420", IMG_YUV420P, 12 }, { "YV12", IMG_YV12, 12 }, { "YUY2", IMG_YUY2, 16 }, { "UYVY", IMG_UYVY, 16 }, { "YVYU", IMG_YVYU, 16 }, { "Y800", IMG_Y8, 8 }, { "RGB", IMG_RGB24, 24 }, { NULL, IMG_NONE, 0 } }; static ImageFormat tc_csp_translate(TCCodecID id) { switch (id) { case CODEC_RGB: /* fallthrough */ case TC_CODEC_RGB: return IMG_RGB24; case CODEC_YUV: /* fallthrough */ case TC_CODEC_YUV420P: return IMG_YUV420P; case CODEC_YUV422: /* fallthrough */ case TC_CODEC_YUV422P: return IMG_YUV422P; default: /* cannot happen */ return IMG_NONE; } return IMG_NONE; /*cannot happen */ } static TCCodecID tc_cdc_translate(int id) { switch(id) { case CODEC_YUV: return TC_CODEC_YUV420P; case CODEC_YUV422: return TC_CODEC_YUV422P; case CODEC_RGB: return TC_CODEC_RGB; default: /*cannot happen */ return TC_CODEC_UNKNOWN; } return TC_CODEC_ERROR; /* cannot happen */ } /* ------------------------------------------------------------ * * open stream * * ------------------------------------------------------------*/ MOD_open { double fps=0; char *codec=NULL; long rate=0, bitrate=0; int format=0, chan=0, bits=0; struct stat fbuf; char import_cmd_buf[TC_BUF_MAX]; long sret; param->fd = NULL; if (param->flag == TC_AUDIO) { // Is the input file actually a directory - if so use // tccat to dump out the audio. N.B. This isn't going // to work if a particular track is needed /* directory content should really be handled by upper levels... -- FR */ if ((xio_stat(vob->audio_in_file, &fbuf)) == 0 && S_ISDIR(fbuf.st_mode)) { sret = tc_snprintf(import_cmd_buf, TC_BUF_MAX, "tccat -a -i \"%s\" -d %d", vob->video_in_file, vob->verbose); if (sret < 0) return TC_ERROR; if (verbose_flag) tc_log_info(MOD_NAME, "%s", import_cmd_buf); param->fd = popen(import_cmd_buf, "r"); if (param->fd == NULL) { return TC_ERROR; } return TC_OK; } // Otherwise proceed to open the file directly and decode here if (avifile_aud == NULL) { if (vob->nav_seek_file) { avifile_aud = AVI_open_input_indexfile(vob->audio_in_file, 0, vob->nav_seek_file); } else { avifile_aud = AVI_open_input_file(vob->audio_in_file, 1); } if (avifile_aud == NULL) { AVI_print_error("avi open error"); return TC_ERROR; } } // set selected for multi-audio AVI-files AVI_set_audio_track(avifile_aud, vob->a_track); rate = AVI_audio_rate(avifile_aud); chan = AVI_audio_channels(avifile_aud); if (!chan) { tc_log_warn(MOD_NAME, "error: no audio track found"); return TC_ERROR; } bits = AVI_audio_bits(avifile_aud); bits = (!bits) ?16 :bits; format = AVI_audio_format(avifile_aud); bitrate= AVI_audio_mp3rate(avifile_aud); if (verbose_flag) tc_log_info(MOD_NAME, "format=0x%x, rate=%ld Hz, bits=%d, " "channels=%d, bitrate=%ld", format, rate, bits, chan, bitrate); if (vob->im_a_codec == CODEC_PCM && format != CODEC_PCM) { tc_log_info(MOD_NAME, "error: invalid AVI audio format '0x%x'" " for PCM processing", format); return TC_ERROR; } // go to a specific byte for seeking AVI_set_audio_position(avifile_aud, vob->vob_offset * vob->im_a_size); audio_codec = vob->im_a_codec; return TC_OK; } if (param->flag == TC_VIDEO) { int i = 0; if(avifile_vid==NULL) { if (vob->nav_seek_file) { avifile_vid = AVI_open_input_indexfile(vob->video_in_file, 0, vob->nav_seek_file); } else { avifile_vid = AVI_open_input_file(vob->video_in_file, 1); } if (avifile_vid == NULL) { AVI_print_error("avi open error"); return TC_ERROR; } } if (vob->vob_offset > 0) AVI_set_video_position(avifile_vid, vob->vob_offset); // read all video parameter from input file width = AVI_video_width(avifile_vid); height = AVI_video_height(avifile_vid); fps = AVI_frame_rate(avifile_vid); codec = AVI_video_compressor(avifile_vid); tc_log_info(MOD_NAME, "codec=%s, fps=%6.3f, width=%d, height=%d", codec, fps, width, height); if (AVI_max_video_chunk(avifile_vid) > SIZE_RGB_FRAME) { tc_log_error(MOD_NAME, "invalid AVI video frame chunk size detected"); return TC_ERROR; } for (i = 0; formats[i].name != NULL; i++) { if (strcasecmp(formats[i].name, codec) == 0) { srcfmt = formats[i].format; dstfmt = tc_csp_translate(vob->im_v_codec); destsize = vob->im_v_width * vob->im_v_height * formats[i].bpp / 8; break; } } if ((srcfmt && dstfmt) && (srcfmt != dstfmt)) { TCCodecID tc_id = tc_cdc_translate(vob->im_v_codec); tcvhandle = tcv_init(); if (!tcvhandle) { tc_log_error(MOD_NAME, "tcv_convert_init failed"); return TC_ERROR; } tc_log_info(MOD_NAME, "raw source, " "converting colorspace: %s -> %s", formats[i].name, tc_codec_to_string(tc_id)); } return TC_OK; } return TC_ERROR; } /* ------------------------------------------------------------ * * decode stream * * ------------------------------------------------------------*/ #define RETURN_IF_READ_ERROR(RET, MSG) do { \ if ((RET) < 0) { \ if (verbose & TC_DEBUG) \ AVI_print_error((MSG)); \ return TC_ERROR; \ } \ } while (0) MOD_decode { int key; long bytes_read = 0; if (param->flag == TC_VIDEO) { int i, mod = width % 4; // If we are using tccat, then do nothing here if (param->fd != NULL) { return TC_OK; } param->size = AVI_read_frame(avifile_vid, param->buffer, &key); // Fixup: For uncompressed AVIs, it must be aligned at // a 4-byte boundary if (mod && vob->im_v_codec == CODEC_RGB) { for (i = 0; i < height; i++) { memmove(param->buffer+(i*width*3), param->buffer+(i*width*3) + (mod)*i, width*3); } } if (verbose & TC_STATS && key) tc_log_info(MOD_NAME, "keyframe %d", vframe_count); if (param->size < 0) { if (verbose & TC_DEBUG) AVI_print_error("AVI read video frame"); return TC_ERROR; } if ((srcfmt && dstfmt) && (srcfmt != dstfmt)) { int ret = tcv_convert(tcvhandle, param->buffer, param->buffer, width, height, srcfmt, dstfmt); if (!ret) { tc_log_error(MOD_NAME, "image conversion failed"); return TC_ERROR; } if (destsize) param->size = destsize; } if (key) param->attributes |= TC_FRAME_IS_KEYFRAME; vframe_count++; return TC_IMPORT_OK; } if (param->flag == TC_AUDIO) { if (audio_codec == CODEC_RAW) { int r = 0; bytes_read = AVI_audio_size(avifile_aud, aframe_count); RETURN_IF_READ_ERROR(bytes_read, "AVI audio size frame"); r = AVI_read_audio(avifile_aud, param->buffer, bytes_read); RETURN_IF_READ_ERROR(bytes_read, "AVI audio read frame"); aframe_count++; // XXX ?? -- FR } else { bytes_read = AVI_read_audio(avifile_aud, param->buffer, param->size); RETURN_IF_READ_ERROR(bytes_read, "AVI audio read frame"); } param->size = bytes_read; return TC_OK; } return TC_ERROR; } /* ------------------------------------------------------------ * * close stream * * ------------------------------------------------------------*/ #define CLOSE_AVIFILE(AF) do { \ if ((AF) != NULL) { \ AVI_close((AF)); \ (AF) = NULL; \ } \ } while (0) MOD_close { if (param->fd != NULL) pclose(param->fd); if (param->flag == TC_AUDIO) { CLOSE_AVIFILE(avifile_aud); return TC_OK; } if (param->flag == TC_VIDEO) { CLOSE_AVIFILE(avifile_vid); return TC_OK; } if (tcvhandle) { tcv_free(tcvhandle); tcvhandle = NULL; } return TC_ERROR; }