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