#include #include #include #include #include /* Some things I use for debugging */ #ifdef NODUMP # define DUMPf(fmt, args...) #else # define DUMPf(fmt, args...) fprintf(stderr, "%s:%s:%d " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##args) #endif #define DUMP() DUMPf("") #define DUMP_d(v) DUMPf("%s = %d", #v, v) #define DUMP_x(v) DUMPf("%s = 0x%x", #v, v) #define DUMP_s(v) DUMPf("%s = %s", #v, v) #define DUMP_c(v) DUMPf("%s = '%c' (0x%02x)", #v, v, v) #define DUMP_p(v) DUMPf("%s = %p", #v, v) uint32_t read_callback(void *user_data, void *buffer, uint32_t length) { return fread(buffer, 1, length, (FILE*)user_data); } uint32_t seek_callback(void *user_data, uint64_t position) { return fseek((FILE*)user_data, position, SEEK_SET); } static int GetAACTrack(mp4ff_t *infile) { /* find AAC track */ int i, rc; int numTracks = mp4ff_total_tracks(infile); for (i = 0; i < numTracks; i++) { unsigned char *buff = NULL; int buff_size = 0; mp4AudioSpecificConfig mp4ASC; mp4ff_get_decoder_config(infile, i, &buff, &buff_size); if (buff) { rc = NeAACDecAudioSpecificConfig(buff, buff_size, &mp4ASC); free(buff); if (rc < 0) continue; return i; } } /* can't decode this */ return -1; } int play_file(snd_pcm_t *snd, FILE *f) { int track; mp4ff_t *infile; mp4ff_callback_t mp4cb; NeAACDecHandle hDecoder; NeAACDecConfigurationPtr config; mp4AudioSpecificConfig mp4ASC; unsigned char *buffer; int buffer_size; unsigned long samplerate; unsigned char channels; long sampleId, numSamples; void *sample_buffer; mp4cb.read = read_callback; mp4cb.seek = seek_callback; mp4cb.user_data = f; infile = mp4ff_open_read(&mp4cb); if (! infile) { fprintf(stderr, "Unable to open stream\n"); return 1; } if ((track = GetAACTrack(infile)) < 0) { fprintf(stderr, "GetAACTrack\n"); return 1; } hDecoder = NeAACDecOpen(); config = NeAACDecGetCurrentConfiguration(hDecoder); config->outputFormat = FAAD_FMT_16BIT; NeAACDecSetConfiguration(hDecoder, config); buffer = NULL; buffer_size = 0; mp4ff_get_decoder_config(infile, track, &buffer, &buffer_size); if (NeAACDecInit2(hDecoder, buffer, buffer_size, &samplerate, &channels) < 0) { fprintf(stderr, "Initializing decoder\n"); return 1; } if (snd_pcm_set_params(snd, SND_PCM_FORMAT_S16, SND_PCM_ACCESS_RW_INTERLEAVED, channels, samplerate, 1, 500000) < 0) { fprintf(stderr, "Set ALSA params\n"); return 1; } if (buffer) { free(buffer); } numSamples = mp4ff_num_samples(infile, track); for (sampleId = 0; sampleId < numSamples; sampleId++) { int rc; long dur; unsigned int sample_count; unsigned int delay = 0; NeAACDecFrameInfo frameInfo; buffer = NULL; buffer_size = 0; dur = mp4ff_get_sample_duration(infile, track, sampleId); rc = mp4ff_read_sample(infile, track, sampleId, &buffer, &buffer_size); if (rc == 0) { fprintf(stderr, "Read failed\n"); return 1; } sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, buffer, buffer_size); sample_count = frameInfo.samples; snd_pcm_writei(snd, sample_buffer, sample_count / channels); } return 0; } int main(int argc, char *argv[]) { int i; snd_pcm_t *snd; if (snd_pcm_open(&snd, "default", SND_PCM_STREAM_PLAYBACK, 0) < 0) { fprintf(stderr, "Opening ALSA\n"); return 1; } if (argc == 1) { return play_file(snd, stdin); } for (i = 1; i < argc; i += 1) { char *fn = argv[i]; FILE *f = fopen(fn, "r"); if ((fn[0] == '-') && (fn[1] == 0)) { f = stdin; fn = "[stdin]"; } if (! f) { fprintf(stderr, "Opening %s: %m\n", fn); continue; } printf("%s\n", fn); play_file(snd, f); fclose(f); } return 0; }