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