Commit b5134345 authored by Mikalai S.'s avatar Mikalai S. Committed by Albert Astals Cid
Browse files

Port ffmpeg plugin to FFMpeg 5.0 API

parent 7341cec2
......@@ -126,7 +126,7 @@ if(K3B_ENABLE_MUSICBRAINZ)
endif()
if(K3B_BUILD_FFMPEG_DECODER_PLUGIN)
find_package(FFmpeg)
find_package(FFmpeg 4.4.0)
set_package_properties(FFmpeg PROPERTIES
PURPOSE "Needed for the K3b FFmpeg decoder plugin which can decode virtually all audio types."
URL "https://ffmpeg.org/"
......
......@@ -34,8 +34,6 @@
#cmakedefine HAVE_FFMPEG_AVFORMAT_FIND_STREAM_INFO
#cmakedefine HAVE_FFMPEG_AVFORMAT_CLOSE_INPUT
#cmakedefine HAVE_FFMPEG_AVCODEC_OPEN2
#cmakedefine HAVE_FFMPEG_AVCODEC_DECODE_AUDIO2
#cmakedefine HAVE_FFMPEG_AVCODEC_DECODE_AUDIO3
#cmakedefine HAVE_FFMPEG_AVCODEC_DECODE_AUDIO4
#cmakedefine HAVE_FFMPEG_AVMEDIA_TYPE
#cmakedefine HAVE_FFMPEG_CODEC_MP3
kcoreaddons_add_plugin(k3bffmpegdecoder
SOURCES k3bffmpegdecoder.cpp k3bffmpegwrapper.cpp
INSTALL_NAMESPACE "k3b")
INSTALL_NAMESPACE "k3b_plugins")
if(FFMPEG_INCLUDE_DIR_OLD_STYLE)
message(STATUS "didn't find new ffmpegcodecpath")
......
......@@ -24,56 +24,34 @@ extern "C" {
#endif
}
#include <string.h>
#include <math.h>
#include <cstring>
#include <cmath>
#define FFMPEG_CODEC(s) (s->codec)
#ifndef HAVE_FFMPEG_AVFORMAT_OPEN_INPUT
// this works because the parameters/options are not used
# define avformat_open_input(c,s,f,o) av_open_input_file(c,s,f,0,o)
#endif
#ifndef HAVE_FFMPEG_AV_DUMP_FORMAT
# define av_dump_format(c,x,f,y) dump_format(c,x,f,y)
#endif
#ifndef HAVE_FFMPEG_AVFORMAT_FIND_STREAM_INFO
# define avformat_find_stream_info(c,o) av_find_stream_info(c)
#endif
#ifndef HAVE_FFMPEG_AVFORMAT_CLOSE_INPUT
# define avformat_close_input(c) av_close_input_file(*c)
#endif
#ifndef HAVE_FFMPEG_AVCODEC_OPEN2
# define avcodec_open2(a,c,o) avcodec_open(a,c)
#endif
#ifndef HAVE_FFMPEG_AVMEDIA_TYPE
# define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
#endif
#ifndef HAVE_FFMPEG_CODEC_MP3
# define CODEC_ID_MP3 CODEC_ID_MP3LAME
#endif
K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = 0;
K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = nullptr;
class K3bFFMpegFile::Private
{
public:
::AVFormatContext* formatContext;
::AVCodec* codec;
const ::AVCodec* codec;
::AVCodecContext* codecContext;
::AVStream *audio_stream;
K3b::Msf length;
// for decoding. ffmpeg requires 16-byte alignment.
::AVFrame* frame;
char* outputBufferPos;
int outputBufferSize;
::AVPacket packet;
quint8* packetData;
int packetSize;
::AVFrame* frame = nullptr;
::AVPacket* packet = nullptr;
char* outputBufferPos = nullptr;
int outputBufferSize = 0;
::AVSampleFormat sampleFormat;
bool isSpacious;
int sampleFormat;
};
......@@ -81,8 +59,8 @@ K3bFFMpegFile::K3bFFMpegFile( const QString& filename )
: m_filename(filename)
{
d = new Private;
d->formatContext = 0;
d->codec = 0;
d->formatContext = nullptr;
d->codec = nullptr;
d->audio_stream = nullptr;
d->frame = av_frame_alloc();
}
......@@ -127,38 +105,43 @@ bool K3bFFMpegFile::open()
}
}
// urgh... ugly
::AVCodecContext* codecContext = FFMPEG_CODEC(d->audio_stream);
if( codecContext->codec_type != AVMEDIA_TYPE_AUDIO)
{
qDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename;
return false;
}
// get the codec
d->codec = ::avcodec_find_decoder(codecContext->codec_id);
d->codec = ::avcodec_find_decoder(
d->audio_stream->codecpar->codec_id
);
if( !d->codec ) {
qDebug() << "(K3bFFMpegFile) no codec found for " << m_filename;
return false;
}
// get the codec context
d->codecContext = ::avcodec_alloc_context3(d->codec);
::avcodec_parameters_to_context(d->codecContext, d->audio_stream->codecpar);
if(d->codecContext->codec_type != AVMEDIA_TYPE_AUDIO) {
qDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename;
return false;
}
// open the codec on our context
qDebug() << "(K3bFFMpegFile) found codec for " << m_filename;
if( ::avcodec_open2( codecContext, d->codec, 0 ) < 0 ) {
if( ::avcodec_open2( d->codecContext, d->codec, 0 ) < 0 ) {
qDebug() << "(K3bFFMpegDecoderFactory) could not open codec.";
return false;
}
// determine the length of the stream
d->length = K3b::Msf::fromSeconds( (double)d->formatContext->duration / (double)AV_TIME_BASE );
d->length = K3b::Msf::fromSeconds(
static_cast<double>(d->formatContext->duration) / static_cast<double>(AV_TIME_BASE));
if( d->length == 0 ) {
qDebug() << "(K3bFFMpegDecoderFactory) invalid length.";
return false;
}
d->sampleFormat = d->audio_stream->codecpar->format;
d->isSpacious = ::av_sample_fmt_is_planar((AVSampleFormat)d->sampleFormat) && d->audio_stream->codecpar->channels > 1;
d->sampleFormat = d->codecContext->sample_fmt;
d->isSpacious = ::av_sample_fmt_is_planar(d->sampleFormat) &&
d->codecContext->channels > 1;
d->packet = ::av_packet_alloc();
// dump some debugging info
::av_dump_format( d->formatContext, 0, m_filename.toLocal8Bit(), 0 );
......@@ -170,17 +153,18 @@ bool K3bFFMpegFile::open()
void K3bFFMpegFile::close()
{
d->outputBufferSize = 0;
d->packetSize = 0;
d->packetData = 0;
::av_packet_free(&d->packet);
if( d->codec ) {
::avcodec_close( FFMPEG_CODEC(d->audio_stream) );
d->codec = 0;
::avcodec_close(d->codecContext);
d->codec = nullptr;
::avcodec_free_context(&d->codecContext);
d->codecContext = nullptr;
}
if( d->formatContext ) {
::avformat_close_input( &d->formatContext );
d->formatContext = 0;
d->formatContext = nullptr;
}
d->audio_stream = nullptr;
......@@ -195,29 +179,25 @@ K3b::Msf K3bFFMpegFile::length() const
int K3bFFMpegFile::sampleRate() const
{
return d->audio_stream->codecpar->sample_rate;
return d->codecContext->sample_rate;
}
int K3bFFMpegFile::channels() const
{
return d->audio_stream->codecpar->channels;
return d->codecContext->channels;
}
int K3bFFMpegFile::type() const
{
return d->audio_stream->codecpar->codec_id;
return d->codecContext->codec_id;
}
QString K3bFFMpegFile::typeComment() const
{
switch( type() ) {
case AV_CODEC_ID_WMAV1:
return i18n("Windows Media v1");
case AV_CODEC_ID_WMAV2:
return i18n("Windows Media v2");
case AV_CODEC_ID_WAVPACK:
return i18n("WavPack");
case AV_CODEC_ID_APE:
......@@ -233,7 +213,7 @@ QString K3bFFMpegFile::typeComment() const
QString K3bFFMpegFile::title() const
{
// FIXME: is this UTF8 or something??
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "TITLE", NULL, 0 );
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "TITLE", nullptr, 0 );
return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
}
......@@ -241,7 +221,7 @@ QString K3bFFMpegFile::title() const
QString K3bFFMpegFile::author() const
{
// FIXME: is this UTF8 or something??
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "ARTIST", NULL, 0 );
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "ARTIST", nullptr, 0 );
return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
}
......@@ -249,115 +229,117 @@ QString K3bFFMpegFile::author() const
QString K3bFFMpegFile::comment() const
{
// FIXME: is this UTF8 or something??
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "COMMENT", NULL, 0 );
AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "COMMENT", nullptr, 0 );
return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
}
int K3bFFMpegFile::read(char* buf, int bufLen)
{
if (!buf || !d->outputBufferPos)
if (!buf ) {
return -1;
}
if(d->outputBufferSize <= 0)
d->outputBufferPos = new char[bufLen];
int ret = fillOutputBuffer();
if (ret <= 0) {
return ret;
}
int len = qMin(bufLen, d->outputBufferSize);
int len = qMin(bufLen, ret);
::memcpy(buf, d->outputBufferPos, len);
if(d->isSpacious && bufLen > d->outputBufferSize)
delete[] d->outputBufferPos; // clean up allocated space
// TODO: only swap if needed
for(int i=0; i<len-1; i+=2)
qSwap(buf[i], buf[i+1]); // BE -> LE
for(int i = 0; i < len-1; i += 2)
std::swap(buf[i], buf[i+1]); // BE -> LE
d->outputBufferSize -= len;
if(d->outputBufferSize > 0)
d->outputBufferPos += len;
else
delete[] d->outputBufferPos;
return len;
}
// fill d->packetData with data to decode
int K3bFFMpegFile::readPacket()
{
if( d->packetSize <= 0 ) {
::av_init_packet( &d->packet );
if( ::av_read_frame( d->formatContext, &d->packet ) < 0 ) {
return 0;
}
d->packetSize = d->packet.size;
d->packetData = d->packet.data;
if( ::av_read_frame( d->formatContext, d->packet ) < 0 ) {
return 0;
}
return d->packetSize;
return d->packet->size;
}
// decode data in d->packetData and fill d->outputBuffer
int K3bFFMpegFile::fillOutputBuffer()
{
static int ret = -1;
// decode if the output buffer is empty
while(d->outputBufferSize <= 0) {
while( d->outputBufferSize <= 0 ) {
d->outputBufferSize = 0;
// make sure we have data to decode
if( readPacket() == 0 ) {
return 0;
}
if ( ret >= 0 ) {
ret = ::avcodec_receive_frame( d->codecContext, d->frame );
int gotFrame = 0;
int len = ::avcodec_decode_audio4(
FFMPEG_CODEC(d->audio_stream),
d->frame,
&gotFrame,
&d->packet );
if( d->packetSize <= 0 || len < 0 )
::av_packet_unref( &d->packet );
if( len < 0 ) {
qDebug() << "(K3bFFMpegFile) decoding failed for " << m_filename;
return -1;
}
if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) {
ret = -1;
continue;
}
else if ( ret < 0 ) {
qDebug() << "(K3bFFMpegFile) decoding failed for " << m_filename;
return -1;
}
if (gotFrame) {
int nb_s = d->frame->nb_samples;
int nb_ch = 2; // copy only two channels even if there're more
d->outputBufferSize = nb_s * nb_ch * 2; // 2 means 2 bytes (16bit)
d->outputBufferPos = reinterpret_cast<char*>(
d->frame->extended_data[0]);
if(d->isSpacious) {
d->outputBufferPos = new char[d->outputBufferSize];
if(d->sampleFormat == AV_SAMPLE_FMT_FLTP) {
int width = sizeof(float); // sample width of float audio
for(int sample=0; sample<nb_s; sample++) {
for(int ch=0; ch<nb_ch; ch++) {
const int nb_ch = 2; // copy only 2 channels even if there're more
d->outputBufferSize = nb_s * nb_ch * 2; // 2 means 2 bytes (16bit)
if( d->isSpacious ) {
if( d->sampleFormat == AV_SAMPLE_FMT_FLTP ) {
constexpr int width = sizeof(float); // sample width of float audio
for( int sample = 0; sample < nb_s; sample++ ) {
for( int ch = 0; ch < nb_ch; ch++ ) {
float val = *(reinterpret_cast<float*>(
d->frame->extended_data[ch] + sample * width));
val = ::abs(val) > 1 ? ::copysign(1.0, val) : val;
int16_t result = static_cast<int16_t>(
val * 32767.0 + 32768.5) - 32768;
::memcpy(d->outputBufferPos + (sample*nb_ch+ch) * 2,
&result,
2); // 2 is sample width of 16 bit audio
::memcpy(
d->outputBufferPos + (sample * nb_ch + ch) * 2,
&result,
2 // 2 is sample width of 16 bit audio
);
}
}
} else {
for(int sample=0; sample<nb_s; sample++) {
for(int ch=0; ch<nb_ch; ch++) {
::memcpy(d->outputBufferPos + (sample*nb_ch+ch) * 2,
d->frame->extended_data[ch] + sample * 2,
2); // 16 bit here as well
for(int sample = 0; sample < nb_s; sample++) {
for(int ch = 0; ch < nb_ch; ch++) {
::memcpy(
d->outputBufferPos + (sample * nb_ch + ch) * 2,
d->frame->extended_data[ch] + sample * 2,
2 // 16 bit here as well
);
}
}
}
}
} else {
// make sure we have data to decode
if( readPacket() == 0 ) {
return 0;
}
ret = ::avcodec_send_packet( d->codecContext, d->packet );
if( ret < 0 ) {
qDebug() << "(K3bFFMpegFile) error submitting packet to the decoder";
return -1;
}
continue;
}
d->packetSize -= len;
d->packetData += len;
}
return d->outputBufferSize;
......@@ -367,7 +349,6 @@ int K3bFFMpegFile::fillOutputBuffer()
bool K3bFFMpegFile::seek( const K3b::Msf& msf )
{
d->outputBufferSize = 0;
d->packetSize = 0;
double seconds = (double)msf.totalFrames()/75.0;
quint64 timestamp = (quint64)(seconds * (double)AV_TIME_BASE);
......@@ -383,13 +364,13 @@ bool K3bFFMpegFile::seek( const K3b::Msf& msf )
K3bFFMpegWrapper::K3bFFMpegWrapper()
{
::av_register_all();
//::av_register_all();
}
K3bFFMpegWrapper::~K3bFFMpegWrapper()
{
s_instance = 0;
s_instance = nullptr;
}
......@@ -413,9 +394,7 @@ K3bFFMpegFile* K3bFFMpegWrapper::open( const QString& filename ) const
// mp3 being one of them sadly. Most importantly: allow the libsndfile decoder to do
// its thing.
//
if( file->type() == AV_CODEC_ID_WMAV1 ||
file->type() == AV_CODEC_ID_WMAV2 ||
file->type() == AV_CODEC_ID_AAC ||
if( file->type() == AV_CODEC_ID_AAC ||
file->type() == AV_CODEC_ID_APE ||
file->type() == AV_CODEC_ID_WAVPACK )
#endif
......@@ -423,5 +402,5 @@ K3bFFMpegFile* K3bFFMpegWrapper::open( const QString& filename ) const
}
delete file;
return 0;
return nullptr;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment