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.
616 lines
21 KiB
616 lines
21 KiB
/*
|
|
* decode_dv.c - Digital Video decoding routines, using libdv
|
|
* Written by Andrew Church <achurch@achurch.org>
|
|
*
|
|
* This file is part of transcode, a video stream processing tool.
|
|
* transcode is free software, distributable under the terms of the GNU
|
|
* General Public License (version 2 or later). See the file COPYING
|
|
* for details.
|
|
*/
|
|
|
|
#include "transcode.h"
|
|
#include "tcinfo.h"
|
|
#include "libtcvideo/tcvideo.h"
|
|
#include "ioaux.h" /* for import_exit() prototype */
|
|
#include "tc.h" /* for function prototypes */
|
|
|
|
#ifdef HAVE_LIBDV
|
|
# include <libdv/dv.h>
|
|
#endif
|
|
|
|
#define DV_FRAME_SIZE_525_60 120000
|
|
#define DV_FRAME_SIZE_625_50 144000
|
|
|
|
/* Redefine libdv constants to match our naming style */
|
|
#define DV_SYSTEM_525_60 e_dv_system_525_60
|
|
#define DV_SYSTEM_625_50 e_dv_system_625_50
|
|
#define DV_COLOR_YUV e_dv_color_yuv
|
|
#define DV_COLOR_RGB e_dv_color_rgb
|
|
|
|
/*************************************************************************/
|
|
|
|
#ifdef HAVE_LIBDV
|
|
|
|
/**
|
|
* check_yuy2: Internal routine to check whether libdv returns YUY2 or
|
|
* YV12 data for PAL DV frames.
|
|
*
|
|
* Parameters:
|
|
* None.
|
|
* Return value:
|
|
* 1 if the decoded video data is in YUY2 format,
|
|
* 0 if the decoded video data is in YV12 format,
|
|
* -1 if the decoded video data format is unknown.
|
|
*/
|
|
|
|
static int check_yuy2(void)
|
|
{
|
|
static uint8_t dv_frame[12][150][80]; /* Input DV frame (generated) */
|
|
static uint8_t Y[720*576*2]; /* Output Y/YUY2 plane */
|
|
static uint8_t U[(720/2)*(576/2)]; /* Output U plane */
|
|
static uint8_t V[(720/2)*(576/2)]; /* Output V plane */
|
|
uint8_t *video[3] = {Y, U, V};
|
|
int linesize[3] = {720*2, 720/2, 720/2};
|
|
dv_decoder_t *decoder;
|
|
int i, j, k;
|
|
|
|
/* Generate an off-white PAL DV frame (Y=0xD0 U/V=0x80) */
|
|
for (i = 0; i < 12; i++) {
|
|
dv_frame[i][0][0] = 0x1F;
|
|
dv_frame[i][0][1] = i<<4 | 0x07;
|
|
dv_frame[i][0][2] = 0x00;
|
|
dv_frame[i][0][3] = 0xBF;
|
|
dv_frame[i][0][4] = 0x68;
|
|
dv_frame[i][0][5] = 0x78;
|
|
dv_frame[i][0][6] = 0x78;
|
|
dv_frame[i][0][7] = 0x78;
|
|
memset(&dv_frame[i][0][8], 0xFF, 72);
|
|
for (j = 0; j < 2; j++) {
|
|
dv_frame[i][j+1][0] = 0x3F;
|
|
dv_frame[i][j+1][1] = i<<4 | 0x07;
|
|
dv_frame[i][j+1][2] = j;
|
|
dv_frame[i][j+1][3] = (i>=6 ? 0x80 : 0) | ((i+12) >> 3);
|
|
dv_frame[i][j+1][4] = ((i+12) << 5) | (j*6);
|
|
memset(&dv_frame[i][j+1][5], 0xFF, 75);
|
|
}
|
|
for (j = 0; j < 3; j++) {
|
|
dv_frame[i][j+3][0] = 0x5F;
|
|
dv_frame[i][j+3][1] = i<<4 | 0x07;
|
|
dv_frame[i][j+3][2] = j;
|
|
memset(&dv_frame[i][j+3][3], 0xFF, 77);
|
|
if (i == 0 && j == 0) {
|
|
dv_frame[i][j+3][ 3] = 0x70;
|
|
dv_frame[i][j+3][ 4] = 0xC5;
|
|
dv_frame[i][j+3][ 5] = 0x41;
|
|
dv_frame[i][j+3][ 6] = 0x20;
|
|
dv_frame[i][j+3][ 7] = 0xFF;
|
|
dv_frame[i][j+3][ 8] = 0x71;
|
|
dv_frame[i][j+3][ 9] = 0xFF;
|
|
dv_frame[i][j+3][10] = 0x7F;
|
|
dv_frame[i][j+3][11] = 0xFF;
|
|
dv_frame[i][j+3][12] = 0xFF;
|
|
dv_frame[i][j+3][13] = 0x7F;
|
|
dv_frame[i][j+3][14] = 0xFF;
|
|
dv_frame[i][j+3][15] = 0xFF;
|
|
dv_frame[i][j+3][16] = 0x38;
|
|
dv_frame[i][j+3][17] = 0x81;
|
|
} else if (j == 2) {
|
|
dv_frame[i][j+3][48] = 0x60;
|
|
dv_frame[i][j+3][48] = 0xFF;
|
|
dv_frame[i][j+3][48] = 0xFF;
|
|
dv_frame[i][j+3][48] = 0x20;
|
|
dv_frame[i][j+3][48] = 0xFF;
|
|
dv_frame[i][j+3][48] = 0x61;
|
|
dv_frame[i][j+3][48] = 0x33;
|
|
dv_frame[i][j+3][48] = 0xC8;
|
|
dv_frame[i][j+3][48] = 0xFD;
|
|
dv_frame[i][j+3][48] = 0xFF;
|
|
}
|
|
}
|
|
for (j = 0; j < 9; j++) {
|
|
dv_frame[i][j*16+6][0] = 0x7B;
|
|
dv_frame[i][j*16+6][1] = i<<4 | 0x07;
|
|
dv_frame[i][j*16+6][2] = j;
|
|
if (j == 0) {
|
|
dv_frame[i][j*16+6][3] = 0x50;
|
|
dv_frame[i][j*16+6][4] = 0xD8;
|
|
dv_frame[i][j*16+6][5] = i>=6 ? 0x01 : 0x00;
|
|
dv_frame[i][j*16+6][6] = 0xE0;
|
|
dv_frame[i][j*16+6][7] = 0xC0;
|
|
} else if (j == 1) {
|
|
dv_frame[i][j*16+6][3] = 0x51;
|
|
dv_frame[i][j*16+6][4] = 0x33;
|
|
dv_frame[i][j*16+6][5] = 0xCF;
|
|
dv_frame[i][j*16+6][6] = 0xA0;
|
|
dv_frame[i][j*16+6][7] = 0xFF;
|
|
} else {
|
|
dv_frame[i][j*16+6][3] = 0xFF;
|
|
dv_frame[i][j*16+6][4] = 0xFF;
|
|
dv_frame[i][j*16+6][5] = 0xFF;
|
|
dv_frame[i][j*16+6][6] = 0xFF;
|
|
dv_frame[i][j*16+6][7] = 0xFF;
|
|
}
|
|
memset(&dv_frame[i][j*16+6][8], 0, 72);
|
|
for (k = 0; k < 15; k++) {
|
|
dv_frame[i][j*16+k+7][0] = 0x9B;
|
|
dv_frame[i][j*16+k+7][1] = i<<4 | 0x07;
|
|
dv_frame[i][j*16+k+7][2] = j*15 + k;
|
|
dv_frame[i][j*16+k+7][3] = 0x0F;
|
|
memset(&dv_frame[i][4], 0, 76);
|
|
dv_frame[i][j*16+k+7][ 4] = 0x50;
|
|
dv_frame[i][j*16+k+7][ 5] = 0x06;
|
|
dv_frame[i][j*16+k+7][18] = 0x50;
|
|
dv_frame[i][j*16+k+7][19] = 0x06;
|
|
dv_frame[i][j*16+k+7][32] = 0x50;
|
|
dv_frame[i][j*16+k+7][33] = 0x06;
|
|
dv_frame[i][j*16+k+7][46] = 0x50;
|
|
dv_frame[i][j*16+k+7][47] = 0x06;
|
|
dv_frame[i][j*16+k+7][61] = 0x16;
|
|
dv_frame[i][j*16+k+7][71] = 0x26;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Decode the generated frame */
|
|
decoder = dv_decoder_new(1, 0, 0);
|
|
if (!decoder) {
|
|
if (verbose & TC_DEBUG) {
|
|
tc_log_warn(__FILE__,
|
|
"check_yuy2: Unable to initialize DV decoder");
|
|
}
|
|
return -1;
|
|
}
|
|
decoder->quality = DV_QUALITY_BEST;
|
|
if (dv_parse_header(decoder, (uint8_t *)dv_frame) < 0) {
|
|
if (verbose & TC_DEBUG) {
|
|
tc_log_warn(__FILE__,
|
|
"check_yuy2: Parsing test DV frame header failed");
|
|
}
|
|
return -1;
|
|
}
|
|
dv_decode_full_frame(decoder, (uint8_t *)dv_frame, DV_COLOR_YUV,
|
|
video, linesize);
|
|
dv_decoder_free(decoder);
|
|
|
|
/* Return whether the frame is YUY2-encoded or not */
|
|
if (Y[0] >= 0xCF && Y[0] <= 0xD1) {
|
|
if (Y[1] >= 0xCF && Y[1] <= 0xD1) {
|
|
/* Planar YUV data */
|
|
return 0;
|
|
} else if (Y[1] >= 0x7F && Y[1] <= 0x81) {
|
|
/* Packed YUY2 data */
|
|
return 1;
|
|
}
|
|
}
|
|
/* Else a buggy library? */
|
|
if (verbose & TC_DEBUG) {
|
|
tc_log_warn(__FILE__,
|
|
"check_yuy2: Bad video data (Y=%02X %02X %02X %02X,"
|
|
" U=%02X %02X, V=%02X %02X)", Y[0], Y[1], Y[2], Y[3],
|
|
U[0], U[1], V[0], V[1]);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
#endif // HAVE_LIBDV
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* decode_dv: DV decoding loop, called from tcdecode. Reads raw DV frames
|
|
* from stdin and writes the decoded video or audio data to stdout.
|
|
*
|
|
* Parameters:
|
|
* decode: Pointer to decoding parameter structure.
|
|
* Return value:
|
|
* None.
|
|
*/
|
|
|
|
void decode_dv(decode_t *decode)
|
|
{
|
|
#ifdef HAVE_LIBDV
|
|
|
|
dv_decoder_t *decoder;
|
|
/* FIXME: should this buffer be static instead (to reduce stack usage)? */
|
|
uint8_t framebuf[DV_FRAME_SIZE_625_50]; /* Buffer for input frame */
|
|
uint8_t *video[3]; /* Decoded video data */
|
|
uint8_t *video_conv_buf[3]; /* For YUY2/420P conversion */
|
|
TCVHandle tcvhandle; /* For YUY2/420P conversion */
|
|
ImageFormat srcfmt = 0, destfmt = 0; /* For YUY2/420P conversion */
|
|
dv_color_space_t colorspace = 0; /* For dv_decode_full_frame() */
|
|
int linesize[3], planesize[3]; /* video[] line/plane size */
|
|
int video_conv_linesize[3]; /* Same, for video_conv_buf[] */
|
|
int16_t audio_in[4][DV_AUDIO_MAX_SAMPLES]; /* Decoded audio data */
|
|
int16_t *audio_inptr[4]; /* List of pointers for same */
|
|
int16_t audio_out[4*DV_AUDIO_MAX_SAMPLES]; /* Interleaved audio data */
|
|
int yuy2_mode; /* libdv YUY2/YV12 selector */
|
|
int error = 0; /* Set to signal an error */
|
|
int ispal; /* Is it a 625/50 stream? */
|
|
int nread; /* Result of tc_pread() */
|
|
|
|
|
|
/**** Sanity check and variable setup ****/
|
|
|
|
if (!decode) {
|
|
tc_log_error(__FILE__, "Invalid parameter to decode_dv()");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
audio_inptr[0] = audio_in[0];
|
|
audio_inptr[1] = audio_in[1];
|
|
audio_inptr[2] = audio_in[2];
|
|
audio_inptr[3] = audio_in[3];
|
|
|
|
/**** Initialize libtcvideo for potential image format conversion ****/
|
|
|
|
tcvhandle = tcv_init();
|
|
if (!tcvhandle) {
|
|
tc_log_error(__FILE__, "Unable to initialize libtcvideo");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
|
|
/**** Initialize libdv decoder ****/
|
|
|
|
decoder = dv_decoder_new(1, 0, 0);
|
|
if (!decoder) {
|
|
tc_log_error(__FILE__, "Unable to initialize DV decoder");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
switch (decode->quality) {
|
|
case 1: decoder->quality = DV_QUALITY_FASTEST; break;
|
|
case 2: decoder->quality = DV_QUALITY_AC_1; break;
|
|
case 3: decoder->quality = DV_QUALITY_AC_2; break;
|
|
case 4: decoder->quality = DV_QUALITY_AC_1 | DV_QUALITY_COLOR; break;
|
|
case 5:
|
|
default: decoder->quality = DV_QUALITY_BEST; break;
|
|
}
|
|
|
|
/**** Set up image formats and line size per plane (*16) ****/
|
|
|
|
switch (decode->format) {
|
|
case TC_CODEC_YUV420P:
|
|
srcfmt = IMG_UNKNOWN; /* Source format not yet known */
|
|
destfmt = IMG_YUV420P;
|
|
colorspace = DV_COLOR_YUV;
|
|
linesize[0] = 16;
|
|
linesize[1] = linesize[2] = 8;
|
|
break;
|
|
case TC_CODEC_YUY2:
|
|
srcfmt = IMG_UNKNOWN; /* Source format not yet known */
|
|
destfmt = IMG_YUY2;
|
|
colorspace = DV_COLOR_YUV;
|
|
linesize[0] = 32;
|
|
linesize[1] = linesize[2] = 0;
|
|
break;
|
|
case TC_CODEC_RGB:
|
|
srcfmt = destfmt = IMG_RGB24;
|
|
colorspace = DV_COLOR_RGB;
|
|
linesize[0] = 48;
|
|
linesize[1] = linesize[2] = 0;
|
|
break;
|
|
case TC_CODEC_PCM:
|
|
linesize[0] = linesize[1] = linesize[2] = 0; // what video? ;)
|
|
break;
|
|
default:
|
|
tc_log_error(__FILE__, "Invalid output format (%08lX)",
|
|
decode->format);
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
|
|
/**** Read the first frame and analyze the frame header. We start ****
|
|
**** by reading in enough data for an NTSC frame; if it turns out ****
|
|
**** to be PAL instead, we read the rest of it once we find that ****
|
|
**** out. ****/
|
|
|
|
nread = tc_pread(decode->fd_in, framebuf, DV_FRAME_SIZE_525_60);
|
|
if (nread != DV_FRAME_SIZE_525_60) {
|
|
tc_log_error(__FILE__, "No DV frames found!");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
if (dv_parse_header(decoder, framebuf) < 0) {
|
|
tc_log_error(__FILE__, "Unable to parse frame header!");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
if (decoder->system == DV_SYSTEM_525_60) {
|
|
ispal = 0;
|
|
} else if (decoder->system == DV_SYSTEM_625_50) {
|
|
ispal = 1;
|
|
} else {
|
|
tc_log_error(__FILE__, "Unknown or invalid DV frame type!");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
if (ispal) {
|
|
nread = tc_pread(decode->fd_in, framebuf + DV_FRAME_SIZE_525_60,
|
|
DV_FRAME_SIZE_625_50 - DV_FRAME_SIZE_525_60);
|
|
if (nread != DV_FRAME_SIZE_625_50 - DV_FRAME_SIZE_525_60) {
|
|
tc_log_error(__FILE__, "No DV frames found!");
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/**** Print a stream information summary ****/
|
|
|
|
if (verbose) {
|
|
if (decode->format == TC_CODEC_PCM) {
|
|
tc_log_info(__FILE__, "audio: %d Hz, %d channels",
|
|
decoder->audio->frequency,
|
|
decoder->audio->num_channels);
|
|
} else {
|
|
tc_log_info(__FILE__, "%s video: %dx%d framesize=%lu sampling=%d",
|
|
ispal ? "PAL" : "NTSC", decoder->width,
|
|
decoder->height, (unsigned long)decoder->frame_size,
|
|
decoder->sampling);
|
|
}
|
|
}
|
|
|
|
/**** Allocate video buffers ****/
|
|
|
|
/* Plane 0: packed RGB, packed YUV, or planar Y */
|
|
linesize[0] = (linesize[0] * decoder->width) / 16;
|
|
planesize[0] = linesize[0] * decoder->height;
|
|
|
|
/* Plane 1: planar U (half height) */
|
|
linesize[1] = (linesize[1] * decoder->width) / 16;
|
|
planesize[1] = linesize[1] * (decoder->height/2);
|
|
|
|
/* Plane 2: planar V (half height) */
|
|
linesize[2] = (linesize[2] * decoder->width) / 16;
|
|
planesize[2] = linesize[2] * (decoder->height/2);
|
|
|
|
/* Set up pointers into a contiguous buffer */
|
|
video[0] = tc_bufalloc(planesize[0] + planesize[1] + planesize[2]);
|
|
video[1] = video[0] + planesize[0];
|
|
video[2] = video[1] + planesize[1];
|
|
|
|
/* Conversion buffer (allocate just in case) */
|
|
video_conv_buf[0] = tc_bufalloc(decoder->width * decoder->height * 2);
|
|
video_conv_buf[1] = video_conv_buf[0] + decoder->width * decoder->height;
|
|
video_conv_buf[2] = video_conv_buf[1] + (decoder->width/2)*decoder->height;
|
|
|
|
/* Make sure we got everything we need */
|
|
if (!video[0] || !video_conv_buf[0]) {
|
|
tc_log_error(__FILE__, "No memory for video buffers!");
|
|
tc_buffree(video_conv_buf[0]);
|
|
tc_buffree(video[0]);
|
|
import_exit(1);
|
|
return;
|
|
}
|
|
|
|
/**** Determine whether YUV data is YUY2 or YV12 (really I420??) ****/
|
|
|
|
if (ispal) {
|
|
if (decode->dv_yuy2_mode != -1) {
|
|
/* User specified --dv_yuy2_mode or --dv_yv12_mode */
|
|
yuy2_mode = decode->dv_yuy2_mode;
|
|
} else {
|
|
int result = check_yuy2();
|
|
if (result >= 1)
|
|
yuy2_mode = result;
|
|
else /* we don't know, use the libdv default of YUY2 as a guess */
|
|
yuy2_mode = 1;
|
|
}
|
|
} else {
|
|
/* NTSC is always returned in YUY2 format */
|
|
yuy2_mode = 1;
|
|
}
|
|
if (srcfmt == IMG_UNKNOWN) {
|
|
if (yuy2_mode) {
|
|
srcfmt = IMG_YUY2;
|
|
video_conv_linesize[0] = decoder->width * 2;
|
|
video_conv_linesize[1] = video_conv_linesize[2] = 0;
|
|
} else {
|
|
srcfmt = IMG_YUV420P;
|
|
video_conv_linesize[0] = decoder->width;
|
|
video_conv_linesize[1] = video_conv_linesize[2] = decoder->width/2;
|
|
}
|
|
}
|
|
|
|
/**** Decoding loop ****/
|
|
|
|
for (;;) {
|
|
int toread;
|
|
|
|
/* Process the data as requested */
|
|
|
|
switch (decode->format) {
|
|
|
|
case TC_CODEC_YUV420P:
|
|
case TC_CODEC_YUY2:
|
|
case TC_CODEC_RGB:
|
|
if (srcfmt == destfmt) {
|
|
dv_decode_full_frame(decoder, framebuf, colorspace, video,
|
|
linesize);
|
|
} else {
|
|
dv_decode_full_frame(decoder, framebuf, colorspace,
|
|
video_conv_buf, video_conv_linesize);
|
|
if (!tcv_convert(tcvhandle, video_conv_buf[0], video[0],
|
|
decoder->width, decoder->height,
|
|
srcfmt, destfmt)) {
|
|
tc_log_error(__FILE__, "Image format conversion failed!");
|
|
error = 1;
|
|
goto done;
|
|
}
|
|
}
|
|
if ((planesize[0] && tc_pwrite(decode->fd_out, video[0],
|
|
planesize[0]) != planesize[0])
|
|
|| (planesize[1] && tc_pwrite(decode->fd_out, video[1],
|
|
planesize[1]) != planesize[1])
|
|
|| (planesize[2] && tc_pwrite(decode->fd_out, video[2],
|
|
planesize[2]) != planesize[2])
|
|
) {
|
|
tc_log_error(__FILE__, "Write failed: %s", strerror(errno));
|
|
error = 1;
|
|
goto done;
|
|
}
|
|
break;
|
|
|
|
case TC_CODEC_PCM: {
|
|
int i, ch;
|
|
int16_t *outptr = audio_out;
|
|
dv_decode_full_audio(decoder, framebuf, audio_inptr);
|
|
for (i = 0; i < decoder->audio->samples_this_frame; i++) {
|
|
for (ch = 0; ch < decoder->audio->num_channels; ch++) {
|
|
*outptr++ = audio_in[ch][i];
|
|
}
|
|
}
|
|
i = decoder->audio->samples_this_frame
|
|
* decoder->audio->num_channels * 2;
|
|
if (tc_pwrite(decode->fd_out, (void *)audio_out, i) != i) {
|
|
tc_log_error(__FILE__, "Write failed: %s", strerror(errno));
|
|
error = 1;
|
|
goto done;
|
|
}
|
|
break;
|
|
} /* case TC_CODEC_PCM */
|
|
|
|
} /* switch (decode->format) */
|
|
|
|
/* Read in the next frame, if any, and parse the header */
|
|
|
|
retry:
|
|
toread = ispal ? DV_FRAME_SIZE_625_50 : DV_FRAME_SIZE_525_60;
|
|
nread = tc_pread(decode->fd_in, framebuf, toread);
|
|
if (nread != toread) {
|
|
if (verbose & TC_DEBUG)
|
|
tc_log_info(__FILE__, "End of stream reached.");
|
|
break;
|
|
}
|
|
if (dv_parse_header(decoder, framebuf) < 0) {
|
|
tc_log_warn(__FILE__, "Unable to parse frame header, skipping...");
|
|
goto retry;
|
|
}
|
|
/* Sanity check: make sure it's the same video system */
|
|
if (decoder->system != (ispal ? DV_SYSTEM_625_50 : DV_SYSTEM_525_60)) {
|
|
tc_log_error(__FILE__, "Video system (NTSC/PAL) changed"
|
|
" midstream! Aborting.");
|
|
error = 1;
|
|
break;
|
|
}
|
|
|
|
} /* end decoding loop */
|
|
|
|
/**** All done ****/
|
|
|
|
done:
|
|
tc_buffree(video_conv_buf[0]);
|
|
tc_buffree(video[0]);
|
|
dv_decoder_free(decoder);
|
|
tcv_free(tcvhandle);
|
|
|
|
import_exit(error ? 1 : 0);
|
|
|
|
#else /* !HAVE_LIBDV */
|
|
|
|
tc_log_error(__FILE__, "No support for Digital Video configured - exit.");
|
|
import_exit(1);
|
|
|
|
#endif
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* probe_dv: Probe the input stream (on stdin) and set stream parameters
|
|
* as appropriate.
|
|
*
|
|
* Parameters: info: Pointer to stream parameter structure.
|
|
* Return value: None.
|
|
* Preconditions: None.
|
|
*/
|
|
|
|
void probe_dv(info_t *info)
|
|
{
|
|
#ifdef HAVE_LIBDV
|
|
dv_decoder_t *decoder;
|
|
uint8_t framebuf[DV_FRAME_SIZE_525_60]; // FIXME: too big for the stack?
|
|
int nread, ispal;
|
|
#endif
|
|
|
|
if (!info) {
|
|
tc_log_error(__FILE__, "Invalid parameter to probe_dv()");
|
|
return;
|
|
}
|
|
|
|
#ifdef HAVE_LIBDV
|
|
|
|
/* Read in an NTSC frame; for probing, this is enough for PAL as well */
|
|
nread = tc_pread(info->fd_in, framebuf, sizeof(framebuf));
|
|
if (nread < sizeof(framebuf)) {
|
|
tc_log_error(__FILE__, "Stream too short");
|
|
info->error = 1;
|
|
return;
|
|
}
|
|
|
|
/* Initialize libdv decoder */
|
|
decoder = dv_decoder_new(1, 0, 0);
|
|
if (!decoder) {
|
|
tc_log_error(__FILE__, "Unable to initialize DV decoder");
|
|
info->error = 1;
|
|
return;
|
|
}
|
|
|
|
/* Parse frame header and check video system type */
|
|
if (dv_parse_header(decoder, framebuf) < 0) {
|
|
tc_log_error(__FILE__, "No valid DV frame found");
|
|
info->error = 1;
|
|
return;
|
|
}
|
|
if (decoder->system == DV_SYSTEM_525_60) {
|
|
ispal = 0;
|
|
} else if (decoder->system == DV_SYSTEM_625_50) {
|
|
ispal = 1;
|
|
} else {
|
|
tc_log_error(__FILE__, "Unknown or invalid DV frame type");
|
|
info->error = 1;
|
|
return;
|
|
}
|
|
|
|
/* Set stream parameters */
|
|
|
|
info->probe_info->magic = ispal ? TC_MAGIC_PAL : TC_MAGIC_NTSC;
|
|
|
|
info->probe_info->width = decoder->width;
|
|
info->probe_info->height = decoder->height;
|
|
info->probe_info->fps = ispal ? PAL_FPS : NTSC_VIDEO;
|
|
info->probe_info->frc = ispal ? 3 : 4;
|
|
info->probe_info->asr = dv_format_wide(decoder) ? 3 :
|
|
dv_format_normal(decoder) ? 2 : 0;
|
|
|
|
info->probe_info->track[0].samplerate = decoder->audio->frequency;
|
|
info->probe_info->track[0].chan = decoder->audio->num_channels;
|
|
info->probe_info->track[0].bits = 16;
|
|
info->probe_info->track[0].format = CODEC_PCM;
|
|
info->probe_info->track[0].bitrate =
|
|
(decoder->audio->frequency * decoder->audio->num_channels * 16) / 1000;
|
|
info->probe_info->num_tracks = 1;
|
|
|
|
/* All done */
|
|
dv_decoder_free(decoder);
|
|
|
|
#endif /* HAVE_LIBDV */
|
|
|
|
/* Even without libdv, we know this much... */
|
|
info->probe_info->codec = TC_CODEC_DV;
|
|
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|