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;
}
Exemple #2
0
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;
}