Mercurial > audlegacy-plugins
view src/madplug/decoder.c @ 2494:862477de235b
Automated merge with ssh://hg.atheme.org//hg/audacious-plugins
author | William Pitcock <nenolod@atheme.org> |
---|---|
date | Mon, 31 Mar 2008 04:45:30 -0500 |
parents | b419d8924c0b |
children | bad05467e8e0 |
line wrap: on
line source
/* * mad plugin for audacious * Copyright (C) 2005-2007 William Pitcock, Yoshiki Yazawa * * Portions derived from xmms-mad: * Copyright (C) 2001-2002 Sam Clegg - See COPYING * * This program 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; under version 2 of the License. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* #define AUD_DEBUG 1 */ #include <math.h> #include <assert.h> #include <audacious/plugin.h> #include "plugin.h" #include "input.h" #define BUFFER_SIZE 16*1024 #define N_AVERAGE_FRAMES 10 void write_output(struct mad_info_t *info, struct mad_pcm *pcm, struct mad_header *header) { unsigned int nsamples; mad_fixed_t const *left_ch, *right_ch; mad_fixed_t *output; int outlen = 0; int outbyte = 0; int pos = 0; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; outlen = nsamples * MAD_NCHANNELS(header); outbyte = outlen * sizeof(mad_fixed_t); output = (mad_fixed_t *) g_malloc(outbyte); while (nsamples--) { output[pos++] = *left_ch++; if (MAD_NCHANNELS(header) == 2) { output[pos++] = *right_ch++; } } assert(pos == outlen); if (!info->playback->playing) { g_free(output); return; } info->playback->pass_audio(info->playback, info->fmt, MAD_NCHANNELS(header), outbyte, output, &(info->playback->playing)); g_free(output); } /** * Decode all headers in the file and fill in stats * @return FALSE if scan failed. */ gboolean scan_file(struct mad_info_t * info, gboolean fast) { struct mad_stream stream; struct mad_header header; int remainder = 0; int data_used = 0; int len = 0; int tagsize = 0; unsigned char buffer[BUFFER_SIZE]; struct mad_frame frame; /* to read xing data */ gboolean has_xing = FALSE; guint bitrate_frames = 0; double xing_bitrate = 0.0; double accum_bitrate = 0.0; mad_stream_init(&stream); mad_stream_options(&stream, 0); // check CRC mad_header_init(&header); mad_frame_init(&frame); xing_init(&info->xing); info->bitrate = 0; info->pos = mad_timer_zero; info->duration = mad_timer_zero; // should be cleared before loop, if we use it as break condition. if(info->fileinfo_request == TRUE) { aud_tuple_associate_int(info->tuple, FIELD_LENGTH, NULL, -1); info->fileinfo_request = FALSE; } AUDDBG("f: scan_file\n"); AUDDBG("scan_file frames = %d\n", info->frames); while (1) { remainder = stream.bufend - stream.next_frame; if(buffer != stream.this_frame && remainder) memmove(buffer, stream.this_frame, remainder); len = input_get_data(info, buffer + remainder, BUFFER_SIZE - remainder); if (len <= 0) { AUDDBG("scan_file: len <= 0 len = %d\n", len); break; } mad_stream_buffer(&stream, buffer, remainder + len); while (!fast || (fast && info->frames < N_AVERAGE_FRAMES)) { if (mad_header_decode(&header, &stream) == -1) { if (stream.error == MAD_ERROR_BUFLEN) { break; } if (!MAD_RECOVERABLE(stream.error)) { AUDDBG("(fatal) error decoding header %d: %s\n", info->frames, mad_stream_errorstr(&stream)); AUDDBG("remainder = %d\n", remainder); AUDDBG("len = %d\n", len); break; } if (stream.error == MAD_ERROR_LOSTSYNC) { /* ignore LOSTSYNC due to ID3 tags */ tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { AUDDBG("skipping id3_tag: %d\n", tagsize); mad_stream_skip(&stream, tagsize); continue; } } AUDDBG("(recovered) error decoding header %d: %s\n", info->frames, mad_stream_errorstr(&stream)); AUDDBG("remainder = %d\n", remainder); AUDDBG("len = %d\n", len); continue; } info->frames++; #ifdef DEBUG_INTENSIVELY AUDDBG("header bitrate = %ld\n", header.bitrate); AUDDBG("duration = %ul\n", mad_timer_count(header.duration, MAD_UNITS_MILLISECONDS)); AUDDBG("size = %d\n", stream.next_frame - stream.this_frame); #endif if(aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL) == -1) mad_timer_add(&info->duration, header.duration); else { gint length = aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL); info->duration.seconds = length / 1000; info->duration.fraction = length % 1000; } data_used += stream.next_frame - stream.this_frame; if (info->frames == 1) { /* most of these *should* remain constant */ info->bitrate = header.bitrate; info->freq = header.samplerate; info->channels = MAD_NCHANNELS(&header); info->mpeg_layer = header.layer; info->mode = header.mode; if (audmad_config->use_xing) { frame.header = header; if (mad_frame_decode(&frame, &stream) == -1) { AUDDBG("xing frame decode failed\n"); goto no_xing; } if (xing_parse(&info->xing, stream.anc_ptr, stream.anc_bitlen) == 0) { AUDDBG("xing header found\n"); has_xing = TRUE; info->vbr = TRUE; /* otherwise xing header would have been 'Info' */ AUDDBG("xing: bytes = %ld frames = %ld\n", info->xing.bytes, info->xing.frames); /* we have enough info to calculate bitrate and duration */ if(info->xing.bytes && info->xing.frames) { xing_bitrate = 8 * (double)info->xing.bytes * 38 / (double)info->xing.frames; //38fps in MPEG1. #ifdef AUD_DEBUG { gint tmp = (gint)(info->xing.bytes * 8 / xing_bitrate); AUDDBG("xing: bitrate = %4.1f kbps\n", xing_bitrate / 1000); AUDDBG("xing: duration = %d:%02d\n", tmp / 60, tmp % 60); } #endif } continue; } #ifdef AUD_DEBUG else { AUDDBG("no usable xing header\n"); continue; } #endif } /* xing */ } else { /* perhaps we have a VBR file */ if (info->bitrate != header.bitrate) info->vbr = TRUE; if (info->vbr) { accum_bitrate += (double)header.bitrate; bitrate_frames++; } /* check for changin layer/samplerate/channels */ if (info->mpeg_layer != header.layer) g_warning("layer varies!!"); if (info->freq != header.samplerate) g_warning("samplerate varies!!"); if (info->channels != MAD_NCHANNELS(&header)) g_warning("number of channels varies!!"); } no_xing: if (fast && info->frames >= N_AVERAGE_FRAMES) { float frame_size = ((double) data_used) / N_AVERAGE_FRAMES; AUDDBG("bitrate = %ld samplerate = %d\n", header.bitrate, header.samplerate); AUDDBG("data_used = %d info->frames = %d info->size = %d tagsize = %d frame_size = %lf\n", data_used, info->frames, info->size, tagsize, frame_size); if(info->size != 0) info->frames = (info->size - tagsize) / frame_size; AUDDBG("info->frames = %d\n", info->frames); if(aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL) == -1) { if(xing_bitrate > 0.0) { /* calc duration with xing info */ double tmp = 8 * (double)info->xing.bytes * 1000 / xing_bitrate; info->duration.seconds = (guint)(tmp / 1000); info->duration.fraction = (guint)(tmp - info->duration.seconds * 1000); } else { info->duration.seconds /= N_AVERAGE_FRAMES; info->duration.fraction /= N_AVERAGE_FRAMES; mad_timer_multiply(&info->duration, info->frames); } } else { gint length = aud_tuple_get_int(info->tuple, FIELD_LENGTH, NULL); info->duration.seconds = length / 1000; info->duration.fraction = length % 1000; } #ifdef AUD_DEBUG AUDDBG("using fast playtime calculation\n"); AUDDBG("data used = %d [tagsize=%d framesize=%f]\n", data_used, tagsize, frame_size); AUDDBG("frames = %d, frequency = %d, channels = %d\n", info->frames, info->freq, info->channels); long millis = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); AUDDBG("duration = %ld:%02ld\n", millis / 1000 / 60, (millis / 1000) % 60); #endif /* DEBUG */ break; } } /* while */ if (stream.error != MAD_ERROR_BUFLEN) break; } if (info->xing.frames) info->frames = info->xing.frames; if (info->vbr && xing_bitrate != 0) { info->bitrate = (guint)xing_bitrate; } else if (info->vbr && xing_bitrate == 0 && bitrate_frames != 0) { info->bitrate = accum_bitrate / bitrate_frames; } aud_tuple_associate_int(info->tuple, FIELD_BITRATE, NULL, info->bitrate / 1000); mad_frame_finish(&frame); mad_header_finish(&header); mad_stream_finish(&stream); xing_finish(&info->xing); AUDDBG("scan_file: info->frames = %d\n", info->frames); AUDDBG("e: scan_file\n"); return (info->frames != 0 || info->remote == TRUE); } /* sanity check for audio open parameters */ static gboolean check_audio_param(struct mad_info_t *info) { if(info->fmt != FMT_FIXED32 && (info->fmt < FMT_U8 || info->fmt > FMT_S16_NE)) return FALSE; if(info->freq < 0) // not sure about maximum frequency. --yaz return FALSE; if(info->channels < 1 || info->channels > 2) return FALSE; return TRUE; } gpointer decode_loop(gpointer arg) { unsigned char buffer[BUFFER_SIZE]; int len; gboolean seek_skip = FALSE; int remainder = 0; gint tlen; unsigned int iteration = 0; /* mad structs */ struct mad_stream stream; struct mad_frame frame; struct mad_synth synth; /* track info is passed in as thread argument */ struct mad_info_t *info = (struct mad_info_t *) arg; AUDDBG("f: decode\n"); /* init mad stuff */ mad_frame_init(&frame); mad_stream_init(&stream); mad_stream_options(&stream, MAD_OPTION_IGNORECRC); mad_synth_init(&synth); if(!info->playback){ AUDDBG("decode: playback == NULL\n"); return NULL; } AUDDBG("decode: fmt = %d freq = %d channels = %d\n", info->fmt, info->freq, info->channels); if(check_audio_param(info) == FALSE) return NULL; if (!info->playback->output->open_audio(info->fmt, info->freq, info->channels)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); g_message("failed to open audio output: %s", info->playback->output->description); return NULL; } /* set mainwin title */ if (info->title) g_free(info->title); info->title = aud_tuple_formatter_make_title_string(info->tuple, audmad_config->title_override == TRUE ? audmad_config->id3_format : aud_get_gentitle_format()); tlen = (gint) mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS), info->playback->set_params(info->playback, info->title, (tlen == 0 || info->size <= 0) ? -1 : tlen, info->bitrate, info->freq, info->channels); AUDDBG("decode: tlen = %d\n", tlen); /* main loop */ do { if (!info->playback->playing) { AUDDBG("decode: stop signaled\n"); break; } if (seek_skip) remainder = 0; else { remainder = stream.bufend - stream.next_frame; if(buffer != stream.this_frame && remainder) memmove(buffer, stream.this_frame, remainder); } len = input_get_data(info, buffer + remainder, BUFFER_SIZE - remainder); input_process_remote_metadata(info); if (len <= 0) { AUDDBG("finished decoding\n"); break; } len += remainder; if (len < MAD_BUFFER_GUARD) { int i; for (i = len; i < MAD_BUFFER_GUARD; i++) buffer[i] = 0; len = MAD_BUFFER_GUARD; } mad_stream_buffer(&stream, buffer, len); if (seek_skip) { AUDDBG("skipping: %d\n", seek_skip); int skip = 2; do { if (mad_frame_decode(&frame, &stream) == 0) { mad_timer_add(&info->pos, frame.header.duration); if (--skip == 0) mad_synth_frame(&synth, &frame); } else if (!MAD_RECOVERABLE(stream.error)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); break; } } while (skip); seek_skip = FALSE; } while (info->playback->playing) { if (info->seek != -1 && info->size > 0) { AUDDBG("seeking: %ld\n", info->seek); int new_position; gulong milliseconds = mad_timer_count(info->duration, MAD_UNITS_MILLISECONDS); if (info->seek >= milliseconds) info->seek = milliseconds; mad_timer_set(&info->pos, 0, info->seek, 1000); // in millisecond new_position = ((double) info->seek / (double) milliseconds) * info->size; if(new_position < 0) new_position = 0; AUDDBG("seeking to: %d bytes\n", new_position); if (aud_vfs_fseek(info->infile, new_position, SEEK_SET) == -1) audmad_error("failed to seek to: %d", new_position); mad_frame_mute(&frame); mad_synth_mute(&synth); stream.error = MAD_ERROR_BUFLEN; info->playback->output->flush(mad_timer_count(info->pos, MAD_UNITS_MILLISECONDS)); stream.sync = 0; info->seek = -1; seek_skip = TRUE; break; } if (mad_header_decode(&frame.header, &stream) == -1) { if (!MAD_RECOVERABLE(stream.error)) { break; } if (stream.error == MAD_ERROR_LOSTSYNC) { /* ignore LOSTSYNC due to ID3 tags */ int tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { mad_stream_skip(&stream, tagsize); continue; } } AUDDBG("(recovered) error decoding header %d: %s\n", info->current_frame, mad_stream_errorstr(&stream)); continue; } info->bitrate = frame.header.bitrate; if (!audmad_config->show_avg_vbr_bitrate && info->vbr && (iteration % 40 == 0)) { #ifdef DEBUG_INTENSIVELY AUDDBG("decode vbr tlen = %d\n", tlen); #endif info->playback->set_params(info->playback, info->title, (tlen == 0 || info->size <= 0) ? -1 : tlen, info->bitrate, info->freq, info->channels); } iteration++; if (mad_frame_decode(&frame, &stream) == -1) { if (!MAD_RECOVERABLE(stream.error)) break; AUDDBG("(recovered) error decoding frame %d: %s\n", info->current_frame, mad_stream_errorstr(&stream)); } info->current_frame++; if (info->freq != frame.header.samplerate || info->channels != (guint) MAD_NCHANNELS(&frame.header)) { AUDDBG("change in audio type detected\n"); AUDDBG("old: frequency = %d, channels = %d\n", info->freq, info->channels); AUDDBG("new: frequency = %d, channels = %d\n", frame.header.samplerate, (guint) MAD_NCHANNELS(&frame.header)); info->freq = frame.header.samplerate; info->channels = MAD_NCHANNELS(&frame.header); if(audmad_config->force_reopen_audio && check_audio_param(info)) { gint current_time = info->playback->output->output_time(); AUDDBG("re-opening audio due to change in audio type\n"); info->playback->output->close_audio(); if (!info->playback->output->open_audio(info->fmt, info->freq, info->channels)) { g_mutex_lock(pb_mutex); info->playback->error = TRUE; info->playback->eof = 1; g_mutex_unlock(pb_mutex); g_message("failed to re-open audio output: %s", info->playback->output->description); return NULL; } // restore time and advance 0.5sec info->seek = current_time + 500; } } if (!info->playback->playing) break; mad_synth_frame(&synth, &frame); mad_stream_sync(&stream); write_output(info, &synth.pcm, &frame.header); mad_timer_add(&info->pos, frame.header.duration); } } while (stream.error == MAD_ERROR_BUFLEN); /* free mad stuff */ mad_frame_finish(&frame); mad_stream_finish(&stream); mad_synth_finish(&synth); if (info->playback->playing) { GTimeVal sleeptime; info->playback->output->buffer_free(); info->playback->output->buffer_free(); while (info->playback->output->buffer_playing()) { AUDDBG("f: buffer_playing=%d\n", info->playback->output->buffer_playing()); g_get_current_time(&sleeptime); g_time_val_add(&sleeptime, 500000); g_mutex_lock(mad_mutex); g_cond_timed_wait(mad_cond, mad_mutex, &sleeptime); g_mutex_unlock(mad_mutex); if (!info->playback->playing) { break; } } } AUDDBG("e: decode\n"); aud_tuple_free(info->tuple); info->tuple = NULL; info->playback->output->close_audio(); g_mutex_lock(mad_mutex); info->playback->playing = 0; g_mutex_unlock(mad_mutex); return NULL; /* dummy */ }