core/vidl/vidl_ffmpeg_ostream_v2.txx
Go to the documentation of this file.
00001 // This is core/vidl/vidl_ffmpeg_ostream_v2.txx
00002 #ifndef vidl_ffmpeg_ostream_v2_txx_
00003 #define vidl_ffmpeg_ostream_v2_txx_
00004 #include "vidl_ffmpeg_ostream.h"
00005 //:
00006 // \file
00007 // \author Matt Leotta
00008 // \author Amitha Perera
00009 // \date   14 Jan 2008
00010 //
00011 // Updated for ffmpeg 51.49.0.
00012 //
00013 //-----------------------------------------------------------------------------
00014 
00015 #include "vidl_ffmpeg_init.h"
00016 #include "vidl_ffmpeg_convert.h"
00017 #include "vidl_frame.h"
00018 #include "vidl_convert.h"
00019 #include <vcl_cstring.h>
00020 #include <vcl_climits.h>
00021 #include <vil/vil_memory_chunk.h>
00022 
00023 extern "C" {
00024 #if FFMPEG_IN_SEVERAL_DIRECTORIES
00025 #include <libavformat/avformat.h>
00026 #else
00027 #include <ffmpeg/avformat.h>
00028 #endif
00029 }
00030 
00031 //-----------------------------------------------------------------------------
00032 
00033 
00034 struct vidl_ffmpeg_ostream::pimpl
00035 {
00036   pimpl()
00037   : fmt_cxt_( 0 ),
00038   file_opened_( false ),
00039   codec_opened_( false ),
00040   cur_frame_( 0 ),
00041   video_rc_eq_(NULL)
00042   { }
00043 
00044 
00045   AVFormatContext* fmt_cxt_;
00046   bool file_opened_;
00047   bool codec_opened_;
00048   vil_memory_chunk_sptr bit_buf_;
00049   unsigned int cur_frame_;
00050   char* video_rc_eq_;
00051 };
00052 
00053 
00054 //-----------------------------------------------------------------------------
00055 
00056 
00057 //: Constructor
00058 vidl_ffmpeg_ostream::
00059 vidl_ffmpeg_ostream()
00060   : os_( new vidl_ffmpeg_ostream::pimpl )
00061 {
00062   vidl_ffmpeg_init();
00063 }
00064 
00065 
00066 //: Destructor
00067 vidl_ffmpeg_ostream::
00068 ~vidl_ffmpeg_ostream()
00069 {
00070   close();
00071   delete os_;
00072 }
00073 
00074 
00075 //: Constructor - opens a stream
00076 vidl_ffmpeg_ostream::
00077 vidl_ffmpeg_ostream(const vcl_string& filename,
00078                     const vidl_ffmpeg_ostream_params& params)
00079   : os_( new vidl_ffmpeg_ostream::pimpl ),
00080     filename_(filename), params_(params)
00081 {
00082   vidl_ffmpeg_init();
00083 }
00084 
00085 
00086 //: Open the stream
00087 bool
00088 vidl_ffmpeg_ostream::
00089 open()
00090 {
00091   // Close any open files
00092   close();
00093 
00094   // a raw video packet is the same size as the input image. Others
00095   // are smaller.
00096   os_->bit_buf_ = new vil_memory_chunk( params_.ni_ * params_.nj_ * 3, VIL_PIXEL_FORMAT_BYTE );
00097 
00098   os_->fmt_cxt_ = av_alloc_format_context();
00099 
00100   AVOutputFormat* file_oformat = 0;
00101   if ( params_.file_format_ == vidl_ffmpeg_ostream_params::GUESS ) {
00102     file_oformat = guess_format(NULL, filename_.c_str(), NULL);
00103     if (!file_oformat) {
00104       vcl_cerr << "ffmpeg: Unable for find a suitable output format for "
00105                << filename_ << '\n';
00106       close();
00107       return false;
00108     }
00109   } else {
00110     close();
00111     return false;
00112   }
00113 
00114   os_->fmt_cxt_->oformat = file_oformat;
00115   os_->fmt_cxt_->nb_streams = 0;
00116 
00117   // Create stream
00118   AVStream* st = av_new_stream( os_->fmt_cxt_, 0 );
00119   if ( !st ) {
00120     vcl_cerr << "ffmpeg: could not alloc stream\n";
00121     close();
00122     return false;
00123   }
00124 
00125   //os_->fmt_cxt_->nb_streams = 1;
00126 
00127   AVCodecContext *video_enc = st->codec;
00128 
00129   if ( vcl_strcmp(file_oformat->name, "mp4") != 0 ||
00130        vcl_strcmp(file_oformat->name, "mov") != 0 ||
00131        vcl_strcmp(file_oformat->name, "3gp") != 0 )
00132     video_enc->flags |= CODEC_FLAG_GLOBAL_HEADER;
00133 
00134   video_enc->codec_type = CODEC_TYPE_VIDEO;
00135 
00136   switch ( params_.encoder_ )
00137   {
00138    case vidl_ffmpeg_ostream_params::DEFAULT:
00139     video_enc->codec_id = file_oformat->video_codec;
00140     break;
00141    case vidl_ffmpeg_ostream_params::MPEG4:
00142     video_enc->codec_id = CODEC_ID_MPEG4;
00143     break;
00144    case vidl_ffmpeg_ostream_params::MPEG2VIDEO:
00145     video_enc->codec_id = CODEC_ID_MPEG2VIDEO;
00146     break;
00147    case vidl_ffmpeg_ostream_params::MSMPEG4V2:
00148     video_enc->codec_id = CODEC_ID_MSMPEG4V2;
00149     break;
00150    case vidl_ffmpeg_ostream_params::RAWVIDEO:
00151     video_enc->codec_id = CODEC_ID_RAWVIDEO;
00152     break;
00153    case vidl_ffmpeg_ostream_params::LJPEG:
00154     video_enc->codec_id = CODEC_ID_LJPEG;
00155     break;
00156    case vidl_ffmpeg_ostream_params::HUFFYUV:
00157     video_enc->codec_id = CODEC_ID_HUFFYUV;
00158     break;
00159    case vidl_ffmpeg_ostream_params::DVVIDEO:
00160     video_enc->codec_id = CODEC_ID_DVVIDEO;
00161     break;
00162    default:
00163     vcl_cout << "ffmpeg: Unknown encoder type\n";
00164     return false;
00165   }
00166 
00167   AVCodec* codec = avcodec_find_encoder( video_enc->codec_id );
00168   if ( !codec )
00169   {
00170     vcl_cerr << "ffmpeg_writer:: couldn't find encoder for " << video_enc->codec_id << '\n';
00171     return false;
00172   }
00173 
00174   video_enc->bit_rate = params_.bit_rate_ * 1000;
00175   video_enc->bit_rate_tolerance = params_.video_bit_rate_tolerance_;
00176   video_enc->time_base.num = 1000;
00177   video_enc->time_base.den = int(params_.frame_rate_*1000);
00178 
00179   if ( codec && codec->supported_framerates )
00180   {
00181     AVRational const* p = codec->supported_framerates;
00182     AVRational req = { video_enc->time_base.den, video_enc->time_base.num };
00183     AVRational const* best = NULL;
00184     AVRational best_error = { INT_MAX, 1 };
00185     for (; p->den!=0; p++)
00186     {
00187       AVRational error = av_sub_q(req, *p);
00188       if ( error.num < 0 )   error.num *= -1;
00189       if ( av_cmp_q( error, best_error ) < 0 )
00190       {
00191         best_error= error;
00192         best= p;
00193       }
00194     }
00195     video_enc->time_base.den= best->num;
00196     video_enc->time_base.num= best->den;
00197   }
00198 
00199   video_enc->width  = params_.ni_;
00200   video_enc->height = params_.nj_;
00201   video_enc->sample_aspect_ratio = av_d2q(params_.frame_aspect_ratio_*params_.ni_/params_.nj_, 255);
00202 
00203   // Our source is packed RGB. Use that if possible.
00204   video_enc->pix_fmt = PIX_FMT_RGB24;
00205   if ( codec && codec->pix_fmts )
00206   {
00207     const enum PixelFormat* p= codec->pix_fmts;
00208     for ( ; *p != -1; p++ )
00209     {
00210       if ( *p == video_enc->pix_fmt )
00211         break;
00212     }
00213     if ( *p == -1 )
00214       video_enc->pix_fmt = codec->pix_fmts[0];
00215   }
00216   else if ( codec && ( codec->id == CODEC_ID_RAWVIDEO ||
00217                       codec->id == CODEC_ID_HUFFYUV ) )
00218   {
00219     // these formats only support the YUV input image formats
00220     video_enc->pix_fmt = PIX_FMT_YUV420P;
00221   }
00222 
00223   if (!params_.intra_only_)
00224     video_enc->gop_size = params_.gop_size_;
00225   else
00226     video_enc->gop_size = 0;
00227   if (params_.video_qscale_ || params_.same_quality_)
00228   {
00229     video_enc->flags |= CODEC_FLAG_QSCALE;
00230     st->quality = FF_QP2LAMBDA * params_.video_qscale_;
00231   }
00232   // if (bitexact)
00233   //   video_enc->flags |= CODEC_FLAG_BITEXACT;
00234 
00235   video_enc->mb_decision = params_.mb_decision_;
00236   video_enc->mb_cmp = params_.mb_cmp_;
00237   video_enc->ildct_cmp = params_.ildct_cmp_;
00238   video_enc->me_sub_cmp = params_.sub_cmp_;
00239   video_enc->me_cmp = params_.cmp_;
00240   video_enc->me_pre_cmp = params_.pre_cmp_;
00241   video_enc->pre_me = params_.pre_me_;
00242   video_enc->lumi_masking = params_.lumi_mask_;
00243   video_enc->dark_masking = params_.dark_mask_;
00244   video_enc->spatial_cplx_masking = params_.scplx_mask_;
00245   video_enc->temporal_cplx_masking = params_.tcplx_mask_;
00246   video_enc->p_masking = params_.p_mask_;
00247   video_enc->quantizer_noise_shaping= params_.qns_;
00248 
00249   if (params_.use_umv_)
00250   {
00251     video_enc->flags |= CODEC_FLAG_H263P_UMV;
00252   }
00253   if (params_.use_ss_)
00254   {
00255     video_enc->flags |= CODEC_FLAG_H263P_SLICE_STRUCT;
00256   }
00257   if (params_.use_aic_)
00258   {
00259     video_enc->flags |= CODEC_FLAG_H263P_AIC;
00260   }
00261   if (params_.use_aiv_)
00262   {
00263     video_enc->flags |= CODEC_FLAG_H263P_AIV;
00264   }
00265   if (params_.use_4mv_)
00266   {
00267     video_enc->flags |= CODEC_FLAG_4MV;
00268   }
00269   if (params_.use_obmc_)
00270   {
00271     video_enc->flags |= CODEC_FLAG_OBMC;
00272   }
00273   if (params_.use_loop_)
00274   {
00275     video_enc->flags |= CODEC_FLAG_LOOP_FILTER;
00276   }
00277 
00278   if (params_.use_part_)
00279   {
00280     video_enc->flags |= CODEC_FLAG_PART;
00281   }
00282   if (params_.use_alt_scan_)
00283   {
00284     video_enc->flags |= CODEC_FLAG_ALT_SCAN;
00285   }
00286   if (params_.use_trell_)
00287   {
00288     video_enc->flags |= CODEC_FLAG_TRELLIS_QUANT;
00289   }
00290   if (params_.use_scan_offset_)
00291   {
00292     video_enc->flags |= CODEC_FLAG_SVCD_SCAN_OFFSET;
00293   }
00294   if (params_.closed_gop_)
00295   {
00296     video_enc->flags |= CODEC_FLAG_CLOSED_GOP;
00297   }
00298   if (params_.use_qpel_)
00299   {
00300     video_enc->flags |= CODEC_FLAG_QPEL;
00301   }
00302   if (params_.use_qprd_)
00303   {
00304     video_enc->flags |= CODEC_FLAG_QP_RD;
00305   }
00306   if (params_.use_cbprd_)
00307   {
00308     video_enc->flags |= CODEC_FLAG_CBP_RD;
00309   }
00310   if (params_.b_frames_)
00311   {
00312     video_enc->max_b_frames = params_.b_frames_;
00313     video_enc->b_frame_strategy = 0;
00314     video_enc->b_quant_factor = 2.0;
00315   }
00316   if (params_.do_interlace_dct_)
00317   {
00318     video_enc->flags |= CODEC_FLAG_INTERLACED_DCT;
00319   }
00320   if (params_.do_interlace_me_)
00321   {
00322     video_enc->flags |= CODEC_FLAG_INTERLACED_ME;
00323   }
00324   video_enc->qmin = params_.video_qmin_;
00325   video_enc->qmax = params_.video_qmax_;
00326   video_enc->lmin = params_.video_lmin_;
00327   video_enc->lmax = params_.video_lmax_;
00328   video_enc->mb_qmin = params_.video_mb_qmin_;
00329   video_enc->mb_qmax = params_.video_mb_qmax_;
00330   video_enc->max_qdiff = params_.video_qdiff_;
00331   video_enc->qblur = params_.video_qblur_;
00332   video_enc->qcompress = params_.video_qcomp_;
00333 
00334   // delete when the stream is closed
00335   os_->video_rc_eq_ = new char[params_.video_rc_eq_.length()+1];
00336   vcl_strcpy(os_->video_rc_eq_, params_.video_rc_eq_.c_str());
00337   video_enc->rc_eq = os_->video_rc_eq_;
00338 
00339   video_enc->debug = params_.debug_;
00340   video_enc->debug_mv = params_.debug_mv_;
00341   video_enc->thread_count = 1;
00342 
00343   video_enc->rc_max_rate = params_.video_rc_max_rate_;
00344   video_enc->rc_min_rate = params_.video_rc_min_rate_;
00345   video_enc->rc_buffer_size = params_.video_rc_buffer_size_;
00346   video_enc->rc_buffer_aggressivity= params_.video_rc_buffer_aggressivity_;
00347   video_enc->rc_initial_cplx= params_.video_rc_initial_cplx_;
00348   video_enc->i_quant_factor = params_.video_i_qfactor_;
00349   video_enc->b_quant_factor = params_.video_b_qfactor_;
00350   video_enc->i_quant_offset = params_.video_i_qoffset_;
00351   video_enc->b_quant_offset = params_.video_b_qoffset_;
00352   video_enc->intra_quant_bias = params_.video_intra_quant_bias_;
00353   video_enc->inter_quant_bias = params_.video_inter_quant_bias_;
00354   video_enc->dct_algo = params_.dct_algo_;
00355   video_enc->idct_algo = params_.idct_algo_;
00356   video_enc->me_threshold= params_.me_threshold_;
00357   video_enc->mb_threshold= params_.mb_threshold_;
00358   video_enc->intra_dc_precision= params_.intra_dc_precision_ - 8;
00359   video_enc->strict_std_compliance = params_.strict_;
00360   video_enc->error_rate = params_.error_rate_;
00361   video_enc->noise_reduction= params_.noise_reduction_;
00362   video_enc->scenechange_threshold= params_.sc_threshold_;
00363   video_enc->me_range = params_.me_range_;
00364   video_enc->coder_type= params_.coder_;
00365   video_enc->context_model= params_.context_;
00366   video_enc->prediction_method= params_.predictor_;
00367 
00368   if (params_.packet_size_)
00369   {
00370     video_enc->rtp_mode= 1;
00371     video_enc->rtp_payload_size= params_.packet_size_;
00372   }
00373 
00374   if (params_.do_psnr_)
00375     video_enc->flags|= CODEC_FLAG_PSNR;
00376 
00377   video_enc->me_method = params_.me_method_;
00378 
00379   /* two pass mode */
00380   if (params_.do_pass_)
00381   {
00382     if (params_.do_pass_ == 1)
00383     {
00384       video_enc->flags |= CODEC_FLAG_PASS1;
00385     }
00386     else
00387     {
00388       video_enc->flags |= CODEC_FLAG_PASS2;
00389     }
00390   }
00391 
00392   os_->fmt_cxt_->timestamp = 0;
00393   os_->fmt_cxt_->title[0] = '\0';
00394   os_->fmt_cxt_->author[0] = '\0';
00395   os_->fmt_cxt_->copyright[0] = '\0';
00396   os_->fmt_cxt_->comment[0] = '\0';
00397 
00398   vcl_strncpy( os_->fmt_cxt_->filename, filename_.c_str(), 1023 );
00399 
00400   if ( url_fopen( &os_->fmt_cxt_->pb, filename_.c_str(), URL_WRONLY) < 0 )
00401   {
00402     vcl_cerr << "ffmpeg: couldn't open " << filename_ << " for writing\n";
00403     close();
00404     return false;
00405   }
00406   os_->file_opened_ = true;
00407 
00408   AVFormatParameters fmt_param;
00409   vcl_memset( &fmt_param, 0, sizeof(fmt_param) );
00410   if ( av_set_parameters( os_->fmt_cxt_, &fmt_param ) < 0 )
00411   {
00412     vcl_cerr << "ffmpeg: invalid encoding parameter\n";
00413     close();
00414     return false;
00415   }
00416 
00417   //dump_format( os_->fmt_cxt_, 1, filename_, 1 );
00418 
00419   if ( avcodec_open( video_enc, codec ) < 0 )
00420   {
00421     vcl_cerr << "ffmpeg: couldn't open codec\n";
00422     close();
00423     return false;
00424   }
00425   os_->codec_opened_ = true;
00426 
00427   if ( av_write_header( os_->fmt_cxt_ ) < 0 )
00428   {
00429     vcl_cerr << "ffmpeg: couldn't write header\n";
00430     close();
00431     return false;
00432   }
00433 
00434   return true;
00435 }
00436 
00437 
00438 //: Close the stream
00439 void
00440 vidl_ffmpeg_ostream::
00441 close()
00442 {
00443   delete os_->video_rc_eq_;
00444   os_->video_rc_eq_ = NULL;
00445 
00446   if ( os_->fmt_cxt_ ) {
00447 
00448     if ( os_->file_opened_ ) {
00449       av_write_trailer( os_->fmt_cxt_ );
00450       url_fclose( os_->fmt_cxt_->pb );
00451       os_->file_opened_ = false;
00452     }
00453 
00454     if ( os_->fmt_cxt_->nb_streams > 0 ) {
00455       if ( os_->codec_opened_ ) {
00456         for ( unsigned i = 0; i < os_->fmt_cxt_->nb_streams; ++i ) {
00457           AVCodecContext* codec = os_->fmt_cxt_->streams[i]->codec;
00458           if ( codec->stats_in ) {
00459             av_freep( codec->stats_in );
00460           }
00461           avcodec_close( codec );
00462         }
00463       }
00464       os_->codec_opened_ = false;
00465       for ( unsigned i = 0; i < os_->fmt_cxt_->nb_streams; ++i ) {
00466         av_free( os_->fmt_cxt_->streams[i] );
00467       }
00468     }
00469 
00470     av_free( os_->fmt_cxt_ );
00471     os_->fmt_cxt_ = 0;
00472   }
00473 }
00474 
00475 
00476 //: Return true if the stream is open for writing
00477 bool
00478 vidl_ffmpeg_ostream::
00479 is_open() const
00480 {
00481   return os_->file_opened_;
00482 }
00483 
00484 
00485 //: Write and image to the stream
00486 // \retval false if the image could not be written
00487 bool
00488 vidl_ffmpeg_ostream::
00489 write_frame(const vidl_frame_sptr& frame)
00490 {
00491   if (!is_open()) {
00492     // resize to the first frame
00493     params_.size(frame->ni(),frame->nj());
00494     open();
00495   }
00496 
00497   AVCodecContext* codec = os_->fmt_cxt_->streams[0]->codec;
00498 
00499   if ( unsigned( codec->width ) != frame->ni() ||
00500        unsigned( codec->height ) != frame->nj() ) {
00501     vcl_cerr << "ffmpeg: Input image has wrong size. Expecting ("
00502              << codec->width << 'x' << codec->height << "), got ("
00503              << frame->ni() << 'x' << frame->nj() << ")\n";
00504     return false;
00505   }
00506 
00507 
00508   PixelFormat fmt = vidl_pixel_format_to_ffmpeg(frame->pixel_format());
00509 
00510   vidl_pixel_format target_fmt = vidl_pixel_format_from_ffmpeg(codec->pix_fmt);
00511   static vidl_frame_sptr temp_frame = new vidl_shared_frame(NULL,frame->ni(),frame->nj(),target_fmt);
00512 
00513   AVFrame out_frame;
00514   avcodec_get_frame_defaults( &out_frame );
00515 
00516   // The frame is in the correct format to encode directly
00517   if ( codec->pix_fmt == fmt )
00518   {
00519     avpicture_fill((AVPicture*)&out_frame, (uint8_t*) frame->data(),
00520                    fmt, frame->ni(), frame->nj());
00521   }
00522   else
00523   {
00524     if (!temp_frame->data()) {
00525       unsigned ni = frame->ni();
00526       unsigned nj = frame->nj();
00527       unsigned out_size = vidl_pixel_format_buffer_size(ni,nj,target_fmt);
00528       temp_frame = new vidl_memory_chunk_frame(ni, nj, target_fmt,
00529                                                new vil_memory_chunk(out_size, VIL_PIXEL_FORMAT_BYTE));
00530     }
00531     // try conversion with FFMPEG functions
00532     if (!vidl_ffmpeg_convert(frame, temp_frame)) {
00533       // try conversion with vidl functions
00534       if (!vidl_convert_frame(*frame, *temp_frame)) {
00535         vcl_cout << "unable to convert " << frame->pixel_format() << " to "<<target_fmt<<vcl_endl;
00536         return false;
00537       }
00538     }
00539     avpicture_fill((AVPicture*)&out_frame, (uint8_t*) temp_frame->data(),
00540                    codec->pix_fmt, frame->ni(), frame->nj());
00541   }
00542 
00543   AVPacket pkt;
00544   av_init_packet( &pkt );
00545   pkt.stream_index = 0;
00546 
00547   out_frame.pts = os_->cur_frame_;
00548 
00549   int ret = avcodec_encode_video( codec, (uint8_t*)os_->bit_buf_->data(), os_->bit_buf_->size(), &out_frame );
00550 
00551   if ( ret ) {
00552     pkt.data = (uint8_t*)os_->bit_buf_->data();
00553     pkt.size = ret;
00554     if ( codec->coded_frame ) {
00555       pkt.pts = codec->coded_frame->pts;
00556     }
00557     if ( codec->coded_frame && codec->coded_frame->key_frame ) {
00558       pkt.flags |= PKT_FLAG_KEY;
00559     }
00560     av_interleaved_write_frame( os_->fmt_cxt_, &pkt );
00561   } else {
00562     return false;
00563   }
00564 
00565   ++os_->cur_frame_;
00566   return true;
00567 }
00568 
00569 #endif // vidl_ffmpeg_ostream_v2_txx_