bool ExportFFmpeg::EncodeAudioFrame(int16_t *pFrame, int frameSize) { AVPacket pkt; int nBytesToWrite = 0; uint8_t * pRawSamples = NULL; int nAudioFrameSizeOut = mEncAudioCodecCtx->frame_size * mEncAudioCodecCtx->channels * sizeof(int16_t); if (mEncAudioCodecCtx->frame_size == 1) nAudioFrameSizeOut = mEncAudioEncodedBufSiz; int ret; nBytesToWrite = frameSize; pRawSamples = (uint8_t*)pFrame; #if FFMPEG_STABLE FFmpegLibsInst->av_fifo_realloc(&mEncAudioFifo, FFmpegLibsInst->av_fifo_size(&mEncAudioFifo) + frameSize); #else FFmpegLibsInst->av_fifo_realloc2(mEncAudioFifo, FFmpegLibsInst->av_fifo_size(mEncAudioFifo) + frameSize); #endif // Put the raw audio samples into the FIFO. #if FFMPEG_STABLE ret = FFmpegLibsInst->av_fifo_generic_write(&mEncAudioFifo, pRawSamples, nBytesToWrite,NULL); #else ret = FFmpegLibsInst->av_fifo_generic_write(mEncAudioFifo, pRawSamples, nBytesToWrite,NULL); #endif wxASSERT(ret == nBytesToWrite); // Read raw audio samples out of the FIFO in nAudioFrameSizeOut byte-sized groups to encode. #if FFMPEG_STABLE while ((ret = FFmpegLibsInst->av_fifo_size(&mEncAudioFifo)) >= nAudioFrameSizeOut) { ret = FFmpegLibsInst->av_fifo_read(&mEncAudioFifo, mEncAudioFifoOutBuf, nAudioFrameSizeOut); #else while ((ret = FFmpegLibsInst->av_fifo_size(mEncAudioFifo)) >= nAudioFrameSizeOut) { ret = FFmpegLibsInst->av_fifo_generic_read(mEncAudioFifo, mEncAudioFifoOutBuf, nAudioFrameSizeOut, NULL); #endif FFmpegLibsInst->av_init_packet(&pkt); pkt.size = FFmpegLibsInst->avcodec_encode_audio(mEncAudioCodecCtx, mEncAudioEncodedBuf, mEncAudioEncodedBufSiz, // out (int16_t*)mEncAudioFifoOutBuf); // in if (mEncAudioCodecCtx->frame_size == 1) { wxASSERT(pkt.size == mEncAudioEncodedBufSiz); } if (pkt.size < 0) { wxLogMessage(wxT("FFmpeg : ERROR - Can't encode audio frame.")); return false; } // Rescale from the codec time_base to the AVStream time_base. if (mEncAudioCodecCtx->coded_frame && mEncAudioCodecCtx->coded_frame->pts != int64_t(AV_NOPTS_VALUE)) pkt.pts = FFmpegLibsInst->av_rescale_q(mEncAudioCodecCtx->coded_frame->pts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); //wxLogMessage(wxT("FFmpeg : (%d) Writing audio frame with PTS: %lld."), mEncAudioCodecCtx->frame_number, pkt.pts); pkt.stream_index = mEncAudioStream->index; pkt.data = mEncAudioEncodedBuf; pkt.flags |= PKT_FLAG_KEY; // Write the encoded audio frame to the output file. if ((ret = FFmpegLibsInst->av_interleaved_write_frame(mEncFormatCtx, &pkt)) != 0) { wxLogMessage(wxT("FFmpeg : ERROR - Failed to write audio frame to file.")); return false; } } return true; } int ExportFFmpeg::Export(AudacityProject *project, int channels, wxString fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, Tags *metadata, int subformat) { if (!CheckFFmpegPresence()) return false; mChannels = channels; // subformat index may not correspond directly to fmts[] index, convert it mSubFormat = AdjustFormatIndex(subformat); if (channels > ExportFFmpegOptions::fmts[mSubFormat].maxchannels) { wxLogMessage(wxT("Attempted to export %d channels, but max. channels = %d"),channels,ExportFFmpegOptions::fmts[mSubFormat].maxchannels); wxMessageBox(wxString::Format(_("Attempted to export %d channels, but max. channels for selected output format is %d"),channels,ExportFFmpegOptions::fmts[mSubFormat].maxchannels),_("Error")); return false; } mName = fName; TrackList *tracks = project->GetTracks(); bool ret = true; if (mSubFormat >= FMT_LAST) return false; wxString shortname(ExportFFmpegOptions::fmts[mSubFormat].shortname); if (mSubFormat == FMT_OTHER) shortname = gPrefs->Read(wxT("/FileFormats/FFmpegFormat"),wxT("matroska")); ret = Init(shortname.mb_str(),project, metadata); if (!ret) return false; int pcmBufferSize = 1024; int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = new Mixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, channels, pcmBufferSize, true, mSampleRate, int16Sample, true, mixerSpec); delete [] waveTracks; ProgressDialog *progress = new ProgressDialog(wxFileName(fName).GetName(), selectionOnly ? wxString::Format(_("Exporting selected audio as %s"), ExportFFmpegOptions::fmts[mSubFormat].description) : wxString::Format(_("Exporting entire file as %s"), ExportFFmpegOptions::fmts[mSubFormat].description)); int updateResult = eProgressSuccess; while(updateResult == eProgressSuccess) { sampleCount pcmNumSamples = mixer->Process(pcmBufferSize); if (pcmNumSamples == 0) break; short *pcmBuffer = (short *)mixer->GetBuffer(); EncodeAudioFrame(pcmBuffer,(pcmNumSamples)*sizeof(int16_t)*mChannels); updateResult = progress->Update(mixer->MixGetCurrentTime()-t0, t1-t0); } delete progress; delete mixer; Finalize(); return updateResult; } void AddStringTagUTF8(char field[], int size, wxString value) { memset(field,0,size); memcpy(field,value.ToUTF8(),(int)strlen(value.ToUTF8()) > size -1 ? size -1 : strlen(value.ToUTF8())); }
int ExportFFmpeg::Export(AudacityProject *project, int channels, const wxString &fName, bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata, int subformat) { if (!CheckFFmpegPresence()) return false; mChannels = channels; // subformat index may not correspond directly to fmts[] index, convert it mSubFormat = AdjustFormatIndex(subformat); if (channels > ExportFFmpegOptions::fmts[mSubFormat].maxchannels) { wxMessageBox( wxString::Format( _("Attempted to export %d channels, but maximum number of channels for selected output format is %d"), channels, ExportFFmpegOptions::fmts[mSubFormat].maxchannels), _("Error")); return false; } mName = fName; TrackList *tracks = project->GetTracks(); bool ret = true; if (mSubFormat >= FMT_LAST) return false; wxString shortname(ExportFFmpegOptions::fmts[mSubFormat].shortname); if (mSubFormat == FMT_OTHER) shortname = gPrefs->Read(wxT("/FileFormats/FFmpegFormat"),wxT("matroska")); ret = Init(shortname.mb_str(),project, metadata, subformat); if (!ret) return false; int pcmBufferSize = 1024; int numWaveTracks; WaveTrack **waveTracks; tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks); Mixer *mixer = CreateMixer(numWaveTracks, waveTracks, tracks->GetTimeTrack(), t0, t1, channels, pcmBufferSize, true, mSampleRate, int16Sample, true, mixerSpec); delete[] waveTracks; int updateResult = eProgressSuccess; { ProgressDialog progress(wxFileName(fName).GetName(), selectionOnly ? wxString::Format(_("Exporting selected audio as %s"), ExportFFmpegOptions::fmts[mSubFormat].description) : wxString::Format(_("Exporting entire file as %s"), ExportFFmpegOptions::fmts[mSubFormat].description)); while (updateResult == eProgressSuccess) { sampleCount pcmNumSamples = mixer->Process(pcmBufferSize); if (pcmNumSamples == 0) break; short *pcmBuffer = (short *)mixer->GetBuffer(); EncodeAudioFrame(pcmBuffer, (pcmNumSamples)*sizeof(int16_t)*mChannels); updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0); } } delete mixer; Finalize(); return updateResult; }