/*************************************************************************** * Copyright (C) 2006-2012 by Ilya Kotov * * forkotov02@hotmail.ru * * * * 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; either version 2 of the License, or * * (at your option) any later version. * * * * 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., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #include #include #include "decoder_ffmpeg.h" // callbacks static int ffmpeg_read(void *data, uint8_t *buf, int size) { DecoderFFmpeg *d = (DecoderFFmpeg*)data; return (int)d->input()->read((char*)buf, size); } static int64_t ffmpeg_seek(void *data, int64_t offset, int whence) { DecoderFFmpeg *d = (DecoderFFmpeg*)data; int64_t absolute_pos = 0; /*if(d->input()->isSequential()) return -1;*/ switch( whence ) { case AVSEEK_SIZE: return d->input()->size(); case SEEK_SET: absolute_pos = offset; break; case SEEK_CUR: absolute_pos = d->input()->pos() + offset; break; case SEEK_END: absolute_pos = d->input()->size() - offset; default: return -1; } if(absolute_pos < 0 || absolute_pos > d->input()->size()) return -1; return d->input()->seek(absolute_pos); } // Decoder class #if (LIBAVCODEC_VERSION_INT >= ((53<<16)+(34<<8)+0)) DecoderFFmpeg::DecoderFFmpeg(const QString &path, QIODevice *i) : Decoder(i) { m_bitrate = 0; m_totalTime = 0; ic = 0; m_path = path; m_temp_pkt.size = 0; m_pkt.size = 0; m_pkt.data = 0; m_output_at = 0; m_skipBytes = 0; m_stream = 0; m_decoded_frame = 0; av_init_packet(&m_pkt); av_init_packet(&m_temp_pkt); } DecoderFFmpeg::~DecoderFFmpeg() { m_bitrate = 0; m_temp_pkt.size = 0; if (ic) avformat_free_context(ic); if(m_pkt.data) av_free_packet(&m_pkt); if(m_stream) av_free(m_stream); if(m_decoded_frame) av_free(m_decoded_frame); } bool DecoderFFmpeg::initialize() { m_bitrate = 0; m_totalTime = 0; m_seekTime = -1; avcodec_register_all(); avformat_network_init(); av_register_all(); ic = avformat_alloc_context(); AVProbeData pd; uint8_t buf[PROBE_BUFFER_SIZE + AVPROBE_PADDING_SIZE]; pd.filename = m_path.toLocal8Bit().constData(); pd.buf_size = input()->peek((char*)buf, sizeof(buf) - AVPROBE_PADDING_SIZE); pd.buf = buf; if(pd.buf_size < PROBE_BUFFER_SIZE) { qWarning("DecoderFFmpeg: too small buffer size: %d bytes", pd.buf_size); return false; } AVInputFormat *fmt = av_probe_input_format(&pd, 1); if(!fmt) { qWarning("DecoderFFmpeg: usupported format"); return false; } qDebug("DecoderFFmpeg: detected format: %s", fmt->long_name); qDebug("=%s=", fmt->name); m_stream = avio_alloc_context(m_input_buf, INPUT_BUFFER_SIZE, 0, this, ffmpeg_read, NULL, ffmpeg_seek); if(!m_stream) { qWarning("DecoderFFmpeg: unable to initialize I/O callbacks"); return false; } m_stream->seekable = !input()->isSequential(); m_stream->max_packet_size = INPUT_BUFFER_SIZE; ic->pb = m_stream; if(avformat_open_input(&ic, 0, fmt, 0) != 0) { qDebug("DecoderFFmpeg: avformat_open_input() failed"); return false; } avformat_find_stream_info(ic, 0); if(ic->pb) ic->pb->eof_reached = 0; if (input()->isSequential()) { QMap metaData; AVDictionaryEntry *album = av_dict_get(ic->metadata,"album",0,0); if(!album) album = av_dict_get(ic->metadata,"WM/AlbumTitle",0,0); AVDictionaryEntry *artist = av_dict_get(ic->metadata,"artist",0,0); if(!artist) artist = av_dict_get(ic->metadata,"author",0,0); AVDictionaryEntry *comment = av_dict_get(ic->metadata,"comment",0,0); AVDictionaryEntry *genre = av_dict_get(ic->metadata,"genre",0,0); AVDictionaryEntry *title = av_dict_get(ic->metadata,"title",0,0); AVDictionaryEntry *year = av_dict_get(ic->metadata,"WM/Year",0,0); if(!year) year = av_dict_get(ic->metadata,"year",0,0); if(!year) year = av_dict_get(ic->metadata,"date",0,0); AVDictionaryEntry *track = av_dict_get(ic->metadata,"track",0,0); if(!track) track = av_dict_get(ic->metadata,"WM/Track",0,0); if(!track) track = av_dict_get(ic->metadata,"WM/TrackNumber",0,0); if(album) metaData.insert(Qmmp::ALBUM, QString::fromUtf8(album->value).trimmed()); if(artist) metaData.insert(Qmmp::ARTIST, QString::fromUtf8(artist->value).trimmed()); if(comment) metaData.insert(Qmmp::COMMENT, QString::fromUtf8(comment->value).trimmed()); if(genre) metaData.insert(Qmmp::GENRE, QString::fromUtf8(genre->value).trimmed()); if(title) metaData.insert(Qmmp::TITLE, QString::fromUtf8(title->value).trimmed()); if(year) metaData.insert(Qmmp::YEAR, year->value); if(track) metaData.insert(Qmmp::TRACK, track->value); metaData.insert(Qmmp::URL, m_path); addMetaData(metaData); } ic->flags |= AVFMT_FLAG_GENPTS; av_read_play(ic); for (wma_idx = 0; wma_idx < (int)ic->nb_streams; wma_idx++) { c = ic->streams[wma_idx]->codec; if (c->codec_type == AVMEDIA_TYPE_AUDIO) break; } if (c->channels > 0) c->request_channels = qMin(2, c->channels); else c->request_channels = 2; av_dump_format(ic,0,0,0); AVCodec *codec = avcodec_find_decoder(c->codec_id); if (!codec) { qWarning("DecoderFFmpeg: unsupported codec for output stream"); return false; } if (avcodec_open2(c, codec, 0) < 0) { qWarning("DecoderFFmpeg: error while opening codec for output stream"); return false; } m_decoded_frame = avcodec_alloc_frame(); m_totalTime = input()->isSequential() ? 0 : ic->duration * 1000 / AV_TIME_BASE; if(c->codec_id == CODEC_ID_SHORTEN) //ffmpeg bug workaround m_totalTime = 0; Qmmp::AudioFormat format = Qmmp::PCM_UNKNOWM; switch(c->sample_fmt) { case AV_SAMPLE_FMT_U8: case AV_SAMPLE_FMT_U8P: format = Qmmp::PCM_S8; break; case AV_SAMPLE_FMT_S16: case AV_SAMPLE_FMT_S16P: format = Qmmp::PCM_S16LE; break; case AV_SAMPLE_FMT_S32: case AV_SAMPLE_FMT_FLT: case AV_SAMPLE_FMT_FLTP: format = Qmmp::PCM_S32LE; break; default: qWarning("DecoderFFmpeg: unsupported audio format"); return false; } configure(c->sample_rate, c->request_channels, format); if(ic->bit_rate) m_bitrate = ic->bit_rate/1000; if(c->bit_rate) m_bitrate = c->bit_rate/1000; qDebug("DecoderFFmpeg: initialize succes"); #ifdef Q_OS_WIN qDebug("total time = %I64d", m_totalTime); #else qDebug("total time = %lld ", m_totalTime); #endif return true; } qint64 DecoderFFmpeg::totalTime() { return m_totalTime; } int DecoderFFmpeg::bitrate() { return m_bitrate; } qint64 DecoderFFmpeg::read(char *audio, qint64 maxSize) { m_skipBytes = 0; if(!m_output_at) fillBuffer(); if(!m_output_at) return 0; qint64 len = qMin(m_output_at, maxSize); if(av_sample_fmt_is_planar(c->sample_fmt) && c->request_channels > 1) { int bps = av_get_bytes_per_sample(c->sample_fmt); for(int i = 0; i < len >> 1; i+=bps) { memcpy(audio + 2*i, m_decoded_frame->extended_data[0] + i, bps); memcpy(audio + 2*i + bps, m_decoded_frame->extended_data[1] + i, bps); } m_output_at -= len; memmove(m_decoded_frame->extended_data[0], m_decoded_frame->extended_data[0] + len/2, m_output_at/2); memmove(m_decoded_frame->extended_data[1], m_decoded_frame->extended_data[1] + len/2, m_output_at/2); } else { memcpy(audio, m_decoded_frame->extended_data[0], len); m_output_at -= len; memmove(m_decoded_frame->extended_data[0], m_decoded_frame->extended_data[0] + len, m_output_at); } if(c->sample_fmt == AV_SAMPLE_FMT_FLTP || c->sample_fmt == AV_SAMPLE_FMT_FLT) { //convert float to signed 32 bit LE for(int i = 0; i < len >> 2; i++) { int32_t *out = (int32_t *)audio; float *in = (float *) audio; out[i] = qBound(-1.0f, in[i], +1.0f) * (double) 0x7fffffff; } } return len; } qint64 DecoderFFmpeg::ffmpeg_decode() { int out_size = 0; int got_frame = 0; if((m_pkt.stream_index == wma_idx)) { avcodec_get_frame_defaults(m_decoded_frame); int l = avcodec_decode_audio4(c, m_decoded_frame, &got_frame, &m_temp_pkt); if(got_frame) out_size = av_samples_get_buffer_size(0, c->channels, m_decoded_frame->nb_samples, c->sample_fmt, 1); else out_size = 0; if(c->bit_rate) m_bitrate = c->bit_rate/1000; if(l < 0) { return l; } m_temp_pkt.data += l; m_temp_pkt.size -= l; } if (!m_temp_pkt.size && m_pkt.data) av_free_packet(&m_pkt); return out_size; } void DecoderFFmpeg::seek(qint64 pos) { int64_t timestamp = int64_t(pos)*AV_TIME_BASE/1000; if (ic->start_time != (qint64)AV_NOPTS_VALUE) timestamp += ic->start_time; m_seekTime = timestamp; av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(c); av_free_packet(&m_pkt); m_temp_pkt.size = 0; } void DecoderFFmpeg::fillBuffer() { while(!m_output_at) { if(!m_temp_pkt.size) { if (av_read_frame(ic, &m_pkt) < 0) { m_temp_pkt.size = 0; break; } m_temp_pkt.size = m_pkt.size; m_temp_pkt.data = m_pkt.data; if(m_pkt.stream_index != wma_idx) { if(m_pkt.data) av_free_packet(&m_pkt); m_temp_pkt.size = 0; continue; } if(m_seekTime && c->codec_id == CODEC_ID_APE) { int64_t rescaledPts = av_rescale(m_pkt.pts, AV_TIME_BASE * (int64_t) ic->streams[m_pkt.stream_index]->time_base.num, ic->streams[m_pkt.stream_index]->time_base.den); m_skipBytes = (m_seekTime - rescaledPts) * c->sample_rate * 4 / AV_TIME_BASE; } else m_skipBytes = 0; m_seekTime = 0; } if(m_skipBytes > 0 && c->codec_id == CODEC_ID_APE) { while (m_skipBytes > 0) { m_output_at = ffmpeg_decode(); if(m_output_at < 0) break; m_skipBytes -= m_output_at; } if(m_skipBytes < 0) { qint64 size = m_output_at; m_output_at = - m_skipBytes; m_output_at = m_output_at/4*4; memmove(m_decoded_frame->data[0], (m_decoded_frame->data[0] + size - m_output_at), m_output_at); m_skipBytes = 0; } } else m_output_at = ffmpeg_decode(); if(m_output_at < 0) { m_output_at = 0; m_temp_pkt.size = 0; if(c->codec_id == CODEC_ID_SHORTEN || c->codec_id == CODEC_ID_TWINVQ) { if(m_pkt.data) av_free_packet(&m_pkt); m_pkt.data = 0; break; } continue; } else if(m_output_at == 0) { if(c->codec_id == CODEC_ID_SHORTEN || c->codec_id == CODEC_ID_TWINVQ) continue; if(m_pkt.data) av_free_packet(&m_pkt); m_pkt.data = 0; break; } } } #else //legacy ffmpeg support DecoderFFmpeg::DecoderFFmpeg(const QString &path, QIODevice *i) : Decoder(i) { m_bitrate = 0; m_skip = false; m_totalTime = 0; ic = 0; m_path = path; m_temp_pkt.size = 0; m_pkt.size = 0; m_pkt.data = 0; m_output_buf = 0; m_output_at = 0; m_skipBytes = 0; m_stream = 0; av_init_packet(&m_pkt); av_init_packet(&m_temp_pkt); } DecoderFFmpeg::~DecoderFFmpeg() { m_bitrate = 0; m_temp_pkt.size = 0; if (ic) av_close_input_stream(ic); if(m_pkt.data) av_free_packet(&m_pkt); if(m_output_buf) av_free(m_output_buf); if(m_stream) av_free(m_stream); } bool DecoderFFmpeg::initialize() { m_bitrate = 0; m_skip = false; m_totalTime = 0; m_seekTime = -1; av_register_all(); AVProbeData pd; uint8_t buf[PROBE_BUFFER_SIZE + AVPROBE_PADDING_SIZE]; pd.filename = m_path.toLocal8Bit().constData(); pd.buf_size = input()->peek((char*)buf, sizeof(buf) - AVPROBE_PADDING_SIZE); pd.buf = buf; if(pd.buf_size < PROBE_BUFFER_SIZE) { qWarning("DecoderFFmpeg: too small buffer size: %d bytes", pd.buf_size); return false; } AVInputFormat *fmt = av_probe_input_format(&pd, 1); if(!fmt) { qWarning("DecoderFFmpeg: usupported format"); return false; } qDebug("DecoderFFmpeg: detected format: %s", fmt->long_name); qDebug("=%s=", fmt->name); #if (LIBAVFORMAT_VERSION_INT >= ((52<<16)+(105<<8)+0)) m_stream = avio_alloc_context(m_input_buf, INPUT_BUFFER_SIZE, 0, this, ffmpeg_read, NULL, ffmpeg_seek); if(!m_stream) { qWarning("DecoderFFmpeg: unable to initialize I/O callbacks"); return false; } m_stream->seekable = !input()->isSequential(); #else m_stream = (ByteIOContext *)av_malloc(sizeof(ByteIOContext)); init_put_byte(m_stream, m_input_buf, INPUT_BUFFER_SIZE, 0, this, ffmpeg_read, NULL, ffmpeg_seek); m_stream->is_streamed = input()->isSequential(); #endif m_stream->max_packet_size = INPUT_BUFFER_SIZE; AVFormatParameters ap; memset(&ap, 0, sizeof(ap)); if(av_open_input_stream(&ic, m_stream, m_path.toLocal8Bit(), fmt, &ap) != 0) { qDebug("DecoderFFmpeg: av_open_input_stream() failed"); return false; } av_find_stream_info(ic); if(ic->pb) ic->pb->eof_reached = 0; if (input()->isSequential()) { QMap metaData; AVMetadataTag *album = av_metadata_get(ic->metadata,"album",0,0); if(!album) album = av_metadata_get(ic->metadata,"WM/AlbumTitle",0,0); AVMetadataTag *artist = av_metadata_get(ic->metadata,"artist",0,0); if(!artist) artist = av_metadata_get(ic->metadata,"author",0,0); AVMetadataTag *comment = av_metadata_get(ic->metadata,"comment",0,0); AVMetadataTag *genre = av_metadata_get(ic->metadata,"genre",0,0); AVMetadataTag *title = av_metadata_get(ic->metadata,"title",0,0); AVMetadataTag *year = av_metadata_get(ic->metadata,"WM/Year",0,0); if(!year) year = av_metadata_get(ic->metadata,"year",0,0); if(!year) year = av_metadata_get(ic->metadata,"date",0,0); AVMetadataTag *track = av_metadata_get(ic->metadata,"track",0,0); if(!track) track = av_metadata_get(ic->metadata,"WM/Track",0,0); if(!track) track = av_metadata_get(ic->metadata,"WM/TrackNumber",0,0); if(album) metaData.insert(Qmmp::ALBUM, QString::fromUtf8(album->value).trimmed()); if(artist) metaData.insert(Qmmp::ARTIST, QString::fromUtf8(artist->value).trimmed()); if(comment) metaData.insert(Qmmp::COMMENT, QString::fromUtf8(comment->value).trimmed()); if(genre) metaData.insert(Qmmp::GENRE, QString::fromUtf8(genre->value).trimmed()); if(title) metaData.insert(Qmmp::TITLE, QString::fromUtf8(title->value).trimmed()); if(year) metaData.insert(Qmmp::YEAR, year->value); if(track) metaData.insert(Qmmp::TRACK, track->value); metaData.insert(Qmmp::URL, m_path); addMetaData(metaData); } ic->flags |= AVFMT_FLAG_GENPTS; av_read_play(ic); for (wma_idx = 0; wma_idx < (int)ic->nb_streams; wma_idx++) { c = ic->streams[wma_idx]->codec; #if LIBAVCODEC_VERSION_MAJOR < 53 if (c->codec_type == CODEC_TYPE_AUDIO) #else if (c->codec_type == AVMEDIA_TYPE_AUDIO) #endif break; } if (c->channels > 0) c->request_channels = qMin(2, c->channels); else c->request_channels = 2; #if (LIBAVCODEC_VERSION_INT >= ((52<<16)+(101<<8)+0)) av_dump_format(ic,0,0,0); #else dump_format(ic,0,0,0); #endif AVCodec *codec = avcodec_find_decoder(c->codec_id); if (!codec) { qWarning("DecoderFFmpeg: unsupported codec for output stream"); return false; } if (avcodec_open(c, codec) < 0) { qWarning("DecoderFFmpeg: error while opening codec for output stream"); return false; } m_totalTime = input()->isSequential() ? 0 : ic->duration * 1000 / AV_TIME_BASE; m_output_buf = (uint8_t *)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE*2); #if (LIBAVCODEC_VERSION_INT >= ((52<<16)+(20<<8)+0)) if(c->codec_id == CODEC_ID_SHORTEN) //ffmpeg bug workaround m_totalTime = 0; #endif #if (LIBAVUTIL_VERSION_INT >= ((50<<16)+(38<<8)+0)) if(c->sample_fmt == AV_SAMPLE_FMT_S32) configure(c->sample_rate, c->request_channels, Qmmp::PCM_S32LE); else configure(c->sample_rate, c->request_channels, Qmmp::PCM_S16LE); #else if(c->sample_fmt == SAMPLE_FMT_S32) configure(c->sample_rate, c->request_channels, Qmmp::PCM_S32LE); else configure(c->sample_rate, c->request_channels, Qmmp::PCM_S16LE); #endif if(ic->bit_rate) m_bitrate = ic->bit_rate/1000; if(c->bit_rate) m_bitrate = c->bit_rate/1000; qDebug("DecoderFFmpeg: initialize succes"); return true; } qint64 DecoderFFmpeg::totalTime() { return m_totalTime; } int DecoderFFmpeg::bitrate() { return m_bitrate; } qint64 DecoderFFmpeg::read(char *audio, qint64 maxSize) { m_skipBytes = 0; if (m_skip) { while(m_temp_pkt.size) ffmpeg_decode(m_output_buf); m_output_at = 0; m_skip = false; } if(!m_output_at) fillBuffer(); if(!m_output_at) return 0; qint64 len = qMin(m_output_at, maxSize); memcpy(audio, m_output_buf, len); m_output_at -= len; memmove(m_output_buf, m_output_buf + len, m_output_at); return len; } qint64 DecoderFFmpeg::ffmpeg_decode(uint8_t *audio) { int out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 2; if((m_pkt.stream_index == wma_idx)) { #if (LIBAVCODEC_VERSION_INT >= ((52<<16)+(23<<8)+0)) int l = avcodec_decode_audio3(c, (int16_t *)(audio), &out_size, &m_temp_pkt); #else int l = avcodec_decode_audio2(c, (int16_t *)(audio), &out_size, m_temp_pkt.data, m_temp_pkt.size); #endif if(c->bit_rate) m_bitrate = c->bit_rate/1000; if(l < 0) return l; m_temp_pkt.data += l; m_temp_pkt.size -= l; } if (!m_temp_pkt.size && m_pkt.data) av_free_packet(&m_pkt); return out_size; } void DecoderFFmpeg::seek(qint64 pos) { int64_t timestamp = int64_t(pos)*AV_TIME_BASE/1000; if (ic->start_time != (qint64)AV_NOPTS_VALUE) timestamp += ic->start_time; m_seekTime = timestamp; av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); if(m_pkt.size) m_skip = true; } void DecoderFFmpeg::fillBuffer() { while(!m_output_at) { if(!m_temp_pkt.size) { if (av_read_frame(ic, &m_pkt) < 0) { m_temp_pkt.size = 0; break; } m_temp_pkt.size = m_pkt.size; m_temp_pkt.data = m_pkt.data; if(m_pkt.stream_index != wma_idx) { if(m_pkt.data) av_free_packet(&m_pkt); m_temp_pkt.size = 0; continue; } if(m_seekTime && c->codec_id == CODEC_ID_APE) { int64_t rescaledPts = av_rescale(m_pkt.pts, AV_TIME_BASE * (int64_t) ic->streams[m_pkt.stream_index]->time_base.num, ic->streams[m_pkt.stream_index]->time_base.den); m_skipBytes = (m_seekTime - rescaledPts) * c->sample_rate * 4 / AV_TIME_BASE; } else m_skipBytes = 0; m_seekTime = 0; } if(m_skipBytes > 0 && c->codec_id == CODEC_ID_APE) { while (m_skipBytes > 0) { m_output_at = ffmpeg_decode(m_output_buf); if(m_output_at < 0) break; m_skipBytes -= m_output_at; } if(m_skipBytes < 0) { qint64 size = m_output_at; m_output_at = - m_skipBytes; m_output_at = m_output_at/4*4; memmove(m_output_buf, (m_output_buf + size - m_output_at), m_output_at); m_skipBytes = 0; } } else m_output_at = ffmpeg_decode(m_output_buf); if(m_output_at < 0) { m_output_at = 0; m_temp_pkt.size = 0; if(c->codec_id == CODEC_ID_SHORTEN) { if(m_pkt.data) av_free_packet(&m_pkt); m_pkt.data = 0; break; } continue; } else if(m_output_at == 0) { if(c->codec_id == CODEC_ID_SHORTEN) continue; if(m_pkt.data) av_free_packet(&m_pkt); m_pkt.data = 0; break; } } } #endif