KeyFinder::AudioData* LibAvDecoder::decodeFile(const QString& filePath, const int maxDuration){ QMutexLocker codecMutexLocker(&codecMutex); // mutex the preparatory section of this method AVCodec *codec = NULL; AVFormatContext *fCtx = NULL; AVCodecContext *cCtx = NULL; AVDictionary* dict = NULL; // convert filepath #ifdef Q_OS_WIN const wchar_t* filePathWc = reinterpret_cast<const wchar_t*>(filePath.constData()); const char* filePathCh = utf16_to_utf8(filePathWc); #else QByteArray encodedPath = QFile::encodeName(filePath); const char* filePathCh = encodedPath; #endif // open file int openInputResult = avformat_open_input(&fCtx, filePathCh, NULL, NULL); if(openInputResult != 0){ throw KeyFinder::Exception(GuiStrings::getInstance()->libavCouldNotOpenFile(openInputResult).toLocal8Bit().constData()); } if(avformat_find_stream_info(fCtx, NULL) < 0){ av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavCouldNotFindStreamInformation().toLocal8Bit().constData()); } int audioStream = -1; for(int i=0; i<(signed)fCtx->nb_streams; i++){ if(fCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){ audioStream = i; break; } } if(audioStream == -1){ av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavCouldNotFindAudioStream().toLocal8Bit().constData()); } // Determine duration int durationSeconds = fCtx->duration / AV_TIME_BASE; int durationMinutes = durationSeconds / 60; // First condition is a hack for bizarre overestimation of some MP3s if(durationMinutes < 720 && durationSeconds > maxDuration * 60){ av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->durationExceedsPreference(durationMinutes, durationSeconds % 60, maxDuration).toLocal8Bit().constData()); } // Determine stream codec cCtx = fCtx->streams[audioStream]->codec; codec = avcodec_find_decoder(cCtx->codec_id); if(codec == NULL){ av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavUnsupportedCodec().toLocal8Bit().constData()); } // Open codec int codecOpenResult = avcodec_open2(cCtx, codec, &dict); if(codecOpenResult < 0){ av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavCouldNotOpenCodec(codec->long_name, codecOpenResult).toLocal8Bit().constData()); } ReSampleContext* rsCtx = av_audio_resample_init( cCtx->channels, cCtx->channels, cCtx->sample_rate, cCtx->sample_rate, AV_SAMPLE_FMT_S16, cCtx->sample_fmt, 0, 0, 0, 0); if(rsCtx == NULL){ avcodec_close(cCtx); av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavCouldNotCreateResampleContext().toLocal8Bit().constData()); } qDebug("Decoding %s (%s, %d)", filePathCh, av_get_sample_fmt_name(cCtx->sample_fmt), cCtx->sample_rate); codecMutexLocker.unlock(); // Prep buffer KeyFinder::AudioData *audio = new KeyFinder::AudioData(); audio->setFrameRate(cCtx->sample_rate); audio->setChannels(cCtx->channels); // Decode stream AVPacket avpkt; int badPacketCount = 0; int badPacketThreshold = 100; while(true){ av_init_packet(&avpkt); if(av_read_frame(fCtx, &avpkt) < 0) break; if(avpkt.stream_index == audioStream){ try{ int result = decodePacket(cCtx, rsCtx, &avpkt, audio); if(result != 0){ if(badPacketCount < badPacketThreshold){ badPacketCount++; }else{ avcodec_close(cCtx); av_close_input_file(fCtx); throw KeyFinder::Exception(GuiStrings::getInstance()->libavTooManyBadPackets(badPacketThreshold).toLocal8Bit().constData()); } } }catch(KeyFinder::Exception& e){ throw e; } } av_free_packet(&avpkt); } codecMutexLocker.relock(); audio_resample_close(rsCtx); int codecCloseResult = avcodec_close(cCtx); if(codecCloseResult < 0){ qCritical("Error closing audio codec: %s (%d)", codec->long_name, codecCloseResult); } codecMutexLocker.unlock(); av_close_input_file(fCtx); return audio; }
KeyFinder::AudioData* LibAvDecoder::decodeFile(const QString& filePath){ QMutexLocker codecMutexLocker(&codecMutex); // mutex the preparatory section of this method AVCodec *codec = NULL; AVFormatContext *fCtx = NULL; AVCodecContext *cCtx = NULL; AVDictionary* dict = NULL; // convert filepath #ifdef Q_OS_WIN const wchar_t* filePathWc = reinterpret_cast<const wchar_t*>(filePath.constData()); const char* filePathCh = utf16_to_utf8(filePathWc); #else QByteArray encodedPath = QFile::encodeName(filePath); const char* filePathCh = encodedPath; #endif // open file int openInputResult = avformat_open_input(&fCtx, filePathCh, NULL, NULL); if(openInputResult != 0){ std::ostringstream ss; ss << "Could not open audio file (" << openInputResult << ")"; throw KeyFinder::Exception(ss.str()); } if(avformat_find_stream_info(fCtx,NULL) < 0){ throw KeyFinder::Exception("Could not find stream information"); } int audioStream = -1; for(int i=0; i<(signed)fCtx->nb_streams; i++){ if(fCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){ audioStream = i; break; } } if(audioStream == -1){ throw KeyFinder::Exception("Could not find an audio stream"); } // Determine stream codec cCtx = fCtx->streams[audioStream]->codec; codec = avcodec_find_decoder(cCtx->codec_id); if(codec == NULL){ throw KeyFinder::Exception("Audio stream has unsupported codec"); } // Open codec int codecOpenResult = avcodec_open2(cCtx, codec, &dict); if(codecOpenResult < 0){ std::ostringstream ss; ss << "Could not open audio codec: " << codec->long_name << " (" << codecOpenResult << ")"; throw KeyFinder::Exception(ss.str()); } ReSampleContext* rsCtx = av_audio_resample_init( cCtx->channels, cCtx->channels, cCtx->sample_rate, cCtx->sample_rate, AV_SAMPLE_FMT_S16, cCtx->sample_fmt, 0, 0, 0, 0); if(rsCtx == NULL){ throw KeyFinder::Exception("Could not create ReSampleContext"); } qDebug("Decoding %s (%s, %d)", filePathCh, av_get_sample_fmt_name(cCtx->sample_fmt), cCtx->sample_rate); codecMutexLocker.unlock(); // Prep buffer KeyFinder::AudioData *audio = new KeyFinder::AudioData(); audio->setFrameRate(cCtx->sample_rate); audio->setChannels(cCtx->channels); // Decode stream AVPacket avpkt; int badPacketCount = 0; while(true){ av_init_packet(&avpkt); if(av_read_frame(fCtx, &avpkt) < 0) break; if(avpkt.stream_index == audioStream){ try{ int result = decodePacket(cCtx, rsCtx, &avpkt, audio); if(result != 0){ if(badPacketCount < 100){ badPacketCount++; }else{ throw KeyFinder::Exception("100 bad packets"); } } }catch(KeyFinder::Exception& e){ throw e; } } av_free_packet(&avpkt); } codecMutexLocker.relock(); audio_resample_close(rsCtx); int codecCloseResult = avcodec_close(cCtx); if(codecCloseResult < 0){ qCritical("Error closing audio codec: %s (%d)", codec->long_name, codecCloseResult); } codecMutexLocker.unlock(); av_close_input_file(fCtx); return audio; }