/* Attempts to read an ID3 tag at the current location in stream and * consume it all. Returns -1 if no tag is found. Its up to caller * to recover. */ static int mp3_inputtag(snd_stream_t *stream) { mp3_priv_t *p = (mp3_priv_t *) stream->priv; int rc = -1; size_t remaining; size_t tagsize; /* FIXME: This needs some more work if we are to ever * look at the ID3 frame. This is because the Stream * may not be able to hold the complete ID3 frame. * We should consume the whole frame inside tagtype() * instead of outside of tagframe(). That would support * recovering when Stream contains less then 8-bytes (header) * and also when ID3v2 is bigger then Stream buffer size. * Need to pass in stream so that buffer can be * consumed as well as letting additional data to be * read in. */ remaining = p->Stream.bufend - p->Stream.next_frame; tagsize = mp3_tagsize(p->Stream.this_frame, remaining); if (tagsize != 0) { mad_stream_skip(&p->Stream, tagsize); rc = 0; } /* We know that a valid frame hasn't been found yet * so help libmad out and go back into frame seek mode. * This is true whether an ID3 tag was found or not. */ mad_stream_sync(&p->Stream); return rc; }
static enum mad_flow handle_error(void *data, struct mad_stream *stream, struct mad_frame *frame) { signed long tagsize; switch(stream->error) { case MAD_ERROR_BADDATAPTR: return MAD_FLOW_CONTINUE; case MAD_ERROR_LOSTSYNC: tagsize = id3_tag_query(stream->this_frame,stream->bufend - stream->this_frame); if(tagsize > 0) { mad_stream_skip(stream, tagsize); return MAD_FLOW_CONTINUE; } default: break; } if(stream->error == MAD_ERROR_BADCRC) { mad_frame_mute(frame); return MAD_FLOW_IGNORE; } return MAD_FLOW_CONTINUE; }
// Attempts to read an ID3 tag at the current location in stream and // consume it all. Returns SOX_EOF if no tag is found. Its up to // caller to recover. // Returns true if a tag was found and consumed. // bool MadDecoder::consumeId3Tag() { // FIXME: This needs some more work if we are to ever // look at the ID3 frame. This is because the Stream // may not be able to hold the complete ID3 frame. // We should consume the whole frame inside getId3TagSize() // instead of outside of tagframe(). That would support // recovering when Stream contains less then 8-bytes (header) // and also when ID3v2 is bigger then Stream buffer size. // Need to pass in stream so that buffer can be // consumed as well as letting additional data to be // read in. // bool result = false; size_t leftover = madStream_.bufend - madStream_.next_frame; size_t tagsize = getId3TagSize(madStream_.this_frame, leftover); if (tagsize > 0) { mad_stream_skip(&madStream_, tagsize); result = true; } /* We know that a valid frame hasn't been found yet * so help libmad out and go back into frame seek mode. * This is true whether an ID3 tag was found or not. */ mad_stream_sync(&madStream_); return result; }
int TrackDuration::duration(const QFileInfo &fileinfo) { QString fn = fileinfo.absoluteFilePath(); if (fn.isEmpty()) return 0; QFile file(fn); if (!file.open(QFile::ReadOnly)) return 0; mad_stream infostream; mad_header infoheader; mad_timer_t infotimer; mad_stream_init(&infostream); mad_header_init(&infoheader); mad_timer_reset(&infotimer); qint64 r; qint64 l = 0; unsigned char* buf = new unsigned char[INPUT_BUFFER_SIZE]; while (!file.atEnd()) { if (l < INPUT_BUFFER_SIZE) { r = file.read(reinterpret_cast<char*>(buf) + l, INPUT_BUFFER_SIZE - l); l += r; } mad_stream_buffer(&infostream, buf, l); for (;;) { if (mad_header_decode(&infoheader, &infostream)) { if (!MAD_RECOVERABLE(infostream.error)) break; if (infostream.error == MAD_ERROR_LOSTSYNC) { TagLib::ID3v2::Header header; uint size = (uint)(infostream.bufend - infostream.this_frame); if (size >= header.size()) { header.setData(TagLib::ByteVector(reinterpret_cast<const char*>(infostream.this_frame), size)); uint tagsize = header.tagSize(); if (tagsize > 0) { mad_stream_skip(&infostream, qMin(tagsize, size)); continue; } } } qDebug() << "header decode error while getting file info" << infostream.error; continue; } mad_timer_add(&infotimer, infoheader.duration); } if (infostream.error != MAD_ERROR_BUFLEN && infostream.error != MAD_ERROR_BUFPTR) break; memmove(buf, infostream.next_frame, &(buf[l]) - infostream.next_frame); l -= (infostream.next_frame - buf); } mad_stream_finish(&infostream); mad_header_finish(&infoheader); delete[] buf; return timerToMs(&infotimer); }
static enum mp3_action decode_next_frame_header(struct mp3_data *data, G_GNUC_UNUSED struct tag **tag) { enum mad_layer layer; if ((data->stream).buffer == NULL || (data->stream).error == MAD_ERROR_BUFLEN) { if (!mp3_fill_buffer(data)) return DECODE_BREAK; } if (mad_header_decode(&data->frame.header, &data->stream)) { if ((data->stream).error == MAD_ERROR_LOSTSYNC && (data->stream).this_frame) { signed long tagsize = id3_tag_query((data->stream). this_frame, (data->stream). bufend - (data->stream). this_frame); if (tagsize > 0) { if (tag && !(*tag)) { mp3_parse_id3(data, (size_t)tagsize, tag); } else { mad_stream_skip(&(data->stream), tagsize); } return DECODE_CONT; } } if (MAD_RECOVERABLE((data->stream).error)) { return DECODE_SKIP; } else { if ((data->stream).error == MAD_ERROR_BUFLEN) return DECODE_CONT; else { g_warning("unrecoverable frame level error " "(%s).\n", mad_stream_errorstr(&data->stream)); return DECODE_BREAK; } } } layer = data->frame.header.layer; if (!data->layer) { if (layer != MAD_LAYER_II && layer != MAD_LAYER_III) { /* Only layer 2 and 3 have been tested to work */ return DECODE_SKIP; } data->layer = layer; } else if (layer != data->layer) { /* Don't decode frames with a different layer than the first */ return DECODE_SKIP; } return DECODE_OK; }
int LibMadWrapper::findValidHeader(struct mad_header &header) { int ret; while ((ret = mad_header_decode(&header, this->stream)) != 0 && MAD_RECOVERABLE(this->stream->error)) { if (this->stream->error == MAD_ERROR_LOSTSYNC) { long tagsize = id3_tag_query(this->stream->this_frame, this->stream->bufend - this->stream->this_frame); if (tagsize > 0) { mad_stream_skip(this->stream, tagsize); continue; } } } return ret; }
static enum mp3_action decodeNextFrame(struct mp3_data *data) { if ((data->stream).buffer == NULL || (data->stream).error == MAD_ERROR_BUFLEN) { if (!mp3_fill_buffer(data)) return DECODE_BREAK; } if (mad_frame_decode(&data->frame, &data->stream)) { #ifdef HAVE_ID3TAG if ((data->stream).error == MAD_ERROR_LOSTSYNC) { signed long tagsize = id3_tag_query((data->stream). this_frame, (data->stream). bufend - (data->stream). this_frame); if (tagsize > 0) { mad_stream_skip(&(data->stream), tagsize); return DECODE_CONT; } } #endif if (MAD_RECOVERABLE((data->stream).error)) { return DECODE_SKIP; } else { if ((data->stream).error == MAD_ERROR_BUFLEN) return DECODE_CONT; else { g_warning("unrecoverable frame level error " "(%s).\n", mad_stream_errorstr(&data->stream)); return DECODE_BREAK; } } } return DECODE_OK; }
/** * MP3音乐播放回调函数, * 负责将解码数据填充声音缓存区 * * @note 声音缓存区的格式为双声道,16位低字序 * * @param buf 声音缓冲区指针 * @param reqn 缓冲区帧大小 * @param pdata 用户数据,无用 */ static int mp3_audiocallback(void *buf, unsigned int reqn, void *pdata) { int avail_frame; int snd_buf_frame_size = (int) reqn; int ret; double incr; signed short *audio_buf = buf; unsigned i; uint16_t *output; UNUSED(pdata); if (g_status != ST_PLAYING) { if (handle_seek() == -1) { __end(); return -1; } xAudioClearSndBuf(buf, snd_buf_frame_size); xrKernelDelayThread(100000); return 0; } while (snd_buf_frame_size > 0) { avail_frame = g_buff_frame_size - g_buff_frame_start; if (avail_frame >= snd_buf_frame_size) { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * 2], snd_buf_frame_size, 2); g_buff_frame_start += snd_buf_frame_size; audio_buf += snd_buf_frame_size * 2; snd_buf_frame_size = 0; } else { send_to_sndbuf(audio_buf, &g_buff[g_buff_frame_start * 2], avail_frame, 2); snd_buf_frame_size -= avail_frame; audio_buf += avail_frame * 2; if (stream.buffer == NULL || stream.error == MAD_ERROR_BUFLEN) { size_t read_size, remaining = 0; uint8_t *read_start; int bufsize; if (stream.next_frame != NULL) { remaining = stream.bufend - stream.next_frame; memmove(g_input_buff, stream.next_frame, remaining); read_start = g_input_buff + remaining; read_size = BUFF_SIZE - remaining; } else { read_size = BUFF_SIZE; read_start = g_input_buff; remaining = 0; } if (mp3_data.use_buffer) bufsize = buffered_reader_read(mp3_data.r, read_start, read_size); else bufsize = xrIoRead(mp3_data.fd, read_start, read_size); if (bufsize <= 0) { __end(); return -1; } if (bufsize < read_size) { uint8_t *guard = read_start + read_size; memset(guard, 0, MAD_BUFFER_GUARD); read_size += MAD_BUFFER_GUARD; } mad_stream_buffer(&stream, g_input_buff, read_size + remaining); stream.error = 0; } ret = mad_frame_decode(&frame, &stream); if (ret == -1) { if (MAD_RECOVERABLE(stream.error) || stream.error == MAD_ERROR_BUFLEN) { if (stream.error == MAD_ERROR_LOSTSYNC) { long tagsize = id3_tag_query(stream.this_frame, stream.bufend - stream.this_frame); if (tagsize > 0) { mad_stream_skip(&stream, tagsize); } if (mad_header_decode(&frame.header, &stream) == -1) { if (stream.error != MAD_ERROR_BUFLEN) { if (!MAD_RECOVERABLE(stream.error)) { __end(); return -1; } } } else { stream.error = MAD_ERROR_NONE; } } g_buff_frame_size = 0; g_buff_frame_start = 0; continue; } else { __end(); return -1; } } output = &g_buff[0]; if (stream.error != MAD_ERROR_NONE) { continue; } mad_synth_frame(&synth, &frame); for (i = 0; i < synth.pcm.length; i++) { signed short sample; if (MAD_NCHANNELS(&frame.header) == 2) { /* Left channel */ sample = MadFixedToSshort(synth.pcm.samples[0][i]); *(output++) = sample; sample = MadFixedToSshort(synth.pcm.samples[1][i]); *(output++) = sample; } else { sample = MadFixedToSshort(synth.pcm.samples[0][i]); *(output++) = sample; *(output++) = sample; } } g_buff_frame_size = synth.pcm.length; g_buff_frame_start = 0; incr = frame.header.duration.seconds; incr += mad_timer_fraction(frame.header.duration, MAD_UNITS_MILLISECONDS) / 1000.0; g_play_time += incr; add_bitrate(&g_inst_br, frame.header.bitrate, incr); } } return 0; }
void LibMadWrapper::render(pcm_t *const bufferToFill, const uint32_t Channels, frame_t framesToRender) { framesToRender = min(framesToRender, this->getFrames() - this->framesAlreadyRendered); int32_t *pcm = static_cast<int32_t *>(bufferToFill); // the outer loop, used for decoding and synthesizing MPEG frames while (framesToRender > 0 && !this->stopFillBuffer) { // write back tempbuffer, i.e. frames weve buffered from previous calls to libmad (necessary due to inelegant API of libmad, i.e. cant tell how many frames to render during one call) { const size_t itemsToCpy = min<size_t>(this->tempBuf.size(), framesToRender * Channels); memcpy(pcm, this->tempBuf.data(), itemsToCpy * sizeof(int32_t)); this->tempBuf.erase(this->tempBuf.begin(), this->tempBuf.begin() + itemsToCpy); const size_t framesCpyd = itemsToCpy / Channels; framesToRender -= framesCpyd; this->framesAlreadyRendered += framesCpyd; // again: adjust position pcm += itemsToCpy; } int framesToDoNow = (framesToRender / gConfig.FramesToRender) > 0 ? gConfig.FramesToRender : framesToRender % gConfig.FramesToRender; if (framesToDoNow == 0) { continue; } int ret = mad_frame_decode(&this->frame.Value, this->stream); if (ret != 0) { if (this->stream->error == MAD_ERROR_LOSTSYNC) { long tagsize = id3_tag_query(this->stream->this_frame, this->stream->bufend - this->stream->this_frame); if (tagsize > 0) { mad_stream_skip(this->stream, tagsize); continue; } } string errstr = mad_stream_errorstr(this->stream); if (MAD_RECOVERABLE(this->stream->error)) { errstr += " (recoverable)"; CLOG(LogLevel_t::Info, errstr); continue; } else { errstr += " (not recoverable)"; CLOG(LogLevel_t::Warning, errstr); break; } } mad_synth_frame(&this->synth.Value, &this->frame.Value); /* save PCM samples from synth.pcm */ /* &synth.pcm->samplerate contains the sampling frequency */ unsigned short nsamples = this->synth->pcm.length; mad_fixed_t const *left_ch = this->synth->pcm.samples[0]; mad_fixed_t const *right_ch = this->synth->pcm.samples[1]; unsigned int item = 0; /* audio normalization */ const float absoluteGain = (numeric_limits<int32_t>::max()) / (numeric_limits<int32_t>::max() * this->gainCorrection); for (; !this->stopFillBuffer && framesToDoNow > 0 && // frames left during this loop framesToRender > 0 && // frames left during this call nsamples > 0; // frames left from libmad framesToRender--, nsamples--, framesToDoNow--) { int32_t sample; /* output sample(s) in 24-bit signed little-endian PCM */ sample = LibMadWrapper::toInt24Sample(*left_ch++); sample = gConfig.useAudioNormalization ? floor(sample * absoluteGain) : sample; pcm[item++] = sample; if (Channels == 2) // our buffer is for 2 channels { if (this->synth.Value.pcm.channels == 2) // ...but did mad also decoded for 2 channels? { sample = LibMadWrapper::toInt24Sample(*right_ch++); sample = gConfig.useAudioNormalization ? floor(sample * absoluteGain) : sample; pcm[item++] = sample; } else { // what? only one channel in a stereo file? well then: pseudo stereo pcm[item++] = sample; CLOG(LogLevel_t::Warning, "decoded only one channel, though this is a stereo file!"); } } this->framesAlreadyRendered++; } pcm += item /* % this->count*/; // "bufferToFill" (i.e. "pcm") seems to be full, drain the rest pcm samples from libmad and temporarily save them while (!this->stopFillBuffer && nsamples > 0) { int32_t sample; /* output sample(s) in 24-bit signed little-endian PCM */ sample = LibMadWrapper::toInt24Sample(*left_ch++); this->tempBuf.push_back(gConfig.useAudioNormalization ? floor(sample * absoluteGain) : sample); if (Channels == 2) { sample = LibMadWrapper::toInt24Sample(*right_ch++); this->tempBuf.push_back(gConfig.useAudioNormalization ? floor(sample * absoluteGain) : sample); } /* DONT do this: this->framesAlreadyRendered++; since we use framesAlreadyRendered as offset for "bufferToFill"*/ nsamples--; } if (item > this->count) { CLOG(LogLevel_t::Error, "THIS SHOULD NEVER HAPPEN: read " << item << " items but only expected " << this->count << "\n"); break; } } }
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, struct tag **mpd_tag, struct replay_gain_info **replay_gain_info_r) { struct id3_tag *id3_tag = NULL; id3_length_t count; id3_byte_t const *id3_data; id3_byte_t *allocated = NULL; count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { id3_data = data->stream.this_frame; mad_stream_skip(&(data->stream), tagsize); } else { allocated = g_malloc(tagsize); memcpy(allocated, data->stream.this_frame, count); mad_stream_skip(&(data->stream), count); while (count < tagsize) { size_t len; len = decoder_read(data->decoder, data->input_stream, allocated + count, tagsize - count); if (len == 0) break; else count += len; } if (count != tagsize) { g_debug("error parsing ID3 tag"); g_free(allocated); return; } id3_data = allocated; } id3_tag = id3_tag_parse(id3_data, tagsize); if (id3_tag == NULL) { g_free(allocated); return; } if (mpd_tag) { struct tag *tmp_tag = tag_id3_import(id3_tag); if (tmp_tag != NULL) { if (*mpd_tag != NULL) tag_free(*mpd_tag); *mpd_tag = tmp_tag; } } if (replay_gain_info_r) { struct replay_gain_info *tmp_rgi = parse_id3_replay_gain_info(id3_tag); if (tmp_rgi != NULL) { if (*replay_gain_info_r) replay_gain_info_free(*replay_gain_info_r); *replay_gain_info_r = tmp_rgi; } } id3_tag_delete(id3_tag); g_free(allocated); }
static void mp3_parse_id3(struct mp3_data *data, size_t tagsize, struct tag **mpd_tag) { #ifdef HAVE_ID3TAG struct id3_tag *id3_tag = NULL; id3_length_t count; id3_byte_t const *id3_data; id3_byte_t *allocated = NULL; count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { id3_data = data->stream.this_frame; mad_stream_skip(&(data->stream), tagsize); } else { allocated = g_malloc(tagsize); memcpy(allocated, data->stream.this_frame, count); mad_stream_skip(&(data->stream), count); while (count < tagsize) { size_t len; len = decoder_read(data->decoder, data->input_stream, allocated + count, tagsize - count); if (len == 0) break; else count += len; } if (count != tagsize) { g_debug("error parsing ID3 tag"); g_free(allocated); return; } id3_data = allocated; } id3_tag = id3_tag_parse(id3_data, tagsize); if (id3_tag == NULL) { g_free(allocated); return; } if (mpd_tag) { struct tag *tmp_tag = tag_id3_import(id3_tag); if (tmp_tag != NULL) { if (*mpd_tag != NULL) tag_free(*mpd_tag); *mpd_tag = tmp_tag; } } if (data->decoder != NULL) { struct replay_gain_info rgi; char *mixramp_start; char *mixramp_end; float replay_gain_db = 0; if (parse_id3_replay_gain_info(&rgi, id3_tag)) { replay_gain_db = decoder_replay_gain(data->decoder, &rgi); data->found_replay_gain = true; } if (parse_id3_mixramp(&mixramp_start, &mixramp_end, id3_tag)) { g_debug("setting mixramp_tags"); decoder_mixramp(data->decoder, replay_gain_db, mixramp_start, mixramp_end); } } id3_tag_delete(id3_tag); g_free(allocated); #else /* !HAVE_ID3TAG */ (void)mpd_tag; /* This code is enabled when libid3tag is disabled. Instead of parsing the ID3 frame, it just skips it. */ size_t count = data->stream.bufend - data->stream.this_frame; if (tagsize <= count) { mad_stream_skip(&data->stream, tagsize); } else { mad_stream_skip(&data->stream, count); while (count < tagsize) { size_t len = tagsize - count; char ignored[1024]; if (len > sizeof(ignored)) len = sizeof(ignored); len = decoder_read(data->decoder, data->input_stream, ignored, len); if (len == 0) break; else count += len; } } #endif }
/* Handle first-stage decoding: extracting the MP3 frame data. */ int RageSoundReader_MP3::do_mad_frame_decode( bool headers_only ) { int bytes_read = 0; while(1) { int ret; /* Always actually decode the first packet, so we cleanly parse Xing tags. */ if( headers_only && !mad->first_frame ) ret=mad_header_decode( &mad->Frame.header,&mad->Stream ); else ret=mad_frame_decode( &mad->Frame,&mad->Stream ); if( ret == -1 && (mad->Stream.error == MAD_ERROR_BUFLEN || mad->Stream.error == MAD_ERROR_BUFPTR) ) { if( bytes_read > 25000 ) { /* We've read this much without actually getting a frame; error. */ SetError( "Can't find data" ); return -1; } ret = fill_buffer(); if( ret <= 0 ) return ret; bytes_read += ret; continue; } if( ret == -1 && mad->Stream.error == MAD_ERROR_LOSTSYNC ) { /* This might be an ID3V2 tag. */ const int tagsize = id3_tag_query(mad->Stream.this_frame, mad->Stream.bufend - mad->Stream.this_frame); if( tagsize ) { mad_stream_skip(&mad->Stream, tagsize); /* Don't count the tagsize against the max-read-per-call figure. */ bytes_read -= tagsize; continue; } } if( ret == -1 && mad->Stream.error == MAD_ERROR_BADDATAPTR ) { /* * Something's corrupt. One cause of this is cutting an MP3 in the middle * without reencoding; the first two frames will reference data from previous * frames that have been removed. The frame is valid--we can get a header from * it, we just can't synth useful data. * * BASS pretends the bad frames are silent. Emulate that, for compatibility. */ ret = 0; /* pretend success */ } if( !ret ) { /* OK. */ if( mad->first_frame ) { /* We're at the beginning. Is this a Xing tag? */ if(handle_first_frame()) { /* The first frame contained a header. Continue searching. */ continue; } /* We've decoded the first frame of data. * * We want mad->Timer to represent the timestamp of the first sample of the * currently decoded frame. Don't increment mad->Timer on the first frame, * or it'll be the time of the *next* frame. (All frames have the same * duration.) */ mad->first_frame = false; mad->Timer = mad_timer_zero; mad->header_bytes = get_this_frame_byte(mad); } else { mad_timer_add( &mad->Timer,mad->Frame.header.duration ); } fill_frame_index_cache( mad ); return 1; } if( mad->Stream.error == MAD_ERROR_BADCRC ) { /* XXX untested */ mad_frame_mute(&mad->Frame); mad_synth_mute(&mad->Synth); continue; } if( !MAD_RECOVERABLE(mad->Stream.error) ) { /* We've received an unrecoverable error. */ SetError( mad_stream_errorstr(&mad->Stream) ); return -1; } } }
int MP3Decoder::mp3_decode(unsigned char const *inData, unsigned long inDataLength, unsigned char *outData, unsigned long *outDataLength) { int result = -1; unsigned long curOutDataIndex = 0; // 当前已输出数据的index unsigned long curInDataRemaining = inDataLength;// 当前输入数据剩余数据量 if (_isClearPreDeocodeBuffer) { Stream.next_frame = NULL; _isClearPreDeocodeBuffer = false; } /* 开始解码 */ do { /* 如果缓冲区空了或不足一帧数据, 就向缓冲区填充数据 */ if (Stream.buffer == NULL || Stream.error == MAD_ERROR_BUFLEN) { size_t BufferSize; /* 缓冲区大小 */ size_t Remaining; /* 帧剩余数据 */ unsigned char *BufferStart; /* 头指针 */ if (Stream.next_frame != NULL) // 还有上一帧的缓存 { /* 把剩余没解码完的数据补充到这次的缓冲区中 */ Remaining = Stream.bufend - Stream.next_frame; memmove(Mp3_InputBuffer, Stream.next_frame, Remaining); BufferStart = Mp3_InputBuffer + Remaining; BufferSize = INPUT_BUFFER_SIZE - Remaining; } else // 没有上一帧的缓存 { /* 设置了缓冲区地址, 但还没有填充数据 */ BufferSize = INPUT_BUFFER_SIZE; BufferStart = Mp3_InputBuffer; Remaining = 0; } /* 从输入数据中读取数据并填充缓冲区 */ if (curInDataRemaining == 0) { BufferSize = 0; // 没有数据可读取 } else { if (curInDataRemaining >= BufferSize) // 剩余的数据量满足准备读取的数据量 { memcpy(BufferStart, inData + (inDataLength - curInDataRemaining), BufferSize); curInDataRemaining -= BufferSize; } else // 剩余的数据量不足,全部取 出来 { memcpy(BufferStart, inData + (inDataLength - curInDataRemaining), curInDataRemaining); BufferSize = curInDataRemaining; curInDataRemaining = 0; } } if (BufferSize <= 0) // 未读取到数据,直接返回 { /*printf("文件读取失败\n"); exit(-1);*/ result = 0; break; } mad_stream_buffer(&Stream, Mp3_InputBuffer, BufferSize + Remaining); Stream.error = MAD_ERROR_NONE; } if (mad_frame_decode(&Frame, &Stream)) { // 解码出错 if (MAD_RECOVERABLE(Stream.error)) { // 可恢复的错误,继续执行 continue; } else { if (Stream.error == MAD_ERROR_BUFLEN) { continue; /* buffer解码光了, 需要继续填充 */ } else if (Stream.error == MAD_ERROR_LOSTSYNC) { // 丢失同步,这里可以不处理,因此该错误属于可恢复的错误 int tagsize = 0; //tagsize = id3_tag_query(Stream.this_frame, Stream.bufend - Stream.this_frame); if (tagsize > 0) { mad_stream_skip(&Stream, tagsize); } continue; } else { // 严重错误,无法继续解码 result = -1; break; } } } /* 设置每帧的播放时间 */ //mad_timer_add(&Timer, Frame.header.duration); /* 解码成音频数据 */ mad_synth_frame(&Synth, &Frame); struct mad_pcm *pcm = &Synth.pcm; unsigned int nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; /* pcm->samplerate contains the sampling frequency */ nchannels = pcm->channels; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; int curDataLen = pcm->length * pcm->channels * sizeof(short); short *buf = (short *) malloc(curDataLen); // 将解码后的数据进行填充 int i = 0; while (nsamples--) { signed int sample; // output sample(s) in 16-bit signed little-endian PCM sample = scale(*left_ch++); buf[i++] = sample & 0xFFFF; if (nchannels == 2) { sample = scale(*right_ch++); buf[i++] = sample & 0xFFFF; } } memcpy(outData + curOutDataIndex, buf, curDataLen); curOutDataIndex += curDataLen; *outDataLength = curOutDataIndex; free(buf); } while (1); return result; }
int64 MadDecoder::getDurationMs(unsigned char* buffer, size_t bufferSize) { struct mad_stream madStream; struct mad_frame madFrame; struct mad_header madHeader; mad_timer_t time = mad_timer_zero; bool depadded = false; bool vbr = false; size_t tagsize = 0; size_t consumed = 0; size_t numFrames = 0; size_t initialBitrate = 0; mad_stream_init(&madStream); mad_header_init(&madHeader); mad_frame_init(&madFrame); do // Read data from the MP3 file { int padding = 0; size_t leftover = madStream.bufend - madStream.next_frame; memcpy(buffer, madStream.this_frame, leftover); int bytesRead = fread(buffer + leftover, (size_t)1, bufferSize - leftover, handle_); if (bytesRead <= 0) { break; } for (; !depadded && padding < bytesRead && !buffer[padding]; ++padding); depadded = true; mad_stream_buffer(&madStream, buffer + padding, leftover + bytesRead - padding); while (true) // decode frame headers { madStream.error = MAD_ERROR_NONE; if (mad_header_decode(&madHeader, &madStream) == -1) { if (madStream.error == MAD_ERROR_BUFLEN) // Normal behaviour; get some more data from the file break; if (MAD_RECOVERABLE(madStream.error) == 0) break; if (madStream.error == MAD_ERROR_LOSTSYNC) { unsigned available = (madStream.bufend - madStream.this_frame); tagsize = getId3TagSize(madStream.this_frame, (size_t)available); if (tagsize) // It's some ID3 tags, so just skip { if (tagsize >= available) { _fseeki64(handle_, (int64)(tagsize - available), SEEK_CUR); depadded = false; } mad_stream_skip(&madStream, std::min(tagsize, available)); } } continue; // not an audio frame } mad_timer_add(&time, madHeader.duration); consumed += madStream.next_frame - madStream.this_frame; if (numFrames == 0) { initialBitrate = madHeader.bitrate; // Get the precise frame count from the XING header if present madFrame.header = madHeader; if (mad_frame_decode(&madFrame, &madStream) == -1) { if (MAD_RECOVERABLE(madStream.error) == 0) { break; } } if ((numFrames = xingFrames(madStream.anc_ptr, madStream.anc_bitlen))) { mad_timer_multiply(&time, (signed long)numFrames); break; } } else { vbr |= madHeader.bitrate != initialBitrate; } // If not VBR, we can time just a few frames then extrapolate (not exact!) if (++numFrames == 25 && !vbr) { struct stat st; fstat(fileno(handle_), &st); timerMultiply(&time, (double)(st.st_size - tagsize) / consumed); break; } } // while(true) } while (madStream.error == MAD_ERROR_BUFLEN); mad_frame_finish(&madFrame); mad_header_finish(&madHeader); mad_stream_finish(&madStream); rewind(handle_); return mad_timer_count(time, MAD_UNITS_MILLISECONDS); }