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.

720 lines
18 KiB

/*
* tcscan.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.
*
*/
#undef TCF_DEBUG
#include "transcode.h"
#include "tcinfo.h"
#include "libtc/xio.h"
#include "libtc/ratiocodes.h"
#include "ioaux.h"
#include "tc.h"
#include "avilib/avilib.h"
#include <math.h>
#define EXE "tcscan"
int verbose=TC_QUIET;
int tc_get_mp3_header(unsigned char* hbuf, int* chans, int* srate, int *bitrate);
#define tc_decode_mp3_header(hbuf) tc_get_mp3_header(hbuf, NULL, NULL, NULL)
void import_exit(int code)
{
if(verbose & TC_DEBUG)
tc_log_msg(EXE, "(pid=%d) exit (code %d)", (int) getpid(), code);
exit(code);
}
#define CHUNK_SIZE 4096
static int min=0, max=0;
static void check (int v)
{
if (v > max) {
max = v;
} else if (v < min) {
min = v;
}
return;
}
/*************************************************************************/
/* from ac3scan.c */
static int get_ac3_bitrate(uint8_t *ptr)
{
static const int bitrates[] = {
32, 40, 48, 56,
64, 80, 96, 112,
128, 160, 192, 224,
256, 320, 384, 448,
512, 576, 640
};
int ratecode = (ptr[2] & 0x3E) >> 1;
if (ratecode < sizeof(bitrates)/sizeof(*bitrates))
return bitrates[ratecode];
return -1;
}
static int get_ac3_samplerate(uint8_t *ptr)
{
static const int samplerates[] = {48000, 44100, 32000, -1};
return samplerates[ptr[2]>>6];
}
static int get_ac3_framesize(uint8_t *ptr)
{
int bitrate = get_ac3_bitrate(ptr);
int samplerate = get_ac3_samplerate(ptr);
if (bitrate < 0 || samplerate < 0)
return -1;
return bitrate * 96000 / samplerate + (samplerate==44100 ? ptr[2]&1 : 0);
}
/*************************************************************************/
/* enc_bitrate: Print bitrate information about the source data.
*
* Parameters:
* frames: Number of frames in the source.
* fps: Frames per second of the source.
* abitrate: Audio bitrate (bits per second).
* discsize: User-specified disc size in bytes, or 0 for none.
* Return value:
* None.
*/
static void enc_bitrate(long frames, double fps, int abitrate, double discsize)
{
static const int defsize[] = {650, 700, 1300, 1400};
long time;
double audiosize, videosize, vbitrate;
if (frames <= 0 || fps <= 0.0)
return;
time = frames / fps;
audiosize = (double)abitrate/8 * time;
/* Print basic source information */
printf("[%s] V: %ld frames, %ld sec @ %.3f fps\n",
EXE, frames, time, fps);
printf("[%s] A: %.2f MB @ %d kbps\n",
EXE, audiosize/(1024*1024), abitrate/1000);
/* Print recommended bitrates for user-specified or default disc sizes */
if (discsize) {
videosize = discsize - audiosize;
vbitrate = videosize / time * 8;
printf("USER CDSIZE: %4d MB | V: %6.1f MB @ %.1f kbps\n",
(int)floor(discsize/(1024*1024)), videosize/(1024*1024),
vbitrate/1024);
} else {
int i;
for (i = 0; i < sizeof(defsize) / sizeof(*defsize); i++) {
videosize = defsize[i]*1024*1024 - audiosize;
vbitrate = videosize / time * 8;
printf("USER CDSIZE: %4d MB | V: %6.1f MB @ %.1f kbps\n",
defsize[i], videosize/(1024*1024),
vbitrate/1024);
}
}
}
/*************************************************************************/
/* ------------------------------------------------------------
*
* print a usage/version message
*
* ------------------------------------------------------------*/
void version(void)
{
/* print id string to stderr */
fprintf(stderr, "%s (%s v%s) (C) 2001-2003 Thomas Oestreich,"
" 2003-2010 Transcode Team\n",
EXE, PACKAGE, VERSION);
}
static void usage(int status)
{
version();
fprintf(stderr,"\nUsage: %s [options]\n", EXE);
fprintf(stderr," -i file input file name [stdin]\n");
fprintf(stderr," -x codec source codec\n");
fprintf(stderr," -e r[,b[,c]] PCM audio stream parameter [%d,%d,%d]\n", RATE, BITS, CHANNELS);
fprintf(stderr," -f rate,frc frame rate [%.3f][,frc]\n", PAL_FPS);
fprintf(stderr," -w num estimate bitrate for num frames\n");
fprintf(stderr," -b bitrate audio encoder bitrate kBits/s [%d]\n", ABITRATE);
fprintf(stderr," -c cdsize user defined CD size in MB [0]\n");
fprintf(stderr," -d mode verbosity mode\n");
fprintf(stderr," -v print version\n");
exit(status);
}
/* ------------------------------------------------------------
*
* scan stream
*
* ------------------------------------------------------------*/
int main(int argc, char *argv[])
{
info_t ipipe;
long stream_stype = TC_STYPE_UNKNOWN;
long magic = TC_MAGIC_UNKNOWN;
FILE *in_file;
int n=0, ch;
char *codec=NULL, *name=NULL;
int bytes_per_sec, bytes_read, bframes=0;
uint64_t total=0;
int a_rate=RATE, a_bits=BITS, chan=CHANNELS;
int on=1;
short *s;
char buffer[CHUNK_SIZE];
double fps=PAL_FPS, frames, fmin, fmax, vol=0.0;
int frc;
int pseudo_frame_size=0;
int ac_bytes=0, frame_size, bitrate=ABITRATE;
float rbytes;
uint32_t i=0, j=0;
uint16_t sync_word = 0;
double cdsize = 0.0;
//proper initialization
memset(&ipipe, 0, sizeof(info_t));
libtc_init(&argc, &argv);
while ((ch = getopt(argc, argv, "c:b:e:i:vx:f:d:w:?h")) != -1) {
switch (ch) {
case 'c':
if(optarg[0]=='-') usage(EXIT_FAILURE);
cdsize = atof(optarg) * (1024*1024); /* MB -> bytes */
break;
case 'd':
if(optarg[0]=='-') usage(EXIT_FAILURE);
verbose = atoi(optarg);
break;
case 'e':
if(optarg[0]=='-') usage(EXIT_FAILURE);
if (3 != sscanf(optarg,"%d,%d,%d", &a_rate, &a_bits, &chan)) {
tc_log_error(EXE, "invalid pcm parameter set for option -e");
usage(EXIT_FAILURE);
}
if(a_rate > RATE || a_rate <= 0) {
tc_log_error(EXE, "invalid pcm parameter 'rate' for option -e");
usage(EXIT_FAILURE);
}
if(!(a_bits == 16 || a_bits == 8)) {
tc_log_error(EXE, "invalid pcm parameter 'bits' for option -e");
usage(EXIT_FAILURE);
}
if(!(chan == 0 || chan == 1 || chan == 2)) {
tc_log_error(EXE, "invalid pcm parameter 'channels' for option -e");
usage(EXIT_FAILURE);
}
break;
case 'i':
if(optarg[0]=='-') usage(EXIT_FAILURE);
name = optarg;
break;
case 'x':
if(optarg[0]=='-') usage(EXIT_FAILURE);
codec = optarg;
break;
case 'f':
if(optarg[0]=='-') usage(EXIT_FAILURE);
n = sscanf(optarg,"%lf,%d", &fps, &frc);
if (n == 2 && (frc > 0 && frc <= 0x10))
tc_frc_code_to_value(frc, &fps);
if(fps<=0) {
tc_log_error(EXE,"invalid frame rate for option -f");
exit(1);
}
break;
case 'w':
if(optarg[0]=='-') usage(EXIT_FAILURE);
bframes = atoi(optarg);
if(bframes <=0) {
tc_log_error(EXE,"invalid parameter for option -w");
exit(1);
}
break;
case 'b':
if(optarg[0]=='-') usage(EXIT_FAILURE);
bitrate = atoi(optarg);
if(bitrate < 0) {
tc_log_error(EXE,"invalid bitrate for option -b");
exit(1);
}
break;
case 'v':
version();
exit(0);
break;
case 'h':
usage(EXIT_SUCCESS);
default:
usage(EXIT_FAILURE);
}
}
ac_init(AC_ALL);
/* ------------------------------------------------------------
*
* fill out defaults for info structure
*
* ------------------------------------------------------------*/
// simple bitrate calculator
if(bframes) {
enc_bitrate(bframes, fps, bitrate*1000, cdsize);
exit(0);
}
// assume defaults
if(name==NULL) stream_stype=TC_STYPE_STDIN;
// no autodetection yet
if(codec==NULL && name == NULL) {
tc_log_error(EXE, "invalid codec %s", codec);
usage(EXIT_FAILURE);
}
if(codec==NULL) codec="";
// do not try to mess with the stream
if(stream_stype!=TC_STYPE_STDIN) {
if(tc_file_check(name)) exit(1);
if((ipipe.fd_in = xio_open(name, O_RDONLY))<0) {
tc_log_perror(EXE, "open file");
exit(1);
}
magic = fileinfo(ipipe.fd_in, 0);
} else ipipe.fd_in = STDIN_FILENO;
/* ------------------------------------------------------------
*
* AC3 stream
*
* ------------------------------------------------------------*/
if(strcmp(codec,"ac3")==0 || magic==TC_MAGIC_AC3) {
for(;;) {
for(;;) {
if (tc_pread(ipipe.fd_in, buffer, 1) !=1) {
tc_log_perror(EXE, "ac3 sync frame scan failed");
goto ac3_summary;
}
sync_word = (sync_word << 8) + (uint8_t) buffer[0];
++i;
if(sync_word == 0x0b77) break;
}
i=i-2;
if (tc_pread(ipipe.fd_in, buffer, 3) !=3) {
tc_log_perror(EXE, "ac3 header read failed");
goto ac3_summary;
}
if((frame_size = 2*get_ac3_framesize(buffer)) < 1) {
tc_log_warn(EXE, "ac3 framesize %d invalid - frame broken?", frame_size);
goto more;
}
//FIXME: I assume that a single AC3 frame contains 6kB PCM bytes
rbytes = (float) SIZE_PCM_FRAME/1024/6 * frame_size;
pseudo_frame_size = (int) rbytes;
bitrate = get_ac3_bitrate(buffer);
printf("[%s] [%05d] offset %06d (%06d) %04d bytes, bitrate %03d kBits/s\n", EXE, n++, i, j, frame_size, bitrate);
// read the rest
ac_bytes = frame_size-5;
if(ac_bytes>CHUNK_SIZE) {
tc_log_error(EXE, "Oops, no buffer space framesize %d", ac_bytes);
exit(1);
}
if ((bytes_read=tc_pread(ipipe.fd_in, buffer, ac_bytes)) != ac_bytes) {
tc_log_warn(EXE, "error reading ac3 frame (%d/%d)", bytes_read, ac_bytes);
break;
}
more:
i+=frame_size;
j=i;
}
ac3_summary:
vol = (double) (n * 1024 * 6)/4/RATE;
printf("[%s] valid AC3 frames=%d, estimated clip length=%.2f seconds\n", EXE, n, vol);
return(0);
}
/* ------------------------------------------------------------
*
* PCM stream
*
* ------------------------------------------------------------*/
if(strcmp(codec,"pcm")==0) {
while(on) {
if( (bytes_read = tc_pread(ipipe.fd_in, buffer, CHUNK_SIZE)) != CHUNK_SIZE) on = 0;
total += (uint64_t) bytes_read;
s=(short *) buffer;
for(n=0; n<bytes_read>>1; ++n) {
check((int) (*s));
s++;
}
}
bytes_per_sec = a_rate * (a_bits/8) * chan;
frames = (fps*((double)total)/bytes_per_sec);
fmin = -((double) min)/SHRT_MAX;
fmax = ((double) max)/SHRT_MAX;
if(min==0 || max == 0) exit(0);
vol = (fmin<fmax) ? 1./fmax : 1./fmin;
printf("[%s] audio frames=%.2f, estimated clip length=%.2f seconds\n", EXE, frames, frames/fps);
printf("[%s] (min/max) amplitude=(%.3f/%.3f), suggested volume rescale=%.3f\n", EXE, -fmin, fmax, vol);
enc_bitrate((long) frames, fps, bitrate*1000, cdsize);
return(0);
}
if(strcmp(codec,"mp3")==0 || magic == TC_MAGIC_MP3) {
char header[4];
int framesize = 0;
int chunks = 0;
int srate=0 , chans=0, bitrate=0;
unsigned long bitrate_add = 0;
off_t pos=0;
double ms = 0;
char bitrate_buf[TC_BUF_MIN];
min = 500;
max = 0;
pos = lseek(ipipe.fd_in, 0, SEEK_CUR);
// find mp3 header
while ((total += read(ipipe.fd_in, header, 4))) {
if ( (framesize = tc_get_mp3_header (header, &chans, &srate, &bitrate)) > 0) {
break;
}
pos++;
lseek(ipipe.fd_in, pos, SEEK_SET);
}
tc_log_msg(EXE, "POS %lld", (long long)pos);
// Example for _1_ mp3 chunk
//
// fps = 25
// framesize = 480 bytes
// bitrate = 160 kbit/s == 20 kbytes/s == 20480 bytes/s == 819.20 bytes / frame
//
// 480 bytes = 480/20480 s/bytes = .0234 s = 23.4 ms
//
// ms = (framesize*1000*8)/(bitrate*1000);
// why 1000 and not 1024?
// correct? yes! verified with "cat file.mp3|mpg123 -w /dev/null -v -" -- tibit
while (on) {
if ( (bytes_read = read(ipipe.fd_in, buffer, framesize-4)) != framesize-4) {
on = 0;
} else {
total += bytes_read;
while ((total += read(ipipe.fd_in, header, 4))) {
//tc_log_msg(EXE, "%x %x %x %x", header[0]&0xff, header[1]&0xff, header[2]&0xff, header[3]&0xff);
if ( (framesize = tc_get_mp3_header (header, &chans, &srate, &bitrate)) < 0) {
tc_log_warn(EXE, "corrupt mp3 file?");
on = 0;
break;
} else {
/*
tc_log_msg(EXE, "Found new header (%d) (framesize = %d) chan(%d) srate(%d) bitrate(%d)",
chunks, framesize, chans, srate, bitrate);
*/
bitrate_add += bitrate;
check(bitrate);
ms += (framesize*8)/(bitrate);
++chunks;
break;
}
}
}
}
if (min != max)
tc_snprintf(bitrate_buf, sizeof(bitrate_buf), "(%d-%d)", min, max);
else
tc_snprintf(bitrate_buf, sizeof(bitrate_buf), "(cbr)");
printf("[%s] MPEG-1 layer-3 stream. Info: -e %d,%d,%d\n", EXE,
srate, 16, chans);
printf("[%s] Found %d MP3 chunks. Average bitrate is %3.2f kbps %s\n", EXE,
chunks, (double)bitrate_add/chunks, bitrate_buf);
printf("[%s] AVI overhead will be max. %d*(8+16) = %d bytes (%dk)\n", EXE,
chunks, chunks*8+chunks*16, (chunks*8+chunks*16)/1024);
printf("[%s] Estimated time is %.0f ms (%02d:%02d:%02d.%02d)\n", EXE,
ms,
(int)(ms/1000.0/60.0/60.0),
(int)(ms/1000.0/60.0)%60,
(int)(ms/1000)%60,
(int)(ms)%(1000));
return(0);
}
/* ------------------------------------------------------------
*
* MPEG program stream
*
* ------------------------------------------------------------*/
if(strcmp(codec, "mpeg2")==0 || strcmp(codec, "mpeg")==0 || strcmp(codec, "vob")==0 || magic==TC_MAGIC_VOB || magic==TC_MAGIC_M2V) {
in_file = fdopen(ipipe.fd_in, "r");
scan_pes(verbose, in_file);
return(0);
}
/* ------------------------------------------------------------
*
* AVI
*
* ------------------------------------------------------------*/
if(magic==TC_MAGIC_AVI || TC_MAGIC_WAV) {
if(name!=NULL) AVI_scan(name);
return(0);
}
tc_log_error(EXE, "unable to handle codec/filetype %s", codec);
exit(1);
}
// from mencoder
//----------------------- mp3 audio frame header parser -----------------------
static int tabsel_123[2][3][16] = {
{ {0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0},
{0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0},
{0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0} },
{ {0,32,48,56,64,80,96,112,128,144,160,176,192,224,256,0},
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0},
{0,8,16,24,32,40,48,56,64,80,96,112,128,144,160,0} }
};
static long freqs[9] = { 44100, 48000, 32000, 22050, 24000, 16000 , 11025 , 12000 , 8000 };
/*
* return frame size or -1 (bad frame)
*/
int tc_get_mp3_header(unsigned char* hbuf, int* chans, int* srate, int *bitrate){
int stereo, ssize, crc, lsf, mpeg25, framesize;
int padding, bitrate_index, sampling_frequency;
unsigned long newhead =
hbuf[0] << 24 |
hbuf[1] << 16 |
hbuf[2] << 8 |
hbuf[3];
#if 1
// head_check:
if( (newhead & 0xffe00000) != 0xffe00000 ||
(newhead & 0x0000fc00) == 0x0000fc00){
//tc_log_warn(EXE, "head_check failed");
return -1;
}
#endif
if((4-((newhead>>17)&3))!=3){
//tc_log_warn(EXE, "not layer-3");
return -1;
}
if( newhead & ((long)1<<20) ) {
lsf = (newhead & ((long)1<<19)) ? 0x0 : 0x1;
mpeg25 = 0;
} else {
lsf = 1;
mpeg25 = 1;
}
if(mpeg25)
sampling_frequency = 6 + ((newhead>>10)&0x3);
else
sampling_frequency = ((newhead>>10)&0x3) + (lsf*3);
if(sampling_frequency>8){
tc_log_warn(EXE, "invalid sampling_frequency");
return -1; // valid: 0..8
}
crc = ((newhead>>16)&0x1)^0x1;
bitrate_index = ((newhead>>12)&0xf);
padding = ((newhead>>9)&0x1);
// fr->extension = ((newhead>>8)&0x1);
// fr->mode = ((newhead>>6)&0x3);
// fr->mode_ext = ((newhead>>4)&0x3);
// fr->copyright = ((newhead>>3)&0x1);
// fr->original = ((newhead>>2)&0x1);
// fr->emphasis = newhead & 0x3;
stereo = ( (((newhead>>6)&0x3)) == 3) ? 1 : 2;
if(!bitrate_index){
tc_log_warn(EXE, "Free format not supported.");
return -1;
}
if(lsf)
ssize = (stereo == 1) ? 9 : 17;
else
ssize = (stereo == 1) ? 17 : 32;
if(crc) ssize += 2;
framesize = tabsel_123[lsf][2][bitrate_index] * 144000;
if (bitrate) *bitrate = tabsel_123[lsf][2][bitrate_index];
if(!framesize){
tc_log_warn(EXE, "invalid framesize/bitrate_index");
return -1; // valid: 1..14
}
framesize /= freqs[sampling_frequency]<<lsf;
framesize += padding;
// if(framesize<=0 || framesize>MAXFRAMESIZE) return FALSE;
if(srate) *srate = freqs[sampling_frequency];
if(chans) *chans = stereo;
return framesize;
}
#include "libtc/static_xio.h"