k3bffmpegwrapper.cpp 11.8 KB
Newer Older
1
/*
2
 *
Laurent Montel's avatar
Laurent Montel committed
3
 *
4
 * Copyright (C) 2004-2008 Sebastian Trueg <trueg@k3b.org>
5 6
 *
 * This file is part of the K3b project.
7
 * Copyright (C) 1998-2008 Sebastian Trueg <trueg@k3b.org>
8 9 10 11 12 13 14
 *
 * 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.
 * See the file "COPYING" for the exact licensing terms.
 */
15
#include "k3bffmpegwrapper.h"
16
#include "k3bplugin_i18n.h"
17

Laurent Montel's avatar
Laurent Montel committed
18
#include <config-k3b.h>
19

20
extern "C" {
21
/*
22 23
 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
24
 it's supported by many implementations.
25
 See bug 236036 and discussion: https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2010-May/095488.html
26 27
 */
#define __STDC_CONSTANT_MACROS
28 29 30 31
#ifdef NEWFFMPEGAVCODECPATH
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#else
32 33
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>
34
#endif
35
}
36 37

#include <string.h>
38
#include <math.h>
39 40


41
#define FFMPEG_CODEC(s) (s->codec)
42

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
#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
65 66 67 68 69 70 71

K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = 0;


class K3bFFMpegFile::Private
{
public:
72 73
    ::AVFormatContext* formatContext;
    ::AVCodec* codec;
74
    ::AVStream *audio_stream;
75 76 77

    K3b::Msf length;

78
    // for decoding. ffmpeg requires 16-byte alignment.
79
    ::AVFrame* frame;
80 81
    char* outputBufferPos;
    int outputBufferSize;
82
    ::AVPacket packet;
83 84
    quint8* packetData;
    int packetSize;
85 86
    bool isSpacious;
    int sampleFormat;
87 88 89 90
};


K3bFFMpegFile::K3bFFMpegFile( const QString& filename )
91
    : m_filename(filename)
92
{
93 94 95
    d = new Private;
    d->formatContext = 0;
    d->codec = 0;
96
    d->audio_stream = nullptr;
97
    d->frame = av_frame_alloc();
98 99 100 101 102
}


K3bFFMpegFile::~K3bFFMpegFile()
{
103
    close();
104
    av_frame_free(&d->frame);
105
    delete d;
106 107 108 109 110
}


bool K3bFFMpegFile::open()
{
111
    close();
112

113
    // open the file
114
    int err = ::avformat_open_input( &d->formatContext, m_filename.toLocal8Bit(), 0, 0 );
115
    if( err < 0 ) {
116
        qDebug() << "(K3bFFMpegFile) unable to open " << m_filename << " with error " << err;
117 118
        return false;
    }
119

120
    // analyze the streams
121
    ::avformat_find_stream_info( d->formatContext, 0 );
122

123
    // we only handle files containing one audio stream
124 125 126 127 128 129 130 131 132 133 134 135 136 137
    if( d->formatContext->nb_streams == 1 ) {
        d->audio_stream = d->formatContext->streams[0];
    } else  {
        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;
                    qDebug() << "(K3bFFMpegFile) more than one audio stream in " << m_filename;
                    return false;
                }
            }
        }
138
    }
139

140
    // urgh... ugly
141
    ::AVCodecContext* codecContext =  FFMPEG_CODEC(d->audio_stream);
142
    if( codecContext->codec_type != AVMEDIA_TYPE_AUDIO)
143
    {
144
        qDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename;
145 146 147 148
        return false;
    }

    // get the codec
149
    d->codec = ::avcodec_find_decoder(codecContext->codec_id);
150
    if( !d->codec ) {
151
        qDebug() << "(K3bFFMpegFile) no codec found for " << m_filename;
152 153 154 155
        return false;
    }

    // open the codec on our context
156
    qDebug() << "(K3bFFMpegFile) found codec for " << m_filename;
157
    if( ::avcodec_open2( codecContext, d->codec, 0 ) < 0 ) {
158
        qDebug() << "(K3bFFMpegDecoderFactory) could not open codec.";
159 160 161 162 163 164 165
        return false;
    }

    // determine the length of the stream
    d->length = K3b::Msf::fromSeconds( (double)d->formatContext->duration / (double)AV_TIME_BASE );

    if( d->length == 0 ) {
166
        qDebug() << "(K3bFFMpegDecoderFactory) invalid length.";
167 168 169
        return false;
    }

170 171
    d->sampleFormat = d->audio_stream->codecpar->format;
    d->isSpacious = ::av_sample_fmt_is_planar((AVSampleFormat)d->sampleFormat) && d->audio_stream->codecpar->channels > 1;
172

173
    // dump some debugging info
174
    ::av_dump_format( d->formatContext, 0, m_filename.toLocal8Bit(), 0 );
175 176

    return true;
177 178 179 180 181
}


void K3bFFMpegFile::close()
{
182 183 184
    d->outputBufferSize = 0;
    d->packetSize = 0;
    d->packetData = 0;
185

186
    if( d->codec ) {
187
        ::avcodec_close( FFMPEG_CODEC(d->audio_stream) );
188 189
        d->codec = 0;
    }
190

191
    if( d->formatContext ) {
192
        ::avformat_close_input( &d->formatContext );
193 194
        d->formatContext = 0;
    }
195 196

    d->audio_stream = nullptr;
197 198 199 200 201
}


K3b::Msf K3bFFMpegFile::length() const
{
202
    return d->length;
203 204 205 206 207
}


int K3bFFMpegFile::sampleRate() const
{
208
    return d->audio_stream->codecpar->sample_rate;
209 210 211 212 213
}


int K3bFFMpegFile::channels() const
{
214
    return d->audio_stream->codecpar->channels;
215 216 217
}


218 219
int K3bFFMpegFile::type() const
{
220
    return d->audio_stream->codecpar->codec_id;
221 222 223 224
}


QString K3bFFMpegFile::typeComment() const
225
{
226
    switch( type() ) {
227
    case AV_CODEC_ID_WMAV1:
228
        return i18n("Windows Media v1");
229
    case AV_CODEC_ID_WMAV2:
230
        return i18n("Windows Media v2");
231 232 233 234
    case AV_CODEC_ID_WAVPACK:
        return i18n("WavPack");
    case AV_CODEC_ID_APE:
        return i18n("Monkey's Audio (APE)");
235
    case AV_CODEC_ID_AAC:
236 237 238 239
        return i18n("Advanced Audio Coding (AAC)");
    default:
        return QString::fromLocal8Bit( d->codec->name );
    }
240 241 242 243 244
}


QString K3bFFMpegFile::title() const
{
245
    // FIXME: is this UTF8 or something??
246
    AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "TITLE", NULL, 0 );
247
    return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
248 249 250 251 252
}


QString K3bFFMpegFile::author() const
{
253
    // FIXME: is this UTF8 or something??
254
    AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "ARTIST", NULL, 0 );
255
    return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
256 257 258 259 260
}


QString K3bFFMpegFile::comment() const
{
261
    // FIXME: is this UTF8 or something??
262
    AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "COMMENT", NULL, 0 );
263
    return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString();
264 265 266
}


267
int K3bFFMpegFile::read(char* buf, int bufLen)
268
{
269 270 271
    if (!buf || !d->outputBufferPos)
        return -1;

272 273 274 275 276 277
    int ret = fillOutputBuffer();
    if (ret <= 0) {
        return ret;
    }

    int len = qMin(bufLen, d->outputBufferSize);
278
    ::memcpy(buf, d->outputBufferPos, len);
279

280 281 282
    if(d->isSpacious && bufLen > d->outputBufferSize)
        delete[] d->outputBufferPos; // clean up allocated space

283
    // TODO: only swap if needed
284 285
    for(int i=0; i<len-1; i+=2)
        qSwap(buf[i], buf[i+1]); // BE -> LE
286 287

    d->outputBufferSize -= len;
288 289
    if(d->outputBufferSize > 0)
        d->outputBufferPos += len;
290
    return len;
291 292 293 294 295 296
}


// fill d->packetData with data to decode
int K3bFFMpegFile::readPacket()
{
297
    if( d->packetSize <= 0 ) {
298
        ::av_init_packet( &d->packet );
299

300
        if( ::av_read_frame( d->formatContext, &d->packet ) < 0 ) {
301 302 303 304 305
            return 0;
        }
        d->packetSize = d->packet.size;
        d->packetData = d->packet.data;
    }
306

307
    return d->packetSize;
308 309 310 311 312 313
}


// decode data in d->packetData and fill d->outputBuffer
int K3bFFMpegFile::fillOutputBuffer()
{
314
    // decode if the output buffer is empty
315
    while(d->outputBufferSize <= 0) {
316

317 318 319 320
        // make sure we have data to decode
        if( readPacket() == 0 ) {
            return 0;
        }
321

322 323
        int gotFrame = 0;
        int len = ::avcodec_decode_audio4(
324
            FFMPEG_CODEC(d->audio_stream),
325 326 327
            d->frame,
            &gotFrame,
            &d->packet );
328

329
        if( d->packetSize <= 0 || len < 0 )
330
            ::av_packet_unref( &d->packet );
331
        if( len < 0 ) {
332
            qDebug() << "(K3bFFMpegFile) decoding failed for " << m_filename;
333 334 335
            return -1;
        }

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367
        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++) {
                            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
                        }
                    }
                } 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
                        }
                    }
                }
            }
368
        }
369 370 371
        d->packetSize -= len;
        d->packetData += len;
    }
372

373
    return d->outputBufferSize;
374 375 376 377 378
}


bool K3bFFMpegFile::seek( const K3b::Msf& msf )
{
379 380
    d->outputBufferSize = 0;
    d->packetSize = 0;
381

382 383
    double seconds = (double)msf.totalFrames()/75.0;
    quint64 timestamp = (quint64)(seconds * (double)AV_TIME_BASE);
384

385
    // FIXME: do we really need the start_time and why?
386
    return ( ::av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time, 0 ) >= 0 );
387 388 389 390 391 392 393 394 395
}






K3bFFMpegWrapper::K3bFFMpegWrapper()
{
396
    ::av_register_all();
397 398 399 400 401
}


K3bFFMpegWrapper::~K3bFFMpegWrapper()
{
402
    s_instance = 0;
403 404 405 406 407
}


K3bFFMpegWrapper* K3bFFMpegWrapper::instance()
{
408 409 410
    if( !s_instance ) {
        s_instance = new K3bFFMpegWrapper();
    }
411

412
    return s_instance;
413 414 415 416 417
}


K3bFFMpegFile* K3bFFMpegWrapper::open( const QString& filename ) const
{
418 419
    K3bFFMpegFile* file = new K3bFFMpegFile( filename );
    if( file->open() ) {
420
#ifndef K3B_FFMPEG_ALL_CODECS
421 422 423 424 425
        //
        // 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.
        //
426 427
        if( file->type() == AV_CODEC_ID_WMAV1 ||
            file->type() == AV_CODEC_ID_WMAV2 ||
428 429 430
            file->type() == AV_CODEC_ID_AAC ||
            file->type() == AV_CODEC_ID_APE ||
            file->type() == AV_CODEC_ID_WAVPACK )
431
#endif
432 433
            return file;
    }
434

435 436
    delete file;
    return 0;
437
}