Raw File
bitrate-6d.c
/** \file
 * Bitrate
 */

#include "dryos.h"
#include "bmp.h"
#include "tasks.h"
#include "debug.h"
#include "menu.h"
#include "property.h"
#include "config.h"
#include "gui.h"
#include "lens.h"
#include "cache_hacks.h"

#if 0 /* not minimally invasive; patches Canon firmware even if the settings are disabled */

static CONFIG_INT("h264.bitrate", bitrate, 0);
static CONFIG_INT("h264.initqp", initqp, 0);
static CONFIG_INT("h264.h2config", h2config, 0);
static CONFIG_INT("h264.autoload", autoload, 0);
static CONFIG_INT("h264.flush", bitrate_flushing_rate, 3);
static CONFIG_INT("h264.gop", bitrate_gop_size, 0);
static CONFIG_INT( "time.indicator", time_indicator, 1); // 0 = off, 1 = current clip length, 2 = time remaining until filling the card, 3 = time remaining until 4GB
static CONFIG_INT( "hibr.wav.record", cfg_hibr_wav_record, 0);
//~ PROP_INT(PROP_REBOOT_MAYBE, rebootnow);
PROP_INT(PROP_MOVIE_SOUND_RECORD, sound);
#ifndef CONFIG_5D3
int video_mode[6];
PROP_HANDLER(PROP_VIDEO_MODE)
{
	memcpy(video_mode, buf, 24);
}
#endif

extern int fps_override;
static int ivaparam, patched_errors=0;

static int movie_elapsed_time_01s = 0;   // seconds since starting the current movie * 10
static int measured_bitrate = 0; // mbps
static int movie_bytes_written_32k = 0;

#ifdef FEATURE_NITRATE_WAV_RECORD
int hibr_should_record_wav() { return cfg_hibr_wav_record; }
#else
int hibr_should_record_wav() { return 0; }
#endif


/* Defines */
#ifdef CONFIG_6D
#define l_ivastring 0xAA4F4
#define l_initQP 0x78834
#define l_targetR 0x78830
#define l_EncoMode 0x7882c

#endif
#ifdef CONFIG_5D3
#define l_ivastring 0x4DA68
#define l_initQP 0x277F0
#define l_targetR 0x277EC
#define l_EncoMode 0X277E8

#endif

#define override (autoload == 2) 
#define config_loaded (ivaparam == 1)
#define config_disabled (ivaparam == 2)

static void patch_errors()
{ 
	#ifdef CONFIG_6D
	//FF1F1230 ./MovieRecorder/EncDrvWrapper.c:1926, task MovieRecorder
	cache_fake(0xFF1F1244 , 0xE1A00000, TYPE_ICACHE);
	
	// Rscmgr.c Assert - False
	//~ cache_fake(0xFF0F5D80 , 0xE1A00000, TYPE_ICACHE);
	//~ cache_fake(0xFF0F5D28 , 0xE1A00000, TYPE_ICACHE);

   //FF3BEFE4 RECORDING: ./Fcreate/FcsMaker.c:2314, task MovieRecorder
   cache_fake(0xFF3BEFF8 , 0xE1A00000, TYPE_ICACHE);

	//FF3B060C:   eb3144bb        bl      assert__ram
	// Player: at Graphics.c:3777, task CtrlSrv
	cache_fake(0xFF3B0620 , 0xE1A00000, TYPE_ICACHE);
  
	//at Stub.c:29, task CtrlSrv
	cache_fake(0xFF0C2894 , 0xE1A00000, TYPE_ICACHE);

	//  Uknown Gop
	//~ cache_fake(0xFF3BEFC8 , 0xE1A00000, TYPE_ICACHE);

	//  Gop Mis Match CMP r1, r1
	cache_fake(0xFF0E5208 , 0xE1510001, TYPE_ICACHE);

   //this->dwTimecodeSize1 May Break timecode Freezes
   //~ cache_fake(0xFF1E2748 , 0xE1510001, TYPE_ICACHE);
	#endif
	
	#ifdef CONFIG_5D3
	cache_fake(0xFF1ED1E0 , 0xE1A00000, TYPE_ICACHE);
	cache_fake(0xFF386048 , 0xE1A00000, TYPE_ICACHE);
	cache_fake(0xFF377134 , 0xE1A00000, TYPE_ICACHE); //Player Not sure
	cache_fake(0xFF0C2940 , 0xE1A00000, TYPE_ICACHE);
	//~ cache_fake(0xFF3BEFB4 , 0xE1A00000, TYPE_ICACHE);
	cache_fake(0xFF0E3BDC , 0xE1510001, TYPE_ICACHE);
	//~ cache_fake(0xFF1E2734 , 0xE1510001, TYPE_ICACHE);
	#endif

   patched_errors = 1;

}

static void load_h264_ini()
{
    gui_stop_menu();
	gui_uilock(UILOCK_EVERYTHING);
	if (h2config == 1)
		{   call("IVAParamMode", "B:/ML/cbr.ini");
		    //~ call("IVAParamMode", "ML/cbr.ini");
			NotifyBox(2000, "%s", l_ivastring); //0xaa4f4 78838
		}
	else if (h2config == 2)
		 {   call("IVAParamMode", "B:/ML/vbr.ini");
		   //~ call("IVAParamMode", "ML/vbr.ini");
			NotifyBox(2000, "%s", l_ivastring); //0xaa4f4 78838
		}
	else if (h2config == 3)
		{   call("IVAParamMode", "B:/ML/rc.ini");
		    //~ call("IVAParamMode", "ML/rc.ini");
			NotifyBox(2000, "%s", l_ivastring); //0xaa4f4 78838
		}
	else 
	{	
    	//~ call("IVAParamMode", "ML/H264.ini");
    	call("IVAParamMode", "B:/ML/H264.ini");
		NotifyBox(2000, "%s", l_ivastring); //0xaa4f4 78838
	}
	msleep(1000);
	gui_uilock(UILOCK_NONE);
}	




CONFIG_INT("buffer.warning.level", buffer_warning_level, 70);
static void
buffer_warning_level_display( void * priv, int x, int y, int selected )
{
    bmp_printf(
        selected ? MENU_FONT_SEL : MENU_FONT,
        x, y,
        "BuffWarnLevel : %d%%",
        buffer_warning_level
    );
    menu_draw_icon(x, y, MNI_PERCENT, buffer_warning_level);
}

static void buffer_warning_level_toggle(void* priv, int step)
{
    buffer_warning_level += step;
    if (buffer_warning_level > 100) buffer_warning_level = 30;
    if (buffer_warning_level < 30) buffer_warning_level = 100;
}

int warning = 0;
int is_mvr_buffer_almost_full() 
{
    if (NOT_RECORDING) return 0;
    if (RECORDING_H264_STARTING) return 1;

    int ans = MVR_BUFFER_USAGE > (unsigned int)buffer_warning_level;
    if (ans) warning = 1;
    return warning;
}

void show_mvr_buffer_status()
{
    int fnt = warning ? FONT(FONT_SMALL, COLOR_WHITE, COLOR_RED) : FONT(FONT_SMALL, COLOR_WHITE, COLOR_GREEN2);
    if (warning) warning--;
    if (RECORDING_H264 && get_global_draw() && !gui_menu_shown() && !raw_lv_is_enabled()) bmp_printf(fnt, 680, 55, " %3d%%", MVR_BUFFER_USAGE);
}
int8_t* ivaparamstatus = (int8_t*)(l_EncoMode);
uint8_t oldh2config;
void bitrate_set()
{
	//~ #ifdef FEATURE_NITRATE_WAV_RECORD
	//~ if (hibr_should_record_wav() && input_vol != 0)
		//~ {   static int mic;
		
			//~ sound_level[0] = 00 | input_vol;
			//~ sound_level[2] = input_vol;
			//~ prop_request_change(PROP_MOVIE_REC_VOLUME, &sound_level, 4);
			//~ SoundDevActiveIn(0); // Apply Maybe + meters on.
						
			/* Only Works On Initialize */
			//~ uint8_t* ivol = (uint8_t*) MEM(0x75340); 
			//~ *(ivol + 64) = 0;
			//~ *(ivol + 65) = input_vol; //L
			//~ *(ivol + 66) = input_vol; //R
			//~ SoundDevActiveIn(0); // Apply Maybe + meters on.
			//~ if (!powered_input || mic != mic_inserted) 
			//~ {   
				
				//~ SetAudioVolumeIn(0, input_vol , input_vol);
				//~ SoundDevActiveIn(mic_inserted ? 1 : 0); 
				//~ powered_input = 1; 
			//~ }
			//~ mic = mic_inserted;
		//~ }
	//~ #endif
    if (!lv) return;
    if (!is_movie_mode()) return;
    if (raw_lv_is_enabled())return; 
    //~ if (gui_menu_shown()) return;
    if (RECORDING) return;
	if (!config_disabled) ivaparam= *ivaparamstatus;	
	if (patched_errors == 0) patch_errors();
	//~ if (flush_errors == 0) patch_flush_errors(); // Otherwise Err70 on Play
	if (config_disabled) MEM(l_EncoMode) = 0;
	else {
		//~ if (!gui_menu_shown()) // Don't load config during menu
			//~ {
				if (!config_loaded && h2config !=0 && autoload!=0) load_h264_ini();
				if (autoload!=0 && (oldh2config!=h2config) && h2config !=0)	load_h264_ini();	
			//~ }
		  }

 	   
       if (!config_loaded || override || config_disabled) 
			{   /* TODO
				if( !(autoload==2) && bitrate!=0 && MEM(0X78830)==0 )
					{	NotifyBox(2000,"Eko Mode: Must set both parameters!");
						return;
					}*/
				MEM(l_initQP) = initqp;
			    MEM(l_targetR) = bitrate * 10000000;
			}
	   else { 
			if (MEM(l_initQP) != 0)	
				MEM(l_initQP) = 0;
			if (MEM(l_targetR) != 0)
				MEM(l_targetR) = 0;
			 }
		 oldh2config = h2config;
	  
	/* Flush Rate + GOP size from G3gg0 */
	if (bitrate_flushing_rate == 3)
	{
    cache_fake(CACHE_HACK_FLUSH_RATE_SLAVE, MEM(CACHE_HACK_FLUSH_RATE_SLAVE), TYPE_ICACHE);
	}	
	else 
	{
	cache_fake(CACHE_HACK_FLUSH_RATE_SLAVE, 0xE3A00000 | (bitrate_flushing_rate & 0xFF), TYPE_ICACHE);
	// Until Audio Patched
	if (sound != 1)
	 {	static int mode  = 1;
		prop_request_change(PROP_MOVIE_SOUND_RECORD, &mode, 4);
		NotifyBox(1000,"Canon sound disabled");
	 }
	}
	if (!bitrate_gop_size)
	{    
	#ifdef CONFIG_5D3	
	cache_fake(CACHE_HACK_GOP_SIZE_SLAVE, MEM(CACHE_HACK_GOP_SIZE_SLAVE), TYPE_ICACHE);
	#else
	if (video_mode[5] != 1)
		{	if (video_mode[2] > 30)
			video_mode[3] = 15; // 60FPS
			else
			video_mode[3] = video_mode[2]/2; // FPS/2
			prop_request_change(PROP_VIDEO_MODE, video_mode, 24);
		}
	#endif
	}
	else 
	{
		//~ NotifyBox(2000, "1: %x 2:%x 3:%x\n4:%x 5:%x, 6:%x", video_mode[0],video_mode[1],video_mode[2],video_mode[3],video_mode[4],video_mode[5]);
		/*    30I  30IPB   24I 24IPB 60I 60IPB 30IPB
		0 -    0	0		0	0  	  0		0	0	
		1 -    0    0		0	0	  1		1	2   Mode/Size
		2 -    1e   1e		18	18	  3C	3C	1e	FPS
		3 -    1	f		1	c	  1		f	f	GOP
		4 -    0	0		0	0	  0		0	0
		5 -    1	3		1	3	  1		3	3	Ref Frames (M= in Header)
		*/
	#ifdef CONFIG_5D3
    cache_fake(CACHE_HACK_GOP_SIZE_SLAVE, 0xE3A00000 | (bitrate_gop_size & 0xFF), TYPE_ICACHE);
    //~ cache_fake(0xFF226728, 0xE3A00000 | (bitrate_gop_size & 0xFF), TYPE_ICACHE);
    //~ cache_fake(CACHE_HACK_GOP_SIZE_SLAVE, 0xE3A01000 | (bitrate_gop_size & 0xFF), TYPE_ICACHE);
    //~ cache_fake(0x79968, 0x00000000 | (bitrate_gop_size & 0xFF), TYPE_DCACHE);
    //cache_fake(0x79968, 0x00000000, TYPE_DCACHE);
	 #else
	 if (video_mode[5] != 1) // Doesn't Matter for ALL-I
	  {	video_mode[3] = bitrate_gop_size; //Gop Length
		prop_request_change(PROP_VIDEO_MODE, video_mode, 24);
		}
	#endif
    }		

}

void measure_bitrate() // called from every second task
{
    static uint32_t prev_bytes_written = 0;
    uint32_t bytes_written = MVR_BYTES_WRITTEN;
    int bytes_delta = (((int)(bytes_written >> 1)) - ((int)(prev_bytes_written >> 1))) << 1;
    if (bytes_delta < 0)
    {
        // We're either just starting a recording or we're wrapping over 4GB.
        // either way, don't try to calculate the bitrate this time around.
        prev_bytes_written = 0;
        movie_bytes_written_32k = 0;
        measured_bitrate = 0;
        return;
    }
    prev_bytes_written = bytes_written;
    movie_bytes_written_32k = bytes_written >> 15;
    measured_bitrate = (bytes_delta / 1024) * 8 / 1024;
}

void bitrate_mvr_log(char* mvr_logfile_buffer)
{
    return;
}

static int movie_start_timestamp = 0;
PROP_HANDLER(PROP_MVR_REC_START)
{
    if (buf[0] == 1)
        movie_start_timestamp = get_seconds_clock();
}

void time_indicator_show()
{
    if (!get_global_draw()) return;

    if (!RECORDING_H264)
    {
        //~ free_space_show();
        return;
    }
    
    // time until filling the card
    // in "movie_elapsed_time_01s" seconds, the camera saved "movie_bytes_written_32k"x32kbytes, and there are left "free_space_32k"x32kbytes
    int time_cardfill = movie_elapsed_time_01s * get_free_space_32k(get_shooting_card())  / movie_bytes_written_32k / 10;
    
    // time until 4 GB
    int time_4gb = movie_elapsed_time_01s * (4 * 1024 * 1024 / 32 - movie_bytes_written_32k) / movie_bytes_written_32k / 10;

    //~ bmp_printf(FONT_MED, 0, 300, "%d %d %d %d ", movie_elapsed_time_01s, movie_elapsed_ticks, rec_time_card, rec_time_4gb);

    // what to display
    int dispvalue = time_indicator == 1 ? movie_elapsed_time_01s / 10:
                    time_indicator == 2 ? time_cardfill :
                    time_indicator == 3 ? MIN(time_4gb, time_cardfill)
                    : 0;
    
    if (time_indicator)
    { 
	    bmp_printf(
            FONT(FONT_SMALL, COLOR_WHITE, COLOR_BLACK),
            680 - font_small.width,
            55 + font_small.height,
            "%3d:%02d",
            dispvalue / 60,
            dispvalue % 60
        );

        bmp_printf( FONT_SMALL,
            680 - font_small.width * 5,
            55,
            "A%3d ",
            movie_bytes_written_32k * 32 * 80 / 1024 / movie_elapsed_time_01s);

        bmp_printf(FONT_SMALL,
            680 - font_small.width * 5,
            55 + font_small.height,
            "B%3d ",
            measured_bitrate
        );
    }
}
#ifdef FEATURE_NITRATE_WAV_RECORD
static MENU_SELECT_FUNC(hibr_wav_record_select){
    menu_numeric_toggle(priv, 1, 0, 1);
    if (RECORDING) return;
    int *onoff = (int *)priv;
    if(*onoff == 1){
        if (sound_recording_mode != 1){
            int mode  = 1; //disabled
            prop_request_change(PROP_MOVIE_SOUND_RECORD, &mode, 4);
            NotifyBox(2000,"Canon sound disabled");
            //~ audio_configure(1);
        }
    }
}
/*
static void in_vol_toggle(void * priv, int delta)
{   if (!hibr_should_record_wav()) return; //Yes it will work but cannon may overwrite.
	int *input_volume = (int *)priv;
	*input_volume = MOD(*input_volume + delta, 64);
	SetAudioVolumeIn(0, *input_volume , *input_volume);
}
MENU_UPDATE_FUNC(input_vol_up)
{
	if (input_vol == 0 || !hibr_should_record_wav())	
	{
		MENU_SET_ENABLED(0);
		MENU_SET_VALUE("OFF");
		if (sound == 0 && !recording) { SoundDevShutDownIn(); powered_input = 0; }
	}
	else if(hibr_should_record_wav())
		{
			MENU_SET_ENABLED(1);
			MENU_SET_VALUE("%d dB", input_vol);
		}
}
*/
#endif

MENU_UPDATE_FUNC(bit_rated)
{
if ( (!config_loaded || config_disabled || override) && CURRENT_VALUE != 0)
MENU_SET_ENABLED(1);
else
{ MENU_SET_ENABLED(0);
	MENU_SET_VALUE("OFF");
}

}

MENU_UPDATE_FUNC(init_qp_d)
{
if ( (!config_loaded || (override || config_disabled)) && CURRENT_VALUE != 0)
MENU_SET_ENABLED(1);
else
{ MENU_SET_ENABLED(0);
	MENU_SET_VALUE("OFF");
}

}

MENU_UPDATE_FUNC(ivaparam_d)
{ 
if (!ivaparam) { MENU_SET_ENABLED(0); MENU_SET_VALUE("No"); }
if (ivaparam == 1) { MENU_SET_ENABLED(1);	MENU_SET_VALUE("Yes"); }
if (ivaparam == 2) { MENU_SET_ENABLED(0);	MENU_SET_VALUE("Disable"); }
if (ivaparam == 3) { MENU_SET_ENABLED(0);	MENU_SET_VALUE("Check"); }
					
}

static MENU_UPDATE_FUNC(bitrate_flushing_rate_update)
{
    MENU_SET_VALUE("%d frames", bitrate_flushing_rate );
    if(bitrate_flushing_rate == 3)
    {
        MENU_SET_VALUE("Auto");
    }
}

static MENU_UPDATE_FUNC(bitrate_gop_size_update)
{
    MENU_SET_VALUE("%d frames", bitrate_gop_size);
    if(!bitrate_gop_size)
    {
        MENU_SET_VALUE("Default");
    }
}


static struct menu_entry mov_menus[] = {
    
   {     .name = "Encoder",
		.select = menu_open_submenu,     
		.help = "Change H.264 bitrate. Pick configs. Be careful, recording may stop!",
		.submenu_width = 715,        
        .depends_on = DEP_MOVIE_MODE,
        .children =  (struct menu_entry[]) {
    {
        .name = "Bit Rate     ",
        .priv = &bitrate,
		.update = bit_rated,        
		.min = 0,
        .max = 50,
		.icon_type = IT_BOOL,		
		.help = "H.264 bitrate. One unit = 10 mb/s. 0 = Off",	
		.help2 = "If not loading a config MUST set InitQP"
    },
    {
        .name = "InitQP     ",
        .priv = &initqp,
		.update = init_qp_d,        
        .min = 0,
        .max = 50,
		.icon_type = IT_BOOL,
        .help = "Init QP - Lower is better. 0 = Off",
		.help2 = "Overriding dynamic configs will result in fixed QP"
    },
			{
                .name = "Flush rate",
                .priv = &bitrate_flushing_rate,
                .update = bitrate_flushing_rate_update,
                .min  = 3, // It can do 1 but can't figure patch.
                .max  = 50,
                .help = "Flush movie buffer every n frames."
            },
            {
                .name = "GOP size",
                .priv = &bitrate_gop_size,
                .update = bitrate_gop_size_update,
                .min  = 0,
                .max  = 100,
                .help = "Set GOP size to n frames."
            },
	    {
        .name = "Autoload Conf  ",
        .priv = &autoload,
        .min = 0,
        .max = 2,
		.choices = (const char *[]) {"Off", "On", "Override"},        
		.help = "Auto Load Config at Start and/or Override BR/InitQP"
    },

    {
        .name = "Config Select",
        .priv = &h2config,
        .min = 0,
        .max = 3,
        .choices = (const char *[]) {"Off/H264.ini", "CBR Fixed QP", "VBR", "Rate Control"},
        .help = "Select an Encoder Config. If off H264.ini will load. Exit menu to apply."
    },
		MENU_EOL
        },
    },
    {
        .name = "Load Config",
        .select = load_h264_ini,
        .help = "Manual Load Config from selection"
    },

    {
        .name = "Config Loaded ",
         .priv = &ivaparam,
         .min = 2,
         .max = 3,
		.update = ivaparam_d,        
		.icon_type = IT_BOOL,		
		.choices = (const char *[]) {"Disable", "Check"},    
		.help = "Config Loaded. Disabling may keep some of the parameters"
    },
     

    {
        .name = "REC indicator",
        .priv = &time_indicator,
        .min = 0,
        .max = 3,
        .choices = (const char *[]) {"Off", "Elapsed Time", "Remain.time (card)", "Time Until 4GB"},
        .help = "What to display in top-right corner while recording."
    },
};

#ifdef FEATURE_NITRATE_WAV_RECORD
static struct menu_entry wav_menus[] = {
            {
                .name = "Sound Record",
                .priv = &cfg_hibr_wav_record,
                .select = hibr_wav_record_select,
                .max = 1,
                .choices = (const char *[]) {"Normal", "Separate WAV"},
                .help = "Record audio with WAV separately. Source will auto select.",
            },
/*			{
                .name = "Input Volume",
                .priv = &input_vol,
				.update = input_vol_up,
				.select = in_vol_toggle,
                .min = 0,
                .max = 63,
                .help = "Record audio with WAV separately.",
            },*/
};
#endif


void bitrate_init()
{
    menu_add( "Movie", mov_menus, COUNT(mov_menus) );
	#ifdef FEATURE_NITRATE_WAV_RECORD
	menu_add( "Audio", wav_menus, COUNT(wav_menus) );
	#endif
}

INIT_FUNC(__FILE__, bitrate_init);

void movie_indicators_show()
{
    if (RECORDING_H264 && !gui_menu_shown())
    {
        BMP_LOCK( time_indicator_show(); )
    }
}


static void
bitrate_task( void* unused )
{    
    TASK_LOOP
    {

        if (RECORDING_H264)
        {
            /* uses a bit of CPU, but it's precise */
            wait_till_next_second();
            movie_elapsed_time_01s += 10;
            
            BMP_LOCK( show_mvr_buffer_status(); )
            movie_indicators_show();
            
			if ( (movie_elapsed_time_01s<200) && (movie_elapsed_time_01s>100) && 		
					(measured_bitrate == 0) && (movie_bytes_written_32k == 0) && (!fps_override) )
					//~ && (!fps_override) && (bitrate_flushing_rate == 3) )
						{// ASSERT (0);
							//call ("SHUTDOWN");
							//~ call ("dumpf");
					gui_uilock(UILOCK_NONE);
					NotifyBox(2000,"Not writing! Check settings, reboot!");
					msleep(1000);					
					//~ prop_request_change(PROP_REBOOT_MAYBE, 0, 4);
					//msleep(100000);
					}

        }
        else
        {
			movie_elapsed_time_01s = 0;
           	msleep(1000);
			bitrate_set();
//bmp_printf(FONT_MED, 10,120, "IvaparamStatus: %d, InIq: %d", *ivaparamstatus, ivaparam);

		}
    }
}

TASK_CREATE("bitrate_task", bitrate_task, 0, 0x1d, 0x1000 );


#else /* dummy stubs, just to compile */
int hibr_should_record_wav() { return 0; }
int is_mvr_buffer_almost_full() { return 0; }
void movie_indicators_show() {}
void time_indicator_show() {}
void bitrate_mvr_log(char* mvr_logfile_buffer) {}
void measure_bitrate() {}
#endif
back to top