AvTranscoder  0.9.4
C++APIforLibav/FFmpeg
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
OutputFile.cpp
Go to the documentation of this file.
1 #include "OutputFile.hpp"
2 
3 #include <AvTranscoder/util.hpp>
4 
5 #include <stdexcept>
6 
7 #ifndef FF_INPUT_BUFFER_PADDING_SIZE
8 #define FF_INPUT_BUFFER_PADDING_SIZE 16
9 #endif
10 
11 namespace avtranscoder
12 {
13 
14 OutputFile::OutputFile(const std::string& filename, const std::string& formatName, const std::string& mimeType)
15  : _formatContext(AV_OPT_FLAG_ENCODING_PARAM)
16  , _outputStreams()
17  , _frameCount()
18  , _previousProcessedStreamDuration(0.0)
19  , _profile()
20 {
21  _formatContext.setFilename(filename);
22  _formatContext.setOutputFormat(filename, formatName, mimeType);
23 }
24 
26 {
27  for(std::vector<OutputStream*>::iterator it = _outputStreams.begin(); it != _outputStreams.end(); ++it)
28  {
29  delete(*it);
30  }
31 }
32 
34 {
35  AVStream& stream = _formatContext.addAVStream(videoDesc.getAVCodec());
36 
37  stream.codec->width = videoDesc.getAVCodecContext().width;
38  stream.codec->height = videoDesc.getAVCodecContext().height;
39  stream.codec->bit_rate = videoDesc.getAVCodecContext().bit_rate;
40  stream.codec->pix_fmt = videoDesc.getAVCodecContext().pix_fmt;
41  stream.codec->profile = videoDesc.getAVCodecContext().profile;
42  stream.codec->level = videoDesc.getAVCodecContext().level;
43  stream.codec->field_order = videoDesc.getAVCodecContext().field_order;
44 
45  // some codecs need/can use extradata to decode
46  uint8_t* srcExtradata = videoDesc.getAVCodecContext().extradata;
47  const int srcExtradataSize = videoDesc.getAVCodecContext().extradata_size;
48  stream.codec->extradata = (uint8_t*)av_malloc(srcExtradataSize + FF_INPUT_BUFFER_PADDING_SIZE);
49  memcpy(stream.codec->extradata, srcExtradata, srcExtradataSize);
50  memset(((uint8_t*)stream.codec->extradata) + srcExtradataSize, 0, FF_INPUT_BUFFER_PADDING_SIZE);
51  stream.codec->extradata_size = videoDesc.getAVCodecContext().extradata_size;
52 
53  // need to set the time_base on the AVCodecContext and the AVStream
54  // compensating the frame rate with the ticks_per_frame and keeping
55  // a coherent reading speed.
56  av_reduce(&stream.codec->time_base.num, &stream.codec->time_base.den,
57  videoDesc.getAVCodecContext().time_base.num * videoDesc.getAVCodecContext().ticks_per_frame,
58  videoDesc.getAVCodecContext().time_base.den, INT_MAX);
59 
60  stream.time_base = stream.codec->time_base;
61 
62  OutputStream* outputStream = new OutputStream(*this, _formatContext.getNbStreams() - 1);
63  _outputStreams.push_back(outputStream);
64 
65  return *outputStream;
66 }
67 
69 {
70  AVStream& stream = _formatContext.addAVStream(audioDesc.getAVCodec());
71 
72  stream.codec->sample_rate = audioDesc.getAVCodecContext().sample_rate;
73  stream.codec->channels = audioDesc.getAVCodecContext().channels;
74  stream.codec->channel_layout = audioDesc.getAVCodecContext().channel_layout;
75  stream.codec->sample_fmt = audioDesc.getAVCodecContext().sample_fmt;
76  stream.codec->frame_size = audioDesc.getAVCodecContext().frame_size;
77 
78  // need to set the time_base on the AVCodecContext of the AVStream
79  av_reduce(&stream.codec->time_base.num, &stream.codec->time_base.den, audioDesc.getAVCodecContext().time_base.num,
80  audioDesc.getAVCodecContext().time_base.den, INT_MAX);
81 
82  OutputStream* outputStream = new OutputStream(*this, _formatContext.getNbStreams() - 1);
83  _outputStreams.push_back(outputStream);
84 
85  return *outputStream;
86 }
87 
89 {
91 
92  OutputStream* outputStream = new OutputStream(*this, _formatContext.getNbStreams() - 1);
93  _outputStreams.push_back(outputStream);
94 
95  return *outputStream;
96 }
97 
98 IOutputStream& OutputFile::getStream(const size_t streamIndex)
99 {
100  if(streamIndex >= _outputStreams.size())
101  {
102  std::stringstream msg;
103  msg << "Unable to get the stream ";
104  msg << streamIndex;
105  msg << ": the OutputFile '";
106  msg << getFilename();
107  msg << "' has only ";
108  msg << _outputStreams.size();
109  msg << " streams.";
110  throw std::runtime_error(msg.str());
111  }
112  return *_outputStreams.at(streamIndex);
113 }
114 
115 std::string OutputFile::getFilename() const
116 {
117  return std::string(_formatContext.getAVFormatContext().filename);
118 }
119 
120 std::string OutputFile::getFormatName() const
121 {
122  if(_formatContext.getAVOutputFormat().name == NULL)
123  {
124  LOG_WARN("Unknown muxer format name of '" << getFilename() << "'.")
125  return "";
126  }
127  return std::string(_formatContext.getAVOutputFormat().name);
128 }
129 
130 std::string OutputFile::getFormatLongName() const
131 {
132  if(_formatContext.getAVOutputFormat().long_name == NULL)
133  {
134  LOG_WARN("Unknown muxer format long name of '" << getFilename() << "'.")
135  return "";
136  }
137  return std::string(_formatContext.getAVOutputFormat().long_name);
138 }
139 
140 std::string OutputFile::getFormatMimeType() const
141 {
142  if(_formatContext.getAVOutputFormat().mime_type == NULL)
143  {
144  LOG_WARN("Unknown muxer format mime type of '" << getFilename() << "'.")
145  return "";
146  }
147  return std::string(_formatContext.getAVOutputFormat().mime_type);
148 }
149 
151 {
152  LOG_DEBUG("Begin wrap of OutputFile")
153 
154  _formatContext.openRessource(getFilename(), AVIO_FLAG_WRITE);
156 
157  // set specific wrapping options
159 
160  _frameCount.clear();
161  _frameCount.resize(_outputStreams.size(), 0);
162 
163  return true;
164 }
165 
166 IOutputStream::EWrappingStatus OutputFile::wrap(const CodedData& data, const size_t streamIndex)
167 {
168  if(!data.getSize())
170 
171  LOG_DEBUG("Wrap on stream " << streamIndex << " (" << data.getSize() << " bytes for frame "
172  << _frameCount.at(streamIndex) << ")")
173 
174  // Packet to wrap
175  AVPacket packet;
176  av_init_packet(&packet);
177  packet.stream_index = streamIndex;
178  packet.data = (uint8_t*)data.getData();
179  packet.size = data.getSize();
180  packet.flags = data.getAVPacket().flags;
181 
182  // copy timing information
183  if(!_outputStreams.at(streamIndex)->isPTSGenerated())
184  {
185  if(data.getAVStream() != NULL)
186  {
187  const AVRational& srcTimeBase = data.getAVStream()->time_base;
188  const AVRational& dstTimeBase = _formatContext.getAVStream(streamIndex).time_base;
189  // duration
190  packet.duration = av_rescale_q(data.getAVPacket().duration, srcTimeBase, dstTimeBase);
191  // pts
192  if(data.getAVPacket().pts != AV_NOPTS_VALUE)
193  packet.pts = av_rescale_q(data.getAVPacket().pts, srcTimeBase, dstTimeBase);
194  else
195  packet.pts = AV_NOPTS_VALUE;
196  // dts
197  packet.dts = av_rescale_q(data.getAVPacket().dts, srcTimeBase, dstTimeBase);
198  }
199  // add stream PTS if already incremented
200  const int currentStreamPTS = _outputStreams.at(streamIndex)->getStreamPTS();
201  if(packet.pts != AV_NOPTS_VALUE && packet.pts < currentStreamPTS)
202  {
203  packet.pts += currentStreamPTS;
204  packet.dts += currentStreamPTS;
205  }
206  }
207 
208  // copy duration of packet wrapped
209  // @see OutputStream
210  const_cast<CodedData&>(data).getAVPacket().duration = packet.duration;
211 
212  // Write packet
213  _formatContext.writeFrame(packet);
214 
215  const double currentStreamDuration = _outputStreams.at(streamIndex)->getStreamDuration();
216  if(currentStreamDuration < _previousProcessedStreamDuration)
217  {
218  LOG_DEBUG("The output stream " << streamIndex << " is strictly shorter than the previous duration saved ("
219  << currentStreamDuration << "s < " << _previousProcessedStreamDuration
220  << "s): wait for more data.")
222  }
223 
224  _previousProcessedStreamDuration = currentStreamDuration;
225  _frameCount.at(streamIndex)++;
226 
228 }
229 
231 {
232  LOG_DEBUG("End wrap of OutputFile")
233 
236  return true;
237 }
238 
240 {
241  for(PropertyVector::const_iterator it = data.begin(); it != data.end(); ++it)
242  {
243  addMetadata(it->first, it->second);
244  }
245 }
246 
247 void OutputFile::addMetadata(const std::string& key, const std::string& value)
248 {
249  _formatContext.addMetaData(key, value);
250 }
251 
253 {
254  // check the given profile
255  const bool isValid = ProfileLoader::checkFormatProfile(profile);
256  if(!isValid)
257  {
258  const std::string msg("Invalid format profile to setup wrapping.");
259  LOG_ERROR(msg)
260  throw std::runtime_error(msg);
261  }
262 
263  if(!profile.empty())
264  {
265  LOG_INFO("Setup wrapping with:\n" << profile)
266  }
267 
268  // check if output format indicated is valid with the filename extension
269  if(!av_guess_format(profile.find(constants::avProfileFormat)->second.c_str(), getFilename().c_str(), NULL))
270  {
271  throw std::runtime_error("Invalid format according to the file extension.");
272  }
273  // set output format
275 
276  // set common wrapping options
277  setupWrappingOptions(profile);
278 }
279 
281 {
282  // set format options
283  for(ProfileLoader::Profile::const_iterator it = profile.begin(); it != profile.end(); ++it)
284  {
286  (*it).first == constants::avProfileType || (*it).first == constants::avProfileFormat)
287  continue;
288 
289  try
290  {
291  Option& formatOption = _formatContext.getOption((*it).first);
292  formatOption.setString((*it).second);
293  }
294  catch(std::exception& e)
295  {
296  LOG_INFO("OutputFile - option " << (*it).first << " will be saved to be called when beginWrap")
297  _profile[(*it).first] = (*it).second;
298  }
299  }
300 }
301 
303 {
304  // set format options
305  for(ProfileLoader::Profile::const_iterator it = _profile.begin(); it != _profile.end(); ++it)
306  {
308  (*it).first == constants::avProfileType || (*it).first == constants::avProfileFormat)
309  continue;
310 
311  try
312  {
313  Option& formatOption = _formatContext.getOption((*it).first);
314  formatOption.setString((*it).second);
315  }
316  catch(std::exception& e)
317  {
318  LOG_WARN("OutputFile - can't set option " << (*it).first << " to " << (*it).second << ": " << e.what())
319  }
320  }
321 }
322 }
double _previousProcessedStreamDuration
To manage process streams order.
Definition: OutputFile.hpp:106
FormatContext _formatContext
Definition: OutputFile.hpp:102
std::vector< std::pair< std::string, std::string > > PropertyVector
PropertyVector is a vector of pair, because the order of properties matters to us.
Definition: util.hpp:23
void setString(const std::string &value)
Definition: Option.cpp:187
void setupWrapping(const ProfileLoader::Profile &profile)
Set the format and the generic options of the output file.
Definition: OutputFile.cpp:252
#define LOG_ERROR(...)
Definition: log.hpp:35
unsigned char * getData()
Definition: CodedData.hpp:65
void closeRessource()
Close the resource accessed by the AVIOContext and free it.
static bool checkFormatProfile(const Profile &profileToCheck)
const std::string avProfileFormat
std::string getFormatName() const
A comma separated list of short names for the format, or empty if unknown.
Definition: OutputFile.cpp:120
void setOutputFormat(const std::string &filename, const std::string &shortName="", const std::string &mimeType="")
AVOutputFormat & getAVOutputFormat() const
void addMetaData(const std::string &key, const std::string &value)
std::string getFormatMimeType() const
Comma-separated list of mime types, or empty if unknown.
Definition: OutputFile.cpp:140
EWrappingStatus
define wrapping result status
const AVStream * getAVStream() const
Definition: CodedData.hpp:77
AVPacket & getAVPacket()
Definition: CodedData.hpp:78
std::map< std::string, std::string > Profile
const std::string avProfileIdentificator
std::string getFilename() const
Definition: OutputFile.cpp:115
const std::string avProfileIdentificatorHuman
ProfileLoader::Profile _profile
To setup specific wrapping options.
Definition: OutputFile.hpp:113
IOutputStream & addAudioStream(const AudioCodec &audioDesc)
Add an audio output stream.
Definition: OutputFile.cpp:68
AVStream & addAVStream(const AVCodec &avCodec)
#define LOG_INFO(...)
Definition: log.hpp:23
void setupWrappingOptions(const ProfileLoader::Profile &profile)
Definition: OutputFile.cpp:280
void writeTrailer()
Write the stream trailer to an output media file.
void writeHeader(AVDictionary **options=NULL)
Write the stream header to an output media file.
Wrapper of AVOption. Get its type to know what the option is about: Int, Double, Ratio, Choice... Parse its array of options to get the potential childs (Choice and Group).
Definition: Option.hpp:36
IOutputStream::EWrappingStatus wrap(const CodedData &data, const size_t streamIndex)
Wrap a packet of data in the output ressource.
Definition: OutputFile.cpp:166
Option & getOption(const std::string &optionName)
std::string getFormatLongName() const
Descriptive name for the format, meant to be more human-readable than name, or empty if unknown...
Definition: OutputFile.cpp:130
#define LOG_WARN(...)
Definition: log.hpp:29
std::vector< size_t > _frameCount
Number of wrapped frames.
Definition: OutputFile.hpp:104
OutputFile(const OutputFile &outputFile)
bool endWrap()
Close ressource and write trailer.
Definition: OutputFile.cpp:230
size_t getSize() const
Definition: CodedData.hpp:70
void writeFrame(AVPacket &packet, bool interleaved=true)
Write a packet to an output media file.
bool beginWrap()
Open ressource, write header, and setup specific wrapping options given when call setupWrapping...
Definition: OutputFile.cpp:150
AVCodecContext & getAVCodecContext()
Definition: ICodec.hpp:53
#define LOG_DEBUG(...)
Definition: log.hpp:17
This class describes coded data.
Definition: CodedData.hpp:18
void addMetadata(const PropertyVector &data)
Add metadata to the output file.
Definition: OutputFile.cpp:239
void openRessource(const std::string &url, int flags)
Create and initialize a AVIOContext for accessing the resource indicated by url.
#define FF_INPUT_BUFFER_PADDING_SIZE
Definition: OutputFile.cpp:8
AVCodec & getAVCodec()
Definition: ICodec.hpp:55
AVFormatContext & getAVFormatContext() const
IOutputStream & getStream(const size_t streamIndex)
Get the output stream.
Definition: OutputFile.cpp:98
std::vector< OutputStream * > _outputStreams
Has ownership.
Definition: OutputFile.hpp:103
const std::string avProfileType
IOutputStream & addDataStream(const DataCodec &dataDesc)
Add a data output stream.
Definition: OutputFile.cpp:88
AVStream & getAVStream(size_t index) const
IOutputStream & addVideoStream(const VideoCodec &videoDesc)
Add a video output stream.
Definition: OutputFile.cpp:33
void setFilename(const std::string &filename)