planar s16&flt export support

Signed-off-by: Michael Niedermayer <michaelni@gmx.at>
Signed-off-by: Benjamin Drung <bdrung@debian.org>
This commit is contained in:
benjamin.drung@gmail.com 2014-05-23 20:37:27 +00:00
parent 135e80b879
commit 48240202ca
3 changed files with 126 additions and 29 deletions

View File

@ -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

View File

@ -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

View File

@ -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;
}