diff --git a/.travis.yml b/.travis.yml index 1968bf7c..cdd81f46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,8 @@ +# Use container-based infrastructure +sudo: required +services: + - docker + language: cpp os: @@ -15,14 +20,42 @@ env: - DEPENDENCY_INSTALL=${TRAVIS_BUILD_DIR}/install-dependency - CI_NODE_TOTAL=2 matrix: - - DEPENDENCY_MODE=libav ENABLE_COVERAGE=true - - DEPENDENCY_MODE=libav ENABLE_COVERAGE=false + - DEPENDENCY_MODE=libav ENABLE_COVERAGE=true + - DEPENDENCY_MODE=libav ENABLE_COVERAGE=false - DEPENDENCY_MODE=ffmpeg ENABLE_COVERAGE=true - DEPENDENCY_MODE=ffmpeg ENABLE_COVERAGE=false +matrix: + # generate coverage only with gcc + exclude: + - compiler: clang + env: DEPENDENCY_MODE=ffmpeg ENABLE_COVERAGE=true + - compiler: clang + env: DEPENDENCY_MODE=libav ENABLE_COVERAGE=true + allow_failures: + - os: osx + fast_finish: true + # This results in a 2×2×2x2 build matrix. # Where the variables are: os / compiler / DEPENDENCY_MODE / ENABLE_COVERAGE +addons: + apt: + sources: ['kalakris-cmake'] + packages: + - cmake + - swig + - python-dev + - python-nose + - freeglut3-dev + +cache: + # Caching Ubuntu packages + apt: true + # Caching other directories + directories: + - ${DEPENDENCY_INSTALL} + before_script: - env | sort - date -u @@ -39,18 +72,12 @@ before_script: script: # build - - mkdir -p ${AVTRANSCODER_BUILD} - - cd ${AVTRANSCODER_BUILD} - - cmake .. -DCMAKE_INSTALL_PREFIX=${AVTRANSCODER_INSTALL} -DCMAKE_PREFIX_PATH=${DEPENDENCY_INSTALL} -DCMAKE_BUILD_TYPE=Release -DAVTRANSCODER_PYTHON_VERSION_OF_BINDING=2.7 -DAVTRANSCODER_COVERAGE=${ENABLE_COVERAGE} - - make -j${CI_NODE_TOTAL} - - make install + - ./tools/travis.build.sh # launch tests - - if [ ${DEPENDENCY_MODE} = "ffmpeg" ]; then ./../tools/travis.python.nosetests.sh; fi + - if [ ${DEPENDENCY_MODE} = "ffmpeg" ]; then ./tools/travis.python.nosetests.sh; fi after_success: - - cd ${TRAVIS_BUILD_DIR} - # generate coverage for coveralls - if [ ${ENABLE_COVERAGE} ]; then ./tools/travis.gcc.generate.coverage.sh; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ad9230e..2e336bfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(AvTranscoder) # Set AvTranscoder versions set(AVTRANSCODER_VERSION_MAJOR "0") set(AVTRANSCODER_VERSION_MINOR "5") -set(AVTRANSCODER_VERSION_MICRO "8") +set(AVTRANSCODER_VERSION_MICRO "10") set(AVTRANSCODER_VERSION ${AVTRANSCODER_VERSION_MAJOR}.${AVTRANSCODER_VERSION_MINOR}.${AVTRANSCODER_VERSION_MICRO}) # Define AvTranscoder versions @@ -27,8 +27,8 @@ if(MSVC) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT") else() - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -fPIC -pg -g") - set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fPIC -O3") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -fPIC -g") + set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wall -fPIC -O3") endif() # CPP flag to create code coverage report diff --git a/README.md b/README.md index 58703ec1..1832426d 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ C++ API for Libav / FFmpeg Based on Libav/FFmpeg libraries to support various video and audio formats, avTranscoder provides the high level API to re-wrap or transcode media easily. -[![Build Status](https://travis-ci.org/avTranscoder/avTranscoder.svg?branch=master)](https://travis-ci.org/avTranscoder/avTranscoder) +[![Build Status](https://travis-ci.org/avTranscoder/avTranscoder.svg?branch=develop)](https://travis-ci.org/avTranscoder/avTranscoder) [![Coverage Status](https://coveralls.io/repos/avTranscoder/avTranscoder/badge.svg)](https://coveralls.io/r/avTranscoder/avTranscoder) Coverity Scan Build Status @@ -17,12 +17,15 @@ Based on Libav/FFmpeg libraries to support various video and audio formats, avTr #### License See [**COPYING.md**](COPYING.md) -#### Compilation -See [**INSTALL.md**](INSTALL.md) - #### How to use See [**USAGE.md**](USAGE.md) +#### Documentation +See [**Doxygen documentation**](http://avtranscoder.github.io/avTranscoder-doxygen/) + +#### Compilation +See [**INSTALL.md**](INSTALL.md) + #### Tests ###### nosetests diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 9665ed61..8676a24e 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(avProcessor) # Python apps add_subdirectory(pyProcessor) add_subdirectory(pyThumbnail) +add_subdirectory(pyRewrap) diff --git a/app/avPlay/AvReader.cpp b/app/avPlay/AvReader.cpp deleted file mode 100644 index 3febbebe..00000000 --- a/app/avPlay/AvReader.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "AvReader.hpp" - -#include -#include - -AvReader::AvReader( const std::string& filename ) - : _inputFile( filename ) - , _inputVideo( NULL ) - , _sourceImage( NULL ) - , _imageToDisplay( NULL ) - , _videoTransform() - , _videoStream( 0 ) -{ - avtranscoder::NoDisplayProgress p; - _inputFile.analyse( p ); - _videoStream = _inputFile.getProperties().getVideoProperties().at(0).getStreamId(); - _inputFile.activateStream( _videoStream ); - - _inputVideo = new avtranscoder::VideoDecoder( _inputFile.getStream( _videoStream ) ); - _inputVideo->setupDecoder(); - - _sourceImage = new avtranscoder::VideoFrame( _inputFile.getStream( _videoStream ).getVideoCodec().getVideoFrameDesc() ); - - avtranscoder::VideoFrameDesc videoFrameDescToDisplay( _sourceImage->desc().getWidth(), _sourceImage->desc().getHeight(), "rgb24" ); - _imageToDisplay = new avtranscoder::VideoFrame( videoFrameDescToDisplay ); -} - -AvReader::~AvReader() -{ - delete _inputVideo; - delete _sourceImage; - delete _imageToDisplay; -} - -size_t AvReader::getWidth() -{ - return _inputFile.getProperties().getVideoProperties().at(0).getWidth(); -}; - -size_t AvReader::getHeight() -{ - return _inputFile.getProperties().getVideoProperties().at(0).getHeight(); -} - -size_t AvReader::getComponents() -{ - return _inputFile.getProperties().getVideoProperties().at(0).getPixelProperties().getNbComponents(); -} - -size_t AvReader::getBitDepth() -{ - return _inputFile.getProperties().getVideoProperties().at(0).getPixelProperties().getBitsPerPixel(); -} - -AVPixelFormat AvReader::getPixelFormat() -{ - return _inputFile.getProperties().getVideoProperties().at(0).getPixelProperties().getAVPixelFormat(); -} - -const char* AvReader::readNextFrame() -{ - ++_currentFrame; - _inputVideo->decodeNextFrame( *_sourceImage ); - _videoTransform.convert( *_sourceImage, *_imageToDisplay ); - return (const char*)_imageToDisplay->getData(); -} - -const char* AvReader::readPrevFrame() -{ - --_currentFrame; - return readFrameAt( _currentFrame ); -} - -const char* AvReader::readFrameAt( const size_t frame ) -{ - // /std::cout << "seek at " << frame << std::endl; - _inputFile.seekAtFrame( frame ); - _inputVideo->flushDecoder(); - return readNextFrame(); -} - -void AvReader::printMetadatas() -{ - std::cout << _inputFile << std::endl; -} diff --git a/app/avPlay/AvReader.hpp b/app/avPlay/AvReader.hpp deleted file mode 100644 index 7b326912..00000000 --- a/app/avPlay/AvReader.hpp +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef _AVPLAYER_AVREADER_ -#define _AVPLAYER_AVREADER_ - -#include -#include -#include -#include - -#include "Reader.hpp" - -class AvReader : public Reader -{ -public: - AvReader( const std::string& filename ); - ~AvReader(); - - size_t getWidth(); - size_t getHeight(); - size_t getComponents(); - size_t getBitDepth(); - AVPixelFormat getPixelFormat(); - - const char* readNextFrame(); - const char* readPrevFrame(); - const char* readFrameAt( const size_t frame ); - - void printMetadatas(); - -private: - avtranscoder::InputFile _inputFile; - - avtranscoder::VideoDecoder* _inputVideo; - - avtranscoder::VideoFrame* _sourceImage; - avtranscoder::VideoFrame* _imageToDisplay; - - avtranscoder::VideoTransform _videoTransform; - size_t _videoStream; -}; - -#endif diff --git a/app/avPlay/Reader.hpp b/app/avPlay/Reader.hpp deleted file mode 100644 index 418e18dc..00000000 --- a/app/avPlay/Reader.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef _AVPLAYER_READER_ -#define _AVPLAYER_READER_ - -class Reader -{ -public: - Reader() - : _currentFrame( 0 ) - {} - - virtual size_t getWidth() = 0; - virtual size_t getHeight() = 0; - virtual size_t getComponents() = 0; - virtual size_t getBitDepth() = 0; - - virtual const char* readNextFrame() = 0; - virtual const char* readPrevFrame() = 0; - virtual const char* readFrameAt( const size_t frame ) = 0; - - virtual void printMetadatas() = 0; - -protected: - - size_t _currentFrame; -}; - -#endif diff --git a/app/avPlay/Window.cpp b/app/avPlay/Window.cpp index 63d81d48..fd611596 100644 --- a/app/avPlay/Window.cpp +++ b/app/avPlay/Window.cpp @@ -14,7 +14,7 @@ #include #include -Reader* Window::_reader = NULL; +avtranscoder::VideoReader* Window::_reader = NULL; size_t Window::_width = 0; size_t Window::_height = 0; @@ -94,7 +94,7 @@ void loadNewTexture( const char* data, GLint internalFormat, size_t width, size_ loadNewTexture( _imageProperties ); } -Window::Window( Reader& reader ) +Window::Window( avtranscoder::VideoReader& reader ) { _reader = &reader; _width = _reader->getWidth(); @@ -124,7 +124,7 @@ Window::Window( Reader& reader ) void Window::launch() { - displayNextFrame(); + displayFirstFrame(); glutMainLoop(); } @@ -220,11 +220,6 @@ void Window::keyboard( unsigned char k, int x, int y ) case 'a': showAlphaChannelTexture(); break; - - case 'm': - _reader->printMetadatas(); - break; - case 'H': if( shift ) { @@ -398,8 +393,7 @@ void Window::displayHelp() { static const std::string kViewerHelp = "Av Player Viewer Help\n" \ - "i : information about image (dimensions, bit depth, channels)\n"\ - "m : full metadatas of media\n"\ + "i : information about image (dimensions, bit depth, channels) + video stream\n"\ "z : zoom view to 1:1\n"\ "h, F1 : print help\n" \ "SHIFT + V : flip\n" \ @@ -425,6 +419,9 @@ void Window::displayInformations() case GL_FLOAT : textureType += "32 float"; break; } std::cout << textureType << " " << _width << "x" << _height << std::endl; + + // stream info + _reader->printInfo(); } void Window::move( float x, float y ) @@ -532,14 +529,16 @@ void Window::showAlphaChannelTexture( ) void Window::displayNextFrame() { - const char* buffer = _reader->readNextFrame(); + const char* buffer = (const char*)_reader->readNextFrame()->getData(); loadNewTexture( buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE ); + display(); } void Window::displayPrevFrame() { - const char* buffer = _reader->readPrevFrame(); + const char* buffer = (const char*)_reader->readPrevFrame()->getData(); loadNewTexture( buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE ); + display(); } void Window::displayFirstFrame() @@ -549,8 +548,9 @@ void Window::displayFirstFrame() void Window::displayAtFrame( const size_t frame ) { - const char* buffer = _reader->readFrameAt( frame ); + const char* buffer = (const char*)_reader->readFrameAt( frame )->getData(); loadNewTexture( buffer, _reader->getComponents(), _reader->getWidth(), _reader->getHeight(), GL_RGB, GL_UNSIGNED_BYTE ); + display(); } void Window::loopPlaying( int value ) diff --git a/app/avPlay/Window.hpp b/app/avPlay/Window.hpp index 78e6e807..c7c45e01 100644 --- a/app/avPlay/Window.hpp +++ b/app/avPlay/Window.hpp @@ -3,12 +3,12 @@ #include -#include "Reader.hpp" +#include class Window { public: - Window( Reader& reader ); + Window( avtranscoder::VideoReader& reader ); void launch(); @@ -43,7 +43,7 @@ class Window static void loopPlaying( int value ); - static Reader* _reader; + static avtranscoder::VideoReader* _reader; static size_t _width; static size_t _height; diff --git a/app/avPlay/main.cpp b/app/avPlay/main.cpp index eb09cd05..132dc55f 100644 --- a/app/avPlay/main.cpp +++ b/app/avPlay/main.cpp @@ -1,6 +1,6 @@ #include +#include -#include "AvReader.hpp" #include "Window.hpp" @@ -8,7 +8,17 @@ int main( int argc, char** argv ) { avtranscoder::preloadCodecsAndFormats(); - AvReader avReader( argv[1] ); - Window window( avReader ); + if(argc < 2) + { + std::cout << "avplay can play the given video media file." << std::endl; + std::cout << "Provide the filename and the streamIndex (0 by default)" << std::endl; + return( -1 ); + } + + const std::string filename(argv[1]); + const size_t streamIndex = argc > 2 ? atoi(argv[2]) : 0; + + avtranscoder::VideoReader reader( filename, streamIndex ); + Window window( reader ); window.launch(); -} \ No newline at end of file +} diff --git a/app/jFileAnalysis/jFileAnalysis.java b/app/jFileAnalysis/jFileAnalysis.java index 7ec53bd9..40c0868b 100644 --- a/app/jFileAnalysis/jFileAnalysis.java +++ b/app/jFileAnalysis/jFileAnalysis.java @@ -1,25 +1,25 @@ import org.avtranscoder.NoDisplayProgress; import org.avtranscoder.InputFile; +import org.avtranscoder.avtranscoder; public class jFileAnalysis { public static void main( String[] args ){ - System.loadLibrary("avutil"); - System.loadLibrary("swscale"); - System.loadLibrary("swresample"); - System.loadLibrary("avcodec"); - System.loadLibrary("avformat"); - System.loadLibrary("avtranscoder"); System.loadLibrary("avtranscoder-java"); System.out.println( "Start input file analyse"); + avtranscoder.preloadCodecsAndFormats(); + InputFile inputFile = new InputFile( args[0] ); NoDisplayProgress progress = new NoDisplayProgress(); inputFile.analyse( progress ); + // Access the properties you want with + // inputFile.getProperties() + System.out.println( "End input file analyse"); - } + } } // How to use diff --git a/app/pyProcessor/CMakeLists.txt b/app/pyProcessor/CMakeLists.txt index 1edba996..25eec297 100644 --- a/app/pyProcessor/CMakeLists.txt +++ b/app/pyProcessor/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/pyprocessor.py ${CMAKE_INSTALL_PREFIX}/bin/pyprocessor)" ) +endif(UNIX) diff --git a/app/pyRewrap/CMakeLists.txt b/app/pyRewrap/CMakeLists.txt new file mode 100644 index 00000000..13523d43 --- /dev/null +++ b/app/pyRewrap/CMakeLists.txt @@ -0,0 +1,12 @@ +### python/pyRewrap + +# Install app +install( + FILES "pyrewrap.py" + 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/pyrewrap.py ${CMAKE_INSTALL_PREFIX}/bin/pyrewrap)" ) +endif(UNIX) diff --git a/app/pyRewrap/pyrewrap.py b/app/pyRewrap/pyrewrap.py new file mode 100644 index 00000000..23fe7ab3 --- /dev/null +++ b/app/pyRewrap/pyrewrap.py @@ -0,0 +1,85 @@ +#!/usr/bin/env python + +from pyAvTranscoder import avtranscoder as av + + +# Get command line arguments +args = [] +try: + # python2.7+ + import argparse + + # Create command-line interface + parser = argparse.ArgumentParser( + prog='pyrewrap', + description='''Rewrap a video file and can move the index (moov atom) to the beginning of the file.''', + ) + + # requirements + parser.add_argument('inputFileName', type=str, help='It could be any video file. Support file without extension.') + # options + parser.add_argument("-o", "--outputFile", dest="outputFileName", type=str, default="output.mov", help="Set the output filename (thumbnail.jpg by default). Must be with jpg extension!") + parser.add_argument("-c", "--format", dest="format", type=str, default="mov", help="Specify the output format.") + parser.add_argument("-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 = parser.parse_args() + +except ImportError: + import optparse + + # Create command-line interface + parser = optparse.OptionParser( + usage='usage: %prog -o -c [-f] -i ', + 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