Mercurial > mplayer.hg
view divx4_vbr.c @ 8514:a1ff87c254ff
I have rewritten the gif89a vo in order to solve some problems I had
with it. These are:
1) current code is messy
2) poor comments, if any
3) inaccurate frame dropping and delay code
4) output filename hardcoded
5) output framerate as integer
You may specify the output filename and framerate like so:
-vo gif89a:4.33 4.33 fps output
-vo gif89a:some.gif output to some.gif
-vo gif89a:5.02:new.gif output to new.gif at 5.02 fps
The filename defaults to out.gif, and the framerate defaults to 5 fps.
by Joey Parrish <joey@nicewarrior.org>
author | arpi |
---|---|
date | Sat, 21 Dec 2002 21:07:16 +0000 |
parents | 772d6d27fd66 |
children | 11b249ef87b0 |
line wrap: on
line source
/* * divx4_vbr.c * * This file is subject to the terms and conditions of the GNU General Public * License. See the file COPYING in the main directory of the Linux * distribution for more details. * * 2-pass code OpenDivX port: * Copyright (C) 2001 Christoph Lampert <gruel@gmx.de> * * Large parts of this code were taken from VbrControl() from the OpenDivX * project, (C) divxnetworks, written by Eugene Kuznetsov <ekuznetsov@divxnetworks.com> * with the permission of Darrius "Junto" Thompson, Director DivX */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <math.h> #include <inttypes.h> #include "divx4_vbr.h" //#include "transcode.h" #define FALSE 0 #define TRUE 1 /* Absolute maximum and minimum quantizers used in VBR modes */ static const int min_quantizer=1; static const int max_quantizer=31; /* Limits on frame-level deviation of quantizer ( higher values correspond to frames with more changes and vice versa ) */ static const float min_quant_delta=-10.f; static const float max_quant_delta=5.f; /* Limits on stream-level deviation of quantizer ( used to make overall bitrate of stream close to requested value ) */ static const float min_rc_quant_delta=.6f; static const float max_rc_quant_delta=1.5f; /* Crispness parameter controls threshold for decision whether to skip the frame or to code it. */ //static const float max_crispness=100.f; /* Maximum allowed number of skipped frames in a line. */ //static const int max_drops_line=0; // CHL We don't drop frames at the moment! typedef struct entry_s /* max 28 bytes/frame or 5 Mb for 2-hour movie */ { int quant; int text_bits; int motion_bits; int total_bits; float mult; short is_key_frame; short drop; } entry; static int m_iCount; static int m_iQuant; /*static int m_iCrispness;*/ static short m_bDrop; static float m_fQuant; static int64_t m_lEncodedBits; static int64_t m_lExpectedBits; static FILE *m_pFile; static entry vFrame; static entry *m_vFrames; static long lFrameStart; static int iNumFrames; static int dummy; void VbrControl_init_1pass_vbr(int quality, int crispness) { m_fQuant=min_quantizer+((max_quantizer-min_quantizer)/6.)*(6-quality); m_iCount=0; m_bDrop=FALSE; VbrControl_update_1pass_vbr(); } int VbrControl_init_2pass_vbr_analysis(const char *filename, int quality) { m_pFile=fopen(filename, "wb"); if(m_pFile==0) return -1; m_iCount=0; m_bDrop=FALSE; fprintf(m_pFile, "##version 1\n"); fprintf(m_pFile, "quality %d\n", quality); return 0; } int VbrControl_init_2pass_vbr_encoding(const char *filename, int bitrate, double framerate, int crispness, int quality) { int i; int64_t text_bits=0; int64_t total_bits=0; int64_t complexity=0; int64_t new_complexity=0; int64_t motion_bits=0; int64_t denominator=0; float qual_multiplier=1.; char head[20]; int64_t desired_bits; int64_t non_text_bits; float average_complexity; m_pFile=fopen(filename, "rb"); if(m_pFile==0) return -1; m_bDrop=FALSE; m_iCount=0; fread(head, 10, 1, m_pFile); if(!strncmp("##version ", head, 10)) { int version; int iOldQual; float old_qual, new_qual; fscanf(m_pFile, "%d\n", &version); fscanf(m_pFile, "quality %d\n", &iOldQual); switch(iOldQual) { case 5: old_qual=1.f; break; case 4: old_qual=1.1f; break; case 3: old_qual=1.25f; break; case 2: old_qual=1.4f; break; case 1: old_qual=2.f; break; } switch(quality) { case 5: new_qual=1.f; break; case 4: new_qual=1.1f; break; case 3: new_qual=1.25f; break; case 2: new_qual=1.4f; break; case 1: new_qual=2.f; break; } qual_multiplier=new_qual/old_qual; } else fseek(m_pFile, 0, SEEK_SET); lFrameStart=ftell(m_pFile); // save current position /* removed C++ dependencies, now read file twice :-( */ while(!feof(m_pFile)) { fscanf(m_pFile, "Frame %d: intra %hd, quant %d, texture %d, motion %d, total %d\n", &iNumFrames, &(vFrame.is_key_frame), &(vFrame.quant), &(vFrame.text_bits), &(vFrame.motion_bits), &(vFrame.total_bits)); vFrame.total_bits+=vFrame.text_bits*(qual_multiplier-1); vFrame.text_bits*=qual_multiplier; text_bits +=(int64_t)vFrame.text_bits; motion_bits += (int64_t)vFrame.motion_bits; total_bits +=(int64_t)vFrame.total_bits; complexity +=(int64_t)vFrame.text_bits*vFrame.quant; // printf("Frames %d, texture %d, motion %d, quant %d total %d ", // iNumFrames, vFrame.text_bits, vFrame.motion_bits, vFrame.quant, vFrame.total_bits); // printf("texture %d, total %d, complexity %lld \n",vFrame.text_bits,vFrame.total_bits, complexity); } iNumFrames++; average_complexity=complexity/iNumFrames; // if (verbose & TC_DEBUG) { // fprintf(stderr, "(%s) frames %d, texture %lld, motion %lld, total %lld, complexity %lld\n", __FILE__, iNumFrames, text_bits, motion_bits, total_bits, complexity); // } m_vFrames = (entry*)malloc(iNumFrames*sizeof(entry)); if (!m_vFrames) { printf("out of memory"); return -2; //TC_EXPORT_ERROR; } fseek(m_pFile, lFrameStart, SEEK_SET); // start again for (i=0;i<iNumFrames;i++) { fscanf(m_pFile, "Frame %d: intra %hd, quant %d, texture %d, motion %d, total %d\n", &dummy, &(m_vFrames[i].is_key_frame), &(m_vFrames[i].quant), &(m_vFrames[i].text_bits), &(m_vFrames[i].motion_bits), &(m_vFrames[i].total_bits)); m_vFrames[i].total_bits += m_vFrames[i].text_bits*(qual_multiplier-1); m_vFrames[i].text_bits *= qual_multiplier; } if (m_pFile) { fclose(m_pFile); m_pFile=NULL; } desired_bits=(int64_t)bitrate*(int64_t)iNumFrames/framerate; non_text_bits=total_bits-text_bits; if(desired_bits<=non_text_bits) { /* char s[200];*/ printf("Specified bitrate is too low for this clip.\n" "Minimum possible bitrate for the clip is %.0f kbps. Overriding\n" "user-specified value.\n", (float)(non_text_bits*framerate/(int64_t)iNumFrames)); desired_bits=non_text_bits*3/2; /* m_fQuant=max_quantizer; for(int i=0; i<iNumFrames; i++) { m_vFrames[i].drop=0; m_vFrames[i].mult=1; } VbrControl_set_quant(m_fQuant); return 0; */ } desired_bits -= non_text_bits; /** BRIEF EXPLANATION OF WHAT'S GOING ON HERE. We assume that text_bits=complexity / quantizer total_bits-text_bits = const(complexity) where 'complexity' is a characteristic of the frame and does not depend much on quantizer dynamics. Using this equation, we calculate 'average' quantizer to be used for encoding ( 1st order effect ). Having constant quantizer for the entire stream is not very convenient - reconstruction errors are more noticeable in low-motion scenes. To compensate this effect, we multiply quantizer for each frame by (complexity/average_complexity)^k, ( k - parameter of adjustment ). k=0 means 'no compensation' and k=1 is 'constant bitrate mode'. We choose something in between, like 0.5 ( 2nd order effect ). **/ average_complexity=complexity/iNumFrames; for(i=0; i<iNumFrames; i++) { float mult; if(m_vFrames[i].is_key_frame) { if((i+1<iNumFrames) && (m_vFrames[i+1].is_key_frame)) mult=1.25; else mult=.75; } else { mult=m_vFrames[i].text_bits*m_vFrames[i].quant; mult=(float)sqrt(mult/average_complexity); // if(i && m_vFrames[i-1].is_key_frame) // mult *= 0.75; if(mult<0.5) mult=0.5; if(mult>1.5) mult=1.5; } m_vFrames[i].mult=mult; m_vFrames[i].drop=FALSE; new_complexity+=m_vFrames[i].text_bits*m_vFrames[i].quant; denominator+=desired_bits*m_vFrames[i].mult/iNumFrames; } m_fQuant=((double)new_complexity)/(double)denominator; if(m_fQuant<min_quantizer) m_fQuant=min_quantizer; if(m_fQuant>max_quantizer) m_fQuant=max_quantizer; m_pFile=fopen("analyse.log", "wb"); if(m_pFile) { fprintf(m_pFile, "Total frames: %d Avg quantizer: %f\n", iNumFrames, m_fQuant); fprintf(m_pFile, "Expecting %12lld bits\n", desired_bits+non_text_bits); fflush(m_pFile); } VbrControl_set_quant(m_fQuant*m_vFrames[0].mult); m_lEncodedBits=m_lExpectedBits=0; return 0; } int VbrControl_get_intra() { return m_vFrames[m_iCount].is_key_frame; } short VbrControl_get_drop() { return m_bDrop; } int VbrControl_get_quant() { return m_iQuant; } void VbrControl_set_quant(float quant) { m_iQuant=quant; if((rand() % 10)<((quant-m_iQuant) * 10)) m_iQuant++; if(m_iQuant<min_quantizer) m_iQuant=min_quantizer; if(m_iQuant>max_quantizer) m_iQuant=max_quantizer; } void VbrControl_update_1pass_vbr() { VbrControl_set_quant(m_fQuant); m_iCount++; } void VbrControl_update_2pass_vbr_analysis(int is_key_frame, int motion_bits, int texture_bits, int total_bits, int quant) { if(!m_pFile) return; fprintf(m_pFile, "Frame %d: intra %d, quant %d, texture %d, motion %d, total %d\n", m_iCount, is_key_frame, quant, texture_bits, motion_bits, total_bits); m_iCount++; } void VbrControl_update_2pass_vbr_encoding(int motion_bits, int texture_bits, int total_bits) { double q; double dq; if(m_iCount>=iNumFrames) return; m_lExpectedBits+=(m_vFrames[m_iCount].total_bits-m_vFrames[m_iCount].text_bits) + m_vFrames[m_iCount].text_bits*m_vFrames[m_iCount].quant/m_fQuant; m_lEncodedBits+=(int64_t)total_bits; if(m_pFile) fprintf(m_pFile, "Frame %d: PRESENT, complexity %d, quant multiplier %f, texture %d, total %d ", m_iCount, m_vFrames[m_iCount].text_bits*m_vFrames[m_iCount].quant, m_vFrames[m_iCount].mult, texture_bits, total_bits); m_iCount++; q = m_fQuant * m_vFrames[m_iCount].mult; if(q<m_fQuant+min_quant_delta) q=m_fQuant+min_quant_delta; if(q>m_fQuant+max_quant_delta) q=m_fQuant+max_quant_delta; dq = (double)m_lEncodedBits/(double)m_lExpectedBits; dq*=dq; if(dq<min_rc_quant_delta) dq=min_rc_quant_delta; if(dq>max_rc_quant_delta) dq=max_rc_quant_delta; if(m_iCount<20) // no framerate corrections in first frames dq=1; if(m_pFile) fprintf(m_pFile, "Progress: expected %12lld, achieved %12lld, dq %f", m_lExpectedBits, m_lEncodedBits, dq); q *= dq; VbrControl_set_quant(q); if(m_pFile) fprintf(m_pFile, ", new quant %d\n", m_iQuant); } void VbrControl_close() { if(m_pFile) { fclose(m_pFile); m_pFile=NULL; } free(m_vFrames); }