From 1cdebe6c5c4e188d97a7921965bd8d22123b4e09 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Tue, 3 Jun 2014 14:08:59 +0200 Subject: [PATCH 1/5] InputStreamAudio: fix compile error with libAv AVFrame has no "channels" attribute in LibAv. We should use "av_get_channel_layout_nb_channels" on the "channel_layouts". --- src/AvTranscoder/InputStreamAudio.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/AvTranscoder/InputStreamAudio.cpp b/src/AvTranscoder/InputStreamAudio.cpp index baf05183..46244fb6 100644 --- a/src/AvTranscoder/InputStreamAudio.cpp +++ b/src/AvTranscoder/InputStreamAudio.cpp @@ -137,10 +137,12 @@ bool InputStreamAudio::readNextFrame( AudioFrame& audioFrameBuffer ) if( audioFrameBuffer.getSize() != decodedSize ) audioFrameBuffer.getBuffer().resize( decodedSize ); + int nb_channels = av_get_channel_layout_nb_channels( *m_codecContext->codec->channel_layouts ); + unsigned char* dest = audioFrameBuffer.getPtr(); av_samples_fill_arrays(&dest, m_frame->linesize, m_frame->data[0], - m_frame->channels, m_frame->nb_samples, + nb_channels, m_frame->nb_samples, m_codecContext->sample_fmt, 1); } From 423c28b22c3c606ce9e22e9c7c66bbcf4777ac2b Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Tue, 3 Jun 2014 17:33:43 +0200 Subject: [PATCH 2/5] InputStreamVideo: throw if an error occured during video decoding --- src/AvTranscoder/InputStreamVideo.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/AvTranscoder/InputStreamVideo.cpp b/src/AvTranscoder/InputStreamVideo.cpp index ff3291e4..76f78b3a 100644 --- a/src/AvTranscoder/InputStreamVideo.cpp +++ b/src/AvTranscoder/InputStreamVideo.cpp @@ -106,9 +106,17 @@ bool InputStreamVideo::readNextFrame( Image& frameBuffer ) packet.stream_index = m_selectedStream; packet.data = data.getPtr(); packet.size = data.getSize(); - - avcodec_decode_video2( m_codecContext, m_frame, &got_frame, &packet ); - + + int ret = avcodec_decode_video2( m_codecContext, m_frame, &got_frame, &packet ); + + if( ret < 0 ) + { + char err[250]; + av_strerror( ret, err, 250); + + throw std::runtime_error( "an error occured during video decoding - " + std::string(err) ); + } + av_free_packet( &packet ); } From e8f1c2586dceb4a16bad629ddd2663db6f0ac640 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Tue, 3 Jun 2014 17:44:04 +0200 Subject: [PATCH 3/5] InputStreamAudio: fix decode * Use av_samples_copy instead of av_samples_fill_arrays to copy the frame data to our DataBuffer. * Get nb_channels by the CodecContext, intead of av_get_channel_layout_nb_channels (see commit 1cdebe6c5c4e188d97a7921965bd8d22123b4e09). * Throw a more specific runtime_error if an error occured during audio decoding. --- src/AvTranscoder/InputStreamAudio.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/AvTranscoder/InputStreamAudio.cpp b/src/AvTranscoder/InputStreamAudio.cpp index 46244fb6..4485c697 100644 --- a/src/AvTranscoder/InputStreamAudio.cpp +++ b/src/AvTranscoder/InputStreamAudio.cpp @@ -122,7 +122,10 @@ bool InputStreamAudio::readNextFrame( AudioFrame& audioFrameBuffer ) if( ret < 0 ) { - throw std::runtime_error( "an error occured during audio decoding" ); + char err[250]; + av_strerror( ret, err, 250); + + throw std::runtime_error( "an error occured during audio decoding" + std::string( err ) ); } av_free_packet( &packet ); @@ -135,15 +138,13 @@ bool InputStreamAudio::readNextFrame( AudioFrame& audioFrameBuffer ) if( decodedSize ) { if( audioFrameBuffer.getSize() != decodedSize ) - audioFrameBuffer.getBuffer().resize( decodedSize ); - - int nb_channels = av_get_channel_layout_nb_channels( *m_codecContext->codec->channel_layouts ); + audioFrameBuffer.getBuffer().resize( decodedSize, 0 ); unsigned char* dest = audioFrameBuffer.getPtr(); - av_samples_fill_arrays(&dest, m_frame->linesize, - m_frame->data[0], - nb_channels, m_frame->nb_samples, - m_codecContext->sample_fmt, 1); + int nb_channels = m_codecContext->channels; + av_samples_copy(&dest, (uint8_t* const* )m_frame->data, 0, + 0, m_frame->nb_samples, nb_channels, + m_codecContext->sample_fmt); } return true; From 72fc695f54fcdc5370fa94eae49f478f822e38a3 Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Tue, 3 Jun 2014 17:52:56 +0200 Subject: [PATCH 4/5] Audio: encode outputStreamAudio * OutputStreamAudio: * define setup to open codec. * define encodeFrame. * AudioFrame: add nbSamples attribute to get the number of samples to transcode. * InputStreamAudio: * delete print. * set number of samples decoded in the AudioFrame ; get this value during encode. * Update Rewrapper: add encode and wrap process. --- app/audioRewrapper/audioRewrapper.cpp | 35 +++-- .../DatasStructures/AudioFrame.hpp | 5 + src/AvTranscoder/InputStreamAudio.cpp | 4 +- src/AvTranscoder/OutputStreamAudio.cpp | 124 +++++++++++++++++- src/AvTranscoder/OutputStreamAudio.hpp | 21 ++- 5 files changed, 174 insertions(+), 15 deletions(-) diff --git a/app/audioRewrapper/audioRewrapper.cpp b/app/audioRewrapper/audioRewrapper.cpp index 3348d1f8..9415439e 100644 --- a/app/audioRewrapper/audioRewrapper.cpp +++ b/app/audioRewrapper/audioRewrapper.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -54,15 +55,29 @@ void transcodeAudio( const char* inputfilename, const char* outputFilename ) // init audio decoders InputStreamAudio inputStreamAudio( inputFile.getStream( 0 ) ); inputFile.getStream( 0 ).setBufferred( true ); - + OutputFile outputFile( outputFilename ); - outputFile.setup(); - outputFile.addAudioStream( inputFile.getStream( 0 ).getAudioDesc() ); - - DataStream data; - + outputFile.beginWrap(); + + OutputStreamAudio outputStreamAudio; + AudioDesc& audioDesc = outputStreamAudio.getAudioDesc(); + audioDesc.setAudioCodec( "pcm_s16le" ); + audioDesc.setAudioParameters( + inputFile.getStream( 0 ).getAudioDesc().getSampleRate(), + inputFile.getStream( 0 ).getAudioDesc().getChannels(), + inputFile.getStream( 0 ).getAudioDesc().getSampleFormat() + ); + + DataStream codedFrame; + + if( ! outputStreamAudio.setup( ) ) + { + std::cout << "error during initialising audio output stream" << std::endl; + exit( -1 ); + } + // Transcoding std::cout << "start transcoding" << std::endl; @@ -70,8 +85,6 @@ void transcodeAudio( const char* inputfilename, const char* outputFilename ) AudioFrameDesc audioFrameDesc; AudioFrame audioFrame( audioFrameDesc ); - - DataStream codedFrame; while( inputStreamAudio.readNextFrame( audioFrame ) ) { @@ -79,13 +92,15 @@ void transcodeAudio( const char* inputfilename, const char* outputFilename ) // convert - //outputStreamAudio.encodeFrame( audioFrame, codedFrame ); + outputStreamAudio.encodeFrame( audioFrame, codedFrame ); - // outputFile.wrap( codedFrame, 0 ); + outputFile.wrap( codedFrame, 0 ); ++frame; } std::cout << std::endl; + + outputFile.endWrap(); } int main( int argc, char** argv ) diff --git a/src/AvTranscoder/DatasStructures/AudioFrame.hpp b/src/AvTranscoder/DatasStructures/AudioFrame.hpp index 26db2241..4f98f8e2 100644 --- a/src/AvTranscoder/DatasStructures/AudioFrame.hpp +++ b/src/AvTranscoder/DatasStructures/AudioFrame.hpp @@ -65,6 +65,7 @@ class AudioFrame AudioFrame( const AudioFrameDesc& ref ) : m_dataBuffer( ref.getDataSize(), 0 ) , m_audioFrameDesc( ref ) + , m_nbSamples( 0 ) { } const AudioFrameDesc& desc() const { return m_audioFrameDesc; } @@ -74,10 +75,14 @@ class AudioFrame const unsigned char* getPtr() const { return &m_dataBuffer[0]; } #endif size_t getSize() const { return m_dataBuffer.size(); } + + size_t getNbSamples() const { return m_nbSamples; } + void setNbSamples( size_t nbSamples ) { m_nbSamples = nbSamples; } private: DataBuffer m_dataBuffer; const AudioFrameDesc m_audioFrameDesc; + size_t m_nbSamples; }; } diff --git a/src/AvTranscoder/InputStreamAudio.cpp b/src/AvTranscoder/InputStreamAudio.cpp index 4485c697..c65b14b5 100644 --- a/src/AvTranscoder/InputStreamAudio.cpp +++ b/src/AvTranscoder/InputStreamAudio.cpp @@ -50,8 +50,6 @@ InputStreamAudio::InputStreamAudio( AvInputStream& inputStream ) int ret = avcodec_open2( m_codecContext, m_codec, NULL ); - std::cout << "ret value : " << ret << std::endl; - if( ret < 0 || m_codecContext == NULL || m_codec == NULL ) { std::string msg = "unable open audio codec: "; @@ -135,6 +133,8 @@ bool InputStreamAudio::readNextFrame( AudioFrame& audioFrameBuffer ) m_frame->nb_samples, m_codecContext->sample_fmt, 1); + audioFrameBuffer.setNbSamples( m_frame->nb_samples ); + if( decodedSize ) { if( audioFrameBuffer.getSize() != decodedSize ) diff --git a/src/AvTranscoder/OutputStreamAudio.cpp b/src/AvTranscoder/OutputStreamAudio.cpp index f6f13dc0..7a866725 100644 --- a/src/AvTranscoder/OutputStreamAudio.cpp +++ b/src/AvTranscoder/OutputStreamAudio.cpp @@ -1 +1,123 @@ -#include "OutputStreamAudio.hpp" \ No newline at end of file +#include "OutputStreamAudio.hpp" + +extern "C" { +#ifndef __STDC_CONSTANT_MACROS + #define __STDC_CONSTANT_MACROS +#endif +#include +#include +#include +} + +namespace avtranscoder +{ + +OutputStreamAudio::OutputStreamAudio() + : m_audioDesc( "pcm_s16le" ) +{ +} + +bool OutputStreamAudio::setup() +{ + av_register_all(); // Warning: should be called only once + + AVCodecContext* codecContext( m_audioDesc.getCodecContext() ); + + if( codecContext == NULL ) + return false; + + // try to open encoder with parameters. + if( avcodec_open2( codecContext, m_audioDesc.getCodec(), NULL ) < 0 ) + return false; + + return true; +} + +bool OutputStreamAudio::encodeFrame( const AudioFrame& decodedFrame, DataStream& codedFrame ) +{ +#if LIBAVCODEC_VERSION_MAJOR > 54 + AVFrame* frame = av_frame_alloc(); +#else + AVFrame* frame = avcodec_alloc_frame(); +#endif + + AVCodecContext* codecContext = m_audioDesc.getCodecContext(); + + // Set default frame parameters +#if LIBAVCODEC_VERSION_MAJOR > 54 + av_frame_unref( frame ); +#else + avcodec_get_frame_defaults( frame ); +#endif + + frame->nb_samples = decodedFrame.getNbSamples(); + frame->format = codecContext->sample_fmt; + frame->channel_layout = codecContext->channel_layout; + + // we calculate the size of the samples buffer in bytes + int buffer_size = av_samples_get_buffer_size(NULL, codecContext->channels, frame->nb_samples, codecContext->sample_fmt, 0); + if( buffer_size < 0 ) + { + char err[250]; + av_strerror( buffer_size, err, 250); + + throw std::runtime_error( "EncodeFrame error: buffer size < 0 - " + std::string(err) ); + } + + int retvalue = avcodec_fill_audio_frame(frame, codecContext->channels, codecContext->sample_fmt, decodedFrame.getPtr(), buffer_size, 0); + if( retvalue < 0 ) + { + char err[250]; + av_strerror( retvalue, err, 250); + + throw std::runtime_error( "EncodeFrame error: avcodec fill audio frame - " + std::string(err) ); + } + + AVPacket packet; + av_init_packet( &packet ); + + packet.size = 0; + packet.data = NULL; + packet.stream_index = 0; + + if( ( codecContext->coded_frame ) && + ( codecContext->coded_frame->pts != (int)AV_NOPTS_VALUE ) ) + { + packet.pts = codecContext->coded_frame->pts; + } + + if( codecContext->coded_frame && + codecContext->coded_frame->key_frame ) + { + packet.flags |= AV_PKT_FLAG_KEY; + } + +#if LIBAVCODEC_VERSION_MAJOR > 53 + int gotPacket = 0; + int ret = avcodec_encode_audio2( codecContext, &packet, frame, &gotPacket ); + if( ret == 0 && gotPacket == 1 ) + { + codedFrame.getBuffer().resize( packet.size ); + memcpy( codedFrame.getPtr(), packet.data, packet.size ); + } +#else + // throw... +#endif + + av_free_packet( &packet ); + +#if LIBAVCODEC_VERSION_MAJOR > 54 + av_frame_free( &frame ); + return ret == 0 && gotPacket == 1; +#else + #if LIBAVCODEC_VERSION_MAJOR > 53 + avcodec_free_frame( &frame ); + return ret == 0 && gotPacket == 1; + #else + av_free( frame ); + #endif +#endif + return ret == 0; +} + +} \ No newline at end of file diff --git a/src/AvTranscoder/OutputStreamAudio.hpp b/src/AvTranscoder/OutputStreamAudio.hpp index c0bdd0c9..02970c78 100644 --- a/src/AvTranscoder/OutputStreamAudio.hpp +++ b/src/AvTranscoder/OutputStreamAudio.hpp @@ -2,6 +2,9 @@ #define _AV_TRANSCODER_OUTPUT_STREAM_AUDIO_HPP_ #include "OutputStream.hpp" +#include "DatasStructures/AudioFrame.hpp" +#include "DatasStructures/AudioDesc.hpp" +#include "DatasStructures/DataStreamDesc.hpp" namespace avtranscoder { @@ -9,10 +12,24 @@ namespace avtranscoder class OutputStreamAudio : public OutputStream { public: - OutputStreamAudio(){}; + OutputStreamAudio(); + + bool setup(); -private: + /** + * @param[out] codedFrame + */ + bool encodeFrame( const AudioFrame& decodedFrame, DataStream& codedFrame ); + + /** + * get delayed encoded frames + */ + //bool encodeFrame( DataStream& codedFrame ); + AudioDesc& getAudioDesc() { return m_audioDesc; } + +private: + AudioDesc m_audioDesc; }; } From 3a719d02f4e13ede87c3cb8aec60f1d54679618b Mon Sep 17 00:00:00 2001 From: Clement Champetier Date: Tue, 3 Jun 2014 18:16:04 +0200 Subject: [PATCH 5/5] OutputStreamAudio: overlay encodeFrame * This new function gets delayed encoded frames. * Rewrapper: call this new function. --- app/audioRewrapper/audioRewrapper.cpp | 2 ++ src/AvTranscoder/OutputStreamAudio.cpp | 42 +++++++++++++++++++++++++- src/AvTranscoder/OutputStreamAudio.hpp | 2 +- 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/app/audioRewrapper/audioRewrapper.cpp b/app/audioRewrapper/audioRewrapper.cpp index 9415439e..2310bdea 100644 --- a/app/audioRewrapper/audioRewrapper.cpp +++ b/app/audioRewrapper/audioRewrapper.cpp @@ -100,6 +100,8 @@ void transcodeAudio( const char* inputfilename, const char* outputFilename ) } std::cout << std::endl; + outputStreamAudio.encodeFrame( codedFrame ); + outputFile.endWrap(); } diff --git a/src/AvTranscoder/OutputStreamAudio.cpp b/src/AvTranscoder/OutputStreamAudio.cpp index 7a866725..59dc022c 100644 --- a/src/AvTranscoder/OutputStreamAudio.cpp +++ b/src/AvTranscoder/OutputStreamAudio.cpp @@ -101,7 +101,12 @@ bool OutputStreamAudio::encodeFrame( const AudioFrame& decodedFrame, DataStream& memcpy( codedFrame.getPtr(), packet.data, packet.size ); } #else - // throw... + int ret = avcodec_encode_audio( codecContext, packet.data, packet.size, frame ); + if( ret > 0 ) + { + codedFrame.getBuffer().resize( packet.size ); + memcpy( codedFrame.getPtr(), packet.data, packet.size ); + } #endif av_free_packet( &packet ); @@ -120,4 +125,39 @@ bool OutputStreamAudio::encodeFrame( const AudioFrame& decodedFrame, DataStream& return ret == 0; } +bool OutputStreamAudio::encodeFrame( DataStream& codedFrame ) +{ + AVCodecContext* codecContext = m_audioDesc.getCodecContext(); + + AVPacket packet; + av_init_packet( &packet ); + + packet.size = 0; + packet.data = NULL; + packet.stream_index = 0; + +#if LIBAVCODEC_VERSION_MAJOR > 53 + int gotPacket = 0; + int ret = avcodec_encode_audio2( codecContext, &packet, NULL, &gotPacket ); + if( ret == 0 && gotPacket == 1 ) + { + codedFrame.getBuffer().resize( packet.size ); + memcpy( codedFrame.getPtr(), packet.data, packet.size ); + } + av_free_packet( &packet ); + return ret == 0 && gotPacket == 1; + +#else + int ret = avcodec_encode_audio( codecContext, packet.data, packet.size, NULL ); + if( ret > 0 ) + { + codedFrame.getBuffer().resize( packet.size ); + memcpy( codedFrame.getPtr(), packet.data, packet.size ); + } + av_free_packet( &packet ); + return ret == 0; + +#endif +} + } \ No newline at end of file diff --git a/src/AvTranscoder/OutputStreamAudio.hpp b/src/AvTranscoder/OutputStreamAudio.hpp index 02970c78..5686c4f2 100644 --- a/src/AvTranscoder/OutputStreamAudio.hpp +++ b/src/AvTranscoder/OutputStreamAudio.hpp @@ -24,7 +24,7 @@ class OutputStreamAudio : public OutputStream /** * get delayed encoded frames */ - //bool encodeFrame( DataStream& codedFrame ); + bool encodeFrame( DataStream& codedFrame ); AudioDesc& getAudioDesc() { return m_audioDesc; }