Ticket #32865: vidl_ffmpeg_ostream_v3.txx

File vidl_ffmpeg_ostream_v3.txx, 14.7 KB (added by ozt@…, 13 years ago)
Line 
1// This is core/vidl/vidl_ffmpeg_ostream_v3.txx
2#ifndef vidl_ffmpeg_ostream_v3_txx_
3#define vidl_ffmpeg_ostream_v3_txx_
4#include "vidl_ffmpeg_ostream.h"
5//:
6// \file
7// \author Matt Leotta
8// \author Amitha Perera
9// \author David Law
10// \date   26 Jan 2009
11//
12// Updated for ffmpeg based on -r16810
13//
14//-----------------------------------------------------------------------------
15
16#include "vidl_ffmpeg_init.h"
17#include "vidl_ffmpeg_convert.h"
18#include "vidl_frame.h"
19#include "vidl_convert.h"
20#include <vcl_cstring.h>
21#include <vcl_climits.h>
22#include <vil/vil_memory_chunk.h>
23
24extern "C" {
25#if FFMPEG_IN_SEVERAL_DIRECTORIES
26#include <libavformat/avformat.h>
27#else
28#include <ffmpeg/avformat.h>
29#endif
30}
31
32//-----------------------------------------------------------------------------
33
34
35struct vidl_ffmpeg_ostream::pimpl
36{
37  pimpl()
38  : fmt_cxt_( 0 ),
39  file_opened_( false ),
40  codec_opened_( false ),
41  cur_frame_( 0 ),
42  video_rc_eq_(NULL)
43  { }
44
45
46  AVFormatContext* fmt_cxt_;
47  bool file_opened_;
48  bool codec_opened_;
49  vil_memory_chunk_sptr bit_buf_;
50  unsigned int cur_frame_;
51  char* video_rc_eq_;
52};
53
54
55//-----------------------------------------------------------------------------
56
57
58//: Constructor
59vidl_ffmpeg_ostream::
60vidl_ffmpeg_ostream()
61  : os_( new vidl_ffmpeg_ostream::pimpl )
62{
63  vidl_ffmpeg_init();
64}
65
66
67//: Destructor
68vidl_ffmpeg_ostream::
69~vidl_ffmpeg_ostream()
70{
71  close();
72  delete os_;
73}
74
75
76//: Constructor - opens a stream
77vidl_ffmpeg_ostream::
78vidl_ffmpeg_ostream(const vcl_string& filename,
79                    const vidl_ffmpeg_ostream_params& params)
80  : os_( new vidl_ffmpeg_ostream::pimpl ),
81    filename_(filename), params_(params)
82{
83  vidl_ffmpeg_init();
84}
85
86
87//: Open the stream
88bool
89vidl_ffmpeg_ostream::
90open()
91{
92  // Close any open files
93  close();
94
95  // a raw video packet is the same size as the input image. Others
96  // are smaller.
97  os_->bit_buf_ = new vil_memory_chunk( params_.ni_ * params_.nj_ * 3, VIL_PIXEL_FORMAT_BYTE );
98
99  os_->fmt_cxt_ = avformat_alloc_context();
100
101  AVOutputFormat* file_oformat = 0;
102  if ( params_.file_format_ == vidl_ffmpeg_ostream_params::GUESS ) {
103    file_oformat = av_guess_format(NULL, filename_.c_str(), NULL);
104    if (!file_oformat) {
105      vcl_cerr << "ffmpeg: Unable for find a suitable output format for "
106               << filename_ << '\n';
107      close();
108      return false;
109    }
110  } else {
111    close();
112    return false;
113  }
114
115  os_->fmt_cxt_->oformat = file_oformat;
116  os_->fmt_cxt_->nb_streams = 0;
117
118  // Create stream
119  AVStream* st = av_new_stream( os_->fmt_cxt_, 0 );
120  if ( !st ) {
121    vcl_cerr << "ffmpeg: could not alloc stream\n";
122    close();
123    return false;
124  }
125
126  //os_->fmt_cxt_->nb_streams = 1;
127
128  AVCodecContext *video_enc = st->codec;
129
130  if (vcl_strcmp(file_oformat->name, "mp4") != 0 ||
131      vcl_strcmp(file_oformat->name, "mov") != 0 ||
132      vcl_strcmp(file_oformat->name, "3gp") != 0 )
133    video_enc->flags |= CODEC_FLAG_GLOBAL_HEADER;
134
135  video_enc->codec_type = AVMEDIA_TYPE_VIDEO;
136
137  switch ( params_.encoder_ )
138  {
139   case vidl_ffmpeg_ostream_params::DEFAULT:
140    video_enc->codec_id = file_oformat->video_codec;
141    break;
142   case vidl_ffmpeg_ostream_params::MPEG4:
143    video_enc->codec_id = CODEC_ID_MPEG4;
144    break;
145   case vidl_ffmpeg_ostream_params::MPEG2VIDEO:
146    video_enc->codec_id = CODEC_ID_MPEG2VIDEO;
147    break;
148   case vidl_ffmpeg_ostream_params::MSMPEG4V2:
149    video_enc->codec_id = CODEC_ID_MSMPEG4V2;
150    break;
151   case vidl_ffmpeg_ostream_params::RAWVIDEO:
152    video_enc->codec_id = CODEC_ID_RAWVIDEO;
153    break;
154   case vidl_ffmpeg_ostream_params::LJPEG:
155    video_enc->codec_id = CODEC_ID_LJPEG;
156    break;
157   case vidl_ffmpeg_ostream_params::HUFFYUV:
158    video_enc->codec_id = CODEC_ID_HUFFYUV;
159    break;
160   case vidl_ffmpeg_ostream_params::DVVIDEO:
161    video_enc->codec_id = CODEC_ID_DVVIDEO;
162    break;
163   default:
164    vcl_cout << "ffmpeg: Unknown encoder type\n";
165    return false;
166  }
167
168  AVCodec* codec = avcodec_find_encoder( video_enc->codec_id );
169  if ( !codec )
170  {
171    vcl_cerr << "ffmpeg_writer:: couldn't find encoder for " << video_enc->codec_id << '\n';
172    return false;
173  }
174
175  video_enc->bit_rate = params_.bit_rate_ * 1000;
176  video_enc->bit_rate_tolerance = params_.video_bit_rate_tolerance_;
177  video_enc->time_base.num = 1000;
178  video_enc->time_base.den = int(params_.frame_rate_*1000);
179
180  if ( codec && codec->supported_framerates )
181  {
182    AVRational const* p = codec->supported_framerates;
183    AVRational req = { video_enc->time_base.den, video_enc->time_base.num };
184    AVRational const* best = NULL;
185    AVRational best_error = { INT_MAX, 1 };
186    for (; p->den!=0; p++)
187    {
188      AVRational error = av_sub_q(req, *p);
189      if ( error.num < 0 )   error.num *= -1;
190      if ( av_cmp_q( error, best_error ) < 0 )
191      {
192        best_error= error;
193        best= p;
194      }
195    }
196    video_enc->time_base.den= best->num;
197    video_enc->time_base.num= best->den;
198  }
199
200  video_enc->width  = params_.ni_;
201  video_enc->height = params_.nj_;
202  video_enc->sample_aspect_ratio = av_d2q(params_.frame_aspect_ratio_*params_.ni_/params_.nj_, 255);
203
204  // Our source is packed RGB. Use that if possible.
205  video_enc->pix_fmt = PIX_FMT_RGB24;
206  if ( codec && codec->pix_fmts )
207  {
208    const enum PixelFormat* p= codec->pix_fmts;
209    for ( ; *p != -1; p++ )
210    {
211      if ( *p == video_enc->pix_fmt )
212        break;
213    }
214    if ( *p == -1 )
215      video_enc->pix_fmt = codec->pix_fmts[0];
216  }
217  else if ( codec && ( codec->id == CODEC_ID_RAWVIDEO ||
218                      codec->id == CODEC_ID_HUFFYUV ) )
219  {
220    // these formats only support the YUV input image formats
221    video_enc->pix_fmt = PIX_FMT_YUV420P;
222  }
223
224  if (!params_.intra_only_)
225    video_enc->gop_size = params_.gop_size_;
226  else
227    video_enc->gop_size = 0;
228  if (params_.video_qscale_ || params_.same_quality_)
229  {
230    video_enc->flags |= CODEC_FLAG_QSCALE;
231    st->quality = FF_QP2LAMBDA * params_.video_qscale_;
232  }
233  // if (bitexact)
234  //   video_enc->flags |= CODEC_FLAG_BITEXACT;
235
236  video_enc->mb_decision = params_.mb_decision_;
237  video_enc->mb_cmp = params_.mb_cmp_;
238  video_enc->ildct_cmp = params_.ildct_cmp_;
239  video_enc->me_sub_cmp = params_.sub_cmp_;
240  video_enc->me_cmp = params_.cmp_;
241  video_enc->me_pre_cmp = params_.pre_cmp_;
242  video_enc->pre_me = params_.pre_me_;
243  video_enc->lumi_masking = params_.lumi_mask_;
244  video_enc->dark_masking = params_.dark_mask_;
245  video_enc->spatial_cplx_masking = params_.scplx_mask_;
246  video_enc->temporal_cplx_masking = params_.tcplx_mask_;
247  video_enc->p_masking = params_.p_mask_;
248  video_enc->quantizer_noise_shaping= params_.qns_;
249
250  if (params_.use_umv_)
251  {
252    video_enc->flags |= CODEC_FLAG_H263P_UMV;
253  }
254  if (params_.use_ss_)
255  {
256    video_enc->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
257  }
258  if (params_.use_aiv_)
259  {
260    video_enc->flags |= CODEC_FLAG_H263P_AIV;
261  }
262  if (params_.use_4mv_)
263  {
264    video_enc->flags |= CODEC_FLAG_4MV;
265  }
266  if (params_.use_obmc_)
267  {
268    video_enc->flags |= CODEC_FLAG_OBMC;
269  }
270  if (params_.use_loop_)
271  {
272    video_enc->flags |= CODEC_FLAG_LOOP_FILTER;
273  }
274
275  if (params_.use_part_)
276  {
277    video_enc->flags |= CODEC_FLAG_PART;
278  }
279  if (params_.use_alt_scan_)
280  {
281    video_enc->flags |= CODEC_FLAG_ALT_SCAN;
282  }
283  if (params_.use_scan_offset_)
284  {
285    video_enc->flags |= CODEC_FLAG_SVCD_SCAN_OFFSET;
286  }
287  if (params_.closed_gop_)
288  {
289    video_enc->flags |= CODEC_FLAG_CLOSED_GOP;
290  }
291  if (params_.use_qpel_)
292  {
293    video_enc->flags |= CODEC_FLAG_QPEL;
294  }
295  if (params_.use_qprd_)
296  {
297    video_enc->flags |= CODEC_FLAG_QP_RD;
298  }
299  if (params_.use_cbprd_)
300  {
301    video_enc->flags |= CODEC_FLAG_CBP_RD;
302  }
303  if (params_.b_frames_)
304  {
305    video_enc->max_b_frames = params_.b_frames_;
306    video_enc->b_frame_strategy = 0;
307    video_enc->b_quant_factor = 2.0;
308  }
309  if (params_.do_interlace_dct_)
310  {
311    video_enc->flags |= CODEC_FLAG_INTERLACED_DCT;
312  }
313  if (params_.do_interlace_me_)
314  {
315    video_enc->flags |= CODEC_FLAG_INTERLACED_ME;
316  }
317  video_enc->qmin = params_.video_qmin_;
318  video_enc->qmax = params_.video_qmax_;
319  video_enc->lmin = params_.video_lmin_;
320  video_enc->lmax = params_.video_lmax_;
321  // video_enc->mb_qmin = params_.video_mb_qmin_;
322  // video_enc->mb_qmax = params_.video_mb_qmax_;
323  video_enc->max_qdiff = params_.video_qdiff_;
324  video_enc->qblur = params_.video_qblur_;
325  video_enc->qcompress = params_.video_qcomp_;
326
327  // delete when the stream is closed
328  os_->video_rc_eq_ = new char[params_.video_rc_eq_.length()+1];
329  vcl_strcpy(os_->video_rc_eq_, params_.video_rc_eq_.c_str());
330  video_enc->rc_eq = os_->video_rc_eq_;
331
332  video_enc->debug = params_.debug_;
333  video_enc->debug_mv = params_.debug_mv_;
334  video_enc->thread_count = 1;
335
336  video_enc->rc_max_rate = params_.video_rc_max_rate_;
337  video_enc->rc_min_rate = params_.video_rc_min_rate_;
338  video_enc->rc_buffer_size = params_.video_rc_buffer_size_;
339  video_enc->rc_buffer_aggressivity= params_.video_rc_buffer_aggressivity_;
340  video_enc->rc_initial_cplx= params_.video_rc_initial_cplx_;
341  video_enc->i_quant_factor = params_.video_i_qfactor_;
342  video_enc->b_quant_factor = params_.video_b_qfactor_;
343  video_enc->i_quant_offset = params_.video_i_qoffset_;
344  video_enc->b_quant_offset = params_.video_b_qoffset_;
345  video_enc->intra_quant_bias = params_.video_intra_quant_bias_;
346  video_enc->inter_quant_bias = params_.video_inter_quant_bias_;
347  video_enc->dct_algo = params_.dct_algo_;
348  video_enc->idct_algo = params_.idct_algo_;
349  video_enc->me_threshold= params_.me_threshold_;
350  video_enc->mb_threshold= params_.mb_threshold_;
351  video_enc->intra_dc_precision= params_.intra_dc_precision_ - 8;
352  video_enc->strict_std_compliance = params_.strict_;
353  video_enc->error_rate = params_.error_rate_;
354  video_enc->noise_reduction= params_.noise_reduction_;
355  video_enc->scenechange_threshold= params_.sc_threshold_;
356  video_enc->me_range = params_.me_range_;
357  video_enc->coder_type= params_.coder_;
358  video_enc->context_model= params_.context_;
359  video_enc->prediction_method= params_.predictor_;
360
361  if (params_.do_psnr_)
362    video_enc->flags|= CODEC_FLAG_PSNR;
363
364  video_enc->me_method = params_.me_method_;
365
366  /* two pass mode */
367  if (params_.do_pass_)
368  {
369    if (params_.do_pass_ == 1)
370    {
371      video_enc->flags |= CODEC_FLAG_PASS1;
372    }
373    else
374    {
375      video_enc->flags |= CODEC_FLAG_PASS2;
376    }
377  }
378
379  os_->fmt_cxt_->timestamp = 0;
380  // os_->fmt_cxt_->title[0] = '\0';
381  // os_->fmt_cxt_->author[0] = '\0';
382  // os_->fmt_cxt_->copyright[0] = '\0';
383  // os_->fmt_cxt_->comment[0] = '\0';
384
385  vcl_strncpy( os_->fmt_cxt_->filename, filename_.c_str(), 1023 );
386
387  if ( url_fopen( &os_->fmt_cxt_->pb, filename_.c_str(), URL_WRONLY) < 0 )
388  {
389    vcl_cerr << "ffmpeg: couldn't open " << filename_ << " for writing\n";
390    close();
391    return false;
392  }
393  os_->file_opened_ = true;
394
395  AVFormatParameters fmt_param;
396  vcl_memset( &fmt_param, 0, sizeof(fmt_param) );
397  if ( av_set_parameters( os_->fmt_cxt_, &fmt_param ) < 0 )
398  {
399    vcl_cerr << "ffmpeg: invalid encoding parameter\n";
400    close();
401    return false;
402  }
403
404  //dump_format( os_->fmt_cxt_, 1, filename_, 1 );
405
406  if ( avcodec_open( video_enc, codec ) < 0 )
407  {
408    vcl_cerr << "ffmpeg: couldn't open codec\n";
409    close();
410    return false;
411  }
412  os_->codec_opened_ = true;
413
414  if ( av_write_header( os_->fmt_cxt_ ) < 0 )
415  {
416    vcl_cerr << "ffmpeg: couldn't write header\n";
417    close();
418    return false;
419  }
420
421  return true;
422}
423
424
425//: Close the stream
426void
427vidl_ffmpeg_ostream::
428close()
429{
430  delete os_->video_rc_eq_;
431  os_->video_rc_eq_ = NULL;
432
433  if ( os_->fmt_cxt_ ) {
434
435    if ( os_->file_opened_ ) {
436      av_write_trailer( os_->fmt_cxt_ );
437      url_fclose( os_->fmt_cxt_->pb );
438      os_->file_opened_ = false;
439    }
440
441    if ( os_->fmt_cxt_->nb_streams > 0 ) {
442      if ( os_->codec_opened_ ) {
443        for ( unsigned i = 0; i < os_->fmt_cxt_->nb_streams; ++i ) {
444          AVCodecContext* codec = os_->fmt_cxt_->streams[i]->codec;
445          if ( codec->stats_in ) {
446            av_freep( codec->stats_in );
447          }
448          avcodec_close( codec );
449        }
450      }
451      os_->codec_opened_ = false;
452      for ( unsigned i = 0; i < os_->fmt_cxt_->nb_streams; ++i ) {
453        av_free( os_->fmt_cxt_->streams[i] );
454      }
455    }
456
457    av_free( os_->fmt_cxt_ );
458    os_->fmt_cxt_ = 0;
459  }
460}
461
462
463//: Return true if the stream is open for writing
464bool
465vidl_ffmpeg_ostream::
466is_open() const
467{
468  return os_->file_opened_;
469}
470
471
472//: Write and image to the stream
473// \retval false if the image could not be written
474bool
475vidl_ffmpeg_ostream::
476write_frame(const vidl_frame_sptr& frame)
477{
478  if (!is_open()) {
479    // resize to the first frame
480    params_.size(frame->ni(),frame->nj());
481    open();
482  }
483
484  AVCodecContext* codec = os_->fmt_cxt_->streams[0]->codec;
485
486  if (unsigned( codec->width ) != frame->ni() ||
487      unsigned( codec->height) != frame->nj() ) {
488    vcl_cerr << "ffmpeg: Input image has wrong size. Expecting ("
489             << codec->width << 'x' << codec->height << "), got ("
490             << frame->ni() << 'x' << frame->nj() << ")\n";
491    return false;
492  }
493
494
495  PixelFormat fmt = vidl_pixel_format_to_ffmpeg(frame->pixel_format());
496
497  vidl_pixel_format target_fmt = vidl_pixel_format_from_ffmpeg(codec->pix_fmt);
498  static vidl_frame_sptr temp_frame = new vidl_shared_frame(NULL,frame->ni(),frame->nj(),target_fmt);
499
500  AVFrame out_frame;
501  avcodec_get_frame_defaults( &out_frame );
502
503  // The frame is in the correct format to encode directly
504  if ( codec->pix_fmt == fmt )
505  {
506    avpicture_fill((AVPicture*)&out_frame, (uint8_t*) frame->data(),
507                   fmt, frame->ni(), frame->nj());
508  }
509  else
510  {
511    if (!temp_frame->data()) {
512      unsigned ni = frame->ni();
513      unsigned nj = frame->nj();
514      unsigned out_size = vidl_pixel_format_buffer_size(ni,nj,target_fmt);
515      temp_frame = new vidl_memory_chunk_frame(ni, nj, target_fmt,
516                                               new vil_memory_chunk(out_size, VIL_PIXEL_FORMAT_BYTE));
517    }
518    // try conversion with FFMPEG functions
519    if (!vidl_ffmpeg_convert(frame, temp_frame)) {
520      // try conversion with vidl functions
521      if (!vidl_convert_frame(*frame, *temp_frame)) {
522        vcl_cout << "unable to convert " << frame->pixel_format() << " to "<<target_fmt<<vcl_endl;
523        return false;
524      }
525    }
526    avpicture_fill((AVPicture*)&out_frame, (uint8_t*) temp_frame->data(),
527                   codec->pix_fmt, frame->ni(), frame->nj());
528  }
529
530  AVPacket pkt;
531  av_init_packet( &pkt );
532  pkt.stream_index = 0;
533
534  out_frame.pts = os_->cur_frame_;
535
536  int ret = avcodec_encode_video( codec, (uint8_t*)os_->bit_buf_->data(), os_->bit_buf_->size(), &out_frame );
537
538  if ( ret ) {
539    pkt.data = (uint8_t*)os_->bit_buf_->data();
540    pkt.size = ret;
541    if ( codec->coded_frame ) {
542      pkt.pts = codec->coded_frame->pts;
543    }
544    if ( codec->coded_frame && codec->coded_frame->key_frame ) {
545      pkt.flags |= AV_PKT_FLAG_KEY;
546    }
547    av_interleaved_write_frame( os_->fmt_cxt_, &pkt );
548  }
549  else {
550    return false;
551  }
552
553  ++os_->cur_frame_;
554  return true;
555}
556
557#endif // vidl_ffmpeg_ostream_v3_txx_