Fix metainfo processing, AAC decoding, some domestic code clearance

Initially code entirely taken and adopted from k3b upstream
1e09c7d77f
with subsequent fix of the read() function and reducing of the
compiler warnings.

Signed-off-by: Mashiro <m.t.0x73@gmail.com>
pull/8/head
Mashiro 4 years ago committed by Michele Calgaro
parent da0ee3a001
commit 095c3186b1
Signed by: MicheleC
GPG Key ID: 2A75B7CA8ADED5CF

@ -1,10 +1,10 @@
/*
*
* $Id: k3bffmpegwrapper.cpp 641819 2007-03-12 17:29:23Z trueg $
* Copyright (C) 2004-2007 Sebastian Trueg <trueg@k3b.org>
*
* Copyright (C) 2004-2008 Sebastian Trueg <trueg@k3b.org>
*
* This file is part of the K3b project.
* Copyright (C) 1998-2007 Sebastian Trueg <trueg@k3b.org>
* Copyright (C) 1998-2008 Sebastian Trueg <trueg@k3b.org>
*
* 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
@ -16,316 +16,274 @@
#include <config.h>
#include "k3bffmpegwrapper.h"
#include <tdelocale.h>
extern "C" {
/*
Recent versions of FFmpeg uses C99 constant macros which are not present in C++
standard. The macro __STDC_CONSTANT_MACROS allow C++ to use these macros.
Although it's not defined by C++ standard it's supported by many
implementations. See bug 236036 and discussion:
https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2010-May/095488.html
*/
#define __STDC_CONSTANT_MACROS
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#include <math.h>
#include <string.h>
#include <tdelocale.h>
#define FFMPEG_CODEC(s) (s->codec)
#if LIBAVFORMAT_BUILD < 4629
#define FFMPEG_BUILD_PRE_4629
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0)
// 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
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(52, 101, 0)
#define av_dump_format(c, x, f, y) dump_format(c, x, f, y)
#endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 6, 0)
#define avformat_find_stream_info(c, o) av_find_stream_info(c)
#endif
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 17, 0)
#define avformat_close_input(c) av_close_input_file(*c)
#endif
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(53, 8, 0)
#define avcodec_open2(a, c, o) avcodec_open(a, c)
#endif
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(52, 64, 0)
#define AVMEDIA_TYPE_AUDIO CODEC_TYPE_AUDIO
#define AVMEDIA_TYPE_VIDEO CODEC_TYPE_VIDEO
#define AVMEDIA_TYPE_SUBTITLE CODEC_TYPE_SUBTITLE
#endif
// From libavcodec version 54.25, CodecID have been renamed to AVCodecID and all CODEC_ID_* to AV_CODEC_ID_*.
// This code can be simplified once all supported distros have updated to libavcodec version >=54.25
// From libavcodec version 54.25, CodecID have been renamed to AVCodecID and all
// CODEC_ID_* to AV_CODEC_ID_*. This code can be simplified once all supported
// distros have updated to libavcodec version >=54.25
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54, 25, 0)
#define K3B_CODEC_ID_WMAV1 CODEC_ID_WMAV1
#define K3B_CODEC_ID_WMAV2 CODEC_ID_WMAV2
#define K3B_CODEC_ID_MP3 CODEC_ID_MP3
#define K3B_CODEC_ID_AAC CODEC_ID_AAC
#else
#define K3B_CODEC_ID_WMAV1 AV_CODEC_ID_WMAV1
#define K3B_CODEC_ID_WMAV2 AV_CODEC_ID_WMAV2
#define K3B_CODEC_ID_MP3 AV_CODEC_ID_MP3
#define K3B_CODEC_ID_AAC AV_CODEC_ID_AAC
#define AV_CODEC_ID_WMAV1 CODEC_ID_WMAV1
#define AV_CODEC_ID_WMAV2 CODEC_ID_WMAV2
#define AV_CODEC_ID_MP3 CODEC_ID_MP3
#define AV_CODEC_ID_AAC CODEC_ID_AAC
#endif
// TODO: most of the used av_functions there are deprecated and there
// are troubles with improper frame/packet processing that
// leads to aborting of decode process
//
// [wmav2 @ 0xxxxx] Multiple frames in a packet.
// [wmav2 @ 0xxxxx] Got unexpected packet size after a partial decode
K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = 0;
K3bFFMpegWrapper *K3bFFMpegWrapper::s_instance = nullptr;
class K3bFFMpegFile::Private
{
class K3bFFMpegFile::Private {
public:
AVFormatContext* formatContext;
AVCodec* codec;
TQ_UINT8 *packetData;
K3b::Msf length;
// for decoding
char outputBuffer[192000];
char* outputBufferPos;
::AVFormatContext *formatContext;
::AVCodec *codec;
::AVStream *audio_stream;
::AVSampleFormat sampleFormat;
::AVFrame *frame;
::AVPacket packet;
char *outputBufferPos = nullptr;
int outputBufferSize;
AVPacket packet;
TQ_UINT8* packetData;
int packetSize;
bool isSpacious;
};
K3bFFMpegFile::K3bFFMpegFile( const TQString& filename )
: m_filename(filename)
{
K3bFFMpegFile::K3bFFMpegFile(const TQString &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();
}
K3bFFMpegFile::~K3bFFMpegFile()
{
K3bFFMpegFile::~K3bFFMpegFile() {
close();
av_frame_free(&d->frame);
delete d;
}
bool K3bFFMpegFile::open()
{
bool K3bFFMpegFile::open() {
close();
// open the file
# if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 2, 0)
int err = avformat_open_input( &d->formatContext, m_filename.local8Bit(), 0, 0);
# else
int err = av_open_input_file( &d->formatContext, m_filename.local8Bit(), 0, 0, 0);
# endif
if( err < 0 ) {
kdDebug() << "(K3bFFMpegFile) unable to open " << m_filename << " with error " << err << endl;
int err = ::avformat_open_input(&d->formatContext, m_filename.local8Bit(),
nullptr, nullptr);
if (err < 0) {
kdDebug() << "(K3bFFMpegFile) unable to open " << m_filename
<< " with error " << err;
return false;
}
// analyze the streams
# if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 6, 0)
avformat_find_stream_info( d->formatContext, NULL );
# else
av_find_stream_info( d->formatContext );
# endif
::avformat_find_stream_info(d->formatContext, nullptr);
// we only handle files containing one audio stream
if( d->formatContext->nb_streams != 1 ) {
kdDebug() << "(K3bFFMpegFile) more than one stream in " << m_filename << endl;
for (uint i = 0; i < d->formatContext->nb_streams; ++i) {
if (d->formatContext->streams[i]->codecpar->codec_type ==
AVMEDIA_TYPE_AUDIO) {
if (!d->audio_stream) {
d->audio_stream = d->formatContext->streams[i];
} else {
d->audio_stream = nullptr;
kdDebug() << "(K3bFFMpegFile) more than one audio stream in "
<< m_filename;
return false;
}
}
}
// urgh... ugly
#ifdef FFMPEG_BUILD_PRE_4629
AVCodecContext* codecContext = &d->formatContext->streams[0]->codec;
#else
AVCodecContext* codecContext = d->formatContext->streams[0]->codec;
#endif
if( codecContext->codec_type != AVMEDIA_TYPE_AUDIO ) {
kdDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename << endl;
::AVCodecContext *codecContext = FFMPEG_CODEC(d->audio_stream);
if (codecContext->codec_type != AVMEDIA_TYPE_AUDIO) {
kdDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename;
return false;
}
// get the codec
d->codec = avcodec_find_decoder(codecContext->codec_id);
if( !d->codec ) {
kdDebug() << "(K3bFFMpegFile) no codec found for " << m_filename << endl;
d->codec = ::avcodec_find_decoder(codecContext->codec_id);
if (!d->codec) {
kdDebug() << "(K3bFFMpegFile) no codec found for " << m_filename;
return false;
}
// open the codec on our context
kdDebug() << "(K3bFFMpegFile) found codec for " << m_filename << endl;
if(
# if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 8, 0)
avcodec_open2( codecContext, d->codec, NULL ) < 0
# else
avcodec_open( codecContext, d->codec ) < 0
# endif
) {
kdDebug() << "(K3bFFMpegDecoderFactory) could not open codec." << endl;
kdDebug() << "(K3bFFMpegFile) found codec for " << m_filename;
if (::avcodec_open2(codecContext, d->codec, nullptr) < 0) {
kdDebug() << "(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(double(d->formatContext->duration) /
double(AV_TIME_BASE));
if( d->length == 0 ) {
kdDebug() << "(K3bFFMpegDecoderFactory) invalid length." << endl;
if (d->length == 0) {
kdDebug() << "(K3bFFMpegDecoderFactory) invalid length.";
return false;
}
d->sampleFormat =
static_cast<::AVSampleFormat>(d->audio_stream->codecpar->format);
d->isSpacious = ::av_sample_fmt_is_planar(d->sampleFormat) &&
d->audio_stream->codecpar->channels > 1;
// dump some debugging info
# if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 101, 0)
av_dump_format( d->formatContext, 0, m_filename.local8Bit(), 0 );
# else
dump_format( d->formatContext, 0, m_filename.local8Bit(), 0 );
# endif
::av_dump_format(d->formatContext, 0, m_filename.local8Bit(), 0);
return true;
}
void K3bFFMpegFile::close()
{
void K3bFFMpegFile::close() {
d->outputBufferSize = 0;
d->packetSize = 0;
d->packetData = 0;
d->packetData = nullptr;
if( d->codec ) {
#ifdef FFMPEG_BUILD_PRE_4629
avcodec_close( &d->formatContext->streams[0]->codec );
#else
avcodec_close( d->formatContext->streams[0]->codec );
#endif
d->codec = 0;
if (d->codec) {
::avcodec_close(FFMPEG_CODEC(d->audio_stream));
d->codec = nullptr;
}
if( d->formatContext ) {
# if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(53, 17, 0)
avformat_close_input( &d->formatContext );
# else
av_close_input_file( d->formatContext );
# endif
d->formatContext = 0;
if (d->formatContext) {
::avformat_close_input(&d->formatContext);
d->formatContext = nullptr;
}
}
K3b::Msf K3bFFMpegFile::length() const
{
return d->length;
}
int K3bFFMpegFile::sampleRate() const
{
#ifdef FFMPEG_BUILD_PRE_4629
return d->formatContext->streams[0]->codec.sample_rate;
#else
return d->formatContext->streams[0]->codec->sample_rate;
#endif
d->audio_stream = nullptr;
}
K3b::Msf K3bFFMpegFile::length() const { return d->length; }
int K3bFFMpegFile::channels() const
{
#ifdef FFMPEG_BUILD_PRE_4629
return d->formatContext->streams[0]->codec.channels;
#else
return d->formatContext->streams[0]->codec->channels;
#endif
int K3bFFMpegFile::sampleRate() const {
return d->audio_stream->codecpar->sample_rate;
}
int K3bFFMpegFile::type() const
{
#ifdef FFMPEG_BUILD_PRE_4629
return d->formatContext->streams[0]->codec.codec_id;
#else
return d->formatContext->streams[0]->codec->codec_id;
#endif
int K3bFFMpegFile::channels() const {
return d->audio_stream->codecpar->channels;
}
int K3bFFMpegFile::type() const { return d->audio_stream->codecpar->codec_id; }
TQString K3bFFMpegFile::typeComment() const
{
switch( type() ) {
case K3B_CODEC_ID_WMAV1:
TQString K3bFFMpegFile::typeComment() const {
switch (type()) {
case AV_CODEC_ID_WMAV1:
return i18n("Windows Media v1");
case K3B_CODEC_ID_WMAV2:
case AV_CODEC_ID_WMAV2:
return i18n("Windows Media v2");
case K3B_CODEC_ID_MP3:
return i18n("MPEG 1 Layer III");
case K3B_CODEC_ID_AAC:
case AV_CODEC_ID_WAVPACK:
return i18n("WavPack");
case AV_CODEC_ID_APE:
return i18n("Monkey's Audio (APE)");
case AV_CODEC_ID_AAC:
return i18n("Advanced Audio Coding (AAC)");
default:
return TQString::fromLocal8Bit( d->codec->name );
return TQString::fromLocal8Bit(d->codec->name);
}
}
TQString K3bFFMpegFile::title() const
{
TQString K3bFFMpegFile::title() const {
// FIXME: is this UTF8 or something??
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0)
if( d->formatContext->title[0] != '\0' )
return TQString::fromLocal8Bit( d->formatContext->title );
#else
AVDictionaryEntry *entry = av_dict_get(d->formatContext->metadata, "title", NULL, 0);
if( entry->value[0] != '\0' )
return TQString::fromLocal8Bit( entry->value );
#endif
else
return TQString();
AVDictionaryEntry *ade =
av_dict_get(d->formatContext->metadata, "TITLE", nullptr, 0);
return ade && ade->value && ade->value[0] != '\0'
? TQString::fromLocal8Bit(ade->value)
: TQString();
}
TQString K3bFFMpegFile::author() const
{
TQString K3bFFMpegFile::author() const {
// FIXME: is this UTF8 or something??
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0)
if( d->formatContext->author[0] != '\0' )
return TQString::fromLocal8Bit( d->formatContext->author );
#else
AVDictionaryEntry *entry = av_dict_get(d->formatContext->metadata, "author", NULL, 0);
if( entry->value[0] != '\0' )
return TQString::fromLocal8Bit( entry->value );
#endif
else
return TQString();
AVDictionaryEntry *ade =
av_dict_get(d->formatContext->metadata, "ARTIST", nullptr, 0);
return ade && ade->value && ade->value[0] != '\0'
? TQString::fromLocal8Bit(ade->value)
: TQString();
}
TQString K3bFFMpegFile::comment() const
{
TQString K3bFFMpegFile::comment() const {
// FIXME: is this UTF8 or something??
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(53, 2, 0)
if( d->formatContext->comment[0] != '\0' )
return TQString::fromLocal8Bit( d->formatContext->comment );
#else
AVDictionaryEntry *entry = av_dict_get(d->formatContext->metadata, "comment", NULL, 0);
if( entry->value[0] != '\0' )
return TQString::fromLocal8Bit( entry->value );
#endif
else
return TQString();
AVDictionaryEntry *ade =
av_dict_get(d->formatContext->metadata, "COMMENT", nullptr, 0);
return ade && ade->value && ade->value[0] != '\0'
? TQString::fromLocal8Bit(ade->value)
: TQString();
}
int K3bFFMpegFile::read(char *buf, int bufLen) {
int ret = fillOutputBuffer();
if (ret <= 0) {
return ret;
}
int K3bFFMpegFile::read( char* buf, int bufLen )
{
if( fillOutputBuffer() > 0 ) {
int len = TQMIN(bufLen, d->outputBufferSize);
::memcpy( buf, d->outputBufferPos, len );
::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 ) {
char a = buf[i];
buf[i] = buf[i+1];
buf[i+1] = a;
}
for (int i = 0; i < len - 1; i += 2)
tqSwap(buf[i], buf[i + 1]); // BE -> LE
d->outputBufferPos += len;
d->outputBufferSize -= len;
if (d->outputBufferSize > 0)
d->outputBufferPos += len;
return len;
}
else
return 0;
}
// fill d->packetData with data to decode
int K3bFFMpegFile::readPacket()
{
if( d->packetSize <= 0 ) {
av_init_packet( &d->packet );
int K3bFFMpegFile::readPacket() {
if (d->packetSize <= 0) {
::av_init_packet(&d->packet);
if( av_read_frame( d->formatContext, &d->packet ) < 0 ) {
if (::av_read_frame(d->formatContext, &d->packet) < 0) {
return 0;
}
d->packetSize = d->packet.size;
d->packetData = d->packet.data;
}
@ -333,119 +291,107 @@ int K3bFFMpegFile::readPacket()
return d->packetSize;
}
// decode data in d->packetData and fill d->outputBuffer
int K3bFFMpegFile::fillOutputBuffer()
{
int K3bFFMpegFile::fillOutputBuffer() {
// decode if the output buffer is empty
if( d->outputBufferSize <= 0 ) {
while (d->outputBufferSize <= 0) {
// make sure we have data to decode
if( readPacket() == 0 ) {
if (readPacket() == 0) {
return 0;
}
d->outputBufferPos = d->outputBuffer;
#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(52, 64, 0)
AVPacket avp;
av_init_packet( &avp );
avp.data = d->packetData;
avp.size = d->packetSize;
# if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(53, 25, 0)
int len = avcodec_decode_audio4( d->formatContext->streams[0]->codec,
(AVFrame*)d->outputBuffer, &d->outputBufferSize,
&avp );
# else
int len = avcodec_decode_audio3( d->formatContext->streams[0]->codec,
(short*)d->outputBuffer, &d->outputBufferSize,
&avp );
# endif
#else
#ifdef FFMPEG_BUILD_PRE_4629
int len = avcodec_decode_audio2( &d->formatContext->streams[0]->codec,
#else
int len = avcodec_decode_audio2( d->formatContext->streams[0]->codec,
#endif
(short*)d->outputBuffer, &d->outputBufferSize,
d->packetData, d->packetSize );
#endif
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) {
kdDebug() << "(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++) {
double 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
}
}
} 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
}
}
}
}
}
d->packetSize -= len;
d->packetData += len;
if( d->packetSize <= 0 )
av_free_packet( &d->packet );
}
// if it is still empty try again
if( d->outputBufferSize <= 0 )
return fillOutputBuffer();
else
return d->outputBufferSize;
}
bool K3bFFMpegFile::seek( const K3b::Msf& msf )
{
bool K3bFFMpegFile::seek(const K3b::Msf &msf) {
d->outputBufferSize = 0;
d->packetSize = 0;
double seconds = (double)msf.totalFrames()/75.0;
TQ_UINT64 timestamp = (TQ_UINT64)(seconds * (double)AV_TIME_BASE);
double seconds = double(msf.totalFrames()) / 75.0;
int64_t timestamp = static_cast<int64_t>(seconds * double(AV_TIME_BASE));
// FIXME: do we really need the start_time and why?
#if LIBAVFORMAT_BUILD >= 4619
return ( av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time, 0 ) >= 0 );
#else
return ( av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time ) >= 0 );
#endif
return (::av_seek_frame(d->formatContext, -1,
timestamp + d->formatContext->start_time, 0) >= 0);
}
//
// av_register_all is deprecated since ffmpeg 4.0, can be dropped
K3bFFMpegWrapper::K3bFFMpegWrapper() { ::av_register_all(); }
K3bFFMpegWrapper::~K3bFFMpegWrapper() { s_instance = nullptr; }
K3bFFMpegWrapper::K3bFFMpegWrapper()
{
av_register_all();
}
K3bFFMpegWrapper::~K3bFFMpegWrapper()
{
s_instance = 0;
}
K3bFFMpegWrapper* K3bFFMpegWrapper::instance()
{
if( !s_instance ) {
K3bFFMpegWrapper *K3bFFMpegWrapper::instance() {
if (!s_instance) {
s_instance = new K3bFFMpegWrapper();
}
return s_instance;
}
K3bFFMpegFile* K3bFFMpegWrapper::open( const TQString& filename ) const
{
K3bFFMpegFile* file = new K3bFFMpegFile( filename );
if( file->open() ) {
K3bFFMpegFile *K3bFFMpegWrapper::open(const TQString &filename) const {
K3bFFMpegFile *file = new K3bFFMpegFile(filename);
if (file->open()) {
#ifndef K3B_FFMPEG_ALL_CODECS
//
// only allow tested formats. ffmpeg seems not to be too reliable with every format.
// mp3 being one of them sadly. Most importantly: allow the libsndfile decoder to do
// its thing.
// only allow tested formats. ffmpeg seems not to be too reliable with every
// format. mp3 being one of them sadly. Most importantly: allow the
// libsndfile decoder to do its thing.
//
if( file->type() == K3B_CODEC_ID_WMAV1 ||
file->type() == K3B_CODEC_ID_WMAV2 ||
file->type() == K3B_CODEC_ID_AAC )
if (file->type() == AV_CODEC_ID_WMAV1 ||
file->type() == AV_CODEC_ID_WMAV2 || file->type() == AV_CODEC_ID_AAC ||
file->type() == AV_CODEC_ID_APE || file->type() == AV_CODEC_ID_WAVPACK)
#endif
return file;
}
delete file;
return 0;
return nullptr;
}

Loading…
Cancel
Save