changeset 2641:41b67e5e5d38

initial import from transcode
author arpi
date Fri, 02 Nov 2001 23:53:44 +0000
parents a6c378d5ed3a
children 0e6e13261d79
files divx4_vbr.c
diffstat 1 files changed, 443 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/divx4_vbr.c	Fri Nov 02 23:53:44 2001 +0000
@@ -0,0 +1,443 @@
+/*
+ *  divx4_vbr.c
+ *
+ *  Copyright (C) Thomas Östreich - June 2001
+ *
+ *  2-pass code OpenDivX port:
+ *  Copyright (C) 2001 Christoph Lampert <gruel@gmx.de> 
+ *
+ *  This file is part of transcode, a linux video stream processing tool
+ *      
+ *  transcode is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2, or (at your option)
+ *  any later version.
+ *   
+ *  transcode is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *   
+ *  You should have received a copy of the GNU General Public License
+ *  along with GNU Make; see the file COPYING.  If not, write to
+ *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
+ *
+ */
+
+
+/**********************************************************
+ *                Two-pass-code from OpenDivX             *
+ *                                                        *
+ *  Large parts of this code were taken from VbrControl() *
+ *  from the OpenDivX project, (C) divxnetworks,          *
+ *  this code is published under DivX Open license, which *
+ *  can be found... somewhere... oh, whatever...          *
+ **********************************************************/
+ 
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dlfcn.h>
+#include <math.h>
+
+#include <stdint.h>
+
+#include "config.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;
+
+int m_iCount;
+int m_iQuant;
+int m_iCrispness;
+short m_bDrop;
+float m_fQuant;
+
+int64_t m_lEncodedBits;
+int64_t m_lExpectedBits;
+
+FILE  *m_pFile;
+
+entry    vFrame;
+entry *m_vFrames;
+long	lFrameStart;
+
+int	iNumFrames;
+int	dummy;
+
+// methods from class VbrControl
+
+	void VbrControl_init_1pass_vbr(int quality, int crispness);
+	int VbrControl_init_2pass_vbr_encoding(const char* filename, int bitrate, double framerate, int crispness, int quality);
+	int VbrControl_init_2pass_vbr_analysis(const char* filename, int quality);
+
+	void VbrControl_update_1pass_vbr();
+	void VbrControl_update_2pass_vbr_encoding(int motion_bits, int texture_bits, int total_bits);
+	void VbrControl_update_2pass_vbr_analysis(int is_key_frame, int motion_bits, int texture_bits, int total_bits, int quant);
+
+	int VbrControl_get_quant();
+	void VbrControl_set_quant(float q);
+	int VbrControl_get_intra();
+	short VbrControl_get_drop();
+	void VbrControl_close();
+
+
+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 %d, 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 TC_EXPORT_ERROR;
+		}
+			
+	   fseek(m_pFile, lFrameStart, SEEK_SET);		// start again
+		
+		for (i=0;i<iNumFrames;i++)
+		{  fscanf(m_pFile, "Frame %d: intra %d, 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);
+}