diff --git a/src/FFmpeg.cpp b/src/FFmpeg.cpp index dbb2867b3..aa1be8477 100644 --- a/src/FFmpeg.cpp +++ b/src/FFmpeg.cpp @@ -899,12 +899,14 @@ bool FFmpegLibs::InitLibs(wxString libpath_format, bool WXUNUSED(showerr)) FFMPEG_INITDYN(avcodec, avcodec_decode_audio2); #endif FFMPEG_INITDYN(avcodec, avcodec_encode_audio); + FFMPEG_INITDYN(avcodec, avcodec_encode_audio2); FFMPEG_INITDYN(avcodec, avcodec_close); FFMPEG_INITDYN(avcodec, avcodec_register_all); FFMPEG_INITDYN(avcodec, avcodec_version); FFMPEG_INITDYN(avcodec, av_fast_realloc); FFMPEG_INITDYN(avcodec, av_codec_next); FFMPEG_INITDYN(avcodec, av_codec_is_encoder); + FFMPEG_INITDYN(avcodec, avcodec_fill_audio_frame); FFMPEG_INITALT(avcodec, av_get_bits_per_sample_format, av_get_bits_per_sample_fmt); @@ -927,6 +929,7 @@ bool FFmpegLibs::InitLibs(wxString libpath_format, bool WXUNUSED(showerr)) FFMPEG_INITDYN(avutil, avutil_version); FFMPEG_INITDYN(avutil, av_frame_alloc); FFMPEG_INITDYN(avutil, av_frame_free); + FFMPEG_INITDYN(avutil, av_samples_get_buffer_size); wxLogMessage(wxT("All symbols loaded successfully. Initializing the library.")); #endif diff --git a/src/FFmpeg.h b/src/FFmpeg.h index 284bca850..32e897cfc 100644 --- a/src/FFmpeg.h +++ b/src/FFmpeg.h @@ -568,6 +568,12 @@ extern "C" { (AVCodecContext *avctx, uint8_t *buf, int buf_size, const short *samples), (avctx, buf, buf_size, samples) ); + FFMPEG_FUNCTION_WITH_RETURN( + int, + avcodec_encode_audio2, + (AVCodecContext *avctx, AVPacket *pkt, const AVFrame *frame, int *got_output), + (avctx, pkt, frame, got_output) + ); FFMPEG_FUNCTION_WITH_RETURN( int, avcodec_close, @@ -591,6 +597,12 @@ extern "C" { (enum AVSampleFormat sample_fmt), (sample_fmt) ); + FFMPEG_FUNCTION_WITH_RETURN( + int, + avcodec_fill_audio_frame, + (AVFrame *frame, int nb_channels, enum AVSampleFormat sample_fmt, const uint8_t *buf, int buf_size, int align), + (frame, nb_channels, sample_fmt, buf, buf_size, align) + ); // // libavformat @@ -865,6 +877,12 @@ extern "C" { (AVFrame **frame), (frame) ); + FFMPEG_FUNCTION_WITH_RETURN( + int, + av_samples_get_buffer_size, + (int *linesize, int nb_channels, int nb_samples, enum AVSampleFormat sample_fmt, int align), + (linesize, nb_channels, nb_samples, sample_fmt, align) + ); }; #endif diff --git a/src/export/ExportFFmpeg.cpp b/src/export/ExportFFmpeg.cpp index 4cb4de0e3..c300f5579 100644 --- a/src/export/ExportFFmpeg.cpp +++ b/src/export/ExportFFmpeg.cpp @@ -150,7 +150,6 @@ private: AVStream * mEncAudioStream; // the output audio stream (may remain NULL) AVCodecContext * mEncAudioCodecCtx; // the encoder for the output audio stream - uint8_t * mEncAudioEncodedBuf; // buffer to hold frames encoded by the encoder int mEncAudioEncodedBufSiz; AVFifoBuffer * mEncAudioFifo; // FIFO to write incoming audio samples into uint8_t * mEncAudioFifoOutBuf; // buffer to read _out_ of the FIFO into @@ -175,7 +174,6 @@ ExportFFmpeg::ExportFFmpeg() mEncFormatDesc = NULL; // describes our output file to libavformat mEncAudioStream = NULL; // the output audio stream (may remain NULL) mEncAudioCodecCtx = NULL; // the encoder for the output audio stream - mEncAudioEncodedBuf = NULL; // buffer to hold frames encoded by the encoder #define MAX_AUDIO_PACKET_SIZE (128 * 1024) mEncAudioEncodedBufSiz = 4*MAX_AUDIO_PACKET_SIZE; mEncAudioFifoOutBuf = NULL; // buffer to read _out_ of the FIFO into @@ -463,6 +461,21 @@ bool ExportFFmpeg::InitCodecs(AudacityProject *project) return false; } + if (codec->sample_fmts) { + for (int i=0; codec->sample_fmts[i]; i++) { + enum AVSampleFormat fmt = codec->sample_fmts[i]; + if ( fmt == AV_SAMPLE_FMT_S16 + || fmt == AV_SAMPLE_FMT_S16P + || fmt == AV_SAMPLE_FMT_FLT + || fmt == AV_SAMPLE_FMT_FLTP) { + mEncAudioCodecCtx->sample_fmt = fmt; + } + if ( fmt == AV_SAMPLE_FMT_S16 + || fmt == AV_SAMPLE_FMT_S16P) + break; + } + } + if (mEncFormatCtx->oformat->flags & AVFMT_GLOBALHEADER) { mEncAudioCodecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; @@ -482,12 +495,6 @@ bool ExportFFmpeg::InitCodecs(AudacityProject *project) { mEncAudioEncodedBufSiz = FF_MIN_BUFFER_SIZE; } - // Allocate a buffer for the encoder to store encoded audio frames into. - if ((mEncAudioEncodedBuf = (uint8_t*)av_malloc(mEncAudioEncodedBufSiz)) == NULL) - { - wxLogError(wxT("FFmpeg : ERROR - Can't allocate buffer to hold encoded audio.")); - return false; - } // The encoder may require a minimum number of raw audio samples for each encoding but we can't // guarantee we'll get this minimum each time an audio frame is decoded from the input file so @@ -508,6 +515,78 @@ bool ExportFFmpeg::InitCodecs(AudacityProject *project) return true; } +static int encode_audio(AVCodecContext *avctx, AVPacket *pkt, int nFifoBytes, int16_t *audio_samples) +{ + int i, ch, buffer_size, ret, got_output, nEncodedBytes; + void *samples = NULL; + AVFrame *frame = NULL; + + if (audio_samples) { + frame = av_frame_alloc(); + if (!frame) + return AVERROR(ENOMEM); + + frame->nb_samples = avctx->frame_size; + frame->format = avctx->sample_fmt; + frame->channel_layout = avctx->channel_layout; + + buffer_size = av_samples_get_buffer_size(NULL, avctx->channels, avctx->frame_size, + avctx->sample_fmt, 0); + if (buffer_size < 0) { + wxLogError(wxT("FFmpeg : ERROR - Could not get sample buffer siz")); + return buffer_size; + } + samples = av_malloc(buffer_size); + if (!samples) { + wxLogError(wxT("FFmpeg : ERROR - Could not allocate bytes for samples buffer")); + return AVERROR(ENOMEM); + } + /* setup the data pointers in the AVFrame */ + ret = avcodec_fill_audio_frame(frame, avctx->channels, avctx->sample_fmt, + (const uint8_t*)samples, buffer_size, 0); + if (ret < 0) { + wxLogError(wxT("FFmpeg : ERROR - Could not setup audio frame")); + return ret; + } + + for (ch = 0; ch < avctx->channels; ch++) { + for (i = 0; i < frame->nb_samples; i++) { + switch(avctx->sample_fmt) { + case AV_SAMPLE_FMT_S16: + ((int16_t*)(frame->data[0]))[ch + i*avctx->channels] = audio_samples[ch + i*avctx->channels]; + break; + case AV_SAMPLE_FMT_S16P: + ((int16_t*)(frame->data[ch]))[i] = audio_samples[ch + i*avctx->channels]; + break; + case AV_SAMPLE_FMT_FLT: + ((float*)(frame->data[0]))[ch + i*avctx->channels] = audio_samples[ch + i*avctx->channels] / 32767.0; + break; + case AV_SAMPLE_FMT_FLTP: + ((float*)(frame->data[ch]))[i] = audio_samples[ch + i*avctx->channels] / 32767.; + break; + } + } + } + } + av_init_packet(pkt); + pkt->data = NULL; // packet data will be allocated by the encoder + pkt->size = 0; + + ret = avcodec_encode_audio2(avctx, pkt, frame, &got_output); + if (ret < 0) { + wxLogError(wxT("FFmpeg : ERROR - encoding frame failed")); + return ret; + } + + nEncodedBytes = pkt->size; + + av_frame_free(&frame); + av_freep(&samples); + + return nEncodedBytes; +} + + bool ExportFFmpeg::Finalize() { int i, nEncodedBytes; @@ -518,6 +597,8 @@ bool ExportFFmpeg::Finalize() AVPacket pkt; int nFifoBytes = av_fifo_size(mEncAudioFifo); // any bytes left in audio FIFO? + av_init_packet(&pkt); + nEncodedBytes = 0; int nAudioFrameSizeOut = mEncAudioCodecCtx->frame_size * mEncAudioCodecCtx->channels * sizeof(int16_t); if (mEncAudioCodecCtx->frame_size == 1) nAudioFrameSizeOut = mEncAudioEncodedBufSiz; @@ -561,9 +642,9 @@ bool ExportFFmpeg::Finalize() #endif { if (mEncAudioCodecCtx->frame_size != 1) - nEncodedBytes = avcodec_encode_audio(mEncAudioCodecCtx, mEncAudioEncodedBuf, mEncAudioEncodedBufSiz, (int16_t*)mEncAudioFifoOutBuf); + nEncodedBytes = encode_audio(mEncAudioCodecCtx, &pkt, mEncAudioEncodedBufSiz, (int16_t*)mEncAudioFifoOutBuf); else - nEncodedBytes = avcodec_encode_audio(mEncAudioCodecCtx, mEncAudioEncodedBuf, nFifoBytes, (int16_t*)mEncAudioFifoOutBuf); + nEncodedBytes = encode_audio(mEncAudioCodecCtx, &pkt, nFifoBytes, (int16_t*)mEncAudioFifoOutBuf); } mEncAudioCodecCtx->frame_size = nFrameSizeTmp; // restore the native frame size @@ -572,28 +653,25 @@ bool ExportFFmpeg::Finalize() // Now flush the encoder. if (nEncodedBytes <= 0) - nEncodedBytes = avcodec_encode_audio(mEncAudioCodecCtx, mEncAudioEncodedBuf, mEncAudioEncodedBufSiz, NULL); + nEncodedBytes = encode_audio(mEncAudioCodecCtx, &pkt, mEncAudioEncodedBufSiz, NULL); if (nEncodedBytes <= 0) break; - // Okay, we got a final encoded frame we can write to the output file. - av_init_packet(&pkt); - pkt.stream_index = mEncAudioStream->index; - pkt.data = mEncAudioEncodedBuf; - pkt.size = nEncodedBytes; - pkt.flags |= PKT_FLAG_KEY; // Set presentation time of frame (currently in the codec's timebase) in the stream timebase. - if(mEncAudioCodecCtx->coded_frame && mEncAudioCodecCtx->coded_frame->pts != int64_t(AV_NOPTS_VALUE)) - pkt.pts = av_rescale_q(mEncAudioCodecCtx->coded_frame->pts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); + if(pkt.pts != int64_t(AV_NOPTS_VALUE)) + pkt.pts = av_rescale_q(pkt.pts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); + if(pkt.dts != int64_t(AV_NOPTS_VALUE)) + pkt.dts = av_rescale_q(pkt.dts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); if (av_interleaved_write_frame(mEncFormatCtx, &pkt) != 0) { wxLogError(wxT("FFmpeg : ERROR - Couldn't write last audio frame to output file.")); break; } + av_free_packet(&pkt); } // Write any file trailers. @@ -616,9 +694,6 @@ bool ExportFFmpeg::Finalize() // Free any buffers or structures we allocated. av_free(mEncFormatCtx); - if (mEncAudioEncodedBuf != NULL) - av_free(mEncAudioEncodedBuf); - if (mEncAudioFifoOutBuf != NULL) av_free(mEncAudioFifoOutBuf); @@ -660,24 +735,24 @@ bool ExportFFmpeg::EncodeAudioFrame(int16_t *pFrame, int frameSize) av_init_packet(&pkt); - pkt.size = avcodec_encode_audio(mEncAudioCodecCtx, - mEncAudioEncodedBuf, mEncAudioEncodedBufSiz, // out + int ret= encode_audio(mEncAudioCodecCtx, + &pkt, mEncAudioEncodedBufSiz, // out (int16_t*)mEncAudioFifoOutBuf); // in if (mEncAudioCodecCtx->frame_size == 1) { wxASSERT(pkt.size == mEncAudioEncodedBufSiz); } - if (pkt.size < 0) + if (ret < 0) { wxLogError(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 = av_rescale_q(mEncAudioCodecCtx->coded_frame->pts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); + if (pkt.pts != int64_t(AV_NOPTS_VALUE)) + pkt.pts = av_rescale_q(pkt.pts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); + if (pkt.dts != int64_t(AV_NOPTS_VALUE)) + pkt.dts = av_rescale_q(pkt.dts, mEncAudioCodecCtx->time_base, mEncAudioStream->time_base); //wxLogDebug(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 = av_interleaved_write_frame(mEncFormatCtx, &pkt)) != 0) @@ -685,6 +760,7 @@ bool ExportFFmpeg::EncodeAudioFrame(int16_t *pFrame, int frameSize) wxLogError(wxT("FFmpeg : ERROR - Failed to write audio frame to file.")); return false; } + av_free_packet(&pkt); } return true; }