aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp365
-rw-r--r--src/plugins/Input/ffmpeg/decoder_ffmpeg.h31
2 files changed, 170 insertions, 226 deletions
diff --git a/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp b/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp
index b1b1bb4f5..948993184 100644
--- a/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp
+++ b/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp
@@ -69,36 +69,19 @@ static int64_t ffmpeg_seek(void *data, int64_t offset, int whence)
DecoderFFmpeg::DecoderFFmpeg(const QString &path, QIODevice *i)
: Decoder(i)
{
- m_bitrate = 0;
- m_totalTime = 0;
- ic = nullptr;
m_path = path;
- m_temp_pkt.size = 0;
- m_pkt.size = 0;
- m_pkt.data = nullptr;
- m_output_at = 0;
- m_skipBytes = 0;
- m_stream = nullptr;
- m_decoded_frame = nullptr;
- m_channels = 0;
- c = nullptr;
- audioIndex = 0;
- m_seekTime = -1;
- av_init_packet(&m_pkt);
- av_init_packet(&m_temp_pkt);
- m_input_buf = nullptr;
+ m_pkt = av_packet_alloc();
}
DecoderFFmpeg::~DecoderFFmpeg()
{
m_bitrate = 0;
- m_temp_pkt.size = 0;
- if(c)
- avcodec_free_context(&c);
- if (ic)
- avformat_free_context(ic);
- if(m_pkt.data)
- av_packet_unref(&m_pkt);
+ if(m_codecContext)
+ avcodec_free_context(&m_codecContext);
+ if (m_formatContext)
+ avformat_free_context(m_formatContext);
+
+ av_packet_free(&m_pkt);
if(m_stream)
#if (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 80, 100)) //ffmpeg-3.4
@@ -107,8 +90,8 @@ DecoderFFmpeg::~DecoderFFmpeg()
av_free(m_stream);
#endif
- if(m_decoded_frame)
- av_frame_free(&m_decoded_frame);
+ if(m_frame)
+ av_frame_free(&m_frame);
}
bool DecoderFFmpeg::initialize()
@@ -116,8 +99,9 @@ bool DecoderFFmpeg::initialize()
m_bitrate = 0;
m_totalTime = 0;
m_seekTime = -1;
+ m_eof = false;
- ic = avformat_alloc_context();
+ m_formatContext = avformat_alloc_context();
AVProbeData pd;
memset(&pd, 0, sizeof(pd));
@@ -155,45 +139,45 @@ bool DecoderFFmpeg::initialize()
}
m_stream->seekable = !input()->isSequential();
m_stream->max_packet_size = INPUT_BUFFER_SIZE;
- ic->pb = m_stream;
+ m_formatContext->pb = m_stream;
- if(avformat_open_input(&ic, nullptr, fmt, nullptr) != 0)
+ if(avformat_open_input(&m_formatContext, nullptr, fmt, nullptr) != 0)
{
qDebug("DecoderFFmpeg: avformat_open_input() failed");
return false;
}
- avformat_find_stream_info(ic, nullptr);
- if(ic->pb)
- ic->pb->eof_reached = 0;
+ avformat_find_stream_info(m_formatContext, nullptr);
+ if(m_formatContext->pb)
+ m_formatContext->pb->eof_reached = 0;
if (input()->isSequential())
{
QMap<Qmmp::MetaData, QString> metaData;
- AVDictionaryEntry *album = av_dict_get(ic->metadata,"album",nullptr,0);
- AVDictionaryEntry *album_artist = av_dict_get(ic->metadata,"album_artist",nullptr,0);
- AVDictionaryEntry *artist = av_dict_get(ic->metadata,"artist",nullptr,0);
- AVDictionaryEntry *composer = av_dict_get(ic->metadata,"composer",nullptr,0);
- AVDictionaryEntry *comment = av_dict_get(ic->metadata,"comment",nullptr,0);
- AVDictionaryEntry *genre = av_dict_get(ic->metadata,"genre",nullptr,0);
- AVDictionaryEntry *title = av_dict_get(ic->metadata,"title",nullptr,0);
- AVDictionaryEntry *year = av_dict_get(ic->metadata,"date",nullptr,0);
- AVDictionaryEntry *track = av_dict_get(ic->metadata,"track",nullptr,0);
+ AVDictionaryEntry *album = av_dict_get(m_formatContext->metadata,"album",nullptr,0);
+ AVDictionaryEntry *album_artist = av_dict_get(m_formatContext->metadata,"album_artist",nullptr,0);
+ AVDictionaryEntry *artist = av_dict_get(m_formatContext->metadata,"artist",nullptr,0);
+ AVDictionaryEntry *composer = av_dict_get(m_formatContext->metadata,"composer",nullptr,0);
+ AVDictionaryEntry *comment = av_dict_get(m_formatContext->metadata,"comment",nullptr,0);
+ AVDictionaryEntry *genre = av_dict_get(m_formatContext->metadata,"genre",nullptr,0);
+ AVDictionaryEntry *title = av_dict_get(m_formatContext->metadata,"title",nullptr,0);
+ AVDictionaryEntry *year = av_dict_get(m_formatContext->metadata,"date",nullptr,0);
+ AVDictionaryEntry *track = av_dict_get(m_formatContext->metadata,"track",nullptr,0);
if(!album)
- album = av_dict_get(ic->metadata,"WM/AlbumTitle",nullptr,0);
+ album = av_dict_get(m_formatContext->metadata,"WM/AlbumTitle",nullptr,0);
if(!artist)
- artist = av_dict_get(ic->metadata,"author",nullptr,0);
+ artist = av_dict_get(m_formatContext->metadata,"author",nullptr,0);
if(!year)
- year = av_dict_get(ic->metadata,"WM/Year",nullptr,0);
+ year = av_dict_get(m_formatContext->metadata,"WM/Year",nullptr,0);
if(!year)
- year = av_dict_get(ic->metadata,"year",nullptr,0);
+ year = av_dict_get(m_formatContext->metadata,"year",nullptr,0);
if(!track)
- track = av_dict_get(ic->metadata,"WM/Track",nullptr,0);
+ track = av_dict_get(m_formatContext->metadata,"WM/Track",nullptr,0);
if(!track)
- track = av_dict_get(ic->metadata,"WM/TrackNumber",nullptr,0);
+ track = av_dict_get(m_formatContext->metadata,"WM/TrackNumber",nullptr,0);
if(album)
metaData.insert(Qmmp::ALBUM, QString::fromUtf8(album->value).trimmed());
@@ -217,35 +201,35 @@ bool DecoderFFmpeg::initialize()
}
//replay gain
- ReplayGainReader rg(ic);
+ ReplayGainReader rg(m_formatContext);
setReplayGainInfo(rg.replayGainInfo());
- c = avcodec_alloc_context3(nullptr);
- ic->flags |= AVFMT_FLAG_GENPTS;
- av_read_play(ic);
- audioIndex = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
- if(audioIndex < 0)
+ m_codecContext = avcodec_alloc_context3(nullptr);
+ m_formatContext->flags |= AVFMT_FLAG_GENPTS;
+ av_read_play(m_formatContext);
+ m_audioIndex = av_find_best_stream(m_formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
+ if(m_audioIndex < 0)
{
qWarning("DecoderFFmpeg: unable to find audio stream");
return false;
}
- avcodec_parameters_to_context(c, ic->streams[audioIndex]->codecpar);
+ avcodec_parameters_to_context(m_codecContext, m_formatContext->streams[m_audioIndex]->codecpar);
- if (c->channels == 1)
+ if (m_codecContext->channels == 1)
{
- c->request_channel_layout = AV_CH_LAYOUT_MONO;
- m_channels = c->channels;
+ m_codecContext->request_channel_layout = AV_CH_LAYOUT_MONO;
+ m_channels = m_codecContext->channels;
}
else
{
- c->request_channel_layout = AV_CH_LAYOUT_STEREO;
+ m_codecContext->request_channel_layout = AV_CH_LAYOUT_STEREO;
m_channels = 2;
}
- av_dump_format(ic,0,nullptr,0);
+ av_dump_format(m_formatContext,0,nullptr,0);
- AVCodec *codec = avcodec_find_decoder(c->codec_id);
+ AVCodec *codec = avcodec_find_decoder(m_codecContext->codec_id);
if (!codec)
{
@@ -253,21 +237,21 @@ bool DecoderFFmpeg::initialize()
return false;
}
- if (avcodec_open2(c, codec, nullptr) < 0)
+ if (avcodec_open2(m_codecContext, codec, nullptr) < 0)
{
qWarning("DecoderFFmpeg: error while opening codec for output stream");
return false;
}
- m_decoded_frame = av_frame_alloc();
- m_totalTime = input()->isSequential() ? 0 : ic->duration * 1000 / AV_TIME_BASE;
+ m_frame = av_frame_alloc();
+ m_totalTime = input()->isSequential() ? 0 : m_formatContext->duration * 1000 / AV_TIME_BASE;
- if(c->codec_id == AV_CODEC_ID_SHORTEN) //ffmpeg bug workaround
+ if(m_codecContext->codec_id == AV_CODEC_ID_SHORTEN) //ffmpeg bug workaround
m_totalTime = 0;
Qmmp::AudioFormat format = Qmmp::PCM_UNKNOWN;
- switch(c->sample_fmt)
+ switch(m_codecContext->sample_fmt)
{
case AV_SAMPLE_FMT_U8:
case AV_SAMPLE_FMT_U8P:
@@ -290,13 +274,13 @@ bool DecoderFFmpeg::initialize()
return false;
}
- setProperty(Qmmp::FORMAT_NAME, QString::fromLatin1(c->codec->name));
- configure(c->sample_rate, m_channels, format);
+ setProperty(Qmmp::FORMAT_NAME, QString::fromLatin1(m_codecContext->codec->name));
+ configure(m_codecContext->sample_rate, m_channels, format);
- if(ic->bit_rate)
- m_bitrate = ic->bit_rate/1000;
- if(c->bit_rate)
- m_bitrate = c->bit_rate/1000;
+ if(m_formatContext->bit_rate)
+ m_bitrate = m_formatContext->bit_rate/1000;
+ if(m_codecContext->bit_rate)
+ m_bitrate = m_codecContext->bit_rate/1000;
qDebug("DecoderFFmpeg: initialize succes");
qDebug() << "DecoderFFmpeg: total time =" << m_totalTime;
@@ -317,199 +301,160 @@ qint64 DecoderFFmpeg::read(unsigned char *audio, qint64 maxSize)
{
m_skipBytes = 0;
- if(!m_output_at)
+ if(!m_output_size)
fillBuffer();
- if(!m_output_at)
+ if(!m_output_size)
return 0;
- qint64 len = qMin(m_output_at, maxSize);
+ qint64 len = qMin(m_output_size, maxSize);
- if(av_sample_fmt_is_planar(c->sample_fmt) && m_channels > 1)
+ if(av_sample_fmt_is_planar(m_codecContext->sample_fmt) && m_channels > 1)
{
- int bps = av_get_bytes_per_sample(c->sample_fmt);
+ int bps = av_get_bytes_per_sample(m_codecContext->sample_fmt);
for(int i = 0; i < len / bps; i++)
{
- memcpy(audio + i * bps, m_decoded_frame->extended_data[i % m_channels] + i / m_channels * bps, bps);
+ memcpy(audio + i * bps, m_frame->extended_data[i % m_channels] + i / m_channels * bps, bps);
}
- m_output_at -= len;
+ m_output_size -= len;
for(int i = 0; i < m_channels; i++)
{
- memmove(m_decoded_frame->extended_data[i],
- m_decoded_frame->extended_data[i] + len/m_channels, m_output_at/m_channels);
+ memmove(m_frame->extended_data[i],
+ m_frame->extended_data[i] + len / m_channels, m_output_size / m_channels);
}
}
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);
+ memcpy(audio, m_frame->extended_data[0], len);
+ m_output_size -= len;
+ memmove(m_frame->extended_data[0], m_frame->extended_data[0] + len, m_output_size);
}
- return len;
-}
-
-qint64 DecoderFFmpeg::ffmpeg_decode()
-{
- int out_size = 0;
- int got_frame = 0;
- if(m_pkt.stream_index == audioIndex)
- {
- int send_err = 0, receive_err = 0;
-
- if(m_temp_pkt.data)
- {
- if((send_err = avcodec_send_packet(c, &m_temp_pkt)) != 0)
- {
- qWarning("DecoderFFmpeg: avcodec_send_packet error: %d", send_err);
- }
- }
-
- int l = (send_err != 0) ? 0 : m_temp_pkt.size;
-
- if((receive_err = avcodec_receive_frame(c, m_decoded_frame)) != 0)
- {
- qWarning("DecoderFFmpeg: avcodec_receive_frame error: %d", receive_err);
- if(send_err < 0 && receive_err < 0)
- return -1;
-
- if(receive_err == AVERROR(EAGAIN))
- return 0;
- }
- else
- {
- got_frame = m_decoded_frame->pkt_size;
- }
-
- if(got_frame)
- out_size = av_samples_get_buffer_size(nullptr, c->channels, m_decoded_frame->nb_samples,
- c->sample_fmt, 1);
- else
- out_size = 0;
+ if(!m_output_size)
+ av_frame_unref(m_frame);
- 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_packet_unref(&m_pkt);
-
- return out_size;
+ return len;
}
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;
+ if (m_formatContext->start_time != (qint64)AV_NOPTS_VALUE)
+ timestamp += m_formatContext->start_time;
m_seekTime = timestamp;
- av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD);
- avcodec_flush_buffers(c);
- av_packet_unref(&m_pkt);
- m_temp_pkt.size = 0;
+ av_seek_frame(m_formatContext, -1, timestamp, AVSEEK_FLAG_BACKWARD);
+ avcodec_flush_buffers(m_codecContext);
+ av_packet_unref(m_pkt);
+ av_frame_unref(m_frame);
+ m_output_size = 0;
+ m_eof = false;
}
void DecoderFFmpeg::fillBuffer()
{
- while(!m_output_at || m_skipBytes > 0)
+ while(!m_output_size || m_skipBytes > 0)
{
- if(!m_temp_pkt.size)
+ if(!m_pkt->size && !m_eof)
{
- if (av_read_frame(ic, &m_pkt) < 0)
+ int read_error = av_read_frame(m_formatContext, m_pkt);
+ if(!read_error)
{
- m_temp_pkt.size = 0;
- m_temp_pkt.data = nullptr;
- }
- m_temp_pkt.size = m_pkt.size;
- m_temp_pkt.data = m_pkt.data;
+ if(m_pkt->stream_index != m_audioIndex)
+ {
+ av_packet_unref(m_pkt);
+ m_pkt->size = 0;
+ continue;
+ }
- if(m_pkt.stream_index != audioIndex)
+ if(m_seekTime > 0 && m_codecContext->codec_id == AV_CODEC_ID_APE)
+ {
+ int64_t rescaledPts = av_rescale(m_pkt->pts,
+ AV_TIME_BASE * (int64_t) m_formatContext->streams[m_audioIndex]->time_base.num,
+ m_formatContext->streams[m_pkt->stream_index]->time_base.den);
+ m_skipBytes = (m_seekTime - rescaledPts) * m_codecContext->sample_rate / AV_TIME_BASE * audioParameters().frameSize();
+ }
+ m_seekTime = -1;
+ }
+ else
{
- m_temp_pkt.size = 0;
- if(m_pkt.data)
+ if(read_error != AVERROR_EOF)
{
- av_packet_unref(&m_pkt);
- continue;
+ char errbuf[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ av_strerror(read_error, errbuf, sizeof(errbuf));
+ qWarning("DecoderFFmpeg: av_read_frame error: %s", errbuf);
}
- return;
+ m_eof = true;
}
+ }
- if(m_seekTime && c->codec_id == AV_CODEC_ID_APE)
+ int send_error = 0;
+
+ if(m_pkt->size > 0)
+ {
+ if(!(send_error = avcodec_send_packet(m_codecContext, m_pkt)))
{
- 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;
+ av_packet_unref(m_pkt);
+ m_pkt->size = 0;
}
- m_seekTime = 0;
}
- if(m_skipBytes > 0 && c->codec_id == AV_CODEC_ID_APE)
+ int recv_error = avcodec_receive_frame(m_codecContext, m_frame);
+
+ if((m_eof || send_error < 0) && recv_error < 0)
{
- while (m_skipBytes > 0)
+ char errbuf[AV_ERROR_MAX_STRING_SIZE] = { 0 };
+ if(!m_eof)
+ {
+ av_strerror(send_error, errbuf, sizeof(errbuf));
+ qWarning("DecoderFFmpeg: avcodec_send_packet error: %s", errbuf);
+ av_strerror(recv_error, errbuf, sizeof(errbuf));
+ qWarning("DecoderFFmpeg: avcodec_receive_frame error: %s", errbuf);
+ }
+ else
{
- m_output_at = ffmpeg_decode();
- if(m_output_at < 0)
- break;
- m_skipBytes -= m_output_at;
+ qDebug("DecoderFFmpeg: finished");
}
+ return;
+ }
- if(m_skipBytes < 0)
+ if(!recv_error)
+ {
+ m_output_size = av_samples_get_buffer_size(nullptr,
+ m_codecContext->channels,
+ m_frame->nb_samples,
+ m_codecContext->sample_fmt, 1);
+ if(m_codecContext->bit_rate)
+ m_bitrate = m_codecContext->bit_rate / 1000;
+
+ //hack for APE
+ if(m_skipBytes > 0 && m_output_size > 0)
{
- qint64 size = m_output_at;
- m_output_at = - m_skipBytes;
- m_output_at = m_output_at - (m_output_at % 4);
+ qint64 len = qMin(m_output_size, m_skipBytes);
+ m_skipBytes -= len;
+ m_output_size -= len;
- if(av_sample_fmt_is_planar(c->sample_fmt) && m_channels > 1)
+ if(!m_output_size)
{
- memmove(m_decoded_frame->extended_data[0],
- m_decoded_frame->extended_data[0] + (size - m_output_at)/2, m_output_at/2);
- memmove(m_decoded_frame->extended_data[1],
- m_decoded_frame->extended_data[1] + (size - m_output_at)/2, m_output_at/2);
+ av_frame_unref(m_frame);
}
- else
+ else if(m_output_size > 0)
{
- memmove(m_decoded_frame->extended_data[0],
- m_decoded_frame->extended_data[0] + size - m_output_at, m_output_at);
+ if(av_sample_fmt_is_planar(m_codecContext->sample_fmt) && m_channels > 1)
+ {
+ for(int i = 0; i < m_channels; i++)
+ {
+ memmove(m_frame->extended_data[i],
+ m_frame->extended_data[i] + len / m_channels, m_output_size / m_channels);
+ }
+ }
+ else
+ {
+ memmove(m_frame->extended_data[0], m_frame->extended_data[0] + len, m_output_size);
+ }
}
- m_skipBytes = 0;
}
}
- else
- m_output_at = ffmpeg_decode();
-
- if(m_output_at < 0)
- {
- m_output_at = 0;
- m_temp_pkt.size = 0;
-
- if(m_pkt.data)
- av_packet_unref(&m_pkt);
-
- m_pkt.data = nullptr;
- m_temp_pkt.size = 0;
- break;
- }
- else if(m_output_at == 0 && !m_pkt.data)
- {
- return;
- }
- else if(m_output_at == 0)
- {
- if(m_pkt.data)
- av_packet_unref(&m_pkt);
-
- m_pkt.data = nullptr;
- m_temp_pkt.size = 0;
- continue;
- }
}
}
diff --git a/src/plugins/Input/ffmpeg/decoder_ffmpeg.h b/src/plugins/Input/ffmpeg/decoder_ffmpeg.h
index a5748e394..5bea3901d 100644
--- a/src/plugins/Input/ffmpeg/decoder_ffmpeg.h
+++ b/src/plugins/Input/ffmpeg/decoder_ffmpeg.h
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2006-2019 by Ilya Kotov *
+ * Copyright (C) 2006-2020 by Ilya Kotov *
* forkotov02@ya.ru *
* *
* This program is free software; you can redistribute it and/or modify *
@@ -50,24 +50,23 @@ private:
//helper functions
void fillBuffer();
- AVFormatContext *ic;
- AVCodecContext *c;
-
- int m_bitrate, audioIndex;
+ AVFormatContext *m_formatContext = nullptr;
+ AVCodecContext *m_codecContext = nullptr;
+ AVIOContext *m_stream = nullptr;
+ AVFrame *m_frame = nullptr;
+ int m_bitrate = 0, m_audioIndex = 0;
QString m_path;
- qint64 m_totalTime;
- AVPacket m_pkt;
- AVPacket m_temp_pkt;
- qint64 m_output_at;
- uchar *m_input_buf;
- int64_t m_seekTime;
- qint64 m_skipBytes;
- int m_channels;
+ qint64 m_totalTime = 0;
+ AVPacket *m_pkt = nullptr;
+ qint64 m_output_size = 0;
+ uchar *m_input_buf = nullptr;
+ int64_t m_seekTime = -1;
+ qint64 m_skipBytes = 0;
+ int m_channels = 0;
+ bool m_eof = false;
+
- qint64 ffmpeg_decode();
- AVIOContext *m_stream;
- AVFrame *m_decoded_frame;
};