From 97a55787e6a368e4958623f6dc5b1dd7212d96c3 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Sun, 22 Apr 2001 12:04:00 +0000 Subject: [PATCH] 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 --- ac3dec/Makefile.am | 2 +- ac3dec/ac3dec.c | 169 +++++++++++++++++++++++++++-------- ac3dec/ac3spdif.c | 137 ++++++++++++++++++++++++++++ ac3dec/libac3/ac3.h | 3 +- ac3dec/libac3/ac3_internal.h | 2 +- ac3dec/libac3/bitstream.c | 4 +- ac3dec/libac3/bitstream.h | 2 +- ac3dec/libac3/decode.c | 2 +- ac3dec/libac3/parse.c | 77 +++++++++------- ac3dec/libac3/parse.h | 1 + ac3dec/libac3/stats.c | 2 +- ac3dec/output.c | 129 ++++++++++++++++++-------- ac3dec/output.h | 13 ++- 13 files changed, 424 insertions(+), 119 deletions(-) create mode 100644 ac3dec/ac3spdif.c diff --git a/ac3dec/Makefile.am b/ac3dec/Makefile.am index f215ef4..a3d1560 100644 --- a/ac3dec/Makefile.am +++ b/ac3dec/Makefile.am @@ -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 diff --git a/ac3dec/ac3dec.c b/ac3dec/ac3dec.c index 81111b2..15f7ec3 100644 --- a/ac3dec/ac3dec.c +++ b/ac3dec/ac3dec.c @@ -26,17 +26,39 @@ #include #include #include +#include #include #include +#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 [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; + *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; - ac3_init(&ac3_config); - - ac3_frame = ac3_decode_frame(); - output_open(16,ac3_frame->sampling_rate,channels); + morehelp = 0; + while (1) { + int c; - do - { - //Send the samples to the output device - output_play(ac3_frame->audio_data, 256 * 6); + 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((ac3_frame = ac3_decode_frame())); - output_close(); - fclose(in_file); - return 0; + 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(); + 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; + } + output_close(); + fclose(in_file); + } + return EXIT_SUCCESS; } diff --git a/ac3dec/ac3spdif.c b/ac3dec/ac3spdif.c new file mode 100644 index 0000000..04fd579 --- /dev/null +++ b/ac3dec/ac3spdif.c @@ -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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#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; +} diff --git a/ac3dec/libac3/ac3.h b/ac3dec/libac3/ac3.h index 30d7116..d0ba1b7 100644 --- a/ac3dec/libac3/ac3.h +++ b/ac3dec/libac3/ac3.h @@ -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 diff --git a/ac3dec/libac3/ac3_internal.h b/ac3dec/libac3/ac3_internal.h index 8de184a..7a20c5b 100644 --- a/ac3dec/libac3/ac3_internal.h +++ b/ac3dec/libac3/ac3_internal.h @@ -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 */ diff --git a/ac3dec/libac3/bitstream.c b/ac3dec/libac3/bitstream.c index fdaeaf7..4d0b3ce 100644 --- a/ac3dec/libac3/bitstream.c +++ b/ac3dec/libac3/bitstream.c @@ -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; diff --git a/ac3dec/libac3/bitstream.h b/ac3dec/libac3/bitstream.h index b2a148c..7602d8a 100644 --- a/ac3dec/libac3/bitstream.h +++ b/ac3dec/libac3/bitstream.h @@ -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); diff --git a/ac3dec/libac3/decode.c b/ac3dec/libac3/decode.c index f0d90d2..2b09e56 100644 --- a/ac3dec/libac3/decode.c +++ b/ac3dec/libac3/decode.c @@ -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; diff --git a/ac3dec/libac3/parse.c b/ac3dec/libac3/parse.c index 86e031c..0b16ccb 100644 --- a/ac3dec/libac3/parse.c +++ b/ac3dec/libac3/parse.c @@ -85,36 +85,11 @@ static const struct frmsize_s frmsizecod_tbl[64] = { 640 ,{1280 ,1394 ,1920 } } }; -/* Parse a syncinfo structure, minus the sync word */ void -parse_syncinfo(syncinfo_t *syncinfo) +parse_syncinfo_data(syncinfo_t *syncinfo, uint_8 *data) { - uint_32 tmp = 0; - 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. - // - while(time_out--) - { - sync_word = (sync_word << 8) + bitstream_get_byte(); - - if(sync_word == 0x0b77) - break; - } - - // - // 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; + syncinfo->fscod = (data[2] >> 6) & 0x3; if(syncinfo->fscod == 3) { @@ -130,13 +105,44 @@ parse_syncinfo(syncinfo_t *syncinfo) syncinfo->sampling_rate = 48000; // Get the frame size code - syncinfo->frmsizecod = tmp & 0x3f; + 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_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. + // + while(time_out--) + { + sync_word = (sync_word << 8) + bitstream_get_byte(); + + if(sync_word == 0x0b77) + break; + } + + // + // We need to read in the entire syncinfo struct (0x0b77 + 24 bits) + // in order to determine how big the frame is + // + 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,7 +162,8 @@ parse_syncinfo(syncinfo_t *syncinfo) return; } - stats_print_syncinfo(syncinfo); + if (!(ac3_config.flags & AC3_QUIET)) + stats_print_syncinfo(syncinfo); } /* @@ -290,7 +297,8 @@ parse_bsi(bsi_t *bsi) bsi->addbsi[i] = bitstream_get(8); } - stats_print_bsi(bsi); + if (!(ac3_config.flags & AC3_QUIET)) + stats_print_bsi(bsi); } /* More pain inducing parsing */ @@ -598,7 +606,8 @@ parse_audblk(bsi_t *bsi,audblk_t *audblk) } } - stats_print_audblk(bsi,audblk); + if (!(ac3_config.flags & AC3_QUIET)) + stats_print_audblk(bsi,audblk); } void diff --git a/ac3dec/libac3/parse.h b/ac3dec/libac3/parse.h index 72aa32c..4449326 100644 --- a/ac3dec/libac3/parse.h +++ b/ac3dec/libac3/parse.h @@ -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); diff --git a/ac3dec/libac3/stats.c b/ac3dec/libac3/stats.c index 2ac88e0..d8a8417 100644 --- a/ac3dec/libac3/stats.c +++ b/ac3dec/libac3/stats.c @@ -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); diff --git a/ac3dec/output.c b/ac3dec/output.c index e48c762..c905582 100644 --- a/ac3dec/output.c +++ b/ac3dec/output.c @@ -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,33 +46,36 @@ int output_open(int bits, int rate, int channels) snd_pcm_hw_params_alloca(¶ms); snd_pcm_sw_params_alloca(&swparams); - pcm_channels = channels; + out_config = *output; /* * Open the device driver */ - card = snd_defaults_pcm_card(); - dev = snd_defaults_pcm_device(); - if (card < 0 || dev < 0) { - fprintf(stderr, "defaults are not set\n"); - return -ENODEV; + 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 (output->channels) { + case 1: + case 2: + sprintf(devstr, "plug:%d,%d", card, dev); + break; + case 4: + sprintf(devstr, "surround40:%d,%d", card, dev); + break; + case 6: + sprintf(devstr, "surround51:%d,%d", card, dev); + break; + default: + fprintf(stderr, "%d channels are not supported\n", output->channels); + return -EINVAL; + } + pcm_name = devstr; } - switch (channels) { - case 1: - case 2: - sprintf(devstr, "plug:%d,%d", card, dev); - break; - case 4: - sprintf(devstr, "surround40:%d,%d", card, dev); - break; - case 6: - sprintf(devstr, "surround51:%d,%d", card, dev); - break; - default: - fprintf(stderr, "%d channels are not supported\n", channels); - return -EINVAL; - } - if ((err = snd_pcm_open(&pcm, devstr, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { + 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 = snd_pcm_hw_params_set_period_time_near(pcm, params, - 100000, 0); - if (period_time < 0) { - fprintf(stderr, "Period 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, + 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; } diff --git a/ac3dec/output.h b/ac3dec/output.h index cb5a6aa..1600ed2 100644 --- a/ac3dec/output.h +++ b/ac3dec/output.h @@ -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); -- 2.47.1