You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
11 KiB

/*
* 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;
}