File indexing completed on 2024-12-01 07:32:47
0001 /* 0002 SPDX-FileCopyrightText: 1998-2008 Sebastian Trueg <trueg@k3b.org> 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 #include "k3bffmpegwrapper.h" 0006 #include "k3bplugin_i18n.h" 0007 0008 #include <config-k3b.h> 0009 0010 extern "C" { 0011 /* 0012 Recent versions of FFmpeg uses C99 constant macros which are not present in C++ standard. 0013 The macro __STDC_CONSTANT_MACROS allow C++ to use these macros. Although it's not defined by C++ standard 0014 it's supported by many implementations. 0015 See bug 236036 and discussion: https://lists.ffmpeg.org/pipermail/ffmpeg-devel/2010-May/095488.html 0016 */ 0017 #define __STDC_CONSTANT_MACROS 0018 #ifdef NEWFFMPEGAVCODECPATH 0019 #include <libavcodec/avcodec.h> 0020 #include <libavformat/avformat.h> 0021 #else 0022 #include <ffmpeg/avcodec.h> 0023 #include <ffmpeg/avformat.h> 0024 #endif 0025 } 0026 0027 #include <cstring> 0028 #include <cmath> 0029 0030 0031 #ifndef HAVE_FFMPEG_CODEC_MP3 0032 # define CODEC_ID_MP3 CODEC_ID_MP3LAME 0033 #endif 0034 0035 K3bFFMpegWrapper* K3bFFMpegWrapper::s_instance = nullptr; 0036 0037 0038 class K3bFFMpegFile::Private 0039 { 0040 public: 0041 ::AVFormatContext* formatContext; 0042 const ::AVCodec* codec; 0043 ::AVCodecContext* codecContext; 0044 ::AVStream *audio_stream; 0045 0046 K3b::Msf length; 0047 0048 // for decoding. ffmpeg requires 16-byte alignment. 0049 ::AVFrame* frame = nullptr; 0050 ::AVPacket* packet = nullptr; 0051 char* outputBufferPos = nullptr; 0052 int outputBufferSize = 0; 0053 ::AVSampleFormat sampleFormat; 0054 bool isSpacious; 0055 }; 0056 0057 0058 K3bFFMpegFile::K3bFFMpegFile( const QString& filename ) 0059 : m_filename(filename) 0060 { 0061 d = new Private; 0062 d->formatContext = nullptr; 0063 d->codec = nullptr; 0064 d->audio_stream = nullptr; 0065 d->frame = av_frame_alloc(); 0066 } 0067 0068 0069 K3bFFMpegFile::~K3bFFMpegFile() 0070 { 0071 close(); 0072 av_frame_free(&d->frame); 0073 delete d; 0074 } 0075 0076 0077 bool K3bFFMpegFile::open() 0078 { 0079 close(); 0080 0081 // open the file 0082 int err = ::avformat_open_input( &d->formatContext, m_filename.toLocal8Bit(), 0, 0 ); 0083 if( err < 0 ) { 0084 qDebug() << "(K3bFFMpegFile) unable to open " << m_filename << " with error " << err; 0085 return false; 0086 } 0087 0088 // analyze the streams 0089 ::avformat_find_stream_info( d->formatContext, 0 ); 0090 0091 // we only handle files containing one audio stream 0092 if( d->formatContext->nb_streams == 1 ) { 0093 d->audio_stream = d->formatContext->streams[0]; 0094 } else { 0095 for (uint i = 0; i < d->formatContext->nb_streams; ++i) { 0096 if (d->formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { 0097 if (!d->audio_stream) { 0098 d->audio_stream = d->formatContext->streams[i]; 0099 } else { 0100 d->audio_stream = nullptr; 0101 qDebug() << "(K3bFFMpegFile) more than one audio stream in " << m_filename; 0102 return false; 0103 } 0104 } 0105 } 0106 } 0107 0108 // get the codec 0109 d->codec = ::avcodec_find_decoder( 0110 d->audio_stream->codecpar->codec_id 0111 ); 0112 if( !d->codec ) { 0113 qDebug() << "(K3bFFMpegFile) no codec found for " << m_filename; 0114 return false; 0115 } 0116 0117 // get the codec context 0118 d->codecContext = ::avcodec_alloc_context3(d->codec); 0119 ::avcodec_parameters_to_context(d->codecContext, d->audio_stream->codecpar); 0120 if(d->codecContext->codec_type != AVMEDIA_TYPE_AUDIO) { 0121 qDebug() << "(K3bFFMpegFile) not a simple audio stream: " << m_filename; 0122 return false; 0123 } 0124 0125 // open the codec on our context 0126 qDebug() << "(K3bFFMpegFile) found codec for " << m_filename; 0127 if( ::avcodec_open2( d->codecContext, d->codec, 0 ) < 0 ) { 0128 qDebug() << "(K3bFFMpegDecoderFactory) could not open codec."; 0129 return false; 0130 } 0131 0132 // determine the length of the stream 0133 d->length = K3b::Msf::fromSeconds( 0134 static_cast<double>(d->formatContext->duration) / static_cast<double>(AV_TIME_BASE)); 0135 0136 if( d->length == 0 ) { 0137 qDebug() << "(K3bFFMpegDecoderFactory) invalid length."; 0138 return false; 0139 } 0140 0141 d->sampleFormat = d->codecContext->sample_fmt; 0142 d->isSpacious = ::av_sample_fmt_is_planar(d->sampleFormat) && 0143 d->codecContext->channels > 1; 0144 d->packet = ::av_packet_alloc(); 0145 0146 // dump some debugging info 0147 ::av_dump_format( d->formatContext, 0, m_filename.toLocal8Bit(), 0 ); 0148 0149 return true; 0150 } 0151 0152 0153 void K3bFFMpegFile::close() 0154 { 0155 d->outputBufferSize = 0; 0156 ::av_packet_free(&d->packet); 0157 0158 if( d->codec ) { 0159 ::avcodec_close(d->codecContext); 0160 d->codec = nullptr; 0161 ::avcodec_free_context(&d->codecContext); 0162 d->codecContext = nullptr; 0163 } 0164 0165 if( d->formatContext ) { 0166 ::avformat_close_input( &d->formatContext ); 0167 d->formatContext = nullptr; 0168 } 0169 0170 d->audio_stream = nullptr; 0171 } 0172 0173 0174 K3b::Msf K3bFFMpegFile::length() const 0175 { 0176 return d->length; 0177 } 0178 0179 0180 int K3bFFMpegFile::sampleRate() const 0181 { 0182 return d->codecContext->sample_rate; 0183 } 0184 0185 0186 int K3bFFMpegFile::channels() const 0187 { 0188 return d->codecContext->channels; 0189 } 0190 0191 0192 int K3bFFMpegFile::type() const 0193 { 0194 return d->codecContext->codec_id; 0195 } 0196 0197 0198 QString K3bFFMpegFile::typeComment() const 0199 { 0200 switch( type() ) { 0201 case AV_CODEC_ID_WAVPACK: 0202 return i18n("WavPack"); 0203 case AV_CODEC_ID_APE: 0204 return i18n("Monkey's Audio (APE)"); 0205 case AV_CODEC_ID_AAC: 0206 return i18n("Advanced Audio Coding (AAC)"); 0207 default: 0208 return QString::fromLocal8Bit( d->codec->name ); 0209 } 0210 } 0211 0212 0213 QString K3bFFMpegFile::title() const 0214 { 0215 // FIXME: is this UTF8 or something?? 0216 AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "TITLE", nullptr, 0 ); 0217 return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString(); 0218 } 0219 0220 0221 QString K3bFFMpegFile::author() const 0222 { 0223 // FIXME: is this UTF8 or something?? 0224 AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "ARTIST", nullptr, 0 ); 0225 return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString(); 0226 } 0227 0228 0229 QString K3bFFMpegFile::comment() const 0230 { 0231 // FIXME: is this UTF8 or something?? 0232 AVDictionaryEntry *ade = av_dict_get( d->formatContext->metadata, "COMMENT", nullptr, 0 ); 0233 return ade && ade->value && ade->value[0] != '\0' ? QString::fromLocal8Bit( ade->value ) : QString(); 0234 } 0235 0236 0237 int K3bFFMpegFile::read(char* buf, int bufLen) 0238 { 0239 if (!buf ) { 0240 return -1; 0241 } 0242 0243 if(d->outputBufferSize <= 0) 0244 d->outputBufferPos = new char[bufLen]; 0245 0246 int ret = fillOutputBuffer(); 0247 if (ret <= 0) { 0248 return ret; 0249 } 0250 0251 int len = qMin(bufLen, ret); 0252 ::memcpy(buf, d->outputBufferPos, len); 0253 0254 // TODO: only swap if needed 0255 for(int i = 0; i < len-1; i += 2) 0256 std::swap(buf[i], buf[i+1]); // BE -> LE 0257 0258 d->outputBufferSize -= len; 0259 if(d->outputBufferSize > 0) 0260 d->outputBufferPos += len; 0261 else 0262 delete[] d->outputBufferPos; 0263 0264 return len; 0265 } 0266 0267 0268 int K3bFFMpegFile::readPacket() 0269 { 0270 if( ::av_read_frame( d->formatContext, d->packet ) < 0 ) { 0271 return 0; 0272 } 0273 0274 return d->packet->size; 0275 } 0276 0277 0278 int K3bFFMpegFile::fillOutputBuffer() 0279 { 0280 static int ret = -1; 0281 0282 // decode if the output buffer is empty 0283 while( d->outputBufferSize <= 0 ) { 0284 d->outputBufferSize = 0; 0285 0286 if ( ret >= 0 ) { 0287 ret = ::avcodec_receive_frame( d->codecContext, d->frame ); 0288 0289 if ( ret == AVERROR(EAGAIN) || ret == AVERROR_EOF ) { 0290 ret = -1; 0291 continue; 0292 } 0293 else if ( ret < 0 ) { 0294 qDebug() << "(K3bFFMpegFile) decoding failed for " << m_filename; 0295 return -1; 0296 } 0297 0298 int nb_s = d->frame->nb_samples; 0299 const int nb_ch = 2; // copy only 2 channels even if there're more 0300 d->outputBufferSize = nb_s * nb_ch * 2; // 2 means 2 bytes (16bit) 0301 0302 if( d->isSpacious ) { 0303 if( d->sampleFormat == AV_SAMPLE_FMT_FLTP ) { 0304 constexpr int width = sizeof(float); // sample width of float audio 0305 for( int sample = 0; sample < nb_s; sample++ ) { 0306 for( int ch = 0; ch < nb_ch; ch++ ) { 0307 float val = *(reinterpret_cast<float*>( 0308 d->frame->extended_data[ch] + sample * width)); 0309 val = ::abs(val) > 1 ? ::copysign(1.0, val) : val; 0310 int16_t result = static_cast<int16_t>( 0311 val * 32767.0 + 32768.5) - 32768; 0312 ::memcpy( 0313 d->outputBufferPos + (sample * nb_ch + ch) * 2, 0314 &result, 0315 2 // 2 is sample width of 16 bit audio 0316 ); 0317 } 0318 } 0319 } else if ( d->sampleFormat == AV_SAMPLE_FMT_S16P ) { 0320 for(int sample = 0; sample < nb_s; sample++) { 0321 for(int ch = 0; ch < nb_ch; ch++) { 0322 ::memcpy( 0323 d->outputBufferPos + (sample * nb_ch + ch) * 2, 0324 d->frame->extended_data[ch] + sample * 2, 0325 2 // 16 bit here as well 0326 ); 0327 } 0328 } 0329 } else { 0330 qDebug() << "(K3bFFMpegFile) some planar formats are not supported yet"; 0331 return -1; 0332 } 0333 } else { 0334 qDebug() << "(K3bFFMpegFile) non planar and monophonic audio support is not implemented"; 0335 return -1; 0336 } 0337 } else { 0338 // make sure we have data to decode 0339 if( readPacket() == 0 ) { 0340 return 0; 0341 } 0342 ret = ::avcodec_send_packet( d->codecContext, d->packet ); 0343 if( ret < 0 ) { 0344 qDebug() << "(K3bFFMpegFile) error submitting packet to the decoder"; 0345 return -1; 0346 } 0347 continue; 0348 } 0349 } 0350 0351 return d->outputBufferSize; 0352 } 0353 0354 0355 bool K3bFFMpegFile::seek( const K3b::Msf& msf ) 0356 { 0357 d->outputBufferSize = 0; 0358 0359 double seconds = (double)msf.totalFrames()/75.0; 0360 quint64 timestamp = (quint64)(seconds * (double)AV_TIME_BASE); 0361 0362 // FIXME: do we really need the start_time and why? 0363 return ( ::av_seek_frame( d->formatContext, -1, timestamp + d->formatContext->start_time, 0 ) >= 0 ); 0364 } 0365 0366 0367 0368 0369 0370 0371 K3bFFMpegWrapper::K3bFFMpegWrapper() 0372 { 0373 //::av_register_all(); 0374 } 0375 0376 0377 K3bFFMpegWrapper::~K3bFFMpegWrapper() 0378 { 0379 s_instance = nullptr; 0380 } 0381 0382 0383 K3bFFMpegWrapper* K3bFFMpegWrapper::instance() 0384 { 0385 if( !s_instance ) { 0386 s_instance = new K3bFFMpegWrapper(); 0387 } 0388 0389 return s_instance; 0390 } 0391 0392 0393 K3bFFMpegFile* K3bFFMpegWrapper::open( const QString& filename ) const 0394 { 0395 K3bFFMpegFile* file = new K3bFFMpegFile( filename ); 0396 if( file->open() ) { 0397 #ifndef K3B_FFMPEG_ALL_CODECS 0398 // 0399 // only allow tested formats. ffmpeg seems not to be too reliable with every format. 0400 // mp3 being one of them sadly. Most importantly: allow the libsndfile decoder to do 0401 // its thing. 0402 // 0403 if( file->type() == AV_CODEC_ID_AAC || 0404 file->type() == AV_CODEC_ID_APE || 0405 file->type() == AV_CODEC_ID_WAVPACK ) 0406 #endif 0407 return file; 0408 } 0409 0410 delete file; 0411 return nullptr; 0412 }