SLIP  1.4
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
AvWriter.hpp
Go to the documentation of this file.
1 
9 #ifndef AVWRITER_HPP_
10 #define AVWRITER_HPP_
11 
12 #include <iostream>
13 #include <sstream>
14 
15 extern "C" {
16 #include <libswscale/swscale.h>
17 #include <libavformat/avformat.h>
18 #include <libavcodec/avcodec.h>
19 #include <libavcodec/version.h>
20 #include <libavutil/pixfmt.h>
21 #include <libavutil/mathematics.h>
22 #include <stdio.h>
23 }
24 
25 #include "color_spaces.hpp"
26 #include "error.hpp"
27 #include "ContainerWriter.hpp"
28 
29 
30 template <typename T>
32 {
33  template <typename T1> // T1 models type statically convertible to T
34  T operator()(const T1& x) const { return static_cast<T>(x); }
35 };
36 
37 
38 namespace slip {
39 
54 template<class ContainerVideo, typename T, std::size_t Nb_components>
55 class AvWriter: public ContainerWriter<ContainerVideo, T> {
56 public:
57  typedef ContainerVideo container_type;
58  typedef T value_type;
60 
65 
70  :base(),pFormatCtx(NULL),video_stream_idx_(-1),pictureIN(NULL),pictureOUT(NULL),pSwsCtxOUT(NULL),
71  codec_id_(CODEC_ID_NONE),dimension_optimize_(0),bit_rate_(0),output_video_width_(0),output_video_height_(0),
72  output_video_nbframes_(0),time_base_((AVRational){0,1}),input_pxfmt_(PIX_FMT_GRAY8),
73  output_pxfmt_(PIX_FMT_RGB24),row_stride(0),finished(0),total_nb_slice(0),initialized(false){}
74 
92  AvWriter(std::string output_filename, std::size_t output_video_width, std::size_t output_video_height,
93  std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize = 0,
94  CodecID codec_id = CODEC_ID_NONE)
95  :base(output_filename),pFormatCtx(NULL),video_stream_idx_(-1),pictureIN(NULL),pictureOUT(NULL),pSwsCtxOUT(NULL),
96  codec_id_(codec_id),dimension_optimize_(dimension_optimize), bit_rate_(bit_rate),output_video_width_(output_video_width),
97  output_video_height_(output_video_height),output_video_nbframes_(output_video_nbframes),time_base_(time_base),
98  input_pxfmt_(PIX_FMT_GRAY8),output_pxfmt_(PIX_FMT_RGB24),row_stride(0),finished(0),total_nb_slice(0),initialized(false)
99  {
100  initialize();
101  }
102 
106  virtual ~AvWriter(){
107  release();
108  }
109 
127  void initialize( std::size_t output_video_width, std::size_t output_video_height,
128  std::size_t output_video_nbframes, int bit_rate, AVRational time_base,
129  bool dimension_optimize = 0, CodecID codec_id = CODEC_ID_NONE);
130 
131 
135  void release();
136 
147  int write(const container_type & data);
148 
149 private:
150  AVFormatContext *pFormatCtx;
151  int video_stream_idx_;
153  AVFrame * pictureIN;
155  AVFrame * pictureOUT;
157  struct SwsContext * pSwsCtxOUT;
158 
160  CodecID codec_id_;
163  bool dimension_optimize_;
165  int bit_rate_;
167  std::size_t output_video_width_;
169  std::size_t output_video_height_;
171  std::size_t output_video_nbframes_;
173  AVRational time_base_;
175  PixelFormat input_pxfmt_;
177  PixelFormat output_pxfmt_;
179  int row_stride;
181  bool finished;
183  std::size_t total_nb_slice;
185  bool initialized;
186 
187 
188  void initialize();
189  void alloc_picture(AVFrame *&picture, PixelFormat pix_fmt, int width, int height);
190  void add_video_stream();
191  void write_video_frame(const container_type & data, std::size_t nb_slice);
192 };
193 
194 }
196 namespace slip {
197 
198 template<class ContainerVideo, typename T, std::size_t Nb_components>
199 inline
200 void AvWriter<ContainerVideo,T,Nb_components>::initialize(std::size_t output_video_width, std::size_t output_video_height, std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize, CodecID codec_id){
201  bit_rate_ = bit_rate;
202  output_video_width_ = output_video_width;
203  output_video_height_ = output_video_height;
204  output_video_nbframes_ = output_video_nbframes;
205  time_base_ = time_base;
206  codec_id_ = codec_id;
207  dimension_optimize_ = dimension_optimize;
208  initialize();
209 }
210 template<class ContainerVideo, typename T, std::size_t Nb_components>
211 inline
212 void AvWriter<ContainerVideo,T,Nb_components>::alloc_picture(AVFrame * &picture, PixelFormat pix_fmt, int width, int height)
213 {
214  //AVFrame *picture;
215  uint8_t *picture_buf;
216  int size;
217 
218  picture = avcodec_alloc_frame();
219  if (picture){
220  size = avpicture_get_size(pix_fmt, width, height);
221  picture_buf = reinterpret_cast<uint8_t*>(av_malloc(size));
222  if (!picture_buf)
223  av_free(picture);
224  else
225  avpicture_fill((AVPicture *)picture, picture_buf, pix_fmt, width, height);
226  }
227 }
228 
229 
230 
231 template<class ContainerVideo, typename T, std::size_t Nb_components>
232 inline
233 void AvWriter<ContainerVideo,T,Nb_components>::add_video_stream()
234 {
235  AVStream * video_st = NULL;
236  /* find the video encoder */
237  AVCodec * codec = avcodec_find_encoder(codec_id_);
238  if (!codec) {
239  std::ostringstream err;
240  err << FILE_WRITE_ERROR << this->output_filename_ << " | Codec not found.";
241  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::add_video_stream()"), err.str());
242  throw (exc);
243  }
244 
245  const PixelFormat * possible_pix_fmt = codec->pix_fmts;
246  if (possible_pix_fmt){
247  int loss;
248 #ifndef FF_API_FIND_BEST_PIX_FMT
249  int64_t pix_fmt_mask = (1 << *possible_pix_fmt);
250  possible_pix_fmt++;
251  while(*possible_pix_fmt != -1){
252  pix_fmt_mask = pix_fmt_mask || (1 << *possible_pix_fmt);
253  possible_pix_fmt++;
254  }
255  output_pxfmt_ = avcodec_find_best_pix_fmt(pix_fmt_mask,input_pxfmt_,0,&loss);
256 #else
257  output_pxfmt_ = avcodec_find_best_pix_fmt2(possible_pix_fmt,input_pxfmt_,0,&loss);
258 #endif
259 
260  }else{
261  std::cerr << "Warning -> pixel format unknown for this codec, "
262  << "PIX_FMT_RGB24 has been chosen by default." << std::endl;
263  }
264 
265  video_st = avformat_new_stream(pFormatCtx, codec);
266  if (!video_st) {
267  std::ostringstream err;
268  err << FILE_WRITE_ERROR << this->output_filename_ << " | Could not alloc stream.";
269  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::add_video_stream()"), err.str());
270  throw (exc);
271  }
272 
273  /* frames per second */
274  video_st->codec->time_base= time_base_;
275  video_st->time_base = time_base_;
276  video_st->codec->gop_size = time_base_.den; /* =frame rate */
277  video_st->codec->pix_fmt = output_pxfmt_;
278  if (video_st->codec->codec_id == CODEC_ID_MPEG2VIDEO) {
279  /* just for testing, we also add B frames */
280  video_st->codec->max_b_frames = 2;
281  }
282  if (video_st->codec->codec_id == CODEC_ID_MPEG1VIDEO) {
283  /* Needed to avoid using macroblocks in which some coeffs overflow.
284  * This does not happen with normal video, it just happens here as
285  * the motion of the chroma plane does not match the luma plane. */
286  video_st->codec->mb_decision = 2;
287  }
288 
289  int width = static_cast<int>(output_video_width_);
290  int height = static_cast<int>(output_video_height_);
291  //Resize if needed
292  if(dimension_optimize_){
293  std::cerr << "Old dimensions = (" << width << ", " << height << ")\n";
294  int linesize_align[AV_NUM_DATA_POINTERS];
295  avcodec_align_dimensions2(video_st->codec,&width,&height,linesize_align);
296  std::cerr << "New dimensions = (" << width << ", " << height << ")\n";
297  }
298  video_st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
299  video_st->codec->bit_rate = bit_rate_;
300  video_st->codec->width = width;
301  video_st->codec->height = height;
302 
303  /* Some formats want stream headers to be separate. */
304  if (pFormatCtx->oformat->flags & AVFMT_GLOBALHEADER)
305  video_st->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
306 
307  video_stream_idx_ = video_st->index;
308 
309  // open the video codec
310  if (avcodec_open2(video_st->codec, codec, NULL) < 0) {
311  std::ostringstream err;
312  err << FILE_WRITE_ERROR << this->output_filename_ << " | Can not open codec.";
313  slip::slip_exception exc(std::string("slip"), std::string("AvWritter::add_video_stream()"), err.str());
314  throw (exc);
315  }
316 
317  /* Allocate all pictures. */
318  //pictureIN
319  alloc_picture(pictureIN, input_pxfmt_, output_video_width_, output_video_height_);
320  if (!pictureIN) {
321  std::ostringstream err;
322  err << FILE_WRITE_ERROR << this->output_filename_ << " | Could not allocate input picture buffer.";
323  slip::slip_exception exc(std::string("slip"), std::string("AvWritter::add_video_stream()"), err.str());
324  throw (exc);
325  }
326  //pictureOUT =
327  alloc_picture(pictureOUT, video_st->codec->pix_fmt, video_st->codec->width, video_st->codec->height);
328  if (!pictureOUT) {
329  std::ostringstream err;
330  err << FILE_WRITE_ERROR << this->output_filename_ << " | Could not allocate output picture buffer.";
331  slip::slip_exception exc(std::string("slip"), std::string("AvWritter::add_video_stream()"), err.str());
332  throw (exc);
333  }
334 }
335 
336 template<class ContainerVideo, typename T, std::size_t Nb_components>
337 inline
338 void AvWriter<ContainerVideo,T,Nb_components>::initialize(){
339  if(!initialized){
340  if (Nb_components != 1 && Nb_components != 3){
341  std::ostringstream err;
342  err << FILE_WRITE_ERROR << this->output_filename_ << " | Nb_components should be 1 or 3.";
343  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::initialize()"), err.str());
344  throw (exc);
345  }
346  if (Nb_components == 3){
347  input_pxfmt_ = PIX_FMT_RGB24;
348  }
349 
350  avcodec_register_all();
351  av_register_all();
352  //avformat_network_init();
353 
354  /* Autodetect the output format from the name. default is MPEG. */
355  AVOutputFormat *fmt = av_guess_format(NULL, this->output_filename_.c_str(), NULL);
356  if (!fmt) {
357  std::cerr << "Could not deduce output format from file extension: using MPEG." << std::endl;
358  fmt = av_guess_format("mpeg", NULL, NULL);
359  }
360  if (!fmt) {
361  std::ostringstream err;
362  err << FILE_WRITE_ERROR << this->output_filename_ << " | Could not find suitable output format.";
363  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::initialize()"), err.str());
364  throw (exc);
365  }
369  if(codec_id_ != fmt->video_codec && codec_id_ != CODEC_ID_NONE){
370  std::cerr << "The specified codec id does not match the file extension." << std::endl;
371  }
372  codec_id_ = fmt->video_codec;
373  /* Allocate the output media context. */
374  pFormatCtx = avformat_alloc_context();
375  if (!pFormatCtx) {
376  std::ostringstream err;
377  err << FILE_WRITE_ERROR << this->output_filename_ << " | Memory error.";
378  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::initialize()"), err.str());
379  throw (exc);
380  }
381  pFormatCtx->oformat = fmt;
382  strcpy(pFormatCtx->filename, this->output_filename_.c_str());
383 
384  /* Add the video stream using the default format codecs
385  * and initialize the codecs. */
386  //CHECK -> is this if condition useful?
387  if (pFormatCtx->oformat->video_codec != CODEC_ID_NONE) {
388  add_video_stream();
389  }
390 
391  av_dump_format(pFormatCtx, 0, this->output_filename_.c_str(), 1);
392  /* open the output file*/
393  if (!(pFormatCtx->oformat->flags & AVFMT_NOFILE)) {
394  //CHECK -> URL_WRONLY instead of AVIO_FLAG_WRITE
395  if (avio_open(&pFormatCtx->pb, this->output_filename_.c_str(), AVIO_FLAG_WRITE) < 0) {
396  std::ostringstream err;
397  err << FILE_OPEN_ERROR << this->output_filename_;
398  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::initialize()"), err.str());
399  throw (exc);
400  }
401  }
402 
403  /* Write the stream header, if any. */
404  if (avformat_write_header(pFormatCtx, NULL) < 0){
405  std::ostringstream err;
406  err << FILE_WRITE_ERROR << this->output_filename_ << " | Cannot write a file header.";
407  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::initialize()"), err.str());
408  throw (exc);
409  }
410  row_stride = output_video_width_ * Nb_components;
411  initialized = true;
412  }
413 }
414 
415 template<class ContainerVideo, typename T, std::size_t Nb_components>
416 inline
417 void AvWriter<ContainerVideo,T,Nb_components>::write_video_frame(const container_type & data, std::size_t nb_slice)
418 {
419  int ret = 1;
420  AVStream * video_st = pFormatCtx->streams[video_stream_idx_];
421  AVCodecContext * pCodecCtx = video_st->codec;
422  //CHECK-> replace SWS_FAST_BILINEAR by SWS_BICUBIC
423  if (Nb_components == 1){
424  pSwsCtxOUT = sws_getContext(data.cols(),data.rows(),PIX_FMT_GRAY8,pCodecCtx->width,pCodecCtx->height,
425  pCodecCtx->pix_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL);
426  }else{
427  pSwsCtxOUT = sws_getContext(data.cols(),data.rows(),PIX_FMT_RGB24,pCodecCtx->width,pCodecCtx->height,
428  pCodecCtx->pix_fmt,SWS_FAST_BILINEAR, NULL, NULL, NULL);
429  }
430  if (pSwsCtxOUT == NULL) {
431  std::ostringstream err;
432  err << FILE_WRITE_ERROR << this->output_filename_ << " | Cannot initialize the YUV conversion context.";
433  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::write_video_frame()"), err.str());
434  throw (exc);
435  }
436 
437  if (total_nb_slice >= output_video_nbframes_) {
438  /* No more frames to compress. The codec has a latency of a few
439  * frames if using B-frames, so we get the last frames by
440  * passing the same picture again. */
441  }else {
442  for(std::size_t y=0; y < data.rows(); y++) {
443  const T * data_ptr_i = reinterpret_cast<const T *>(data[nb_slice][y]);
444  std::transform(data_ptr_i, data_ptr_i + row_stride,
445  pictureIN->data[0]+y*pictureIN->linesize[0], av_static_cast_func<unsigned char>());
446  }
447  //Scale into pictureOUT
448  sws_scale(pSwsCtxOUT, pictureIN->data, pictureIN->linesize,0, data.rows(), pictureOUT->data,
449  pictureOUT->linesize);
450  }
451 
452  pictureOUT->pts = //static_cast<int64_t>((1000.0 * av_q2d(time_base_)) * 90.0 *
453  static_cast<double>(total_nb_slice);//);
454 
455  if (pFormatCtx->oformat->flags & AVFMT_RAWPICTURE) {
456  /* Raw video case - the API will change slightly in the near
457  * future for that. */
458  AVPacket pkt;
459  av_init_packet(&pkt);
460 
461  pkt.flags |= AV_PKT_FLAG_KEY;
462  pkt.stream_index = video_stream_idx_;
463  pkt.data = reinterpret_cast<uint8_t *>(pictureOUT);
464  pkt.size = sizeof(AVPicture);
465 
466  ret = av_write_frame(pFormatCtx, &pkt);
467  av_free_packet(&pkt);
468  } else {
469  AVPacket pkt = { 0 };
470  av_init_packet(&pkt);
471  /* encode the image */
472 #ifndef FF_API_OLD_ENCODE_VIDEO
473  int buf_size = static_cast<int>( 9 * video_st->codec->width * video_st->codec->height * Nb_components + 10000);
474  //std::cerr << "buf_size = " << buf_size << "\n";
475  uint8_t * buf = new uint8_t[buf_size];
476  int sizeout = avcodec_encode_video(pCodecCtx, buf, buf_size, pictureOUT);
477  if(sizeout > 0){
478  pkt.data = buf;
479  pkt.size = sizeout;
480  if (pCodecCtx->coded_frame->pts != static_cast<int64_t>(AV_NOPTS_VALUE))
481  pkt.pts= av_rescale_q(pCodecCtx->coded_frame->pts, pCodecCtx->time_base, video_st->time_base);
482  if(pCodecCtx->coded_frame->key_frame)
483  pkt.flags |= AV_PKT_FLAG_KEY;
484  pkt.stream_index = video_st->index;
485  /* Write the compressed frame to the media file. */
486  ret = av_write_frame(pFormatCtx, &pkt);
487  }else{
488  ret = 0;
489  }
490 #else
491  int got_packet;
492  ret = avcodec_encode_video2(pCodecCtx, &pkt, pictureOUT, &got_packet);
493  if(!ret && got_packet && pkt.size){
494  if (pkt.pts != static_cast<int64_t>(AV_NOPTS_VALUE)) {
495  pkt.pts = av_rescale_q(pkt.pts,
496  pCodecCtx->time_base, video_st->time_base);
497  }
498  if (pkt.dts != static_cast<int64_t>(AV_NOPTS_VALUE)) {
499  pkt.dts = av_rescale_q(pkt.dts,
500  pCodecCtx->time_base, video_st->time_base);
501  }
502 
503  pkt.stream_index = video_st->index;
504  /* Write the compressed frame to the media file. */
505  ret = av_write_frame(pFormatCtx, &pkt);
506  }else {
507  ret = 0;
508  }
509 #endif
510  av_free_packet(&pkt);
511  if(buf)
512  delete[] buf;
513  }
514  if (ret != 0) {
515  std::ostringstream err;
516  err << FILE_WRITE_ERROR << this->output_filename_ << " | Error while writing frame.";
517  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::write()"), err.str());
518  throw (exc);
519  exit(1);
520  }
521  total_nb_slice++;
522 }
523 
524 
525 template<class ContainerVideo, typename T, std::size_t Nb_components>
526 inline
528  if(!initialized){
529  std::ostringstream err;
530  err << FILE_WRITE_ERROR << this->output_filename_ << " | The writer to be initialized before writing.";
531  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::write()"), err.str());
532  throw (exc);
533  }
534  if (finished){
535  return 0;
536  }
537 
538  std::size_t nb_slices = data.slices();
539  if(output_video_nbframes_ < (total_nb_slice + nb_slices)){
540  std::cerr << "output_video_nbframes_ = " << output_video_nbframes_ << std::endl;
541  std::cerr << "total_nb_slice = " << total_nb_slice << std::endl;
542  std::cerr << "nb_slices = " << nb_slices << std::endl;
543  std::ostringstream err;
544  err << FILE_WRITE_ERROR << this->output_filename_ << " | Allocation problem during writing.";
545  slip::slip_exception exc(std::string("slip"), std::string("AvWriter::write()"), err.str());
546  throw (exc);
547  }
548 
549  /* encode the video */
550  for(std::size_t i = 0; i < nb_slices; i++) {
551  write_video_frame(data,i);
552  }
553 
554  if (total_nb_slice == output_video_nbframes_){
555  if(pFormatCtx->streams[video_stream_idx_]){
556  double time_base_dble = static_cast<double>(pFormatCtx->streams[video_stream_idx_]->time_base.num) /
557  static_cast<double>(pFormatCtx->streams[video_stream_idx_]->time_base.den);
558  double video_pts = static_cast<double>(pFormatCtx->streams[video_stream_idx_]->pts.val) * time_base_dble;
559  double duration = static_cast<double>(output_video_nbframes_) * time_base_dble;
560  while(video_pts < duration){
561  write_video_frame(data,0);
562  video_pts = static_cast<double>(pFormatCtx->streams[video_stream_idx_]->pts.val) * time_base_dble ;
563  }
564  }
565  av_write_trailer(pFormatCtx);
566  finished = true;
567  }
568 
569  return (!finished);
570 }
571 
572 template<class ContainerVideo, typename T, std::size_t Nb_components>
573 inline
575  if(initialized){
576 
577 
578  av_free(pictureIN->data[0]);
579  av_free(pictureIN);
580  av_free(pictureOUT->data[0]);
581  av_free(pictureOUT);
582  if (!(pFormatCtx->oformat->flags & AVFMT_NOFILE))
583  /* Close the output file. */
584  avio_close(pFormatCtx->pb);
585 
586  for (std::size_t i = 0; i < pFormatCtx->nb_streams; i++) {
587  av_freep(&pFormatCtx->streams[i]->codec);
588  av_freep(&pFormatCtx->streams[i]);
589  }
590 
591  /* free the stream */
592  av_free(pFormatCtx);
593 
594  video_stream_idx_ = -1;
595  codec_id_ = CODEC_ID_NONE;
596  dimension_optimize_ = false;
597  bit_rate_ = 0;
598  output_video_width_ = 0;
599  output_video_height_ = 0;
600  output_video_nbframes_ = 0;
601  time_base_ = (AVRational){0,1};
602  input_pxfmt_ = PIX_FMT_GRAY8;
603  output_pxfmt_ = PIX_FMT_RGB24;
604  row_stride = 0;
605  finished = false;
606  total_nb_slice = 0;
607  initialized = false;
608  }
609 }
610 
611 
612 
613 
614 }
615 #endif /* AVWRITER_HPP_ */
AvWriter is a video writer.
Definition: AvWriter.hpp:55
const std::string FILE_WRITE_ERROR
Definition: error.hpp:91
standard exception extension name have been changed to eliminate conflicts with QT ...
Definition: error.hpp:106
Contains the container writer base class.
void release()
release the writing process. Has to be called when an video has been fully written.
Definition: AvWriter.hpp:574
void initialize(std::size_t output_video_width, std::size_t output_video_height, std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize=0, CodecID codec_id=CODEC_ID_NONE)
initialized the writing process. Has to be called before the first write() call of a given video...
Definition: AvWriter.hpp:200
initialized(false)
Definition: AvWriter.hpp:73
Provides some functors to change of color space.
const std::string FILE_OPEN_ERROR
Definition: error.hpp:82
ContainerVideo container_type
Definition: AvWriter.hpp:57
virtual ~AvWriter()
Definition: AvWriter.hpp:106
ContainerWriter< ContainerVideo, T > base
Definition: AvWriter.hpp:59
T operator()(const T1 &x) const
Definition: AvWriter.hpp:34
Provides SLIP error messages.
ContainerWriter is the base class of the writer classes. All the writers are working the same way: ...
AvWriter(std::string output_filename, std::size_t output_video_width, std::size_t output_video_height, std::size_t output_video_nbframes, int bit_rate, AVRational time_base, bool dimension_optimize=0, CodecID codec_id=CODEC_ID_NONE)
Definition: AvWriter.hpp:92
int write(const container_type &data)
write function. This function write the data within the output file after the previous one...
Definition: AvWriter.hpp:527