Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members | Related Pages

vidl2_ffmpeg_ostream.cxx

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

Generated on Thu Jan 10 14:51:31 2008 for contrib/brl/bbas/vidl2 by  doxygen 1.4.4