Skip to content

Commit b6ed98d

Browse files
committed
Merge pull request #80 from cchampet/dev_avthumbnail
Add avthumbnail app
2 parents 54cf3c6 + d5f749c commit b6ed98d

File tree

11 files changed

+172
-61
lines changed

11 files changed

+172
-61
lines changed

app/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ add_subdirectory(avInfo)
22
add_subdirectory(avMeta)
33
add_subdirectory(avPlay)
44
add_subdirectory(avProcessor)
5+
add_subdirectory(avThumbnail)

app/avProcessor/avProcessor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ int main( int argc, char** argv )
140140
return( -1 );
141141
}
142142

143-
av_log_set_level( AV_LOG_FATAL );
143+
avtranscoder::setLogLevel( AV_LOG_QUIET );
144144
if( verbose )
145-
av_log_set_level( AV_LOG_DEBUG );
145+
avtranscoder::setLogLevel( AV_LOG_DEBUG );
146146

147147
try
148148
{

app/avThumbnail/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
### cpp/avProcessor
2+
3+
# Load custom cmake utilities
4+
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
5+
include(AvTranscoderMacros)
6+
7+
# Build app
8+
add_executable(avthumbnail avThumbnail.cpp)
9+
set_target_properties(avthumbnail PROPERTIES VERSION ${AVTRANSCODER_VERSION})
10+
target_link_libraries(avthumbnail avtranscoder-shared)
11+
12+
# Install app
13+
if(WIN32)
14+
set(BINARY_FILES "${CMAKE_CURRENT_BINARY_DIR}/avthumbnail.exe")
15+
else()
16+
set(BINARY_FILES "${CMAKE_CURRENT_BINARY_DIR}/avthumbnail" "${CMAKE_CURRENT_BINARY_DIR}/avthumbnail-${AVTRANSCODER_VERSION}")
17+
endif()
18+
19+
install(
20+
FILES ${BINARY_FILES}
21+
PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ WORLD_READ WORLD_EXECUTE
22+
DESTINATION "bin/"
23+
)

app/avThumbnail/avThumbnail.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
#include <AvTranscoder/transcoder/Transcoder.hpp>
2+
#include <AvTranscoder/file/OutputFile.hpp>
3+
4+
static std::string outputFileName = "thumbnail.jpg";
5+
static bool seekInFrame = false;
6+
7+
int main( int argc, char** argv )
8+
{
9+
std::string help;
10+
help += "Usage\n";
11+
help += "\tavthumbnail INPUT_FILE_NAME TIME [--output OUTPUT_FILE_NAME] [--frame] [--help]\n";
12+
help += "Command line options\n";
13+
help += "\t--output OUTPUT_FILE_NAME: name of the output file (thumbnail.jpg by default)\n";
14+
help += "\t--frame: express TIME of where to seek in frame (in seconds by default)\n";
15+
help += "\t--help: display this help\n";
16+
17+
// List command line arguments
18+
std::vector< std::string > arguments;
19+
for( int argument = 1; argument < argc; ++argument )
20+
{
21+
arguments.push_back( argv[argument] );
22+
}
23+
for( size_t argument = 0; argument < arguments.size(); ++argument )
24+
{
25+
if( arguments.at( argument ) == "--help" )
26+
{
27+
std::cout << help << std::endl;
28+
return 0;
29+
}
30+
if( arguments.at( argument ) == "--frame" )
31+
{
32+
seekInFrame = true;
33+
}
34+
if( arguments.at( argument ) == "--output" )
35+
{
36+
try
37+
{
38+
outputFileName = arguments.at( argument + 1 );
39+
}
40+
catch( const std::exception& e )
41+
{
42+
std::cout << "Error: need to indicate an output filename if you use option --output" << std::endl << std::endl;
43+
std::cout << help << std::endl;
44+
return 0;
45+
}
46+
}
47+
}
48+
49+
// Check required arguments
50+
if( argc < 3 )
51+
{
52+
std::cout << "avthumbnail creates a thumbnail from an input file at an indicated frame." << std::endl;
53+
std::cout << "Use option --help to display help" << std::endl;
54+
return( -1 );
55+
}
56+
57+
avtranscoder::setLogLevel( AV_LOG_QUIET );
58+
avtranscoder::preloadCodecsAndFormats();
59+
60+
// input file
61+
std::string inputFileName( arguments.at( 0 ) );
62+
avtranscoder::InputFile intputFile( inputFileName );
63+
if( seekInFrame )
64+
intputFile.seekAtFrame( atoi( arguments.at( 1 ).c_str() ) );
65+
else
66+
intputFile.seekAtTime( atof( arguments.at( 1 ).c_str() ) );
67+
68+
// output file (need to set format profile of encoding to force output format to mjpeg)
69+
avtranscoder::ProfileLoader::Profile formatProfile;
70+
formatProfile[ avtranscoder::constants::avProfileIdentificator ] = "thumbnailFormatPreset";
71+
formatProfile[ avtranscoder::constants::avProfileIdentificatorHuman ] = "Thumbnail format preset";
72+
formatProfile[ avtranscoder::constants::avProfileType ] = avtranscoder::constants::avProfileTypeFormat;
73+
formatProfile[ avtranscoder::constants::avProfileFormat ] = "mjpeg";
74+
avtranscoder::OutputFile outputFile( outputFileName );
75+
outputFile.setProfile( formatProfile );
76+
77+
// input stream
78+
avtranscoder::InputStream inputStream( intputFile, 0 );
79+
inputStream.activate();
80+
81+
// output stream
82+
avtranscoder::ProfileLoader profileLoader( true );
83+
avtranscoder::StreamTranscoder outputStream( inputStream, outputFile, profileLoader.getProfile( "mjpeg" ) );
84+
85+
// transcoder
86+
avtranscoder::Transcoder transcoder( outputFile );
87+
transcoder.add( outputStream );
88+
89+
// process
90+
outputFile.beginWrap();
91+
transcoder.preProcessCodecLatency();
92+
transcoder.processFrame();
93+
outputFile.endWrap();
94+
}

ressource/v_mjpeg.prf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
avProfileName=mjpeg
2+
avProfileLongName=Motion JPEG
3+
avProfileType=avProfileTypeVideo
4+
codec=mjpeg
5+
pix_fmt=yuvj420p

src/AvTranscoder/file/IOutputFile.hpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,6 @@ class AvExport IOutputFile
2525
public:
2626
virtual ~IOutputFile() {};
2727

28-
/**
29-
* @brief Initialize the wrapper
30-
**/
31-
virtual bool setup() = 0;
32-
3328
/**
3429
* @brief Add a video output stream
3530
* @note call setup() before adding any stream

src/AvTranscoder/file/InputFile.cpp

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -143,20 +143,24 @@ bool InputFile::readNextPacket( CodedData& data, const size_t streamIndex )
143143

144144
void InputFile::seekAtFrame( const size_t frame )
145145
{
146-
double fps = 1;
147-
// Get Fps from first video stream
148-
if( _properties.getNbVideoStreams() )
149-
fps = _properties.getVideoProperties().at( 0 ).getFps();
146+
uint64_t position = frame / getFps() * AV_TIME_BASE;
147+
seek( position );
148+
}
150149

151-
uint64_t pos = frame / fps * AV_TIME_BASE;
150+
void InputFile::seekAtTime( const double time )
151+
{
152+
uint64_t position = time * AV_TIME_BASE;
153+
seek( position );
154+
}
152155

153-
// Offset of start time
156+
void InputFile::seek( uint64_t position )
157+
{
154158
if( (int)_formatContext.getStartTime() != AV_NOPTS_VALUE )
155-
pos += _formatContext.getStartTime();
159+
position += _formatContext.getStartTime();
156160

157-
if( av_seek_frame( &_formatContext.getAVFormatContext(), -1, pos, AVSEEK_FLAG_BACKWARD ) < 0 )
161+
if( av_seek_frame( &_formatContext.getAVFormatContext(), -1, position, AVSEEK_FLAG_BACKWARD ) < 0 )
158162
{
159-
std::cerr << "Error during seek at " << frame << " (" << pos << ") in file" << std::endl;
163+
std::cerr << "Error during seek at " << position << " (in AV_TIME_BASE units) in file" << std::endl;
160164
}
161165

162166
for( std::vector<InputStream*>::iterator it = _inputStreams.begin(); it != _inputStreams.end(); ++it )
@@ -186,6 +190,14 @@ InputStream& InputFile::getStream( size_t index )
186190
}
187191
}
188192

193+
double InputFile::getFps()
194+
{
195+
double fps = 1;
196+
if( _properties.getNbVideoStreams() )
197+
fps = _properties.getVideoProperties().at( 0 ).getFps();
198+
return fps;
199+
}
200+
189201
void InputFile::setProfile( const ProfileLoader::Profile& profile )
190202
{
191203
for( ProfileLoader::Profile::const_iterator it = profile.begin(); it != profile.end(); ++it )

src/AvTranscoder/file/InputFile.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class AvExport InputFile
4949
* @return if next packet was read succefully
5050
**/
5151
void seekAtFrame( const size_t frame );
52+
void seekAtTime( const double time );
5253

5354
/**
5455
* @brief Activate the indicated stream
@@ -97,6 +98,20 @@ class AvExport InputFile
9798
**/
9899
static FileProperties analyseFile( const std::string& filename, IProgress& progress, const EAnalyseLevel level = eAnalyseLevelFirstGop );
99100

101+
private:
102+
/**
103+
* @brief Get Fps from first video stream
104+
* @note if there is no video stream, return 1.
105+
*/
106+
double getFps();
107+
108+
/**
109+
* @brief Seek at a specific position (in AV_TIME_BASE units)
110+
* @note before seek, add offset of start time
111+
* @note after seek, clear buffering of streams
112+
*/
113+
void seek( uint64_t position );
114+
100115
protected:
101116
FormatContext _formatContext;
102117
FileProperties _properties;

src/AvTranscoder/file/OutputFile.cpp

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ OutputFile::OutputFile( const std::string& filename )
1515
, _filename( filename )
1616
, _previousProcessedStreamDuration( 0.0 )
1717
, _verbose( false )
18-
{}
18+
{
19+
_formatContext.setOutputFormat( _filename );
20+
_formatContext.openRessource( _filename, AVIO_FLAG_WRITE );
21+
}
1922

2023
OutputFile::~OutputFile()
2124
{
@@ -25,13 +28,6 @@ OutputFile::~OutputFile()
2528
}
2629
}
2730

28-
bool OutputFile::setup()
29-
{
30-
_formatContext.setOutputFormat( _filename );
31-
_formatContext.openRessource( _filename, AVIO_FLAG_WRITE );
32-
return true;
33-
}
34-
3531
IOutputStream& OutputFile::addVideoStream( const VideoCodec& videoDesc )
3632
{
3733
AVStream& stream = _formatContext.addAVStream( videoDesc.getAVCodec() );
@@ -157,32 +153,16 @@ void OutputFile::addMetadata( const std::string& key, const std::string& value )
157153

158154
void OutputFile::setProfile( const ProfileLoader::Profile& profile )
159155
{
156+
// check if output format indicated is valid with the filename extension
160157
if( ! matchFormat( profile.find( constants::avProfileFormat )->second, _filename ) )
161158
{
162159
throw std::runtime_error( "Invalid format according to the file extension." );
163160
}
164-
161+
162+
// set output format
165163
_formatContext.setOutputFormat( _filename, profile.find( constants::avProfileFormat )->second );
166-
167-
for( ProfileLoader::Profile::const_iterator it = profile.begin(); it != profile.end(); ++it )
168-
{
169-
if( (*it).first == constants::avProfileIdentificator ||
170-
(*it).first == constants::avProfileIdentificatorHuman ||
171-
(*it).first == constants::avProfileType ||
172-
(*it).first == constants::avProfileFormat )
173-
continue;
174-
175-
try
176-
{
177-
Option& formatOption = _formatContext.getOption( (*it).first );
178-
formatOption.setString( (*it).second );
179-
}
180-
catch( std::exception& e )
181-
{}
182-
}
183-
184-
setup();
185-
164+
165+
// set format options
186166
for( ProfileLoader::Profile::const_iterator it = profile.begin(); it != profile.end(); ++it )
187167
{
188168
if( (*it).first == constants::avProfileIdentificator ||
@@ -198,8 +178,7 @@ void OutputFile::setProfile( const ProfileLoader::Profile& profile )
198178
}
199179
catch( std::exception& e )
200180
{
201-
if( _verbose )
202-
std::cout << "[OutputFile] warning - can't set option " << (*it).first << " to " << (*it).second << ": " << e.what() << std::endl;
181+
std::cout << "[OutputFile] warning - can't set option " << (*it).first << " to " << (*it).second << ": " << e.what() << std::endl;
203182
}
204183
}
205184
}

src/AvTranscoder/file/OutputFile.hpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,6 @@ class AvExport OutputFile : public IOutputFile
2525

2626
~OutputFile();
2727

28-
/**
29-
* @brief Initialize the OutputFile, create format context to wrap essences into output file.
30-
* @note call this before adding streams using addVideoStream() or addAudioStream()
31-
* @exception ios_base::failure launched if unable to guess format or open output
32-
**/
33-
bool setup();
34-
3528
IOutputStream& addVideoStream( const VideoCodec& videoDesc );
3629
IOutputStream& addAudioStream( const AudioCodec& audioDesc );
3730
IOutputStream& addDataStream( const DataCodec& dataDesc );

src/AvTranscoder/transcoder/Transcoder.cpp

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,7 @@ Transcoder::Transcoder( IOutputFile& outputFile )
2121
, _mainStreamIndex( 0 )
2222
, _outputDuration( 0 )
2323
, _verbose( false )
24-
{
25-
// Initialize the OutputFile
26-
_outputFile.setup();
27-
28-
// Print no output from ffmpeg
29-
av_log_set_level( AV_LOG_QUIET );
30-
}
24+
{}
3125

3226
Transcoder::~Transcoder()
3327
{

0 commit comments

Comments
 (0)