diff options
author | Joerg Mueller <nexyon@gmail.com> | 2011-08-06 21:57:20 +0400 |
---|---|---|
committer | Joerg Mueller <nexyon@gmail.com> | 2011-08-06 21:57:20 +0400 |
commit | c334bf69a7282254bb80bb2896bd8716930a4adf (patch) | |
tree | 08291f87da9ba64b80c881d704cf251b884ba7c2 /intern/audaspace/ffmpeg | |
parent | e73cf35f4ad470d553540d6adbe89af5cc0c1f4f (diff) |
3D Audio GSoC:
Mixdown functionality.
* Mixdown possible via libsndfile and ffmpeg!
* Fixed some ffmpeg deprecation warnings
* Mixdown UI only shows working Container, Codec and Format combinations!
* Minor bugs and warnings fixed
Diffstat (limited to 'intern/audaspace/ffmpeg')
-rw-r--r-- | intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp | 31 | ||||
-rw-r--r-- | intern/audaspace/ffmpeg/AUD_FFMPEGReader.h | 4 | ||||
-rw-r--r-- | intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp | 303 | ||||
-rw-r--r-- | intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h | 114 |
4 files changed, 432 insertions, 20 deletions
diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp index b7690d55383..a7534dbed32 100644 --- a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.cpp @@ -176,11 +176,11 @@ static const char* fileopen_error = "AUD_FFMPEGReader: File couldn't be " AUD_FFMPEGReader::AUD_FFMPEGReader(std::string filename) : m_pkgbuf(AVCODEC_MAX_AUDIO_FRAME_SIZE<<1), - m_byteiocontext(NULL), + m_aviocontext(NULL), m_membuf(NULL) { // open file - if(av_open_input_file(&m_formatCtx, filename.c_str(), NULL, 0, NULL)!=0) + if(avformat_open_input(&m_formatCtx, filename.c_str(), NULL, NULL)!=0) AUD_THROW(AUD_ERROR_FILE, fileopen_error); try @@ -204,25 +204,20 @@ AUD_FFMPEGReader::AUD_FFMPEGReader(AUD_Reference<AUD_Buffer> buffer) : { m_membuf = reinterpret_cast<data_t*>(av_malloc(FF_MIN_BUFFER_SIZE + FF_INPUT_BUFFER_PADDING_SIZE)); - m_byteiocontext = av_alloc_put_byte(m_membuf, FF_MIN_BUFFER_SIZE, 0, this, - read_packet, NULL, seek_packet); + m_aviocontext = avio_alloc_context(m_membuf, FF_MIN_BUFFER_SIZE, 0, this, + read_packet, NULL, seek_packet); - if(!m_byteiocontext) + if(!m_aviocontext) { - av_free(m_byteiocontext); + av_free(m_aviocontext); AUD_THROW(AUD_ERROR_FILE, fileopen_error); } - AVProbeData probe_data; - probe_data.filename = ""; - probe_data.buf = reinterpret_cast<data_t*>(buffer.get()->getBuffer()); - probe_data.buf_size = buffer.get()->getSize(); - AVInputFormat* fmt = av_probe_input_format(&probe_data, 1); - - // open stream - if(av_open_input_stream(&m_formatCtx, m_byteiocontext, "", fmt, NULL)!=0) + m_formatCtx = avformat_alloc_context(); + m_formatCtx->pb = m_aviocontext; + if(avformat_open_input(&m_formatCtx, "", NULL, NULL)!=0) { - av_free(m_byteiocontext); + av_free(m_aviocontext); AUD_THROW(AUD_ERROR_FILE, streamopen_error); } @@ -233,7 +228,7 @@ AUD_FFMPEGReader::AUD_FFMPEGReader(AUD_Reference<AUD_Buffer> buffer) : catch(AUD_Exception&) { av_close_input_stream(m_formatCtx); - av_free(m_byteiocontext); + av_free(m_aviocontext); throw; } } @@ -242,10 +237,10 @@ AUD_FFMPEGReader::~AUD_FFMPEGReader() { avcodec_close(m_codecCtx); - if(m_byteiocontext) + if(m_aviocontext) { av_close_input_stream(m_formatCtx); - av_free(m_byteiocontext); + av_free(m_aviocontext); } else av_close_input_file(m_formatCtx); diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h index 06d6fe1e5f6..222a3d8581a 100644 --- a/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGReader.h @@ -86,9 +86,9 @@ private: AVCodecContext* m_codecCtx; /** - * The ByteIOContext to read the data from. + * The AVIOContext to read the data from. */ - ByteIOContext* m_byteiocontext; + AVIOContext* m_aviocontext; /** * The stream ID in the file. diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp new file mode 100644 index 00000000000..f2b7acc5ea2 --- /dev/null +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.cpp @@ -0,0 +1,303 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * Copyright 2009-2011 Jörg Hermann Müller + * + * This file is part of AudaSpace. + * + * Audaspace is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AudaSpace is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Audaspace; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/ffmpeg/AUD_FFMPEGWriter.cpp + * \ingroup audffmpeg + */ + + +// needed for INT64_C +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS +#endif + +#include "AUD_FFMPEGWriter.h" + +extern "C" { +#include <libavcodec/avcodec.h> +#include <libavformat/avformat.h> +#include <libavformat/avio.h> +} + +static const char* context_error = "AUD_FFMPEGWriter: Couldn't allocate context."; +static const char* codec_error = "AUD_FFMPEGWriter: Invalid codec or codec not found."; +static const char* stream_error = "AUD_FFMPEGWriter: Couldn't allocate stream."; +static const char* format_error = "AUD_FFMPEGWriter: Unsupported sample format."; +static const char* file_error = "AUD_FFMPEGWriter: File couldn't be written."; +static const char* write_error = "AUD_FFMPEGWriter: Error writing packet."; + +AUD_FFMPEGWriter::AUD_FFMPEGWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate) : + m_position(0), + m_specs(specs), + m_input_samples(0) +{ + static const char* formats[] = { NULL, "ac3", "flac", "matroska", "mp2", "mp3", "ogg", "wav" }; + + if(avformat_alloc_output_context2(&m_formatCtx, NULL, formats[format], filename.c_str())) + AUD_THROW(AUD_ERROR_FFMPEG, context_error); + + m_outputFmt = m_formatCtx->oformat; + + switch(codec) + { + case AUD_CODEC_AAC: + m_outputFmt->audio_codec = CODEC_ID_AAC; + break; + case AUD_CODEC_AC3: + m_outputFmt->audio_codec = CODEC_ID_AC3; + break; + case AUD_CODEC_FLAC: + m_outputFmt->audio_codec = CODEC_ID_FLAC; + break; + case AUD_CODEC_MP2: + m_outputFmt->audio_codec = CODEC_ID_MP2; + break; + case AUD_CODEC_MP3: + m_outputFmt->audio_codec = CODEC_ID_MP3; + break; + case AUD_CODEC_PCM: + switch(specs.format) + { + case AUD_FORMAT_U8: + m_outputFmt->audio_codec = CODEC_ID_PCM_U8; + break; + case AUD_FORMAT_S16: + m_outputFmt->audio_codec = CODEC_ID_PCM_S16LE; + break; + case AUD_FORMAT_S24: + m_outputFmt->audio_codec = CODEC_ID_PCM_S24LE; + break; + case AUD_FORMAT_S32: + m_outputFmt->audio_codec = CODEC_ID_PCM_S32LE; + break; + case AUD_FORMAT_FLOAT32: + m_outputFmt->audio_codec = CODEC_ID_PCM_F32LE; + break; + case AUD_FORMAT_FLOAT64: + m_outputFmt->audio_codec = CODEC_ID_PCM_F64LE; + break; + default: + m_outputFmt->audio_codec = CODEC_ID_NONE; + break; + } + break; + case AUD_CODEC_VORBIS: + m_outputFmt->audio_codec = CODEC_ID_VORBIS; + break; + default: + m_outputFmt->audio_codec = CODEC_ID_NONE; + break; + } + + try + { + if(m_outputFmt->audio_codec == CODEC_ID_NONE) + AUD_THROW(AUD_ERROR_SPECS, codec_error); + + m_stream = av_new_stream(m_formatCtx, 0); + if(!m_stream) + AUD_THROW(AUD_ERROR_FFMPEG, stream_error); + + m_codecCtx = m_stream->codec; + m_codecCtx->codec_id = m_outputFmt->audio_codec; + m_codecCtx->codec_type = AVMEDIA_TYPE_AUDIO; + m_codecCtx->bit_rate = bitrate; + m_codecCtx->sample_rate = int(m_specs.rate); + m_codecCtx->channels = m_specs.channels; + m_codecCtx->time_base = (AVRational){1, m_codecCtx->sample_rate}; + + switch(m_specs.format) + { + case AUD_FORMAT_U8: + m_convert = AUD_convert_float_u8; + m_codecCtx->sample_fmt = SAMPLE_FMT_U8; + break; + case AUD_FORMAT_S16: + m_convert = AUD_convert_float_s16; + m_codecCtx->sample_fmt = SAMPLE_FMT_S16; + break; + case AUD_FORMAT_S32: + m_convert = AUD_convert_float_s32; + m_codecCtx->sample_fmt = SAMPLE_FMT_S32; + break; + case AUD_FORMAT_FLOAT32: + m_convert = AUD_convert_copy<float>; + m_codecCtx->sample_fmt = SAMPLE_FMT_FLT; + break; + case AUD_FORMAT_FLOAT64: + m_convert = AUD_convert_float_double; + m_codecCtx->sample_fmt = SAMPLE_FMT_DBL; + break; + default: + AUD_THROW(AUD_ERROR_FFMPEG, format_error); + } + + try + { + if(m_formatCtx->oformat->flags & AVFMT_GLOBALHEADER) + m_codecCtx->flags |= CODEC_FLAG_GLOBAL_HEADER; + + AVCodec* codec = avcodec_find_encoder(m_codecCtx->codec_id); + if(!codec) + AUD_THROW(AUD_ERROR_FFMPEG, codec_error); + + if(avcodec_open(m_codecCtx, codec)) + AUD_THROW(AUD_ERROR_FFMPEG, codec_error); + + m_output_buffer.resize(FF_MIN_BUFFER_SIZE); + int samplesize = AUD_MAX(AUD_SAMPLE_SIZE(m_specs), AUD_DEVICE_SAMPLE_SIZE(m_specs)); + + if(m_codecCtx->frame_size <= 1) + m_input_size = 0; + else + { + m_input_buffer.resize(m_codecCtx->frame_size * samplesize); + m_input_size = m_codecCtx->frame_size; + } + + try + { + if(avio_open(&m_formatCtx->pb, filename.c_str(), AVIO_WRONLY)) + AUD_THROW(AUD_ERROR_FILE, file_error); + + avformat_write_header(m_formatCtx, NULL); + } + catch(AUD_Exception&) + { + avcodec_close(m_codecCtx); + av_freep(&m_formatCtx->streams[0]->codec); + throw; + } + } + catch(AUD_Exception&) + { + av_freep(&m_formatCtx->streams[0]); + throw; + } + } + catch(AUD_Exception&) + { + av_free(m_formatCtx); + throw; + } +} + +AUD_FFMPEGWriter::~AUD_FFMPEGWriter() +{ + // writte missing data + if(m_input_samples) + { + sample_t* buf = m_input_buffer.getBuffer(); + memset(buf + m_specs.channels * m_input_samples, 0, + (m_input_size - m_input_samples) * AUD_DEVICE_SAMPLE_SIZE(m_specs)); + + encode(buf); + } + + av_write_trailer(m_formatCtx); + + avcodec_close(m_codecCtx); + + av_freep(&m_formatCtx->streams[0]->codec); + av_freep(&m_formatCtx->streams[0]); + + avio_close(m_formatCtx->pb); + av_free(m_formatCtx); +} + +int AUD_FFMPEGWriter::getPosition() const +{ + return m_position; +} + +AUD_DeviceSpecs AUD_FFMPEGWriter::getSpecs() const +{ + return m_specs; +} + +void AUD_FFMPEGWriter::encode(sample_t* data) +{ + sample_t* outbuf = m_output_buffer.getBuffer(); + + // convert first + if(m_input_size) + m_convert(reinterpret_cast<data_t*>(data), reinterpret_cast<data_t*>(data), m_input_size * m_specs.channels); + + AVPacket packet; + av_init_packet(&packet); + packet.size = avcodec_encode_audio(m_codecCtx, reinterpret_cast<uint8_t*>(outbuf), m_output_buffer.getSize(), reinterpret_cast<short*>(data)); + if(m_codecCtx->coded_frame && m_codecCtx->coded_frame->pts != AV_NOPTS_VALUE) + packet.pts = av_rescale_q(m_codecCtx->coded_frame->pts, m_codecCtx->time_base, m_stream->time_base); + packet.flags |= AV_PKT_FLAG_KEY; + packet.stream_index = m_stream->index; + packet.data = reinterpret_cast<uint8_t*>(outbuf); + + if(av_interleaved_write_frame(m_formatCtx, &packet)) + AUD_THROW(AUD_ERROR_FFMPEG, write_error); +} + +void AUD_FFMPEGWriter::write(unsigned int length, sample_t* buffer) +{ + unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs); + + if(m_input_size) + { + sample_t* inbuf = m_input_buffer.getBuffer(); + + while(length) + { + unsigned int len = AUD_MIN(m_input_size - m_input_samples, length); + + memcpy(inbuf + m_input_samples * m_specs.channels, buffer, len * samplesize); + + buffer += len * m_specs.channels; + m_input_samples += len; + m_position += len; + length -= len; + + if(m_input_samples == m_input_size) + { + encode(inbuf); + + m_input_samples = 0; + } + } + } + else // PCM data, can write directly! + { + int samplesize = AUD_SAMPLE_SIZE(m_specs); + if(m_output_buffer.getSize() != length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8) + m_output_buffer.resize(length * m_specs.channels * m_codecCtx->bits_per_coded_sample / 8); + m_input_buffer.assureSize(length * AUD_MAX(AUD_DEVICE_SAMPLE_SIZE(m_specs), samplesize)); + + sample_t* buf = m_input_buffer.getBuffer(); + m_convert(reinterpret_cast<data_t*>(buf), reinterpret_cast<data_t*>(buffer), length * m_specs.channels); + + encode(buf); + + m_position += length; + } +} diff --git a/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h new file mode 100644 index 00000000000..618ec9402ce --- /dev/null +++ b/intern/audaspace/ffmpeg/AUD_FFMPEGWriter.h @@ -0,0 +1,114 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * Copyright 2009-2011 Jörg Hermann Müller + * + * This file is part of AudaSpace. + * + * Audaspace is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * AudaSpace is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Audaspace; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file audaspace/ffmpeg/AUD_FFMPEGWriter.h + * \ingroup audffmpeg + */ + + +#ifndef AUD_FFMPEGWRITER +#define AUD_FFMPEGWRITER + +#include "AUD_ConverterFunctions.h" +#include "AUD_Buffer.h" +#include "AUD_IWriter.h" + +#include <string> + +struct AVCodecContext; +extern "C" { +#include <libavformat/avformat.h> +} + +/** + * This class writes a sound file via ffmpeg. + */ +class AUD_FFMPEGWriter : public AUD_IWriter +{ +private: + /** + * The current position in samples. + */ + int m_position; + + /** + * The specification of the audio data. + */ + AUD_DeviceSpecs m_specs; + + /** + * The AVFormatContext structure for using ffmpeg. + */ + AVFormatContext* m_formatCtx; + + /** + * The AVCodecContext structure for using ffmpeg. + */ + AVCodecContext* m_codecCtx; + + AVOutputFormat* m_outputFmt; + + AVStream* m_stream; + + AUD_Buffer m_input_buffer; + + AUD_Buffer m_output_buffer; + + unsigned int m_input_samples; + + unsigned int m_input_size; + + /** + * Converter function. + */ + AUD_convert_f m_convert; + + // hide copy constructor and operator= + AUD_FFMPEGWriter(const AUD_FFMPEGWriter&); + AUD_FFMPEGWriter& operator=(const AUD_FFMPEGWriter&); + + void encode(sample_t* data); + +public: + /** + * Creates a new writer. + * \param filename The path to the file to be read. + * \exception AUD_Exception Thrown if the file specified does not exist or + * cannot be read with ffmpeg. + */ + AUD_FFMPEGWriter(std::string filename, AUD_DeviceSpecs specs, AUD_Container format, AUD_Codec codec, unsigned int bitrate); + + /** + * Destroys the writer and closes the file. + */ + virtual ~AUD_FFMPEGWriter(); + + virtual int getPosition() const; + virtual AUD_DeviceSpecs getSpecs() const; + virtual void write(unsigned int length, sample_t* buffer); +}; + +#endif //AUD_FFMPEGWRITER |