More enhancements:

- added getopt_long to parse the command line options
  - added -D (--device) option
  - added -q (--quiet) option
  - added -I (--iec958) option
  - added AC3 to IEC958 encapsulation code (not tested)
  - addec xrun detection and restart
This commit is contained in:
Jaroslav Kysela 2001-04-22 12:04:00 +00:00
parent 3f541f6ba8
commit 97a55787e6
13 changed files with 425 additions and 120 deletions

View file

@ -8,7 +8,7 @@ bin_PROGRAMS = ac3dec
ac3dec_LDADD= -L./libac3 -lac3
noinst_HEADERS = output.h
ac3dec_SOURCES = ac3dec.c output.c
ac3dec_SOURCES = ac3dec.c output.c ac3spdif.c
ac3dec_DEPENDENCIES = libac3/libac3.a

View file

@ -26,17 +26,39 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <sys/errno.h>
#include <errno.h>
#include "config.h"
#include "libac3/ac3.h"
#include "output.h"
void
init_spdif(void);
int
output_spdif(uint_8 *data_start, uint_8 *data_end);
static int quiet = 0;
static void help(void)
{
printf("Usage: ac3dec <options> [file] [[file]] ...\n");
printf("\nAvailable options:\n");
printf(" -h,--help this help\n");
printf(" -v,--version print version of this program\n");
printf(" -D,--device=NAME select PCM by NAME\n");
printf(" -4,--4ch four channels mode\n");
printf(" -6,--6ch six channels mode\n");
printf(" -I,--iec958 raw IEC958 (S/PDIF) mode\n");
printf(" -q,--quit quit mode\n");
}
#define CHUNK_SIZE 2047
uint_8 buf[CHUNK_SIZE];
FILE *in_file;
void fill_buffer(uint_8 **start,uint_8 **end)
ssize_t fill_buffer(uint_8 **start,uint_8 **end)
{
uint_32 bytes_read;
@ -44,56 +66,125 @@ void fill_buffer(uint_8 **start,uint_8 **end)
bytes_read = fread(*start,1,CHUNK_SIZE,in_file);
//FIXME hack...
if (feof(in_file))
return EOF;
if(bytes_read < CHUNK_SIZE)
exit(0);
return EOF;
*end = *start + bytes_read;
return bytes_read;
}
int main(int argc,char *argv[])
{
ac3_frame_t *ac3_frame;
struct option long_option[] =
{
{"help", 0, NULL, 'h'},
{"version", 0, NULL, 'v'},
{"device", 1, NULL, 'D'},
{"4ch", 0, NULL, '4'},
{"6ch", 0, NULL, '6'},
{"iec958", 0, NULL, 'I'},
{"spdif", 0, NULL, 'I'},
{"quit", 0, NULL, 'q'},
{NULL, 0, NULL, 0},
};
ac3_config_t ac3_config;
int idx, channels = 2;
/* If we get an argument then use it as a filename... otherwise use
* stdin */
idx = 1;
if (idx < argc && !strcmp(argv[idx], "-4")) {
channels = 4; idx++;
} else if (idx < argc && !strcmp(argv[idx], "-6")) {
channels = 6; idx++;
}
if (idx < argc && argv[idx] != NULL) {
in_file = fopen(argv[idx],"r");
if(!in_file)
{
fprintf(stderr,"%s - Couldn't open file %s\n",strerror(errno),argv[1]);
exit(1);
}
}
else
in_file = stdin;
output_t out_config;
int morehelp, loop = 0;
char *pcm_name = NULL;
bzero(&ac3_config, sizeof(ac3_config));
ac3_config.fill_buffer_callback = fill_buffer;
ac3_config.num_output_ch = channels;
ac3_config.num_output_ch = 2;
ac3_config.flags = 0;
bzero(&out_config, sizeof(out_config));
out_config.pcm_name = NULL;
out_config.bits = 16;
out_config.rate = 48000;
out_config.channels = 2;
out_config.spdif = 0;
morehelp = 0;
while (1) {
int c;
if ((c = getopt_long(argc, argv, "hvD:46Iq", long_option, NULL)) < 0)
break;
switch (c) {
case 'h':
morehelp++;
break;
case 'v':
printf("ac3dec version " VERSION "\n");
return 1;
case 'D':
pcm_name = optarg;
break;
case '4':
if (!out_config.spdif)
ac3_config.num_output_ch = 4;
break;
case '6':
if (!out_config.spdif)
ac3_config.num_output_ch = 6;
break;
case 'I':
ac3_config.num_output_ch = 2;
out_config.spdif = 1;
break;
case 'q':
ac3_config.flags |= AC3_QUIET;
out_config.quiet = 1;
quiet = 1;
break;
default:
fprintf(stderr, "\07Invalid switch or option needs an argument.\n");
morehelp++;
}
}
out_config.channels = ac3_config.num_output_ch;
if (morehelp) {
help();
return 1;
}
while (1) {
if (argc - optind <= 0) {
if (loop)
break;
in_file = stdin;
} else {
in_file = fopen(argv[optind],"r");
if(!in_file) {
fprintf(stderr,"%s - Couldn't open file %s\n",strerror(errno),argv[optind]);
exit(EXIT_FAILURE);
}
optind++;
loop++;
}
if (!out_config.spdif) {
ac3_frame_t *ac3_frame;
ac3_init(&ac3_config);
ac3_frame = ac3_decode_frame();
output_open(16,ac3_frame->sampling_rate,channels);
do
{
if (output_open(&out_config) < 0) {
fprintf(stderr, "Output open failed\n");
exit(EXIT_FAILURE);
}
do {
//Send the samples to the output device
output_play(ac3_frame->audio_data, 256 * 6);
} while((ac3_frame = ac3_decode_frame()));
} else {
uint_8 *start, *end;
init_spdif();
output_open(&out_config);
while (fill_buffer(&start, &end) > 0)
if (output_spdif(start, end) < 0)
break;
}
while((ac3_frame = ac3_decode_frame()));
output_close();
fclose(in_file);
return 0;
}
return EXIT_SUCCESS;
}

137
ac3dec/ac3spdif.c Normal file
View file

@ -0,0 +1,137 @@
/***************************************************************************/
/* This code has been fully inspired from various place */
/* I will give their name later. May they be bless .... It works */
/* */
/* For the moment test it. */
/* */
/* 27-08-00 -- Ze'ev Maor -- fixed recovery from flase syncword detection */
/* */
/* 24-08-00 -- Ze'ev Maor -- Modified for integrtion with DXR3-OMS-plugin */
/***************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include <libac3/ac3.h>
#include <libac3/ac3_internal.h>
#include <libac3/parse.h>
#include <libac3/crc.h>
#include "output.h"
void swab(const void*, void*, size_t);
#define BLOCK_SIZE 6144
static char buf[BLOCK_SIZE];
static uint32_t sbuffer_size;
static syncinfo_t syncinfo;
static char *sbuffer;
static int done_banner;
static uint32_t
buffer_syncframe(syncinfo_t *syncinfo, uint8_t **start, uint8_t *end)
{
uint8_t *cur = *start;
uint16_t syncword = syncinfo->syncword;
uint32_t ret = 0;
//
// Find an ac3 sync frame.
//
while(syncword != 0x0b77)
{
if(cur >= end)
goto done;
syncword = (syncword << 8) + *cur++;
}
//need the next 3 bytes to decide how big the frame is
while(sbuffer_size < 3)
{
if(cur >= end)
goto done;
sbuffer[sbuffer_size++] = *cur++;
}
parse_syncinfo_data(syncinfo,sbuffer);
while(sbuffer_size < syncinfo->frame_size * 2 - 2)
{
if(cur >= end)
goto done;
sbuffer[sbuffer_size++] = *cur++;
}
crc_init();
crc_process_frame(sbuffer,syncinfo->frame_size * 2 - 2);
if(!crc_validate())
{
//error_flag = 1;
fprintf(stderr,"** CRC failed - skipping frame **\n");
syncword = 0xffff;
sbuffer_size = 0;
bzero(buf,BLOCK_SIZE);
goto done;
}
//
//if we got to this point, we found a valid ac3 frame to decode
//
//reset the syncword for next time
syncword = 0xffff;
sbuffer_size = 0;
ret = 1;
done:
syncinfo->syncword = syncword;
*start = cur;
return ret;
}
void
init_spdif(void)
{
sbuffer_size = 0;
sbuffer = &buf[10];
done_banner = 0;
}
int
output_spdif(uint8_t *data_start, uint8_t *data_end, int quiet)
{
unsigned short *sbuf = (unsigned short *)buf;
int ret = 0, res;
while(buffer_syncframe(&syncinfo, &data_start, data_end))
{
sbuf[0] = 0xf872; //spdif syncword
sbuf[1] = 0x4e1f; // .............
sbuf[2] = 0x0001; // AC3 data
sbuf[3] = syncinfo.frame_size * 16;
sbuf[4] = 0x0b77; // AC3 syncwork
if (!done_banner) {
fprintf(stdout,"AC3 Stream ");
fprintf(stdout,"%2.1f KHz",syncinfo.sampling_rate * 1e-3);
fprintf(stdout,"%4d kbps",syncinfo.bit_rate);
fprintf(stdout,"\n");
done_banner = 1;
}
// extract_ac3 seems to write swabbed data
swab(&buf[10], &buf[10], syncinfo.frame_size * 2 - 2);
res = output_play(sbuf, BLOCK_SIZE / 2 / 2); /* 2 channels, 16-bit samples */
ret = ret < 0 ? ret : res;
bzero(buf,BLOCK_SIZE);
}
return ret;
}

View file

@ -39,13 +39,14 @@ typedef signed char sint_8;
#define AC3_3DNOW_ENABLE 0x2
#define AC3_MMX_ENABLE 0x4
#define AC3_ALTIVEC_ENABLE 0x8
#define AC3_QUIET 0x10
typedef struct ac3_config_s
{
//Bit flags that enable various things
uint_32 flags;
//Callback that points the decoder to new stream data
void (*fill_buffer_callback)(uint_8 **, uint_8 **);
ssize_t (*fill_buffer_callback)(uint_8 **, uint_8 **);
//Number of discrete channels in final output (for downmixing)
uint_16 num_output_ch;
//Which channel of a dual mono stream to select

View file

@ -74,7 +74,7 @@ typedef struct syncinfo_s
{
uint_32 magic;
/* Sync word == 0x0B77 */
/* uint_16 syncword; */
uint_16 syncword;
/* crc for the first 5/8 of the sync block */
/* uint_16 crc1; */
/* Stream Sampling Rate (kHz) 0 = 48, 1 = 44.1, 2 = 32, 3 = reserved */

View file

@ -38,7 +38,7 @@ static uint_8 *chunk_start, *chunk_end;
uint_32 bits_left;
uint_32 current_word;
void (*bitstream_fill_buffer)(uint_8**,uint_8**);
ssize_t (*bitstream_fill_buffer)(uint_8**,uint_8**);
uint_8 bitstream_get_byte(void)
{
@ -122,7 +122,7 @@ bitstream_get_bh(uint_32 num_bits)
}
void
bitstream_init(void(*fill_function)(uint_8**,uint_8**))
bitstream_init(ssize_t(*fill_function)(uint_8**,uint_8**))
{
// Setup the buffer fill callback
bitstream_fill_buffer = fill_function;

View file

@ -50,7 +50,7 @@
extern uint_32 bits_left;
extern uint_32 current_word;
void bitstream_init(void(*fill_function)(uint_8**,uint_8**));
void bitstream_init(ssize_t(*fill_function)(uint_8**,uint_8**));
uint_8 bitstream_get_byte(void);

View file

@ -91,7 +91,7 @@ ac3_decode_frame(void)
parse_bsi(&bsi);
if(!done_banner)
if(!done_banner && !(ac3_config.flags & AC3_QUIET))
{
stats_print_banner(&syncinfo,&bsi);
done_banner = 1;

View file

@ -85,14 +85,43 @@ static const struct frmsize_s frmsizecod_tbl[64] =
{ 640 ,{1280 ,1394 ,1920 } }
};
void
parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data)
{
// Get the sampling rate
syncinfo->fscod = (data[2] >> 6) & 0x3;
if(syncinfo->fscod == 3)
{
//invalid sampling rate code
error_flag = 1;
return;
}
else if(syncinfo->fscod == 2)
syncinfo->sampling_rate = 32000;
else if(syncinfo->fscod == 1)
syncinfo->sampling_rate = 44100;
else
syncinfo->sampling_rate = 48000;
// Get the frame size code
syncinfo->frmsizecod = data[2] & 0x3f;
// Calculate the frame size and bitrate
syncinfo->frame_size =
frmsizecod_tbl[syncinfo->frmsizecod].frm_size[syncinfo->fscod];
syncinfo->bit_rate = frmsizecod_tbl[syncinfo->frmsizecod].bit_rate;
}
/* Parse a syncinfo structure, minus the sync word */
void
parse_syncinfo(syncinfo_t *syncinfo)
{
uint_32 tmp = 0;
uint_8 data[3];
uint_16 sync_word = 0;
uint_32 time_out = 1<<16;
//
// Find a ac3 sync frame. Time out if we read 64k without finding
// one.
@ -109,34 +138,11 @@ parse_syncinfo(syncinfo_t *syncinfo)
// We need to read in the entire syncinfo struct (0x0b77 + 24 bits)
// in order to determine how big the frame is
//
tmp = (tmp << 8) + bitstream_get_byte();
tmp = (tmp << 8) + bitstream_get_byte();
tmp = (tmp << 8) + bitstream_get_byte();
// Get the sampling rate
syncinfo->fscod = (tmp >> 6) & 0x3;
if(syncinfo->fscod == 3)
{
//invalid sampling rate code
error_flag = 1;
return;
}
else if(syncinfo->fscod == 2)
syncinfo->sampling_rate = 32000;
else if(syncinfo->fscod == 1)
syncinfo->sampling_rate = 44100;
else
syncinfo->sampling_rate = 48000;
// Get the frame size code
syncinfo->frmsizecod = tmp & 0x3f;
// Calculate the frame size and bitrate
syncinfo->frame_size =
frmsizecod_tbl[syncinfo->frmsizecod].frm_size[syncinfo->fscod];
syncinfo->bit_rate = frmsizecod_tbl[syncinfo->frmsizecod].bit_rate;
data[0] = bitstream_get_byte();
data[1] = bitstream_get_byte();
data[2] = bitstream_get_byte();
parse_syncinfo_data(syncinfo, data);
// Buffer the entire syncframe
bitstream_buffer_frame(syncinfo->frame_size * 2 - 5);
@ -144,9 +150,9 @@ parse_syncinfo(syncinfo_t *syncinfo)
// Check the crc over the entire frame
crc_init();
crc_process_byte(tmp>>16);
crc_process_byte((tmp>>8) & 0xff);
crc_process_byte(tmp & 0xff);
crc_process_byte(data[0]);
crc_process_byte(data[1]);
crc_process_byte(data[2]);
crc_process_frame(bitstream_get_buffer_start(),syncinfo->frame_size * 2 - 5);
if(!crc_validate())
@ -156,6 +162,7 @@ parse_syncinfo(syncinfo_t *syncinfo)
return;
}
if (!(ac3_config.flags & AC3_QUIET))
stats_print_syncinfo(syncinfo);
}
@ -290,6 +297,7 @@ parse_bsi(bsi_t *bsi)
bsi->addbsi[i] = bitstream_get(8);
}
if (!(ac3_config.flags & AC3_QUIET))
stats_print_bsi(bsi);
}
@ -598,6 +606,7 @@ parse_audblk(bsi_t *bsi,audblk_t *audblk)
}
}
if (!(ac3_config.flags & AC3_QUIET))
stats_print_audblk(bsi,audblk);
}

View file

@ -21,6 +21,7 @@
*
*/
void parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data);
void parse_syncinfo(syncinfo_t *syncinfo);
void parse_audblk(bsi_t *bsi,audblk_t *audblk);
void parse_bsi(bsi_t *bsi);

View file

@ -79,7 +79,7 @@ static const char *language[128] =
void stats_print_banner(syncinfo_t *syncinfo,bsi_t *bsi)
{
fprintf(stdout,PACKAGE"-"VERSION" (C) 2000 Aaron Holtzman (aholtzma@ess.engr.uvic.ca)\n");
// fprintf(stdout,PACKAGE"-"VERSION" (C) 2000 Aaron Holtzman (aholtzma@ess.engr.uvic.ca)\n");
fprintf(stdout,"%d.%d Mode ",bsi->nfchans,bsi->lfeon);
fprintf(stdout,"%2.1f KHz",syncinfo->sampling_rate * 1e-3);

View file

@ -27,14 +27,15 @@ typedef unsigned int uint_32;
#include "output.h"
static int pcm_channels;
static output_t out_config;
static snd_pcm_t *pcm;
/*
* open the audio device for writing to
*/
int output_open(int bits, int rate, int channels)
int output_open(output_t *output)
{
const char *pcm_name = output->pcm_name;
char devstr[128];
int card, dev;
snd_pcm_hw_params_t *params;
@ -45,18 +46,19 @@ int output_open(int bits, int rate, int channels)
snd_pcm_hw_params_alloca(&params);
snd_pcm_sw_params_alloca(&swparams);
pcm_channels = channels;
out_config = *output;
/*
* Open the device driver
*/
if (pcm_name == NULL) {
card = snd_defaults_pcm_card();
dev = snd_defaults_pcm_device();
if (card < 0 || dev < 0) {
fprintf(stderr, "defaults are not set\n");
return -ENODEV;
}
switch (channels) {
switch (output->channels) {
case 1:
case 2:
sprintf(devstr, "plug:%d,%d", card, dev);
@ -68,10 +70,12 @@ int output_open(int bits, int rate, int channels)
sprintf(devstr, "surround51:%d,%d", card, dev);
break;
default:
fprintf(stderr, "%d channels are not supported\n", channels);
fprintf(stderr, "%d channels are not supported\n", output->channels);
return -EINVAL;
}
if ((err = snd_pcm_open(&pcm, devstr, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
pcm_name = devstr;
}
if ((err = snd_pcm_open(&pcm, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
fprintf(stderr, "snd_pcm_open: %s\n", snd_strerror(err));
return err;
}
@ -87,17 +91,17 @@ int output_open(int bits, int rate, int channels)
fprintf(stderr, "Access type not available");
goto __close;
}
err = snd_pcm_hw_params_set_format(pcm, params, bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8);
err = snd_pcm_hw_params_set_format(pcm, params, output->bits == 16 ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8);
if (err < 0) {
fprintf(stderr, "Sample format non available");
goto __close;
}
err = snd_pcm_hw_params_set_channels(pcm, params, channels);
err = snd_pcm_hw_params_set_channels(pcm, params, output->channels);
if (err < 0) {
fprintf(stderr, "Channels count non available");
goto __close;
}
err = snd_pcm_hw_params_set_rate_near(pcm, params, rate, 0);
err = snd_pcm_hw_params_set_rate_near(pcm, params, output->rate, 0);
if (err < 0) {
fprintf(stderr, "Rate not available");
goto __close;
@ -108,12 +112,16 @@ int output_open(int bits, int rate, int channels)
fprintf(stderr, "Buffer time not available");
goto __close;
}
period_time = 100000 * 2;
do {
period_time /= 2;
period_time = snd_pcm_hw_params_set_period_time_near(pcm, params,
100000, 0);
period_time, 0);
if (period_time < 0) {
fprintf(stderr, "Period time not available");
goto __close;
}
} while (buffer_time == period_time && period_time > 10000);
if (buffer_time == period_time) {
fprintf(stderr, "Buffer time and period time match, could not use\n");
goto __close;
@ -123,6 +131,50 @@ int output_open(int bits, int rate, int channels)
goto __close;
}
if (output->spdif) {
snd_pcm_info_t *info;
snd_ctl_elem_value_t *ctl;
snd_ctl_t *ctl_handle;
char ctl_name[12];
int ctl_card;
snd_aes_iec958_t spdif;
memset(&spdif, 0, sizeof(spdif));
spdif.status[0] = IEC958_AES0_NONAUDIO |
IEC958_AES0_CON_EMPHASIS_NONE;
spdif.status[1] = IEC958_AES1_CON_ORIGINAL |
IEC958_AES1_CON_PCM_CODER;
spdif.status[2] = 0;
spdif.status[3] = IEC958_AES3_CON_FS_48000;
snd_pcm_info_alloca(&info);
if ((err = snd_pcm_info(pcm, info)) < 0) {
fprintf(stderr, "pcm info error: %s", snd_strerror(err));
goto __close;
}
snd_ctl_elem_value_alloca(&ctl);
snd_ctl_elem_value_set_interface(ctl, SND_CTL_ELEM_IFACE_PCM);
snd_ctl_elem_value_set_device(ctl, snd_pcm_info_get_device(info));
snd_ctl_elem_value_set_subdevice(ctl, snd_pcm_info_get_subdevice(info));
snd_ctl_elem_value_set_name(ctl, SND_CTL_NAME_IEC958("",PLAYBACK,PCM_STREAM));
snd_ctl_elem_value_set_iec958(ctl, &spdif);
ctl_card = snd_pcm_info_get_card(info);
if (ctl_card < 0) {
fprintf(stderr, "Unable to setup the IEC958 (S/PDIF) interface - PCM has no assigned card\n");
goto __diga_end;
}
sprintf(ctl_name, "hw:%d", ctl_card);
if ((err = snd_ctl_open(&ctl_handle, ctl_name, 0)) < 0) {
fprintf(stderr, "Unable to open the control interface '%s': %s\n", ctl_name, snd_strerror(err));
goto __diga_end;
}
if ((err = snd_ctl_elem_write(ctl_handle, ctl)) < 0) {
fprintf(stderr, "Unable to update the IEC958 control: %s\n", snd_strerror(err));
goto __diga_end;
}
snd_ctl_close(ctl_handle);
__diga_end:
}
return 0;
__close:
@ -134,15 +186,20 @@ int output_open(int bits, int rate, int channels)
/*
* play the sample to the already opened file descriptor
*/
void output_play(sint_16* output_samples, uint_32 num_frames)
int output_play(sint_16* output_samples, uint_32 num_frames)
{
snd_pcm_sframes_t res;
snd_pcm_sframes_t res = 0;
res = snd_pcm_writei(pcm, (void *)output_samples, num_frames);
do {
if (res == -EPIPE)
res = snd_pcm_prepare(pcm);
res = res < 0 ? res : snd_pcm_writei(pcm, (void *)output_samples, num_frames);
} while (res == -EPIPE);
if (res < 0)
fprintf(stderr, "writei returned error: %s\n", snd_strerror(res));
else if (res != num_frames)
fprintf(stderr, "writei retured %li (expected %li)\n", res, (long)(num_frames));
return res < 0 ? (int)res : 0;
}

View file

@ -24,6 +24,15 @@
*
*/
int output_open(int bits, int rate, int channels);
void output_play(sint_16* output_samples, uint_32 num_bytes);
typedef struct {
const char *pcm_name;
int bits;
int rate;
int channels;
int spdif: 1;
int quiet: 1;
} output_t;
int output_open(output_t *output);
int output_play(sint_16* output_samples, uint_32 num_bytes);
void output_close(void);