alsa-tools/ac3dec/tools/extract_ac3.c
2001-04-17 19:31:03 +00:00

314 lines
6.8 KiB
C

/*
* extract_ac3.c
*
* Copyright (C) Aaron Holtzman <aholtzma@ess.engr.uvic.ca> - June 1999
*
* Extracts an AC-3 audio stream from an MPEG-2 system stream
* and writes it to stdout
*
* Ideas and bitstream syntax info borrowed from code written
* by Nathan Laredo <laredo@gnu.org>
*
* Multiple track support by Yuqing Deng <deng@tinker.chem.brown.edu>
*
*
* This file is part of ac3dec, a free Dolby AC-3 stream decoder.
*
* ac3dec 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.
*
* ac3dec 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,
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mman.h>
/* Audio track to play */
static unsigned char track_code = 0x80;
static unsigned char track_table[8] =
{
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87
};
#define BUFSIZE 512 /* needs to be as big as biggest header */
static int vobf;
static unsigned char buf[BUFSIZE];
static unsigned char *cur_pos;
static unsigned char *end_pos;
void file_init(char file_name[])
{
if(file_name[0] == '-' && file_name[1] == '\0')
{
vobf = STDIN_FILENO;
}
else if ((vobf = open(file_name, O_RDONLY)) < 0)
{
fprintf(stderr,"File not found\n");
exit(1);
}
cur_pos = buf;
end_pos = buf;
}
inline void increment_position(long x)
{
if(cur_pos + x <= end_pos)
{
cur_pos += x;
if(cur_pos == end_pos)
{
cur_pos = buf;
end_pos = buf;
}
}
else
{
long size = 0;
x -= (long)(end_pos - cur_pos);
#ifdef SEEK_PIPES
if(lseek(vobf, x, SEEK_CUR) < 0)
{
fprintf(stderr, "Error: unexpected end of stream\n");
exit(1);
}
#else
while(x)
{
size = (x > BUFSIZE) ? BUFSIZE : x;
if(read(vobf, buf, size) < size)
{
fprintf(stderr, "Error: unexpected end of stream\n");
exit(1);
}
x-=size;
}
#endif
cur_pos = buf;
end_pos = buf;
}
}
inline static void load_next_bytes(long count)
{
if(cur_pos + count <= end_pos)
return;
if(cur_pos + count > buf + BUFSIZE - 1 )
{
printf ("No buffer space to read %ld bytes\n", count);
exit(1);
}
count -= (long)(end_pos - cur_pos);
if(read(vobf, end_pos, count) < count)
{
fprintf(stderr, "Error: unexpected end of stream\n");
exit(1);
}
end_pos += count;
}
inline int next_24_bits(long x)
{
load_next_bytes(3);
if (cur_pos[0] != ((x >> 16) & 0xff))
return 0;
if (cur_pos[1] != ((x >> 8) & 0xff))
return 0;
if (cur_pos[2] != ((x ) & 0xff))
return 0;
return 1;
}
inline int next_32_bits(long x)
{
load_next_bytes(4);
if (cur_pos[0] != ((x >> 24) & 0xff))
return 0;
if (cur_pos[1] != ((x >> 16) & 0xff))
return 0;
if (cur_pos[2] != ((x >> 8) & 0xff))
return 0;
if (cur_pos[3] != ((x ) & 0xff))
return 0;
return 1;
}
void read_write_next_bytes(long count, int outfd)
{
long size;
size = (long)(end_pos - cur_pos);
if(size > count)
{
write(outfd, cur_pos, count);
cur_pos +=count;
if(cur_pos == end_pos)
{
cur_pos = buf;
end_pos = buf;
}
return;
}
else if(size > 0)
{
write(outfd, cur_pos, size);
}
while(count)
{
size = (count > BUFSIZE) ? BUFSIZE : count;
if(read(vobf, buf, size) < size ||
write(outfd, buf, size) < size)
{
fprintf(stderr, "Error: unexpected end of stream\n");
}
count -= size;
}
cur_pos = buf;
end_pos = buf;
}
void parse_pes(void)
{
unsigned long data_length;
unsigned long header_length;
load_next_bytes(9);
//The header length is the PES_header_data_length byte plus 6 for the packet
//start code and packet size, 3 for the PES_header_data_length and two
//misc bytes, and finally 4 bytes for the mystery AC3 packet tag
header_length = cur_pos[8] + 6 + 3 + 4 ;
data_length =(cur_pos[4]<<8) + cur_pos[5];
//If we have AC-3 audio then output it
if(cur_pos[3] == 0xbd)
{
load_next_bytes(header_length);
#if 0
//Debugging printfs
fprintf(stderr,"start of pes curpos[] = %02x%02x%02x%02x\n",
cur_pos[0],cur_pos[1],cur_pos[2],cur_pos[3]);
fprintf(stderr,"header_length = %d data_length = %x\n",
header_length, data_length);
fprintf(stderr,"extra crap 0x%02x%02x%02x%02x data size 0x%0lx\n",cur_pos[header_length-4],
cur_pos[header_length-3],cur_pos[header_length-2],cur_pos[header_length-1],data_length);
#endif
//Only extract the track we want
if((cur_pos[header_length-4] == track_code ))
{
increment_position(header_length);
read_write_next_bytes(data_length - header_length + 6, STDOUT_FILENO);
}
else
{
increment_position(data_length + 6);
}
}
else
{
//The packet size is data_length plus 6 bytes to account for the
//packet start code and the data_length itself.
increment_position(data_length + 6);
}
}
void parse_pack(void)
{
unsigned long skip_length;
// Deal with the pack header
// The first 13 bytes are junk. The fourteenth byte
// contains the number of stuff bytes
load_next_bytes(14);
skip_length = cur_pos[13] & 0x7;
increment_position(14 + skip_length);
// Deal with the system header if it exists
if(next_32_bits(0x000001bb))
{
// Bytes 5 and 6 contain the length of the header minus 6
load_next_bytes(6);
skip_length = (cur_pos[4] << 8) + cur_pos[5];
increment_position(6 + skip_length);
}
while(next_24_bits(0x000001) && !next_32_bits(0x000001ba))
{
parse_pes();
}
}
int main(int argc, char *argv[])
{
int track = 0;
if (argc < 2) {
fprintf(stderr, "usage: %s mpeg_stream [track number]\n", argv[0]);
exit(1);
}
if (argc == 3)
{
track = strtol(argv[2], NULL, 0);
fprintf(stderr,"Extracting track %d\n",track);
}
if (track < 0 || track > 7)
{
fprintf(stderr, "Invalid track number: %d\n", track);
exit(1);
}
track_code = track_table[track];
file_init(argv[1]);
if(!next_32_bits(0x000001ba))
{
fprintf(stderr, "Non-program streams not handled - exiting\n\n");
exit(1);
}
do
{
parse_pack();
}
while(next_32_bits(0x000001ba));
fprintf(stderr,"curpos[] = %x%x%x%x\n",cur_pos[0],cur_pos[1],cur_pos[2],cur_pos[3]);
if(!next_32_bits(0x000001b9))
{
fprintf(stderr, "Error: expected end of stream code\n");
exit(1);
}
if(vobf != STDIN_FILENO) close(vobf);
return 0;
}