From 9d3d1339845435d5d7bb44b858ea6b3a94ce2cb9 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sat, 15 Mar 2008 19:03:18 +0100 Subject: [PATCH] Implement streaming mode for para_afh. --- README | 10 +++-- afh.c | 129 ++++++++++++++++++++++++++++++++++++++++++++------------ afh.ggo | 112 ++++++++++++++++++++++++++++++++++++++++++++++-- error.h | 1 + 4 files changed, 217 insertions(+), 35 deletions(-) diff --git a/README b/README index 0e1e5cb4..3308d0b3 100644 --- a/README +++ b/README @@ -102,10 +102,12 @@ para_afh A small stand-alone program that prints tech info about the given audio file to stdout. It can be instructed to print a "chunk table", -an array of offsets within the audio file. This allows third-party -streaming software that is unaware of the particular audio format -to send complete frames in real time. Currently, mp3, ogg vorbis and -aac are supported. +an array of offsets within the audio file or to write the content of +the audio file in complete chunks 'just in time'. + +This allows third-party streaming software that is unaware of +the particular audio format to send complete frames in real +time. Currently, mp3, ogg vorbis and aac are supported. ---------- para_write diff --git a/afh.c b/afh.c index 84a061b9..2ff2c786 100644 --- a/afh.c +++ b/afh.c @@ -7,6 +7,7 @@ /** \file afh.c Paraslash's standalone audio format handler tool. */ #include +#include #include "para.h" #include "string.h" @@ -22,9 +23,100 @@ const char *status_item_list[] = {STATUS_ITEM_ARRAY}; INIT_AFH_ERRLISTS; INIT_STDERR_LOGGING(conf.loglevel_arg) +static void print_info(int audio_format_num, struct afh_info *afhi) +{ + printf("%s: %dkbit/s\n" /* bitrate */ + "%s: %s\n" /* format */ + "%s: %dHz\n" /* frequency */ + "%s: %d\n" /* channels */ + "%s: %lu\n" /* seconds total */ + "%s" /* tag info */ + "%s: %lu: %lu\n" /* chunk time */ + "%s: %lu\n", /* num chunks */ + status_item_list[SI_BITRATE], afhi->bitrate, + status_item_list[SI_FORMAT], audio_format_name(audio_format_num), + status_item_list[SI_FREQUENCY], afhi->frequency, + status_item_list[SI_CHANNELS], afhi->channels, + status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total, + afhi->info_string, + status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec, + (long unsigned)afhi->chunk_tv.tv_usec, + status_item_list[SI_NUM_CHUNKS], afhi->chunks_total + ); +} + +static void print_chunk_table(struct afh_info *afhi) +{ + int i; + + printf("chunk_table: "); + for (i = 0; i <= afhi->chunks_total; i++) + printf("%u ", afhi->chunk_table[i]); + printf("\n"); +} + +static int cat_file(void *audio_file_data, struct afh_info *afhi) +{ + int ret; + struct timeval stream_start; + long unsigned i, first_chunk, last_chunk; + char *buf; + size_t size; + + + if (conf.begin_chunk_arg < 0) + return -ERRNO_TO_PARA_ERROR(EINVAL); + first_chunk = conf.begin_chunk_arg; + + if (conf.end_chunk_given) { + if (conf.end_chunk_arg < 0) + return -ERRNO_TO_PARA_ERROR(EINVAL); + if (conf.end_chunk_arg >= afhi->chunks_total) + return -ERRNO_TO_PARA_ERROR(EINVAL); + last_chunk = conf.end_chunk_arg; + } else + last_chunk = afhi->chunks_total - 1; + if (!afhi->chunks_total) + return 1; + afh_get_header(afhi, audio_file_data, &buf, &size); + if (size && first_chunk && !conf.no_header_given) { + PARA_INFO_LOG("writing audio file header (%zu bytes)\n", size); + ret = write(STDOUT_FILENO, buf, size); + if (ret < 0) + return ret; + if (ret != size) + return -E_AFH_SHORT_WRITE; + } + PARA_NOTICE_LOG("writing chunks %lu - %lu\n", first_chunk, last_chunk); + gettimeofday(&stream_start, NULL); + for (i = first_chunk; i <= last_chunk; i++) { + struct timeval now, diff, next_chunk; + afh_get_chunk(i, afhi, audio_file_data, &buf, &size); + PARA_DEBUG_LOG("chunk %lu: size %zu\n", i, size); + if (conf.just_in_time_given) { + compute_chunk_time(i - first_chunk, &afhi->chunk_tv, + &stream_start, &next_chunk); + gettimeofday(&now, NULL); + ret = tv_diff(&next_chunk, &now, &diff); + if (ret > 0) { + ret = para_select(1, NULL, NULL, &diff); + if (ret < 0) + return ret; + } + } + PARA_INFO_LOG("writing chunk %lu\n", i); + ret = write(STDOUT_FILENO, buf, size); + if (ret < 0) + return ret; + if (ret != size) + return -E_AFH_SHORT_WRITE; + } + return 1; +} + int main(int argc, char **argv) { - int i, ret; + int ret, audio_format_num; void *audio_file_data; size_t audio_file_size; struct afh_info afhi; @@ -42,32 +134,15 @@ int main(int argc, char **argv) ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size, &afhi); if (ret < 0) goto out; - printf("%s: %dkbit/s\n" /* bitrate */ - "%s: %s\n" /* format */ - "%s: %dHz\n" /* frequency */ - "%s: %d\n" /* channels */ - "%s: %lu\n" /* seconds total */ - "%s" /* tag info */ - "%s: %lu: %lu\n" /* chunk time */ - "%s: %lu\n", /* num chunks */ - status_item_list[SI_BITRATE], afhi.bitrate, - status_item_list[SI_FORMAT], audio_format_name(ret), - status_item_list[SI_FREQUENCY], afhi.frequency, - status_item_list[SI_CHANNELS], afhi.channels, - status_item_list[SI_SECONDS_TOTAL], afhi.seconds_total, - afhi.info_string, - status_item_list[SI_CHUNK_TIME], (long unsigned)afhi.chunk_tv.tv_sec, - (long unsigned)afhi.chunk_tv.tv_usec, - status_item_list[SI_NUM_CHUNKS], afhi.chunks_total - ); - ret = 1; - if (!conf.chunk_table_given) - goto out; - printf("chunk_table: "); - for (i = 0; i <= afhi.chunks_total; i++) - printf("%u ", afhi.chunk_table[i]); - printf("\n"); - ret = 1; + audio_format_num = ret; + if (conf.stream_given) + ret = cat_file(audio_file_data, &afhi); + else { + print_info(audio_format_num, &afhi); + if (conf.chunk_table_given) + print_chunk_table(&afhi); + ret = 1; + } out: if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); diff --git a/afh.ggo b/afh.ggo index e013cc36..7fb9d5ac 100644 --- a/afh.ggo +++ b/afh.ggo @@ -1,12 +1,116 @@ +text " +para_afh, the audio format handler tool, is a stand-alone program +contained in the paraslash package for analyzing and streaming audio +files. It can be used to + + - print tech info about the given audio file to stdout. + In particular, the 'chunk table' of the audio file, an array + of offsets within the audio file, may be printed. This table + can be used by other programs unaware of the particular audio + format to stream the audio file. + + - write selected parts of the given audio file in complete + chunks without decoding. Thus para_afh can be used to 'cut' + an audio file. + + - write selected parts of the given audio files 'just in time' + to sdout. This may be useful for third-party software that + is capable of reading from stdin. +" + option "loglevel" l #~~~~~~~~~~~~~~~~~~ "set loglevel (0-6)" +int typestr="level" +default="4" +optional + +defgroup "mode" +#-------------- +groupdesc=" + There are two modes of operation: Info mode and stream mode, + one of which must be selected by the corresponding option. + See below. +" +required + +groupoption "info" i +#~~~~~~~~~~~~~~~~~~~ +"select info mode" +group="mode" +details=" + In this mode, the program prints technical information about + the given audio file to stdout. +" - int typestr="level" - default="4" - optional +groupoption "stream" s +#~~~~~~~~~~~~~~~~~~~~~ +"select stream mode" +group="mode" +details=" + If this mode is selected, the selected parts of the content + of the audio file are written to stdout. Only complete chunks + with respect of the underlying audio format are written. + For example, only complete frames in case of mp3 files. +" + +section "Options for info mode" +#============================== option "chunk_table" c +#~~~~~~~~~~~~~~~~~~~~~ "print also the chunk table" +flag off +dependon="info" + +section "Options for stream mode" +#================================ + + +option "begin_chunk" b +#~~~~~~~~~~~~~~~~~~~~~ +"skip a number of chunks" +int typestr="chunk_num" +default="0" +dependon="stream" +optional +details=" + The chunk_num argument must be non-negative as chunks start + at zero. +" + +option "end_chunk" e +#~~~~~~~~~~~~~~~~~~~ +"only write up to chunk chunk_num" +int typestr="chunk_num" +dependon="stream" +optional +details=" + The chunk_num argument must be less than the total number of + chunks. The default is to write up to the last chunk. +" + +option "just_in_time" j +#~~~~~~~~~~~~~~~~~~~~~~ +"use timed writes" +flag off +dependon="stream" +details=" + Write the specified chunks of data 'just in time', i.e. the + write of each chunk is delayed until the time it is needed + by the decoder/player in order to guarantee an uninterupted + audio stream. +" - flag off +option "no_header" H +#~~~~~~~~~~~~~~~~~~~ +"do not write an audio file header" +flag off +dependon="stream" +details=" + If an audio format needs information about the audio file + in a format-specific header in order to be understood by + the decoding software, a suitable header is automatically + send. This option changes the default behaviour so that no + header is sent. +" diff --git a/error.h b/error.h index 415262d0..df2a240c 100644 --- a/error.h +++ b/error.h @@ -32,6 +32,7 @@ extern const char **para_errlist[]; #define AFH_ERRORS \ PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \ + PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \ #define AFH_COMMON_ERRORS \ -- 2.39.5