Skip to content

Commit ac206c9

Browse files
committed
Merge pull request #169 from cchampet/fix_timingOfPackets
OutputFile: copy timing information to wrapped packets
2 parents 6c4eaca + 8efb9b3 commit ac206c9

26 files changed

+284
-149
lines changed

src/AvTranscoder/file/InputFile.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ bool InputFile::readNextPacket( CodedData& data, const size_t streamIndex )
6767
return false;
6868
}
6969

70+
// Add Stream info to the packet
71+
data.refAVStream( _formatContext.getAVStream( streamIndex ) );
72+
7073
// if the packet stream is the expected one
7174
// return the packet data
7275
const int packetStreamIndex = data.getAVPacket().stream_index;

src/AvTranscoder/file/OutputFile.cpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,17 @@ IOutputStream& OutputFile::addDataStream( const DataCodec& dataDesc )
104104
IOutputStream& OutputFile::getStream( const size_t streamIndex )
105105
{
106106
if( streamIndex >= _outputStreams.size() )
107-
throw std::runtime_error( "unable to get output stream (out of range)" );
107+
{
108+
std::stringstream msg;
109+
msg << "Unable to get the stream ";
110+
msg << streamIndex;
111+
msg << ": the OutputFile '";
112+
msg << getFilename();
113+
msg << "' has only ";
114+
msg << _outputStreams.size();
115+
msg << " streams.";
116+
throw std::runtime_error( msg.str() );
117+
}
108118
return *_outputStreams.at( streamIndex );
109119
}
110120

@@ -166,16 +176,46 @@ IOutputStream::EWrappingStatus OutputFile::wrap( const CodedData& data, const si
166176

167177
LOG_DEBUG( "Wrap on stream " << streamIndex << " (" << data.getSize() << " bytes for frame " << _frameCount.at( streamIndex ) << ")" )
168178

179+
// Packet to wrap
169180
AVPacket packet;
170181
av_init_packet( &packet );
171182
packet.stream_index = streamIndex;
172183
packet.data = (uint8_t*)data.getData();
173184
packet.size = data.getSize();
185+
packet.flags = data.getAVPacket().flags;
174186

175-
_formatContext.writeFrame( packet );
187+
// copy timing information
188+
if( ! _outputStreams.at( streamIndex )->isPTSGenerated() )
189+
{
190+
if( data.getAVStream() != NULL )
191+
{
192+
const AVRational& srcTimeBase = data.getAVStream()->time_base;
193+
const AVRational& dstTimeBase = _formatContext.getAVStream( streamIndex ).time_base;
194+
// duration
195+
packet.duration = av_rescale_q( data.getAVPacket().duration, srcTimeBase, dstTimeBase );
196+
// pts
197+
if( data.getAVPacket().pts != AV_NOPTS_VALUE )
198+
packet.pts = av_rescale_q( data.getAVPacket().pts, srcTimeBase, dstTimeBase );
199+
else
200+
packet.pts = AV_NOPTS_VALUE;
201+
// dts
202+
packet.dts = av_rescale_q( data.getAVPacket().dts, srcTimeBase, dstTimeBase );
203+
}
204+
// add stream PTS if already incremented
205+
const int currentStreamPTS = _outputStreams.at( streamIndex )->getStreamPTS();
206+
if( packet.pts != AV_NOPTS_VALUE && packet.pts < currentStreamPTS )
207+
{
208+
packet.pts += currentStreamPTS;
209+
packet.dts += currentStreamPTS;
210+
}
211+
}
176212

177-
// free packet.side_data, set packet.data to NULL and packet.size to 0
178-
av_free_packet( &packet );
213+
// copy duration of packet wrapped
214+
// @see OutputStream
215+
const_cast<CodedData&>(data).getAVPacket().duration = packet.duration;
216+
217+
// Write packet
218+
_formatContext.writeFrame( packet );
179219

180220
const double currentStreamDuration = _outputStreams.at( streamIndex )->getStreamDuration();
181221
if( currentStreamDuration < _previousProcessedStreamDuration )

src/AvTranscoder/frame/Frame.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,33 @@ namespace avtranscoder
66
{
77

88
Frame::Frame()
9+
: _avStream( NULL )
910
{
1011
initAVPacket();
1112
}
1213

1314
Frame::Frame( const size_t dataSize )
15+
: _avStream( NULL )
1416
{
1517
av_new_packet( &_packet, dataSize );
1618
}
1719

1820
Frame::Frame( const AVPacket& avPacket )
21+
: _avStream( NULL )
1922
{
2023
copyAVPacket( avPacket );
2124
}
2225

2326
Frame::Frame( const Frame& other )
2427
{
2528
copyAVPacket( other.getAVPacket() );
29+
_avStream = other.getAVStream();
2630
}
2731

2832
Frame& Frame::operator=( const Frame& other )
2933
{
3034
copyAVPacket( other.getAVPacket() );
35+
_avStream = other.getAVStream();
3136
return *this;
3237
}
3338

src/AvTranscoder/frame/Frame.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ extern "C" {
77
#include <libavcodec/avcodec.h>
88
}
99

10+
struct AVStream;
11+
1012
namespace avtranscoder
1113
{
1214

@@ -33,6 +35,7 @@ class AvExport Frame
3335
/// Free buffer of data
3436
~Frame();
3537

38+
void refAVStream( const AVStream& avStream ) { _avStream = &avStream; }
3639
/// Resize data buffer
3740
void resize( const size_t newSize );
3841

@@ -58,6 +61,11 @@ class AvExport Frame
5861
size_t getSize() const { return _packet.size; }
5962

6063
#ifndef SWIG
64+
/**
65+
* @return the AVStream which contains the packet
66+
* @note may be NULL in case of generated packets
67+
*/
68+
const AVStream* getAVStream() const { return _avStream; }
6169
AVPacket& getAVPacket() { return _packet; }
6270
const AVPacket& getAVPacket() const { return _packet; }
6371
const unsigned char* getData() const { return _packet.data; }
@@ -69,6 +77,9 @@ class AvExport Frame
6977

7078
private:
7179
AVPacket _packet;
80+
81+
// Stream which contains the packet
82+
const AVStream* _avStream; //< Has link (no ownership)
7283
};
7384

7485
// Typedef to represent buffer of coded data.

src/AvTranscoder/mediaProperty/StreamProperties.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,13 @@ Rational StreamProperties::getTimeBase() const
2929
{
3030
if( ! _formatContext )
3131
throw std::runtime_error( "unknown format context" );
32-
33-
Rational timeBase = {
34-
_formatContext->streams[_streamIndex]->time_base.num,
35-
_formatContext->streams[_streamIndex]->time_base.den,
36-
};
37-
return timeBase;
32+
return _formatContext->streams[_streamIndex]->time_base;
3833
}
3934

4035
float StreamProperties::getDuration() const
4136
{
42-
Rational timeBase = getTimeBase();
43-
return ( timeBase.num / (float) timeBase.den ) * _formatContext->streams[_streamIndex]->duration;
37+
const Rational timeBase = getTimeBase();
38+
return av_q2d( timeBase ) * _formatContext->streams[_streamIndex]->duration;
4439
}
4540

4641
AVMediaType StreamProperties::getStreamType() const

src/AvTranscoder/stream/OutputStream.cpp

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ OutputStream::OutputStream( OutputFile& outputFile, const size_t streamIndex )
1313
, _outputAVStream( outputFile.getFormatContext().getAVStream( streamIndex ) )
1414
, _streamIndex( streamIndex )
1515
, _wrappedPacketsDuration( 0 )
16+
, _lastWrappedPacketDuration( 0 )
17+
, _isPTSGenerated( false )
1618
{
1719
}
1820

@@ -28,14 +30,8 @@ float OutputStream::getStreamDuration() const
2830
return 0.f;
2931
}
3032

31-
// if stream PTS is not set, use the duration of all packets wrapped
32-
if( ! outputPTS.val )
33-
{
34-
LOG_WARN( "PTS generation when outputting stream " << _streamIndex << " is not set." )
35-
if( _wrappedPacketsDuration )
36-
return av_q2d( _outputAVStream.codec->time_base ) * _wrappedPacketsDuration;
37-
}
38-
33+
if( _wrappedPacketsDuration )
34+
return av_q2d( outputTimeBase ) * _wrappedPacketsDuration;
3935
#if AVTRANSCODER_FFMPEG_DEPENDENCY && LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 40, 100)
4036
// returns the pts of the last muxed packet, converted from timebase to seconds
4137
return av_q2d( outputTimeBase ) * av_stream_get_end_pts( &_outputAVStream );
@@ -49,13 +45,61 @@ size_t OutputStream::getNbFrames() const
4945
return _outputAVStream.nb_frames;
5046
}
5147

48+
int OutputStream::getStreamPTS() const
49+
{
50+
const AVFrac& outputPTS = _outputAVStream.pts;
51+
return ( outputPTS.val + ( outputPTS.num / outputPTS.den ) );
52+
}
53+
5254
IOutputStream::EWrappingStatus OutputStream::wrap( const CodedData& data )
5355
{
56+
// If stream PTS will be generated at rewrap time
57+
if( ! _isPTSGenerated && (data.getAVPacket().pts == 0 || data.getAVPacket().pts == AV_NOPTS_VALUE) && data.getAVPacket().dts == AV_NOPTS_VALUE )
58+
{
59+
LOG_WARN( "PTS of output stream " << _streamIndex << " is generated at rewrap time." )
60+
_isPTSGenerated = true;
61+
}
62+
5463
// wrap packet
5564
IOutputStream::EWrappingStatus status = _outputFile.wrap( data, _streamIndex );
5665

57-
// append duration of the packet to the stream
58-
_wrappedPacketsDuration += data.getAVPacket().duration;
66+
// Store duration of the last packet wrapped
67+
if( data.getAVPacket().duration != 0 && data.getAVPacket().duration != _lastWrappedPacketDuration )
68+
_lastWrappedPacketDuration = data.getAVPacket().duration;
69+
70+
// Append duration of the packet to the stream
71+
if( data.getAVPacket().duration )
72+
_wrappedPacketsDuration += data.getAVPacket().duration;
73+
else
74+
{
75+
switch( _outputAVStream.codec->codec_type )
76+
{
77+
case AVMEDIA_TYPE_VIDEO:
78+
{
79+
_wrappedPacketsDuration += _lastWrappedPacketDuration;
80+
break;
81+
}
82+
case AVMEDIA_TYPE_AUDIO:
83+
{
84+
Rational audioPacketDuration;
85+
audioPacketDuration.num = 0;
86+
audioPacketDuration.den = 0;
87+
const int frame_size = av_get_audio_frame_duration(_outputAVStream.codec, data.getSize());
88+
if( frame_size <= 0 || _outputAVStream.codec->sample_rate <= 0 )
89+
break;
90+
audioPacketDuration.num = frame_size;
91+
audioPacketDuration.den = _outputAVStream.codec->sample_rate;
92+
_wrappedPacketsDuration += av_rescale(
93+
1,
94+
audioPacketDuration.num * (int64_t)_outputAVStream.time_base.den * _outputAVStream.codec->ticks_per_frame,
95+
audioPacketDuration.den * (int64_t)_outputAVStream.time_base.num
96+
);
97+
break;
98+
}
99+
default:
100+
break;
101+
}
102+
}
59103

60104
return status;
61105
}

src/AvTranscoder/stream/OutputStream.hpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ class AvExport OutputStream : public IOutputStream
1818
size_t getStreamIndex() const { return _streamIndex; }
1919
float getStreamDuration() const;
2020
size_t getNbFrames() const; ///< If audio stream, returns number of packets
21+
int getStreamPTS() const; ///< Get current AVStream PTS
2122

23+
bool isPTSGenerated() const { return _isPTSGenerated; }
2224
IOutputStream::EWrappingStatus wrap( const CodedData& data );
2325

2426
private:
@@ -27,12 +29,14 @@ class AvExport OutputStream : public IOutputStream
2729

2830
size_t _streamIndex; ///< Index of the stream in the output file
2931

30-
/**
31-
* @brief This will help us to getStreamDuration if PTS of outputStream is not properly set during wrapping.
32-
* It corresponds to the addition of the duration of all packets wrapped by this stream.
33-
* @see getStreamDuration
34-
*/
35-
size_t _wrappedPacketsDuration;
32+
//@{
33+
// @brief These attributes will help us to get the stream duration.
34+
// @see ffmpeg hack: https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mux.c#L585
35+
// @see getStreamDuration
36+
size_t _wrappedPacketsDuration; //< the addition of the duration of all packets wrapped by this stream.
37+
int _lastWrappedPacketDuration; //< The duration of the last packet wrapped by this stream.
38+
bool _isPTSGenerated; //< Is the PTS generated by ffmpeg/libav (in case of generator for example)
39+
//@}
3640
};
3741

3842
}

src/AvTranscoder/transcoder/StreamTranscoder.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -319,15 +319,14 @@ void StreamTranscoder::preProcessCodecLatency()
319319
{
320320
if( ! _outputEncoder )
321321
{
322-
std::stringstream os;
323-
os << "No output encoder found for stream ";
322+
std::stringstream msg;
323+
msg << "No encoder found for input stream ";
324324
if( getProcessCase() == eProcessCaseGenerator )
325-
os << "generator";
325+
msg << "generator";
326326
else
327-
os << _inputStream->getStreamIndex();
328-
os << ": will not preProcessCodecLatency.";
329-
LOG_INFO( os.str() )
330-
327+
msg << _inputStream->getStreamIndex();
328+
msg << ": will not preProcessCodecLatency.";
329+
LOG_WARN( msg.str() )
331330
return;
332331
}
333332

@@ -408,7 +407,7 @@ bool StreamTranscoder::processRewrap()
408407
LOG_DEBUG( "StreamTranscoder::processRewrap" )
409408

410409
// if switched to generator, process frame
411-
if( _currentDecoder == _generator )
410+
if( _currentDecoder && _currentDecoder == _generator )
412411
{
413412
return processTranscode();
414413
}

src/AvTranscoder/transcoder/StreamTranscoder.hpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ class AvExport StreamTranscoder
7070
*/
7171
float getDuration() const;
7272

73-
/// Returns a reference to the current decoder (from input file or from generator)
74-
IDecoder& getCurrentDecoder() const { return *_currentDecoder; }
75-
/// Returns a reference to the encoder
76-
IEncoder& getEncoder() const { return *_outputEncoder; }
73+
/// Returns a pointer to the current decoder (from input file or from generator)
74+
IDecoder* getCurrentDecoder() const { return _currentDecoder; }
75+
/// Returns a pointer to the encoder
76+
IEncoder* getEncoder() const { return _outputEncoder; }
7777

7878
/// Returns a reference to the object which transforms the decoded data
7979
ITransform& getTransform() const { return *_transform; }

src/AvTranscoder/transcoder/Transcoder.cpp

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -241,14 +241,14 @@ ProcessStat Transcoder::process( IProgress& progress )
241241

242242
preProcessCodecLatency();
243243

244-
const double outputDuration = getOutputDuration();
244+
const float outputDuration = getOutputDuration();
245245
LOG_INFO( "Output duration of the process will be " << outputDuration << "s." )
246246

247247
size_t frame = 0;
248248
bool frameProcessed = true;
249249
while( frameProcessed )
250250
{
251-
const double progressDuration = _outputFile.getStream( 0 ).getStreamDuration();
251+
const float progressDuration = _outputFile.getStream( 0 ).getStreamDuration();
252252

253253
// check if JobStatusCancel
254254
if( progress.progress( ( progressDuration > outputDuration ) ? outputDuration : progressDuration, outputDuration ) == eJobStatusCancel )
@@ -528,11 +528,15 @@ void Transcoder::fillProcessStat( ProcessStat& processStat )
528528
case AVMEDIA_TYPE_VIDEO:
529529
{
530530
VideoStat videoStat( stream.getStreamDuration(), stream.getNbFrames() );
531-
const AVCodecContext& encoderContext = _streamTranscoders.at( streamIndex )->getEncoder().getCodec().getAVCodecContext();
532-
if( encoderContext.coded_frame && ( encoderContext.flags & CODEC_FLAG_PSNR) )
531+
IEncoder* encoder = _streamTranscoders.at( streamIndex )->getEncoder();
532+
if( encoder )
533533
{
534-
videoStat._quality = encoderContext.coded_frame->quality;
535-
videoStat._psnr = VideoStat::psnr( encoderContext.coded_frame->error[0] / ( encoderContext.width * encoderContext.height * 255.0 * 255.0 ) );
534+
const AVCodecContext& encoderContext = encoder->getCodec().getAVCodecContext();
535+
if( encoderContext.coded_frame && ( encoderContext.flags & CODEC_FLAG_PSNR) )
536+
{
537+
videoStat._quality = encoderContext.coded_frame->quality;
538+
videoStat._psnr = VideoStat::psnr( encoderContext.coded_frame->error[0] / ( encoderContext.width * encoderContext.height * 255.0 * 255.0 ) );
539+
}
536540
}
537541
processStat.addVideoStat( streamIndex, videoStat );
538542
break;

test/pyTest/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from pyAvTranscoder import avtranscoder as av
2+
3+
av.preloadCodecsAndFormats()
4+
av.Logger.setLogLevel(av.AV_LOG_WARNING)

0 commit comments

Comments
 (0)