AvTranscoder  0.9.4
C++APIforLibav/FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
StreamTranscoder.cpp
Go to the documentation of this file.
1 
2 #include "StreamTranscoder.hpp"
3 
5 
12 
15 
16 #include <cassert>
17 #include <limits>
18 #include <sstream>
19 
20 namespace avtranscoder
21 {
22 
23 StreamTranscoder::StreamTranscoder(IInputStream& inputStream, IOutputFile& outputFile, const float offset)
24  : _inputStream(&inputStream)
25  , _outputStream(NULL)
26  , _sourceBuffer(NULL)
27  , _frameBuffer(NULL)
28  , _inputDecoder(NULL)
29  , _generator(NULL)
30  , _currentDecoder(NULL)
31  , _outputEncoder(NULL)
32  , _transform(NULL)
33  , _filterGraph(NULL)
34  , _subStreamIndex(-1)
35  , _offset(offset)
36  , _needToSwitchToGenerator(false)
37 {
38  // create a re-wrapping case
40  {
41  case AVMEDIA_TYPE_VIDEO:
42  {
43  // output stream
45 
46  try
47  {
48  // filter
50 
52 
53  // generator decoder
54  _generator = new VideoGenerator(inputFrameDesc);
55 
56  // buffers to process
57  _sourceBuffer = new VideoFrame(inputFrameDesc);
58  _frameBuffer = new VideoFrame(inputFrameDesc);
59 
60  // transform
61  _transform = new VideoTransform();
62 
63  // output encoder
65  outputVideo->setupVideoEncoder(inputFrameDesc);
66  _outputEncoder = outputVideo;
67  }
68  catch(std::runtime_error& e)
69  {
70  LOG_WARN("Cannot create the video encoder for stream " << _inputStream->getStreamIndex() << " if needed. "
71  << e.what())
72  }
73 
74  break;
75  }
76  case AVMEDIA_TYPE_AUDIO:
77  {
78  // output stream
80 
81  try
82  {
83  // filter
85 
87 
88  // generator decoder
89  _generator = new AudioGenerator(inputFrameDesc);
90 
91  // buffers to process
92  _sourceBuffer = new AudioFrame(inputFrameDesc);
93  _frameBuffer = new AudioFrame(inputFrameDesc);
94 
95  // transform
96  _transform = new AudioTransform();
97 
98  // output encoder
100  outputAudio->setupAudioEncoder(inputFrameDesc);
101  _outputEncoder = outputAudio;
102  }
103 
104  catch(std::runtime_error& e)
105  {
106  LOG_WARN("Cannot create the audio encoder for stream " << _inputStream->getStreamIndex() << " if needed. "
107  << e.what())
108  }
109 
110  break;
111  }
112  case AVMEDIA_TYPE_DATA:
113  {
114  // @warning: rewrap a data stream can't be lengthen by a generator (end of rewrapping will end the all process)
116  break;
117  }
118  default:
119  break;
120  }
121  setOffset(offset);
122 }
123 
125  const int subStreamIndex, const float offset)
126  : _inputStream(&inputStream)
127  , _outputStream(NULL)
128  , _sourceBuffer(NULL)
129  , _frameBuffer(NULL)
130  , _inputDecoder(NULL)
131  , _generator(NULL)
132  , _currentDecoder(NULL)
133  , _outputEncoder(NULL)
134  , _transform(NULL)
135  , _filterGraph(NULL)
136  , _subStreamIndex(subStreamIndex)
137  , _offset(offset)
138  , _needToSwitchToGenerator(false)
139 {
140  // create a transcode case
142  {
143  case AVMEDIA_TYPE_VIDEO:
144  {
145  // filter
147 
148  // input decoder
149  VideoDecoder* inputVideo = new VideoDecoder(*static_cast<InputStream*>(_inputStream));
150  inputVideo->setupDecoder();
151  _inputDecoder = inputVideo;
153 
154  // output encoder
155  VideoEncoder* outputVideo = new VideoEncoder(profile.at(constants::avProfileCodec));
156  _outputEncoder = outputVideo;
157 
159  outputFrameDesc.setParameters(profile);
160  outputVideo->setupVideoEncoder(outputFrameDesc, profile);
161 
162  // output stream
163  _outputStream = &outputFile.addVideoStream(outputVideo->getVideoCodec());
164 
165  // buffers to process
167  _frameBuffer = new VideoFrame(outputVideo->getVideoCodec().getVideoFrameDesc());
168 
169  // transform
170  _transform = new VideoTransform();
171 
172  // generator decoder
174 
175  break;
176  }
177  case AVMEDIA_TYPE_AUDIO:
178  {
179  // filter
181 
182  // input decoder
183  AudioDecoder* inputAudio = new AudioDecoder(*static_cast<InputStream*>(_inputStream));
184  inputAudio->setupDecoder();
185  _inputDecoder = inputAudio;
187 
188  // output encoder
189  AudioEncoder* outputAudio = new AudioEncoder(profile.at(constants::avProfileCodec));
190  _outputEncoder = outputAudio;
191 
193  outputFrameDesc.setParameters(profile);
194  if(subStreamIndex > -1)
195  {
196  // @todo manage downmix ?
197  outputFrameDesc._nbChannels = 1;
198  }
199  outputAudio->setupAudioEncoder(outputFrameDesc, profile);
200 
201  // output stream
202  _outputStream = &outputFile.addAudioStream(outputAudio->getAudioCodec());
203 
204  // buffers to process
206  if(subStreamIndex > -1)
207  inputFrameDesc._nbChannels = 1;
208 
209  _sourceBuffer = new AudioFrame(inputFrameDesc);
210  _frameBuffer = new AudioFrame(outputAudio->getAudioCodec().getAudioFrameDesc());
211 
212  // transform
213  _transform = new AudioTransform();
214 
215  // generator decoder
216  _generator = new AudioGenerator(outputFrameDesc);
217 
218  break;
219  }
220  default:
221  {
222  throw std::runtime_error("unupported stream type");
223  break;
224  }
225  }
226  setOffset(offset);
227 }
228 
229 StreamTranscoder::StreamTranscoder(const ICodec& inputCodec, IOutputFile& outputFile, const ProfileLoader::Profile& profile)
230  : _inputStream(NULL)
231  , _outputStream(NULL)
232  , _sourceBuffer(NULL)
233  , _frameBuffer(NULL)
234  , _inputDecoder(NULL)
235  , _generator(NULL)
236  , _currentDecoder(NULL)
237  , _outputEncoder(NULL)
238  , _transform(NULL)
239  , _filterGraph(NULL)
240  , _subStreamIndex(-1)
241  , _offset(0)
242  , _needToSwitchToGenerator(false)
243 {
244  if(profile.find(constants::avProfileType)->second == constants::avProfileTypeVideo)
245  {
246  const VideoCodec& inputVideoCodec = static_cast<const VideoCodec&>(inputCodec);
247  // generator decoder
248  _generator = new VideoGenerator(inputVideoCodec.getVideoFrameDesc());
250 
251  // filter
252  _filterGraph = new FilterGraph(inputVideoCodec);
253 
254  // buffers to process
255  VideoFrameDesc inputFrameDesc = inputVideoCodec.getVideoFrameDesc();
256  VideoFrameDesc outputFrameDesc = inputFrameDesc;
257  outputFrameDesc.setParameters(profile);
258  _sourceBuffer = new VideoFrame(inputFrameDesc);
259  _frameBuffer = new VideoFrame(outputFrameDesc);
260 
261  // transform
262  _transform = new VideoTransform();
263 
264  // output encoder
265  VideoEncoder* outputVideo = new VideoEncoder(profile.at(constants::avProfileCodec));
266  outputVideo->setupVideoEncoder(outputFrameDesc, profile);
267  _outputEncoder = outputVideo;
268 
269  // output stream
270  _outputStream = &outputFile.addVideoStream(outputVideo->getVideoCodec());
271  }
272  else if(profile.find(constants::avProfileType)->second == constants::avProfileTypeAudio)
273  {
274  const AudioCodec& inputAudioCodec = static_cast<const AudioCodec&>(inputCodec);
275  // generator decoder
276  _generator = new AudioGenerator(inputAudioCodec.getAudioFrameDesc());
278 
279  // filter
280  _filterGraph = new FilterGraph(inputAudioCodec);
281 
282  // buffers to process
283  AudioFrameDesc inputFrameDesc = inputAudioCodec.getAudioFrameDesc();
284  AudioFrameDesc outputFrameDesc = inputFrameDesc;
285  outputFrameDesc.setParameters(profile);
286  _sourceBuffer = new AudioFrame(inputFrameDesc);
287  _frameBuffer = new AudioFrame(outputFrameDesc);
288 
289  // transform
290  _transform = new AudioTransform();
291 
292  // output encoder
293  AudioEncoder* outputAudio = new AudioEncoder(profile.at(constants::avProfileCodec));
294  outputAudio->setupAudioEncoder(outputFrameDesc, profile);
295  _outputEncoder = outputAudio;
296 
297  // output stream
298  _outputStream = &outputFile.addAudioStream(outputAudio->getAudioCodec());
299  }
300  else
301  {
302  throw std::runtime_error("unupported stream type");
303  }
304 }
305 
307 {
308  delete _sourceBuffer;
309  delete _frameBuffer;
310  delete _generator;
311  delete _outputEncoder;
312  delete _transform;
313  delete _filterGraph;
314  delete _inputDecoder;
315 }
316 
318 {
319  if(!_outputEncoder)
320  {
321  std::stringstream msg;
322  msg << "No encoder found for input stream ";
324  msg << "generator";
325  else
326  msg << _inputStream->getStreamIndex();
327  msg << ": will not preProcessCodecLatency.";
328  LOG_WARN(msg.str())
329  return;
330  }
331 
332  int latency = _outputEncoder->getCodec().getLatency();
333 
334  LOG_DEBUG("Latency of stream: " << latency)
335 
336  if(!latency || latency < _outputEncoder->getCodec().getAVCodecContext().frame_number)
337  return;
338 
339  // set a decoder to preload generated frames
340  bool wasARewrapCase = false;
342  {
344  wasARewrapCase = true;
345  }
346 
347  while((latency--) > 0)
348  {
349  processFrame();
350  }
351 
352  if(wasARewrapCase)
353  _currentDecoder = NULL;
354 }
355 
357 {
358  const EProcessCase processCase = getProcessCase();
359  std::string msg = "Current process case of the stream is a ";
360  switch(processCase)
361  {
363  msg += "transcode.";
364  break;
365  case eProcessCaseRewrap:
366  msg += "rewrap.";
367  break;
369  msg += "generator.";
370  break;
371  }
372  LOG_DEBUG(msg)
373 
374  if(processCase == eProcessCaseGenerator)
375  return processTranscode();
376 
377  // Manage offset
378  if(_offset > 0)
379  {
380  const bool endOfOffset = _outputStream->getStreamDuration() >= _offset;
381  if(endOfOffset)
382  {
383  LOG_INFO("End of positive offset")
384 
387  else
388  _currentDecoder = NULL;
389  _offset = 0;
390  }
391  else
392  {
393  // process generator
395  {
397  }
398  }
399  }
400  else if(_offset < 0)
401  {
402  const bool endOfStream =
404  if(endOfStream)
405  {
406  LOG_INFO("End of negative offset")
407 
409  _offset = 0;
410  }
411  }
412 
413  if(processCase == eProcessCaseRewrap)
414  return processRewrap();
415 
417 }
418 
420 {
421  assert(_inputStream != NULL);
422  assert(_outputStream != NULL);
423  assert(_inputDecoder == NULL);
424 
425  LOG_DEBUG("StreamTranscoder::processRewrap")
426 
427  // if switched to generator, process frame
429  {
430  return processTranscode();
431  }
432 
433  CodedData data;
434  if(!_inputStream->readNextPacket(data))
435  {
437  {
439  return processTranscode();
440  }
441  return false;
442  }
443 
444  const IOutputStream::EWrappingStatus wrappingStatus = _outputStream->wrap(data);
445  switch(wrappingStatus)
446  {
448  return true;
450  // the wrapper needs more data to write the current packet
451  return processFrame();
453  return false;
454  }
455 
456  return true;
457 }
458 
459 bool StreamTranscoder::processTranscode(const int subStreamIndex)
460 {
461  assert(_outputStream != NULL);
462  assert(_currentDecoder != NULL);
463  assert(_outputEncoder != NULL);
464  assert(_sourceBuffer != NULL);
465  assert(_frameBuffer != NULL);
466  assert(_transform != NULL);
467 
468  LOG_DEBUG("StreamTranscoder::processTranscode")
469 
470  LOG_DEBUG("Decode next frame")
471  bool decodingStatus = false;
472  if(subStreamIndex < 0)
473  decodingStatus = _currentDecoder->decodeNextFrame(*_sourceBuffer);
474  else
475  decodingStatus = _currentDecoder->decodeNextFrame(*_sourceBuffer, subStreamIndex);
476 
477  CodedData data;
478  if(decodingStatus)
479  {
480  LOG_DEBUG("Filtering")
482 
483  LOG_DEBUG("Convert")
485 
486  LOG_DEBUG("Encode")
488  }
489  else
490  {
491  LOG_DEBUG("Encode last frame(s)")
492  if(!_outputEncoder->encodeFrame(data))
493  {
495  {
497  return processTranscode();
498  }
499  return false;
500  }
501  }
502 
503  LOG_DEBUG("wrap (" << data.getSize() << " bytes)")
504  const IOutputStream::EWrappingStatus wrappingStatus = _outputStream->wrap(data);
505  switch(wrappingStatus)
506  {
508  return true;
510  // the wrapper needs more data to write the current packet
511  return processFrame();
513  return false;
514  }
515 
516  return true;
517 }
518 
520 {
521  LOG_INFO("Switch to generator decoder")
522 
524  assert(_currentDecoder != NULL);
525 }
526 
528 {
529  LOG_INFO("Switch to input decoder")
530 
532  assert(_currentDecoder != NULL);
533 }
534 
536 {
537  if(_inputStream)
538  {
539  const StreamProperties& streamProperties = _inputStream->getProperties();
540  const float totalDuration = streamProperties.getDuration() + _offset;
541  if(totalDuration < 0)
542  {
543  LOG_WARN("Offset of " << _offset << "s applied to a stream with a duration of " << streamProperties.getDuration()
544  << "s. Set its duration to 0s.")
545  return 0.;
546  }
547  return totalDuration;
548  }
549  // generator
550  else
551  return std::numeric_limits<float>::max();
552 }
553 
555 {
557  return true;
558  return false;
559 }
560 
561 void StreamTranscoder::needToSwitchToGenerator(const bool needToSwitch)
562 {
563  if(needToSwitch && !canSwitchToGenerator())
564  {
565  std::stringstream os;
566  LOG_WARN("The stream " << _inputStream->getStreamIndex() << " has a duration of " << getDuration()
567  << "s. It needs to switch to a generator during the process, but it cannot. "
568  << "No generator will be used for this stream.")
569  return;
570  }
571  _needToSwitchToGenerator = needToSwitch;
572 }
573 
574 void StreamTranscoder::setOffset(const float offset)
575 {
576  _offset = offset;
577  if(_offset > 0)
579 }
580 
582 {
584  return eProcessCaseTranscode;
586  return eProcessCaseRewrap;
587  else
588  return eProcessCaseGenerator;
589 }
590 }
bool canSwitchToGenerator()
Returns if the stream has the ability to switch to a generator.
Description to create a video frame.
Definition: VideoFrame.hpp:21
AVMediaType getStreamType() const
AudioCodec & getAudioCodec()
This class describes decoded video data.
Definition: VideoFrame.hpp:43
virtual bool readNextPacket(CodedData &data)=0
Read the next packet of the stream.
EProcessCase getProcessCase() const
virtual const StreamProperties & getProperties() const =0
IOutputfile is the interface to wrap and write medias. It can be overloaded to integrate custom wrapp...
Definition: IOutputFile.hpp:22
IDecoder * _inputDecoder
Decoder of packets read from _inputStream (has ownership)
virtual EWrappingStatus wrap(const CodedData &data)=0
Wrap a packet of data.
void setupAudioEncoder(const AudioFrameDesc &frameDesc, const ProfileLoader::Profile &profile=ProfileLoader::Profile())
float getDuration() const
Get the total duration (in seconds), ie. duration of the stream and the offset applies.
Frame * _sourceBuffer
Has ownership.
void setupVideoEncoder(const VideoFrameDesc &frameDesc, const ProfileLoader::Profile &profile=ProfileLoader::Profile())
EWrappingStatus
define wrapping result status
virtual IOutputStream & addVideoStream(const VideoCodec &videoCodec)
Add a video output stream.
Definition: IOutputFile.hpp:31
void setOffset(const float offset)
IDecoder * _generator
Generator of audio or video packets (has ownership)
std::map< std::string, std::string > Profile
const std::string avProfileTypeAudio
const std::string avProfileTypeVideo
#define LOG_INFO(...)
Definition: log.hpp:23
float getDuration() const
in seconds
virtual bool encodeFrame(const Frame &sourceFrame, CodedData &codedFrame)=0
Encode a new frame, and get coded frame.
bool processTranscode(const int subStreamIndex=-1)
By default transcode all channels.
VideoCodec & getVideoCodec()
IEncoder * _outputEncoder
Encoder of packets which will be wrapped by _outputStream (has ownership)
int _subStreamIndex
Index of channel that is processed from the input stream (<0 if no demultiplexing).
AudioFrameDesc getAudioFrameDesc() const
Definition: AudioCodec.cpp:24
IDecoder * _currentDecoder
Link to _inputDecoder or _generator.
virtual float getStreamDuration() const =0
#define LOG_WARN(...)
Definition: log.hpp:29
Description to create an audio frame. This corresponds to the number of samples, which corresponds to...
Definition: AudioFrame.hpp:14
virtual IOutputStream & addDataStream(const DataCodec &dataCodec)
Add a data output stream.
Definition: IOutputFile.hpp:49
int getLatency() const
Definition: ICodec.cpp:100
bool processFrame()
process a single frame for the current stream
std::string getCodecName() const
Definition: ICodec.cpp:84
void preProcessCodecLatency()
Pre-process codec latency.
Frame * _frameBuffer
Has ownership.
virtual void convert(const Frame &src, Frame &dst)=0
virtual IOutputStream & addAudioStream(const AudioCodec &audioCodec)
Add an audio output stream.
Definition: IOutputFile.hpp:40
IOutputStream * _outputStream
Output stream to wrap next packet (has link, no ownership)
void setParameters(const ProfileLoader::Profile &profile)
Set the attributes from the given profile.
Definition: AudioFrame.cpp:29
FilterGraph * _filterGraph
Filter graph (has ownership)
void needToSwitchToGenerator(const bool needToSwitch=true)
Set if the stream needs to switch to a generator when ended.
size_t getSize() const
Definition: CodedData.hpp:70
This class describes decoded audio data.
Definition: AudioFrame.hpp:36
virtual ICodec & getCodec()=0
Get codec used for encoding.
virtual AudioCodec & getAudioCodec()=0
void process(Frame &frame)
Pull filtered data from the filter graph, and put result to the given frame.
Definition: FilterGraph.cpp:38
Virtual based class of properties for all types of stream.
Manager of filters.
Definition: FilterGraph.hpp:21
IInputStream * _inputStream
Input stream to read next packet (has link, no ownership)
#define LOG_DEBUG(...)
Definition: log.hpp:17
This class describes coded data.
Definition: CodedData.hpp:18
const std::string avProfileCodec
float _offset
Offset, in seconds, at the beginning of the StreamTranscoder.
void setParameters(const ProfileLoader::Profile &profile)
Set the attributes from the given profile.
Definition: VideoFrame.cpp:32
virtual bool decodeNextFrame(Frame &frameBuffer)=0
Decode next frame.
VideoFrameDesc getVideoFrameDesc() const
Definition: VideoCodec.cpp:24
virtual VideoCodec & getVideoCodec()=0
const std::string avProfileType
ITransform * _transform
Video or audio transform (has ownership)
virtual size_t getStreamIndex() const =0
virtual DataCodec & getDataCodec()=0
StreamTranscoder(const StreamTranscoder &streamTranscoder)