',
+ prog='pyrewrap',
+ description='''Rewrap a video file and can move the index (moov atom) to the beginning of the file.''',
+ )
+
+ # requirements
+ parser.add_option("-i", "--inputFile", dest='inputFileName', type="string", help='It could be any video file. Support file without extension.')
+ # options
+ parser.add_option("-o", "--outputFile", dest="outputFileName", type="string", default="output.mov", help="Set the output filename (thumbnail.jpg by default). Must be with jpg extension!")
+ parser.add_option("-c", "--format", dest="format", type="string", default="mov", help="Specify the output format.")
+ parser.add_option("-f", "--faststart", dest="faststart", action="store_true", default=False, help="Specify if the faststart option must be apply during rewrapping process (warning: 'mov' specific option).")
+ # Parse command-line
+ args, other = parser.parse_args()
+
+ if args.inputFileName is None:
+ parser.print_help()
+ exit(1)
+
+# setup avtranscoder
+logger = av.Logger().setLogLevel(av.AV_LOG_QUIET)
+av.preloadCodecsAndFormats()
+
+# create input file
+inputFile = av.InputFile(args.inputFileName)
+if len(inputFile.getProperties().getVideoProperties()) == 0:
+ print("No video stream found in file ", args.inputFileName)
+ exit(1)
+
+# create output file (need to set format profile of encoding to force output format to mp4)
+formatProfile = av.ProfileMap()
+formatProfile[ av.avProfileIdentificator ] = "mp4WrapFormatPreset"
+formatProfile[ av.avProfileIdentificatorHuman ] = "MP4 rewraping format preset"
+formatProfile[ av.avProfileType ] = av.avProfileTypeFormat
+formatProfile[ av.avProfileFormat ] = args.format
+if args.faststart:
+ formatProfile[ "movflags" ] = "+faststart"
+outputFile = av.OutputFile( args.outputFileName )
+outputFile.setupWrapping( formatProfile )
+
+# create transcoder
+transcoder = av.Transcoder( outputFile )
+
+def addStreamsToTranscoder(transcoder, streams):
+ for st in streams:
+ stIndex = st.getStreamIndex()
+ transcoder.add( args.inputFileName, stIndex )
+
+addStreamsToTranscoder(transcoder, inputFile.getProperties().getVideoProperties())
+addStreamsToTranscoder(transcoder, inputFile.getProperties().getAudioProperties())
+# addStreamsToTranscoder(transcoder, inputFile.getProperties().getDataProperties())
+
+# launch process
+progress = av.ConsoleProgress()
+transcoder.process(progress)
diff --git a/app/pyThumbnail/CMakeLists.txt b/app/pyThumbnail/CMakeLists.txt
index e20d47fd..b3d121bb 100644
--- a/app/pyThumbnail/CMakeLists.txt
+++ b/app/pyThumbnail/CMakeLists.txt
@@ -6,3 +6,7 @@ install(
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE
DESTINATION "bin/python"
)
+
+if(UNIX)
+ install( CODE "EXECUTE_PROCESS(COMMAND ln -sf python/pythumbnail.py ${CMAKE_INSTALL_PREFIX}/bin/pythumbnail)" )
+endif(UNIX)
diff --git a/app/pyThumbnail/pythumbnail.py b/app/pyThumbnail/pythumbnail.py
index a22338d0..ca7eeb64 100644
--- a/app/pyThumbnail/pythumbnail.py
+++ b/app/pyThumbnail/pythumbnail.py
@@ -75,7 +75,7 @@
formatProfile[ av.avProfileType ] = av.avProfileTypeFormat
formatProfile[ av.avProfileFormat ] = "mjpeg"
outputFile = av.OutputFile( args.outputFileName )
-outputFile.setProfile( formatProfile )
+outputFile.setupWrapping( formatProfile )
# create input stream
videoStreamIndex = inputFile.getProperties().getVideoProperties()[0].getStreamIndex()
diff --git a/cmake/FindFFmpeg.cmake b/cmake/FindFFmpeg.cmake
index a567f67e..a2683293 100644
--- a/cmake/FindFFmpeg.cmake
+++ b/cmake/FindFFmpeg.cmake
@@ -94,9 +94,15 @@ macro(manage_components)
# If the component is found.
if(${COMPONENT}_FOUND)
message(STATUS "Component ${COMPONENT} present.")
- set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${COMPONENT}_LIBRARIES})
- set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${COMPONENT}_DEFINITIONS})
- list(APPEND FFMPEG_INCLUDE_DIR ${${COMPONENT}_INCLUDE_DIR})
+ # Skip components which are in a different location
+ # This prevents us to depend on libav system libraries if we build with ffmpeg (and the reverse).
+ if(NOT FFMPEG_INCLUDE_DIR OR FFMPEG_INCLUDE_DIR STREQUAL ${${COMPONENT}_INCLUDE_DIR})
+ set(FFMPEG_INCLUDE_DIR ${${COMPONENT}_INCLUDE_DIR})
+ set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${COMPONENT}_LIBRARIES})
+ set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${COMPONENT}_DEFINITIONS})
+ else()
+ set(${COMPONENT}_FOUND FALSE)
+ endif()
else()
if(FFmpeg_FIND_REQUIRED)
message(SEND_ERROR "Error: required component ${COMPONENT} missing.")
diff --git a/doc/code/index.doxygen b/doc/code/index.doxygen
index 5107d606..a2f650e9 100644
--- a/doc/code/index.doxygen
+++ b/doc/code/index.doxygen
@@ -1,6 +1,6 @@
/** @mainpage AvTranscoder
*
- * An High Level API for transform medias.
+ * An High Level API to transform medias.
* Based on FFMpeg / libav projects.
*
* Links
@@ -8,7 +8,6 @@
* github: | https://github.com/avTranscoder/avTranscoder |
* openhub: | https://www.openhub.net/p/avTranscoder |
* travis-ci: | https://travis-ci.org/avTranscoder/avTranscoder |
- * drone-io: | https://drone.io/github.com/avTranscoder/avTranscoder/latest |
* coverity-scan: | https://scan.coverity.com/projects/2626 |
*
*
diff --git a/src/AvTranscoder/avTranscoder.i b/src/AvTranscoder/avTranscoder.i
index ac5cb1bf..bb39d9c0 100644
--- a/src/AvTranscoder/avTranscoder.i
+++ b/src/AvTranscoder/avTranscoder.i
@@ -39,3 +39,4 @@
%include "AvTranscoder/file/file.i"
%include "AvTranscoder/stat/stat.i"
%include "AvTranscoder/transcoder/transcoder.i"
+%include "AvTranscoder/reader/reader.i"
diff --git a/src/AvTranscoder/codec/ICodec.cpp b/src/AvTranscoder/codec/ICodec.cpp
index d0a778bf..e3ffc407 100644
--- a/src/AvTranscoder/codec/ICodec.cpp
+++ b/src/AvTranscoder/codec/ICodec.cpp
@@ -54,12 +54,12 @@ ICodec::~ICodec()
void ICodec::openCodec()
{
if( ! _avCodecContext )
- throw std::runtime_error( "unable to open a codec with no codec context" );
+ throw std::runtime_error( "Unable to open a codec without codec context" );
- int ret = avcodec_open2( _avCodecContext, _avCodec, NULL );
+ const int ret = avcodec_open2( _avCodecContext, _avCodec, NULL );
if( ret < 0 )
{
- std::string msg = "unable open codec: ";
+ std::string msg = "Unable to open codec: ";
if( _avCodec && _avCodec->long_name )
msg += _avCodec->long_name;
@@ -84,7 +84,7 @@ std::string ICodec::getCodecName() const
assert( _avCodecContext != NULL );
const AVCodecDescriptor * desc = avcodec_descriptor_get( _avCodecContext->codec_id );
if( ! desc )
- throw std::runtime_error( "Codec Descriptor is not available." );
+ throw std::runtime_error( "Codec Descriptor is not available." );
return desc->name;
}
@@ -116,7 +116,7 @@ void ICodec::setCodec( const ECodecType type, const std::string& codecName )
const AVCodecDescriptor* avCodecDescriptor = avcodec_descriptor_get_by_name( codecName.c_str() );
if( ! avCodecDescriptor )
{
- std::string msg( "unable to find codec " );
+ std::string msg( "Unable to find codec " );
msg += codecName;
throw std::runtime_error( msg );
}
@@ -146,7 +146,7 @@ void ICodec::allocateContext()
_avCodecContext = avcodec_alloc_context3( _avCodec );
if( ! _avCodecContext )
{
- throw std::runtime_error( "unable to allocate the codecContext and set its fields to default values" );
+ throw std::runtime_error( "Unable to allocate the codecContext and set its fields to default values" );
}
_avCodecContext->codec = _avCodec;
}
diff --git a/src/AvTranscoder/decoder/AudioDecoder.cpp b/src/AvTranscoder/decoder/AudioDecoder.cpp
index 4e12ad32..eb9432fe 100644
--- a/src/AvTranscoder/decoder/AudioDecoder.cpp
+++ b/src/AvTranscoder/decoder/AudioDecoder.cpp
@@ -18,8 +18,9 @@ namespace avtranscoder
{
AudioDecoder::AudioDecoder( InputStream& inputStream )
- : _inputStream ( &inputStream )
- , _frame ( NULL )
+ : _inputStream( &inputStream )
+ , _frame( NULL )
+ , _isSetup(false)
{
#if LIBAVCODEC_VERSION_MAJOR > 54
_frame = av_frame_alloc();
@@ -52,7 +53,16 @@ AudioDecoder::~AudioDecoder()
void AudioDecoder::setupDecoder( const ProfileLoader::Profile& profile )
{
- LOG_DEBUG( "Set profile of audio decoder with:\n" << profile )
+ // check the given profile
+ const bool isValid = ProfileLoader::checkAudioProfile( profile );
+ if( ! isValid && ! profile.empty() )
+ {
+ const std::string msg( "Invalid audio profile to setup decoder." );
+ LOG_ERROR( msg )
+ throw std::runtime_error( msg );
+ }
+
+ LOG_INFO( "Setup audio decoder with:\n" << profile )
AudioCodec& codec = _inputStream->getAudioCodec();
@@ -84,6 +94,7 @@ void AudioDecoder::setupDecoder( const ProfileLoader::Profile& profile )
// open decoder
_inputStream->getAudioCodec().openCodec();
+ _isSetup = true;
}
bool AudioDecoder::decodeNextFrame( Frame& frameBuffer )
@@ -155,6 +166,9 @@ bool AudioDecoder::decodeNextFrame( Frame& frameBuffer, const size_t subStreamIn
bool AudioDecoder::decodeNextFrame()
{
+ if(!_isSetup)
+ setupDecoder();
+
int got_frame = 0;
while( ! got_frame )
{
@@ -176,4 +190,9 @@ bool AudioDecoder::decodeNextFrame()
return true;
}
+void AudioDecoder::flushDecoder()
+{
+ avcodec_flush_buffers( &_inputStream->getAudioCodec().getAVCodecContext() );
+}
+
}
diff --git a/src/AvTranscoder/decoder/AudioDecoder.hpp b/src/AvTranscoder/decoder/AudioDecoder.hpp
index b9fc3300..973fd7ea 100644
--- a/src/AvTranscoder/decoder/AudioDecoder.hpp
+++ b/src/AvTranscoder/decoder/AudioDecoder.hpp
@@ -21,12 +21,16 @@ class AvExport AudioDecoder : public IDecoder
bool decodeNextFrame( Frame& frameBuffer );
bool decodeNextFrame( Frame& frameBuffer, const size_t subStreamIndex );
+ void flushDecoder();
+
private:
bool decodeNextFrame();
private:
InputStream* _inputStream; ///< Stream from which we read next frames (no ownership, has link)
AVFrame* _frame; ///< Libav object to store decoded data (has ownership)
+
+ bool _isSetup;
};
}
diff --git a/src/AvTranscoder/decoder/AudioGenerator.hpp b/src/AvTranscoder/decoder/AudioGenerator.hpp
index b9defc01..1590b974 100644
--- a/src/AvTranscoder/decoder/AudioGenerator.hpp
+++ b/src/AvTranscoder/decoder/AudioGenerator.hpp
@@ -16,8 +16,6 @@ class AvExport AudioGenerator : public IDecoder
~AudioGenerator();
- void setupDecoder( const ProfileLoader::Profile& profile = ProfileLoader::Profile() ) {}
-
bool decodeNextFrame( Frame& frameBuffer );
bool decodeNextFrame( Frame& frameBuffer, const size_t subStreamIndex );
diff --git a/src/AvTranscoder/decoder/IDecoder.hpp b/src/AvTranscoder/decoder/IDecoder.hpp
index bbbf4fa8..3b65fe47 100644
--- a/src/AvTranscoder/decoder/IDecoder.hpp
+++ b/src/AvTranscoder/decoder/IDecoder.hpp
@@ -18,7 +18,7 @@ class AvExport IDecoder
* @param profile: set decoder parameters from the given profile
* @note Open the decoder.
*/
- virtual void setupDecoder( const ProfileLoader::Profile& profile = ProfileLoader::Profile() ) = 0;
+ virtual void setupDecoder( const ProfileLoader::Profile& profile = ProfileLoader::Profile() ) {}
/**
* @brief Decode next frame
@@ -41,6 +41,13 @@ class AvExport IDecoder
* @param inputFrame: the new next frame
*/
virtual void setNextFrame( Frame& inputFrame ) {}
+
+ /**
+ * @brief Reset the internal decoder state / flush internal buffers.
+ * @note Should be called when seeking or when switching to a different stream.
+ * @note Not sense for generators.
+ */
+ virtual void flushDecoder() {}
};
}
diff --git a/src/AvTranscoder/decoder/VideoDecoder.cpp b/src/AvTranscoder/decoder/VideoDecoder.cpp
index 68503d8b..08f87271 100644
--- a/src/AvTranscoder/decoder/VideoDecoder.cpp
+++ b/src/AvTranscoder/decoder/VideoDecoder.cpp
@@ -17,8 +17,9 @@ namespace avtranscoder
{
VideoDecoder::VideoDecoder( InputStream& inputStream )
- : _inputStream ( &inputStream )
- , _frame ( NULL )
+ : _inputStream( &inputStream )
+ , _frame( NULL )
+ , _isSetup(false)
{
#if LIBAVCODEC_VERSION_MAJOR > 54
_frame = av_frame_alloc();
@@ -50,7 +51,16 @@ VideoDecoder::~VideoDecoder()
void VideoDecoder::setupDecoder( const ProfileLoader::Profile& profile )
{
- LOG_DEBUG( "Set profile of video decoder with:\n" << profile )
+ // check the given profile
+ const bool isValid = ProfileLoader::checkVideoProfile( profile );
+ if( ! isValid && ! profile.empty() )
+ {
+ const std::string msg( "Invalid video profile to setup decoder." );
+ LOG_ERROR( msg )
+ throw std::runtime_error( msg );
+ }
+
+ LOG_INFO( "Setup video decoder with:\n" << profile )
VideoCodec& codec = _inputStream->getVideoCodec();
@@ -82,6 +92,7 @@ void VideoDecoder::setupDecoder( const ProfileLoader::Profile& profile )
// open decoder
_inputStream->getVideoCodec().openCodec();
+ _isSetup = true;
}
bool VideoDecoder::decodeNextFrame( Frame& frameBuffer )
@@ -109,6 +120,9 @@ bool VideoDecoder::decodeNextFrame( Frame& frameBuffer, const size_t subStreamIn
bool VideoDecoder::decodeNextFrame()
{
+ if(!_isSetup)
+ setupDecoder();
+
int got_frame = 0;
while( ! got_frame )
{
diff --git a/src/AvTranscoder/decoder/VideoDecoder.hpp b/src/AvTranscoder/decoder/VideoDecoder.hpp
index 72dfb9cd..e82ed011 100644
--- a/src/AvTranscoder/decoder/VideoDecoder.hpp
+++ b/src/AvTranscoder/decoder/VideoDecoder.hpp
@@ -29,6 +29,8 @@ class AvExport VideoDecoder : public IDecoder
private:
InputStream* _inputStream; ///< Stream from which we read next frames (no ownership, has link)
AVFrame* _frame; ///< Libav object to store decoded data (has ownership)
+
+ bool _isSetup;
};
}
diff --git a/src/AvTranscoder/decoder/VideoGenerator.hpp b/src/AvTranscoder/decoder/VideoGenerator.hpp
index ce7cb4fd..2e9b2b40 100644
--- a/src/AvTranscoder/decoder/VideoGenerator.hpp
+++ b/src/AvTranscoder/decoder/VideoGenerator.hpp
@@ -16,8 +16,6 @@ class AvExport VideoGenerator : public IDecoder
~VideoGenerator();
- void setupDecoder( const ProfileLoader::Profile& profile = ProfileLoader::Profile() ) {}
-
bool decodeNextFrame( Frame& frameBuffer );
bool decodeNextFrame( Frame& frameBuffer, const size_t subStreamIndex );
diff --git a/src/AvTranscoder/encoder/AudioEncoder.cpp b/src/AvTranscoder/encoder/AudioEncoder.cpp
index 9ad4dc74..93e68342 100644
--- a/src/AvTranscoder/encoder/AudioEncoder.cpp
+++ b/src/AvTranscoder/encoder/AudioEncoder.cpp
@@ -37,7 +37,7 @@ AudioEncoder::~AudioEncoder()
void AudioEncoder::setupAudioEncoder( const AudioFrameDesc& frameDesc, const ProfileLoader::Profile& profile )
{
- LOG_DEBUG( "Set profile of audio encoder with:\n" << profile )
+ LOG_INFO( "Setup audio encoder with:\n" << profile )
// set sampleRate, number of channels, sample format
_codec.setAudioParameters( frameDesc );
@@ -48,6 +48,15 @@ void AudioEncoder::setupAudioEncoder( const AudioFrameDesc& frameDesc, const Pro
void AudioEncoder::setupEncoder( const ProfileLoader::Profile& profile )
{
+ // check the given profile
+ const bool isValid = ProfileLoader::checkAudioProfile( profile );
+ if( ! isValid && ! profile.empty() )
+ {
+ const std::string msg( "Invalid audio profile to setup encoder." );
+ LOG_ERROR( msg )
+ throw std::runtime_error( msg );
+ }
+
// set threads before any other options
if( profile.count( constants::avProfileThreads ) )
_codec.getOption( constants::avProfileThreads ).setString( profile.at( constants::avProfileThreads ) );
diff --git a/src/AvTranscoder/encoder/VideoEncoder.cpp b/src/AvTranscoder/encoder/VideoEncoder.cpp
index 089fade3..cca0820e 100644
--- a/src/AvTranscoder/encoder/VideoEncoder.cpp
+++ b/src/AvTranscoder/encoder/VideoEncoder.cpp
@@ -38,7 +38,7 @@ VideoEncoder::~VideoEncoder()
void VideoEncoder::setupVideoEncoder( const VideoFrameDesc& frameDesc, const ProfileLoader::Profile& profile )
{
- LOG_DEBUG( "Set profile of video encoder with:\n" << profile )
+ LOG_INFO( "Setup video encoder with:\n" << profile )
// set width, height, pixel format, fps
_codec.setImageParameters( frameDesc );
@@ -49,6 +49,15 @@ void VideoEncoder::setupVideoEncoder( const VideoFrameDesc& frameDesc, const Pro
void VideoEncoder::setupEncoder( const ProfileLoader::Profile& profile )
{
+ // check the given profile
+ const bool isValid = ProfileLoader::checkVideoProfile( profile );
+ if( ! isValid && ! profile.empty() )
+ {
+ const std::string msg( "Invalid video profile to setup encoder." );
+ LOG_ERROR( msg )
+ throw std::runtime_error( msg );
+ }
+
// set threads before any other options
if( profile.count( constants::avProfileThreads ) )
_codec.getOption( constants::avProfileThreads ).setString( profile.at( constants::avProfileThreads ) );
diff --git a/src/AvTranscoder/file/FormatContext.cpp b/src/AvTranscoder/file/FormatContext.cpp
index bd8de3f9..7dd22cbc 100644
--- a/src/AvTranscoder/file/FormatContext.cpp
+++ b/src/AvTranscoder/file/FormatContext.cpp
@@ -15,7 +15,7 @@ FormatContext::FormatContext( const std::string& filename, int req_flags, AVDict
int ret = avformat_open_input( &_avFormatContext, filename.c_str(), NULL, options );
if( ret < 0 )
{
- std::string msg = "unable to open file ";
+ std::string msg = "Unable to open file ";
msg += filename;
msg += ": ";
msg += getDescriptionFromErrorCode( ret );
@@ -56,7 +56,7 @@ void FormatContext::findStreamInfo( AVDictionary** options )
int err = avformat_find_stream_info( _avFormatContext, options );
if( err < 0 )
{
- throw std::ios_base::failure( "unable to find stream informations: " + getDescriptionFromErrorCode( err ) );
+ throw std::ios_base::failure( "Unable to find stream informations: " + getDescriptionFromErrorCode( err ) );
}
}
@@ -68,7 +68,7 @@ void FormatContext::openRessource( const std::string& url, int flags )
int err = avio_open2( &_avFormatContext->pb, url.c_str(), flags, NULL, NULL );
if( err < 0 )
{
- throw std::ios_base::failure( "error when opening output format: " + getDescriptionFromErrorCode( err ) );
+ throw std::ios_base::failure( "Error when opening output format: " + getDescriptionFromErrorCode( err ) );
}
}
@@ -80,7 +80,7 @@ void FormatContext::closeRessource()
int err = avio_close( _avFormatContext->pb );
if( err < 0 )
{
- throw std::ios_base::failure( "error when close output format: " + getDescriptionFromErrorCode( err ) );
+ throw std::ios_base::failure( "Error when close output format: " + getDescriptionFromErrorCode( err ) );
}
}
@@ -89,7 +89,7 @@ void FormatContext::writeHeader( AVDictionary** options )
int ret = avformat_write_header( _avFormatContext, options );
if( ret != 0 )
{
- throw std::runtime_error( "could not write header: " + getDescriptionFromErrorCode( ret ) );
+ throw std::runtime_error( "Could not write header: " + getDescriptionFromErrorCode( ret ) );
}
// when muxing, priv_data of AVFormatContext is set by avformat_write_header()
if( _avFormatContext->oformat->priv_class )
@@ -109,7 +109,7 @@ void FormatContext::writeFrame( AVPacket& packet, bool interleaved )
if( ret < 0 )
{
- throw std::runtime_error( "error when writting packet in stream: " + getDescriptionFromErrorCode( ret ) );
+ throw std::runtime_error( "Error when writting packet in stream: " + getDescriptionFromErrorCode( ret ) );
}
}
@@ -118,7 +118,7 @@ void FormatContext::writeTrailer()
int ret = av_write_trailer( _avFormatContext );
if( ret != 0 )
{
- throw std::runtime_error( "could not write trailer: " + getDescriptionFromErrorCode( ret ) );
+ throw std::runtime_error( "Could not write trailer: " + getDescriptionFromErrorCode( ret ) );
}
}
@@ -137,20 +137,24 @@ AVStream& FormatContext::addAVStream( const AVCodec& avCodec )
AVStream* stream = avformat_new_stream( _avFormatContext, const_cast(&avCodec) );
if( stream == NULL )
{
- throw std::runtime_error( "unable to add new video stream" );
+ throw std::runtime_error( "Unable to add new video stream" );
}
return *stream;
}
-void FormatContext::seek( uint64_t position, const int flag )
+bool FormatContext::seek( uint64_t position, const int flag )
{
if( (int)getStartTime() != AV_NOPTS_VALUE )
position += getStartTime();
- if( av_seek_frame( _avFormatContext, -1, position, flag ) < 0 )
+ int err = av_seek_frame( _avFormatContext, -1, position, flag );
+ if( err < 0 )
{
LOG_ERROR( "Error when seek at " << position << " (in AV_TIME_BASE units) in file" )
+ LOG_ERROR( getDescriptionFromErrorCode( err ) )
+ return false;
}
+ return true;
}
std::vector