seqencoder.cc
来自「Motion JPEG编解码器源代码」· CC 代码 · 共 620 行 · 第 1/2 页
CC
620 行
if( encparams.fieldpic ) { picture->Adjust2ndField(); mjpeg_debug("Field %s (%d)", (picture->pict_struct == TOP_FIELD) ? "top" : "bot", picture->pict_struct ); if( encparams.encoding_parallelism > 0 ) { despatcher.Despatch( picture, &MacroBlock::Encode ); despatcher.WaitForCompletion(); } else { picture->EncodeMacroBlocks(); } picture->QuantiseAndEncode(ratecontroller); picture->Reconstruct(); } mjpeg_info("Frame %5d %5d %c q=%3.2f sum act=%8.5f %s", picture->decode, picture->input, pict_type_char[picture->pict_type], picture->AQ, picture->sum_avg_act, picture->pad ? "PAD" : " " ); }/********************* * * Init - Setup worker threads an set internal encoding state for * beginning of stream = beginning of first sequence. Do no actual * encoding or I/O.... * N.b. It is *critical* that the reference and b * picture buffers are at least two larger than the number of encoding * worker threads. The despatcher thread *must* have to halt to wait * for free worker threads *before* it re-uses the record for the * Picture record for the oldest frame that could still be needed by * active worker threads. * * Buffers sizes are given by encparams.max_active_ref_frames and * encparams.max_active_b_frames * ********************/ void SeqEncoder::Init(){ // // Setup the parallel job despatcher... // despatcher.Init( encparams.mb_width, encparams.mb_height2, encparams.encoding_parallelism ); old_ref_picture = 0; new_ref_picture = GetPicture(); ReleasePicture( new_ref_picture ); ratecontroller.InitSeq(false); ss.Init( );}/********************* * * EncodeStream - Where it all happens. This is the top-level loop * that despatches all the encoding work. Encoding is always performed * two-pass. * Pass 1: a first encoding that determines the GOP * structure, but may only do rough-and-ready bit allocatino that is * visually sub-optimal and/or may violate the specified maximum bit-rate. * * Pass 2: Pictures from Pass1 are, if necessary, re-quantised and the results * coded to accurate achieve good bit-allocation and satisfy bit-rate limits. * * In 'single-pass mode' pass 2 re-encodes only if it 'has to'. * In 'look-ahead mode' pass 2 always re-encodes. * In 'Pass 1 of two-pass mode' Pass-2 simply dumps frame complexity and motion * estimation data from Pass-1. * In 'Pass 2 of two-pass mode' Pass-1 rebuilds frames based on ME and complexity * data from a 'Pass 1 of two-pass mode' run and Pass-2 does some final optimisation. * * N.b. almost all the interesting stuff occurs in the Pass1 encoding. If selected: * - A GOP may be low-passed and re-encoded if it looks like excessive quantisation * is needed. * - GOP length is determined (P frames with mostly intra-coded blocks are turned * into I-frames. * * NOTE: Eventually there will be support for Pass2 to occur in seperate threads... * ********************/ void SeqEncoder::EncodeStream(){ // // Repeated calls to TransformFrame build up the queue of // Encoded with quantisation controlled by the // pass1 rate controller. do { // If we have Pass2 work to do if( pass2queue.size() != 0 ) { Pass2EncodeFrame(); } else { Pass1EncodeFrame(); ss.Next( BitsAfterMux() ); } } while( pass2queue.size() != 0 || ss.FrameInStream() < reader.NumberOfFrames() ); assert( pass2queue.size() == 0 ); assert( pass1coded.size() == 0 ); StreamEnd();}Picture *SeqEncoder::GetPicture(){ if( free_pictures.size() == 0 ) return new Picture(encparams, writer , quantizer); else { Picture *free = free_pictures.back(); free_pictures.pop_back(); return free; }}void SeqEncoder::ReleasePicture( Picture *picture ){ free_pictures.push_back( picture );}/********************* * * Pass1EncodeFrame - Do a unit of work in building up a queue of * Pass-1 encoded frame's. * * A Picture is encoded based on a normal (maximum) length GOP with quantisation * determined by Pass1 rate controller. * * If the Picture is a P-frame and is almost entirely intra-coded the picture is * converted to an I-frame and the current GOP ended early. * * Once a GOP is succesfully completed its Picture's are transferred to the * pass2queue for Pass-2 encoding. * *********************/ void SeqEncoder::Pass1EncodeFrame(){ old_picture = cur_picture; if ( ss.b_idx == 0 ) // I or P Frame (First frame in B-group) { old_ref_picture = new_ref_picture; new_ref_picture = cur_picture = GetPicture(); cur_picture->fwd_org = old_ref_picture->org_img; cur_picture->fwd_rec = old_ref_picture->rec_img; cur_picture->fwd_ref_frame = old_ref_picture; cur_picture->bwd_ref_frame = 0; } else { cur_picture = GetPicture(); cur_picture->fwd_org = old_ref_picture->org_img; cur_picture->fwd_rec = old_ref_picture->rec_img; cur_picture->bwd_org = new_ref_picture->org_img; cur_picture->bwd_rec = new_ref_picture->rec_img; cur_picture->fwd_ref_frame = old_ref_picture; cur_picture->bwd_ref_frame = new_ref_picture; } cur_picture->SetEncodingParams(ss, reader.NumberOfFrames() ); reader.ReadFrame( cur_picture->input, cur_picture->org_img ); EncodePicture( cur_picture ); if( cur_picture->end_seq ) mjpeg_info( "Sequence end inserted");#ifdef DEBUG writeframe(cur_picture->temp_ref+ss.gop_start_frame,cur_picture->rec_img);#endif // Hard-wired simple 1-pass encoder!!! //cur_picture->Commit(); pass1coded.push_back( cur_picture ); // Figure out how many pictures can be queued on to pass 2 encoding int to_queue = 0; int i; if( cur_picture->end_seq ) { // If end of sequence we flush everything as next GOP won't refer to this frame to_queue = pass1coded.size(); } else if( ss.b_idx == 0 ) // I or P Frame (First frame in B-group) { // Decide if a P frame really should have been an I-frame, and re-encoded // as such if necessary if( cur_picture->IntraCodedBlocks() > 0.8 && ss.g_idx >= encparams.N_min ) { mjpeg_info( "DEVEL: GOP split point found here... %.0f%% intra coded", cur_picture->IntraCodedBlocks() * 100.0 ); //ss.ForceIFrame(); //cur_picture->SetEncodingParams(ss, reader.NumberOfFrames()); //ReEncodePicture( cur_picture ); } // We have a new fwd reference picture: anything decoded before // will no longer be referenced and can be passed on. for( i = 0; i < pass1coded.size(); ++i ) { if( pass1coded[i] == old_ref_picture) break; } to_queue = i == pass1coded.size() ? 0 : i; } for( i = 0; i < to_queue; ++i ) { pass2queue.push_back( pass1coded.front() ); pass1coded.pop_front(); } }/*********************** BitsAfterMux - Estimate the size of the multiplexed stream based* on video stream size and estimate overheads for other* components**********************/uint64_t SeqEncoder::BitsAfterMux() const{ double frame_periods; uint64_t bits_after_mux; if( encparams.pulldown_32 ) frame_periods = (double)ss.FrameInStream()*(5.0/4.0); else frame_periods = (double)ss.FrameInStream(); // // For VBR we estimate total bits based on actual stream size and // an estimate for the other streams based on time. // For CBR we do *both* based on time to account for padding during // muxing. if( encparams.quant_floor > 0.0 ) // VBR bits_after_mux = writer.BitCount() + (uint64_t)((frame_periods / encparams.frame_rate) * encparams.nonvid_bit_rate); else // CBR bits_after_mux = (uint64_t)((frame_periods / encparams.frame_rate) * (encparams.nonvid_bit_rate + encparams.bit_rate)); return bits_after_mux;} /********************* * * Pass2EncodeFrame - Take a frame from pass2queue if necessary * requantize and re-encode then commit the result. * *********************/ void SeqEncoder::Pass2EncodeFrame(){ Picture *cur_pass2_picture = pass2queue.front(); pass2queue.pop_front(); // Simple single-pass encoding (look-ahead coming soon) cur_pass2_picture->Commit(); ReleasePicture( cur_pass2_picture );}void SeqEncoder::StreamEnd(){ uint64_t bits_after_mux = BitsAfterMux(); mjpeg_info( "Guesstimated final muxed size = %lld\n", bits_after_mux/8 ); int i; for( i = 0; i < free_pictures.size(); ++i ) { delete free_pictures[i]; }}/* * Local variables: * c-file-style: "stroustrup" * tab-width: 4 * indent-tabs-mode: nil * End: */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?