Permalink
Find file Copy path
16450 lines (14984 sloc) 552 KB
//
// comskip.c
// Copyright (C) 2004 Scott Michael
// Based on the work of Chris Pinkham of MythTV
// comskip 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 of the License, or
// (at your option) any later version.
// comskip 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 this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "platform.h"
#include "vo.h"
#include <argtable2.h>
#include "comskip.h"
// Define detection methods
#define BLACK_FRAME 1
#define LOGO 2
#define SCENE_CHANGE 4
#define RESOLUTION_CHANGE 8
#define CC 16
#define AR 32
#define SILENCE 64
#define CUTSCENE 128
// Define logo detection directions
#define HORIZ 0
#define VERT 1
#define DIAG1 2
#define DIAG2 3
// Define CC types
#define NONE 0
#define ROLLUP 1
#define POPON 2
#define PAINTON 3
#define COMMERCIAL 4
// Define aspect ratio
#define FULLSCREEN true
#define WIDESCREEN false
#define AR_TREND 0.8
#define DEEP_SILENCE 6 //max_volume / DEEP_SILENCE defines deep silence
#define OPEN_INPUT 1
#define OPEN_INI 2
#define SAVE_DMP 3
#define SAVE_INI 4
#ifdef DONATOR
#define COMSKIPPUBLIC "donator"
#else
#define COMSKIPPUBLIC "public"
#endif
#define MAX(X,Y) (X>Y?X:Y)
#define MIN(X,Y) (X<Y?X:Y)
// max number of frames that can be marked
#define MAX_IDENTIFIERS 300000
#define MAX_COMMERCIALS 100000
int argument_count;
char ** argument = NULL;
bool initialized = false;
const char* progname = "ComSkip";
FILE* out_file = NULL;
FILE* incommercial_file = NULL;
FILE* ini_file = NULL;
FILE* plist_cutlist_file = NULL;
FILE* zoomplayer_cutlist_file = NULL;
FILE* zoomplayer_chapter_file = NULL;
FILE* scf_file = NULL;
FILE* vcf_file = NULL;
FILE* vdr_file = NULL;
FILE* projectx_file = NULL;
FILE* avisynth_file = NULL;
FILE* videoredo_file = NULL;
FILE* videoredo3_file = NULL;
FILE* btv_file = NULL;
FILE* edl_file = NULL;
FILE* ffmeta_file = NULL;
FILE* ffsplit_file = NULL;
FILE* live_file = NULL;
FILE* ipodchap_file = NULL;
FILE* edlp_file = NULL;
FILE* bcf_file = NULL;
FILE* edlx_file = NULL;
FILE* cuttermaran_file = NULL;
FILE* chapters_file = NULL;
FILE* log_file = NULL;
FILE* womble_file = NULL;
FILE* mls_file = NULL;
FILE* mpgtx_file = NULL;
FILE* dvrcut_file = NULL;
FILE* dvrmstb_file = NULL;
FILE* mpeg2schnitt_file = NULL;
FILE* tuning_file = NULL;
FILE* training_file = NULL;
FILE* aspect_file = NULL;
FILE* cutscene_file = NULL;
FILE* mkvtoolnix_chapters_file = NULL;
FILE* mkvtoolnix_tags_file = NULL;
extern int demux_pid;
extern int selected_audio_pid;
extern int selected_subtitle_pid;
extern int selected_video_pid;
extern int demux_asf;
extern int key;
extern char osname[];
int audio_channels;
#define KDOWN 1
#define KUP 2
#define KLEFT 3
#define KRIGHT 4
#define KNEXT 5
#define KPREV 6
extern int xPos,yPos,lMouseDown;
extern int framenum_infer;
extern void list_codes;
extern int64_t headerpos;
int vo_init_done = 0;
extern int soft_seeking;
static FILE* in_file = NULL;
#undef FRAME_WITH_HISTOGRAM
#undef FRAME_WITH_LOGO
#undef FRAME_WITH_AR
typedef struct
{
// long frame;
int brightness;
int schange_percent;
int minY;
int maxY;
int uniform;
int volume;
double currentGoodEdge;
double ar_ratio;
bool logo_present;
bool commercial;
bool isblack;
int64_t goppos;
double pts;
char pict_type;
int minX;
int maxX;
int hasBright;
int dimCount;
int cutscenematch;
double logo_filter;
int xds;
int cur_segment;
int audio_channels;
#ifdef FRAME_WITH_HISTOGRAM
int histogram[256];
#endif
} frame_info;
int debug_cur_segment;
frame_info* frame = NULL;
long frame_count = 0;
long max_frame_count;
double fps = 1.0; // frames per second (NTSC=29.970, PAL=25)
double get_frame_pts(int f) {
if (!frame) {
return(f / fps);
}
if (f < 1)
f = 1;
if (f > frame_count -1)
f = frame_count -1;
return(frame[f].pts);
}
#define F2V(X) (frame != NULL ? ((X) <= 0 ? frame[1].pts : ((X) >= framenum_real ? frame[framenum_real - 1].pts : frame[X].pts )) : (X) / fps)
#define assert(T) (aaa = ((T) ? 1 : *(int *)0))
//#define F2T(X) (F2V(X) - F2V(1))
#define F2T(X) (F2V(X))
#define F2L(X,Y) (F2V(X) - F2V(Y))
#define F2F(X) ((long) (F2T(X) * fps + 1.5 ))
typedef struct
{
long frame;
int percentage;
} schange_info;
schange_info* schange = NULL;
long schange_count = 0;
long max_schange_count = 0;
typedef struct
{
long frame;
int brightness;
long uniform;
int volume;
int cause;
} black_frame_info;
long black_count = 0;
black_frame_info* black = NULL;
long max_black_count;
typedef struct block_info
{
long f_start;
long f_end;
unsigned int b_head;
unsigned int b_tail;
unsigned int bframe_count;
unsigned int schange_count;
double schange_rate; // in changes per second
double length;
double score;
int combined_count;
int cc_type;
// bool ar;
double ar_ratio;
int audio_channels;
int cause;
int more;
int less;
int brightness;
int volume;
int silence;
int uniform;
int stdev;
char reffer;
double logo;
double correlation;
int strict;
int iscommercial;
} block_info;
#define MAX_BLOCKS 1000
struct block_info cblock[MAX_BLOCKS];
long block_count = 0;
long max_block_count;
#define C_c (1<<1)
#define C_l (1<<0)
#define C_s (1<<2)
#define C_a (1<<5)
#define C_u (1<<3)
#define C_b (1<<4)
#define C_r ((long)1<<29)
#define C_STRICT (1<<6)
#define C_NONSTRICT (1<<7)
#define C_COMBINED (1<<8)
#define C_LOGO (1<<9)
#define C_EXCEEDS (1<<10)
#define C_AR (1<<11)
#define C_SC (1<<12)
#define C_H1 (1<<13)
#define C_H2 (1<<14)
#define C_H3 (1<<15)
#define C_H4 ((long)1<<16)
#define C_H5 ((long)1<<17)
#define C_H6 ((long)1<<22)
#define C_v ((long)1<<18)
#define C_BRIGHT ((long)1<<19)
#define C_NOTBRIGHT ((long)1<<20)
#define C_DIM ((long)1<<21)
#define C_AB ((long)1<<23)
#define C_AU ((long)1<<24)
#define C_AL ((long)1<<25)
#define C_AS ((long)1<<26)
#define C_AC ((long)1<<26)
#define C_F ((long)1<<27)
#define C_t ((long)1<<28)
#define C_H7 ((long)1<<29)
#define C_H8 ((long)1<<30)
#define C_CUTMASK (C_c | C_l | C_s | C_a | C_u | C_b | C_t | C_r)
#define CUTCAUSE(c) ( c & C_CUTMASK)
//int minLogo = 30;
//int maxLogo = 120;
typedef struct
{
int start;
int end;
} logo_block_info;
logo_block_info* logo_block = NULL;
long logo_block_count = 0; // How many groups have already been identified. Increment after fill.
long max_logo_block_count;
bool processCC = false;
extern int reorderCC;
typedef struct
{
unsigned char cc1[2];
unsigned char cc2[2];
} ccPacket;
ccPacket lastcc;
ccPacket cc;
typedef struct
{
long start_frame;
long end_frame;
int type;
} cc_block_info;
cc_block_info* cc_block = NULL;
long cc_block_count = 0;
long max_cc_block_count;
int last_cc_type = NONE;
int current_cc_type = NONE;
bool cc_on_screen = false;
bool cc_in_memory = false;
typedef struct
{
long frame;
char name[40];
int v_chip;
int duration;
int position;
int composite1;
int composite2;
} XDS_block_info;
XDS_block_info* XDS_block = NULL;
long XDS_block_count = 0;
long max_XDS_block_count;
typedef struct
{
long start_frame;
long end_frame;
long text_len;
unsigned char text[256];
} cc_text_info;
cc_text_info* cc_text = NULL;
long cc_text_count = 0;
long max_cc_text_count = 0;
#define AR_UNDEF 0.0
typedef struct
{
int start;
int end;
// bool ar;
double ar_ratio;
int volume;
int height, width;
int minX,maxX,minY,maxY;
} ar_block_info;
ar_block_info* ar_block = NULL;
long ar_block_count = 0; // How many groups have already been identified. Increment after fill.
long max_ar_block_count;
int last_audio_channels = 2;
double last_ar_ratio = 0.0;
double ar_ratio_trend = 0.0;
int ar_ratio_trend_counter = 0;
int ar_ratio_start = 0;
int ar_misratio_trend_counter = 0;
int ar_misratio_start = 0;
#define AC_UNDEF 0
typedef struct
{
int start;
int end;
// bool ar;
int audio_channels;
} ac_block_info;
ac_block_info* ac_block = NULL;
long ac_block_count = 0; // How many groups have already been identified. Increment after fill.
long max_ac_block_count;
typedef struct
{
long start;
long end;
} commercial_list_info;
commercial_list_info* commercial_list = NULL;
int commercial_count = -1;
struct
{
long start_frame;
long end_frame;
int start_block;
int end_block;
double length;
} commercial[MAX_COMMERCIALS];
int reffer_count = -1;
struct
{
long start_frame;
long end_frame;
} reffer[MAX_COMMERCIALS];
#define MAX_ASPECT_RATIOS 1000
struct
{
long frames;
double ar_ratio;
} ar_histogram[MAX_ASPECT_RATIOS];
double dominant_ar;
#define MAX_AUDIO_CHANNELS 12
struct
{
long frames;
int audio_channels;
} ac_histogram[MAX_AUDIO_CHANNELS];
int dominant_ac;
int thread_count = 2;
int hardware_decode = 0;
int use_cuvid = 0;
int use_vdpau = 0;
int skip_B_frames = 0;
int lowres = 0;
bool live_tv = false;
bool output_incommercial = false;
int incommercial_frames = 1000;
int live_tv_retries = 6;
int dvrms_live_tv_retries = 300;
int standoff = 0;
int dvrmsstandoff = 120000;
extern int standoff_retries;
extern int standoff_time;
extern int standoff_size;
extern int standoff_initial_size;
extern int standoff_initial_wait;
char incomingCommandLine[MAX_ARG];
char logofilename[MAX_PATH];
char logfilename[MAX_PATH];
char mpegfilename[MAX_PATH];
char exefilename[MAX_PATH];
char inbasename[MAX_PATH];
char workbasename[MAX_PATH];
char outbasename[MAX_PATH];
char shortbasename[MAX_PATH];
char inifilename[MAX_PATH];
char dictfilename[MAX_PATH];
char out_filename[MAX_PATH];
char incommercial_filename[MAX_PATH];
char outputdirname[MAX_PATH];
char filename[MAX_PATH];
int curvolume = -1;
extern int framenum;
//unsigned int frame_period;
//int audio_framenum = 0;
//extern int64_t pts;
extern int64_t initial_pts;
extern int initial_pts_set;
extern char pict_type;
int ascr,scr;
int framenum_real;
int frames_with_logo;
int framesprocessed = 0;
char HomeDir[256]; // comskip home directory
char tempString[256];
double average_score;
int brightness = 0;
long sum_brightness=0;
long sum_count;
int uniformHistogram[256];
#define UNIFORMSCALE 100
int brightHistogram[256];
int blackHistogram[256];
int volumeHistogram[256];
int silenceHistogram[256];
int logoHistogram[256];
int volumeScale = 10;
int last_brightness = 0;
int min_brightness_found;
int min_volume_found;
int max_logo_gap;
int max_nonlogo_block_length;
double logo_overshoot;
double logo_quality;
int width, old_width, videowidth;
int height, old_height;
int ar_width = 0;
int subsample_video = 0x1ff;
//#define MAXWIDTH 800
//#define MAXHEIGHT 600
#define MAXWIDTH 2000
#define MAXHEIGHT 1200
char haslogo[MAXWIDTH*MAXHEIGHT];
// unsigned char oldframe[MAXWIDTH*MAXHEIGHT];
// variables defining options with defaults
int selftest = 0;
int verbose = 0; // show extra info
double avg_fps = 22;
int border = 10; // border around edge of video to ignore
int ticker_tape=0, ticker_tape_percentage=0; // border from bottom to ignore
int ignore_side=0;
int ignore_left_side=0;
int ignore_right_side=0;
int max_brightness = 60; // frame not black if any pixels checked are greater than this (scale 0 to 255)
int maxbright = 1;
int min_hasBright = 255000;
int min_dimCount = 255000;
int test_brightness = 40; // frame not pure black if any pixels are greater than this, will check average
int max_avg_brightness = 19; // maximum average brightness for a dim frame to be considered black (scale 0 to
int max_volume = 500;
int max_silence = 100;
int min_silence = 12;
int punish_no_logo = true;
int validate_silence = true;
int validate_uniform = true;
int validate_scenechange = true;
int remove_silent_segments = 0;
int validate_ar = true;
int punish = 0;
int reward = 0;
int min_volume=0;
int min_uniform = 0;
int volume_slip = 40;
extern int ms_audio_delay;
int max_repair_size = 40;
//int variable_bitrate = 1;
extern int is_h264;
///brightness (scale 0 to 255)
char ini_text[40000];
///255)
double max_commercialbreak = 600; // maximum length in seconds to consider a segment a commercial break
double min_commercialbreak = 20; // minimum length in seconds to consider a segment a commercial break
double max_commercial_size = 120; // maximum time in seconds for a single commercial
double min_commercial_size = 4; // mimimum time in seconds for a single commercial
double min_show_segment_length = 120.0;
bool require_div5 = 0; // set=1 to only mark breaks divisible by 5 as a commercial.
double div5_tolerance = -1;
bool play_nice = false;
double global_threshold = 1.05;
bool intelligent_brightness = false;
double logo_threshold = 0.80;
double logo_percentage_threshold = 0.25;
double logo_max_percentage_of_screen = 0.12;
int logo_filter = 0;
int non_uniformity = 500;
int brightness_jump = 200;
double black_percentile = 0.0076;
double uniform_percentile = 0.003;
double score_percentile = 0.71;
double logo_percentile = 0.92;
double logo_fraction = 0.40;
int commDetectMethod = BLACK_FRAME + LOGO + RESOLUTION_CHANGE + AR + SILENCE + (PROCESS_CC ? CC : 0);
int giveUpOnLogoSearch = 2000; // If no logo is identified after x seconds into the show - give up.
int delay_logo_search = 0; // If no logo is identified after x seconds into the show - give up.
int cut_on_ar_change = 1;
int cut_on_ac_change = 1;
int added_recording = 14;
int after_start = 0;
int before_end = 0;
int delete_show_after_last_commercial = false;
int delete_show_before_first_commercial = false;
int delete_block_after_commercial = 0;
int min_commercial_break_at_start_or_end = 39;
int always_keep_first_seconds = 0;
int always_keep_last_seconds = 0;
bool connect_blocks_with_logo = true;
int delete_show_before_or_after_current = false;
bool deleteLogoFile = true;
bool useExistingLogoFile = true;
bool startOverAfterLogoInfoAvail = true;
int doublCheckLogoCount = 0;
bool output_default = true;
bool output_chapters = false;
bool sage_framenumber_bug = false;
bool sage_minute_bug = false;
bool enable_mencoder_pts = false;
bool output_plist_cutlist = false;
bool output_zoomplayer_cutlist = false;
bool output_zoomplayer_chapter = false;
bool output_scf = false;
bool output_videoredo = false;
bool output_videoredo3 = false;
bool output_ipodchap = false;
int videoredo_offset = 2;
int edl_offset = 0;
int timeline_repair = 1;
int edl_skip_field = 0;
bool output_edl = false;
bool output_live = false;
bool output_edlp = false;
bool output_bsplayer = false;
bool output_edlx = false;
bool output_btv = false;
bool output_cuttermaran = false;
bool output_mpeg2schnitt = false;
char cuttermaran_options[1024];
char mpeg2schnitt_options[1024];
char avisynth_options[1024];
char dvrcut_options[1024];
bool output_demux = false;
bool output_data = false;
bool output_srt = false;
bool output_smi = false;
bool output_timing = false;
bool output_womble = false;
bool output_mls = false;
bool output_mpgtx = false;
bool output_dvrcut = false;
bool output_dvrmstb = false;
bool output_vdr = false;
bool output_vcf = false;
bool output_projectx = false;
bool output_avisynth = false;
int output_mkvtoolnix = 0;
bool output_debugwindow = false;
bool output_console = true;
int disable_heuristics = 0;
char windowtitle[1024] = "Comskip - %s";
bool output_tuning = false;
int output_training = 0;
bool output_false = false;
bool output_aspect = false;
bool output_ffmeta = false;
bool output_ffsplit = false;
int noise_level=5;
bool framearray = true;
bool output_framearray = false;
bool only_strict = false;
double length_strict_modifier = 3.0;
double length_nonstrict_modifier = 1.5;
double combined_length_strict_modifier = 2.0;
double combined_length_nonstrict_modifier = 1.25;
double logo_present_modifier = 0.01;
double punish_modifier = 2.0;
double punish_threshold = 1.3;
double reward_modifier = 0.5;
int after_logo=0;
int before_logo=0;
double shrink_logo=5.0;
int shrink_logo_tail=0;
int where_logo=0;
double excessive_length_modifier = 0.01;
double dark_block_modifier = 0.3;
int padding = 0;
int remove_before = 0;
int remove_after = 0;
double min_schange_modifier = 0.5;
double max_schange_modifier = 2.0;
int schange_threshold = 90;
int schange_cutlevel = 15;
double cc_commercial_type_modifier = 4.0;
double cc_wrong_type_modifier = 2.0;
double cc_correct_type_modifier = 0.75;
double ar_wrong_modifier = 2.0;
double ac_wrong_modifier = 1.0;
double ar_rounding = 100;
double ar_delta = 0.08;
long avg_brightness = 0;
long maxi_volume = 0;
long avg_volume = 0;
long avg_silence = 0;
long avg_uniform = 0;
double avg_schange = 0.0;
double dictionary_modifier = 1.05;
int aggressive_logo_rejection = false;
unsigned int min_black_frames_for_break = 1;
bool detectBlackFrames;
bool detectSceneChanges;
int dummy1;
unsigned char* frame_ptr = 0;
int dummy2;
// bool frameIsBlack;
bool sceneHasChanged;
int sceneChangePercent;
bool lastFrameWasBlack = false;
bool lastFrameWasSceneChange = false;
#include <libavutil/avutil.h> // only for DECLARE_ALIGNED
static DECLARE_ALIGNED(32, long, histogram)[256];
static DECLARE_ALIGNED(32, long, lastHistogram)[256];
#define MAXCSLENGTH 400*300
#define MAXCUTSCENES 8
void LoadCutScene(const char *filename);
void RecordCutScene(int frame_count,int brightness);
char cutscenefile[1024];
char cutscenefile1[1024];
char cutscenefile2[1024];
char cutscenefile3[1024];
char cutscenefile4[1024];
char cutscenefile5[1024];
char cutscenefile6[1024];
char cutscenefile7[1024];
char cutscenefile8[1024];
int cutscenematch;
int cutscenedelta = 10;
int cutsceneno = 0;
int cutscenes=0;
unsigned char cutscene[MAXCUTSCENES][MAXCSLENGTH];
int csbrightness[MAXCUTSCENES];
int cslength[MAXCUTSCENES];
// int cssum[MAXCUTSCENES];
// int csmatch[MAXCUTSCENES];
char debugText[20000];
bool logoInfoAvailable;
bool secondLogoSearch = false;
bool logoBuffersFull = false;
int logoTrendCounter = 0;
double logoFreq = 1.0; // times fps between logo checks
int num_logo_buffers = 50; // How many frames to compare at a time for logo detection;
bool lastLogoTest = false;
// int logoTrendStartFrame;
int* logoFrameNum = NULL; // Keep track of the frame numbers of each buffer
int oldestLogoBuffer; // Which buffer is the oldest?
// int lastRealLogoChange;
bool curLogoTest = false;
int minHitsForTrend = 10;
// bool hindsightLogoState = true;
double logoPercentage = 0.0;
bool reverseLogoLogic = false;
#define MULTI_EDGE_BUFFER 0
#if MULTI_EDGE_BUFFER
unsigned char ** horiz_edges = NULL; // rotating storage for detected horizontal edges
unsigned char ** vert_edges = NULL; // rotating storage for detected vertical edges
#else
unsigned char horiz_count[MAXHEIGHT*MAXWIDTH];
unsigned char vert_count[MAXHEIGHT*MAXWIDTH];
#endif
double borderIgnore = .05; // Percentage of each side to ignore for logo detection
int subtitles = 0;
int logo_at_bottom = 0;
int edge_radius = 2;
int int_edge_radius = 2;
int edge_step = 1;
int edge_level_threshold = 5;
int edge_weight = 10;
int edge_count = 0;
int hedge_count = 0;
int vedge_count = 0;
int newestLogoBuffer = -1; // Which buffer is the newest? Increments prior to fill so start at -1
unsigned char ** logoFrameBuffer = NULL; // rotating storage for frames
int logoFrameBufferSize = 0;
int lwidth;
int lheight;
int tlogoMinX;
int tlogoMaxX;
int tlogoMinY;
int tlogoMaxY;
int edgemask_filled=0;
unsigned char thoriz_edgemask[MAXHEIGHT*MAXWIDTH];
unsigned char tvert_edgemask[MAXHEIGHT*MAXWIDTH];
int clogoMinX;
int clogoMaxX;
int clogoMinY;
int clogoMaxY;
unsigned char choriz_edgemask[MAXHEIGHT*MAXWIDTH];
unsigned char cvert_edgemask[MAXHEIGHT*MAXWIDTH];
int play_nice_start = -1;
int play_nice_end = -1;
long play_nice_sleep = 2L;
FILE *dump_data_file = (FILE *)NULL;
uint8_t ccData[500];
int ccDataLen;
static uint8_t prevccData[500];
static int prevccDataLen;
long cc_count[5] = { 0, 0, 0, 0, 0 };
int most_cc_type = NONE;
unsigned char ** cc_screen = NULL;
unsigned char ** cc_memory = NULL;
int minY; // The top of the picture for aspect ratio calculation
int maxY; // The bottom of the picture for aspect ratio calculation
int minX; // The top of the picture for aspect ratio calculation
int maxX; // The bottom of the picture for aspect ratio calculation
//bool currentAR;
//bool lastAR;
//bool showAvgAR;
bool isSecondPass = false;
long lastFrame = 0;
long lastFrameCommCalculated = 0;
bool ccCheck = false;
bool loadingCSV = false;
bool loadingTXT = false;
int helpflag = 0;
int timeflag = 0;
#define MAXTIMEFLAG 2
int recalculate=0;
char *helptext[]=
{
"Help: press any key to remove",
"Key Action",
"Arrows Reposition current location",
"PgUp/PgDn Reposition current location",
"n/p Jump to next/previous cutpoint",
"e/b Jump to next/previous end of cblock",
"z/u Zoom in/out on the timeline",
"g Graph on/off",
"x XDS info on/off",
"t Toggle current cblock between show and commercial",
"w Write the new cutpoints to the ouput files",
"c Dump this frame as CutScene"
"F2 Reduce the max_volume detection level",
"F3 Reduce the non_uniformity detection level",
"F4 Reduce the max_avg_brighness detection level",
"F5 Toggle frame number / timecode display",
"",
"During commercial break review",
"e Set end of commercial to this position",
"b Set begin of commercial to this position",
"i Insert a new commercial",
"d Delete the commercial at current location",
"s Jump to Start of the recording",
"f Jump to Finish of the recording",
"",
"Divide and conquer commercial break review",
"j Set the before marker frame",
"k Set the end marker frame",
"l Clear the marker frames",
0
};
double currentGoodEdge = 0.0;
int lineStart[MAXHEIGHT]; /* Area to include for black frame detection, non logo area */
int lineEnd[MAXHEIGHT];
unsigned char hor_edgecount[MAXHEIGHT*MAXWIDTH];
unsigned char ver_edgecount[MAXHEIGHT*MAXWIDTH];
unsigned char max_br[MAXHEIGHT*MAXWIDTH];
unsigned char min_br[MAXHEIGHT*MAXWIDTH];
unsigned char graph[MAXHEIGHT*MAXWIDTH*3];
int gy=0;
// Function Prototypes
bool BuildBlocks(bool recalc);
void Recalc(void);
double ValidateBlackFrames(long reason, double ratio, int remove);
int DetectCommercials(int, double);
bool BuildMasterCommList(void);
void WeighBlocks(void);
bool OutputBlocks(void);
void OutputAspect(void);
void OutputTraining(void);
bool ProcessLogoTest(int framenum_real, int curLogoTest, int close);
void OutputStrict(double len, double delta, double tol);
int InputReffer(char *ext, int setfps);
bool IsStandardCommercialLength(double length, double tolerance, bool strict);
bool LengthWithinTolerance(double test_length, double expected_length, double tolerance);
double FindNumber(char* str1, char* str2, double v);
char * FindString(char* str1, char* str2, char *v);
void AddIniString( char *s);
char* intSecondsToStrMinutes(int seconds);
char* dblSecondsToStrMinutes(double seconds);
char* dblSecondsToStrMinutesFrames(double seconds);
FILE* LoadSettings(int argc, char ** argv);
int GetAvgBrightness(void);
bool CheckFrameIsBlack(void);
void BuildBlackFrameCommList(void);
bool CheckSceneHasChanged(void);
#if 0
void BuildSceneChangeCommList(void);
void BuildSceneChangeCommList2(void);
#endif
void backfill_frame_volumes();
void PrintLogoFrameGroups(void);
void PrintCCBlocks(void);
void ResetLogoBuffers(void);
void EdgeDetect(unsigned char* frame_ptr, int maskNumber);
void EdgeCount(unsigned char* frame_ptr);
void FillLogoBuffer(void);
bool SearchForLogoEdges(void);
double CheckStationLogoEdge(unsigned char* testFrame);
double DoubleCheckStationLogoEdge(unsigned char* testFrame);
void SetEdgeMaskArea(unsigned char* temp);
int ClearEdgeMaskArea(unsigned char* temp, unsigned char* test);
int CountEdgePixels(void);
void DumpEdgeMask(unsigned char* buffer, int direction);
void DumpEdgeMasks(void);
void BuildBlackFrameAndLogoCommList(void);
bool CheckFramesForLogo(int start, int end);
char CheckFramesForCommercial(int start, int end);
char CheckFramesForReffer(int start, int end);
void SaveLogoMaskData(void);
void LoadLogoMaskData(void);
double CalculateLogoFraction(int start, int end);
bool CheckFrameForLogo(int i);
int CountSceneChanges(int StartFrame, int EndFrame);
void Debug(int level, char* fmt, ...);
void InitProcessLogoTest(void);
void InitComSkip(void);
void InitLogoBuffers(void);
void FindIniFile(void);
double FindScoreThreshold(double percentile);
void OutputLogoHistogram(int buckets);
void OutputbrightHistogram(void);
void OutputuniformHistogram(void);
void OutputHistogram(int *histogram, int scale, char *title, bool truncate);
int FindBlackThreshold(double percentile);
int FindUniformThreshold(double percentile);
void OutputFrameArray(bool screenOnly);
void OutputBlackArray();
void OutputFrame();
void OpenOutputFiles();
void InitializeFrameArray(long i);
void InitializeBlackArray(long i);
void InitializeSchangeArray(long i);
void InitializeLogoBlockArray(long i);
void InitializeARBlockArray(long i);
void InitializeACBlockArray(long i);
void InitializeBlockArray(long i);
void InitializeCCBlockArray(long i);
void InitializeCCTextArray(long i);
void PrintArgs(void);
void close_dump(void);
void OutputCommercialBlock(int i, long prev, long start, long end, bool last);
void ProcessCSV(FILE *);
void OutputCCBlock(long i);
void ProcessCCData(void);
bool CheckOddParity(unsigned char ch);
void AddNewCCBlock(long current_frame, int type, bool cc_on_screen, bool cc_in_memory);
char* CCTypeToStr(int type);
int DetermineCCTypeForBlock(long start, long end);
double AverageARForBlock(int start, int end);
void SetARofBlocks(void);
bool ProcessCCDict(void);
int FindBlock(long frame);
void BuildCommListAsYouGo(void);
void BuildCommercial(void);
int RetreiveVolume (int f);
void InsertBlackFrame(int f, int b, int u, int v, int c);
extern void DecodeOnePicture(FILE * f, double pts);
int CEW_init(int argc, char *argv[]);
char *CauseString(int i)
{
static char cs[4][80];
static int ii=0;
char *c = &(cs[ii][0]);
char *rc = &(cs[ii][0]);
*c++ = (i & C_H8 ? '8' : ' ');
*c++ = (i & C_H7 ? '7' : ' ');
*c++ = (i & C_H6 ? '6' : ' ');
*c++ = (i & C_H5 ? '5' : ' ');
*c++ = (i & C_H4 ? '4' : ' ');
*c++ = (i & C_H3 ? '3' : ' ');
*c++ = (i & C_H2 ? '2' : ' ');
*c++ = (i & C_H1 ? '1' : ' ');
if (strncmp((char*)cs[ii]," ",7))
*c++ = '{';
else
*c++ = ' ';
*c++ = (i & C_SC ? 'F' : ' ');
*c++ = (i & C_AR ? 'A' : ' ');
*c++ = (i & C_EXCEEDS ? 'E' : ' ');
*c++ = (i & C_LOGO ? 'L' : (i & C_BRIGHT ? 'B': ' '));
*c++ = (i & C_COMBINED ? 'C' : ' ');
*c++ = (i & C_NONSTRICT? 'N' : ' ');
*c++ = (i & C_STRICT ? 'S' : ' ');
*c++ = (i & 2 ? 'c' : (i & C_t ? 't': ' '));
*c++ = (i & 1 ? 'l' : (i & C_v ? 'v': ' '));
*c++ = (i & 4 ? 's' : ' ');
*c++ = (i & 32 ? 'a' : ' ');
*c++ = (i & 8 ? 'u' : ' ');
*c++ = (i & 16 ? 'b' : ' ');
*c++ = (i & C_r ? 'r' : ' ');
*c++ = 0;
ii = (ii + 1) % 4;
return(rc);
}
double ValidateBlackFrames(long reason, double ratio, int remove)
{
int i,k,j,last;
int prev_cause;
int strict_count = 0;
int negative_count = 0;
int positive_count = 0;
int count = 0;
int total_cause;
double length,summed_length;
int incommercial;
char *r = " -undefined- ";
if (reason == C_b)
r = "Black Frame ";
if (reason == C_v)
r = "Volume ";
if (reason == C_s)
r = "Scene Change ";
if (reason == C_c)
r = "Change ";
if (reason == C_u)
r = "Uniform Frame";
if (reason == C_a)
r = "Aspect Ratio ";
if (reason == C_t)
r = "Cut Scene ";
if (reason == C_l)
r = "Logo ";
if (ratio == 0.0)
return(0.0);
#ifndef undef
incommercial = 0;
i = 0; // search for reason
strict_count = 0;
count = 0;
last = 0;
length = 0.0;
summed_length = 0.0;
while(i < black_count)
{
while (i < black_count && (black[i].cause & reason) == 0)
{
i++;
}
k = i;
while (k < black_count && (black[k+1].cause & reason) != 0 && black[k+1].frame == black[k].frame+1)
{
k++;
}
if (i < black_count)
{
length = F2T(black[(i+k)/2].frame) - F2T(black[last].frame);
if (length > max_commercial_size)
{
if (incommercial)
{
incommercial = 0;
if (summed_length < min_commercialbreak && summed_length > 4.7 && black[(i+k)/2].frame < frame_count * 6 / 7 && black[last].frame > frame_count * 1 / 7 )
{
negative_count++;
Debug (10,"Negative %s cutpoint at %6i, commercial too short\n", r,black[last].frame);
}
else
positive_count++;
summed_length = 0.0;
}
else
{
positive_count++;
}
summed_length = 0.0;
}
else
{
summed_length += length;
if (incommercial && summed_length > max_commercialbreak )
{
if (black[(i+k)/2].frame < frame_count * 6 / 7)
{
negative_count++;
Debug (10,"Negative %s cutpoint at %6i, commercial too long\n", r,black[(i+k)/2].frame);
}
}
else
{
positive_count++;
incommercial = 1;
}
}
}
last = (i+k)/2;
i = k+1;
}
Debug (1,"Distribution of %s cutting: %3i positive and %3i negative, ratio is %6.4f\n", r, positive_count, negative_count, (negative_count > 0 ? (double)positive_count / (double)negative_count : 9.99));
if ((logoPercentage < logo_fraction || logoPercentage > logo_percentile) && negative_count > 1)
{
Debug (1,"Confidence of %s cutting: %3i negative without good logo is too much\n", r, negative_count);
if (remove)
{
for (k = black_count - 1; k >= 0; k--)
{
if (black[k].cause & reason)
{
black[k].cause &= ~reason;
if (black[k].cause == 0)
{
for (j = k; j < black_count - 1; j++)
{
black[j] = black[j + 1];
}
black_count--;
}
}
}
}
/*
if (negative_count > 1 && reason == C_v)
{
Debug(1, "Too mutch Silence Frames, disabling silence detection\n");
commDetectMethod &= ~SILENCE;
}
if (negative_count > 1 && reason == C_s)
{
Debug(1, "Too mutch Scene Change, disabling Scene Change detection\n");
commDetectMethod &= ~SCENE_CHANGE;
}
*/
}
#endif
i = 1;
strict_count = 0;
count = 0;
prev_cause = 0;
while(i < black_count)
{
total_cause = black[i].cause;
k = i;
while (k < black_count && black[k+1].frame == black[k].frame+1)
{
k++;
total_cause |= black[k].cause;
}
last = (i+k)/2;
if ((total_cause & reason) && (prev_cause & reason))
{
j = i-1;
while (j > 0 && black[j-1].frame == black[j].frame - 1)
j--;
length = F2T(black[i].frame) - F2T(black[(i-1+j)/2].frame);
if (length > 1.0 && length< max_commercial_size)
{
count++;
if (IsStandardCommercialLength(length, F2T(i) - F2T(j) + 0.8 , false))
{
// if (length > max_commercial_size) {
strict_count++;
}
}
}
prev_cause = reason;
k++;
i = k;
}
if (strict_count < 2 || 100*strict_count < 100*count / ratio)
{
Debug (1,"Confidence of %s cutting: %3i out of %3i are strict, too low\n", r, strict_count, count);
if (remove)
{
for (k = black_count - 1; k >= 0; k--)
{
if (black[k].cause & reason)
{
black[k].cause &= ~reason;
if (black[k].cause == 0)
{
for (j = k; j < black_count - 1; j++)
{
black[j] = black[j + 1];
}
black_count--;
}
}
}
}
}
else
Debug (1,"Confidence of %s cutting: %3i out of %3i are strict\n", r, strict_count, count);
return (count > 0 ? (double)strict_count / (double) count : 0);
}
//Function code blocks
bool BuildBlocks(bool recalc)
{
int i = 0;
int j = 0;
int k = 0;
int a = 0;
int count;
int v_count;
int b_count;
int black_start;
int black_end;
int cause = 0;
int black_threshold;
int uniform_threshold;
int prev_start = 1;
int prev_head = 0;
long b_start, b_end, b_counted;
// char *t = "";
// max_block_count = 80;
max_block_count = MAX_BLOCKS;
block_count = 0;
// cblock = malloc(max_block_count * sizeof(block_info));
recalculate = recalc;
InitializeBlockArray(0);
// If there are no black frames, nothing can be done
// if (!black_count && !ar_block_count) return (false);
// OutputHistogram(volumeHistogram, volumeScale, "Volume", true);
if (!recalc)
{
// Eliminate frames that are too bright from black frame list
if (intelligent_brightness)
{
OutputbrightHistogram();
max_avg_brightness = black_threshold = FindBlackThreshold(black_percentile);
Debug(1, "Setting brightness threshold to %i\n", black_threshold);
}
if ((intelligent_brightness && non_uniformity > 0)
// || (commDetectMethod & BLACK_FRAME && non_uniformity == 0) // Diabled
)
{
OutputuniformHistogram ();
non_uniformity = uniform_threshold = FindUniformThreshold(uniform_percentile);
Debug(1, "Setting uniform threshold to %i\n", uniform_threshold);
if (commDetectMethod & BLACK_FRAME)
{
for (i = 1; i < frame_count; i++)
{
frame[i].isblack &= ~C_u;
if (/*!(frame[i].isblack & C_b) && */ non_uniformity > 0 && frame[i].uniform < non_uniformity && frame[i].brightness < 250 /*&& frame[i].volume < max_volume*/ )
InsertBlackFrame(i,frame[i].brightness,frame[i].uniform,frame[i].volume, (int)C_u);
}
}
}
}
j = 0;
for (i = 2; i < frame_count - 1; i++) // frame 0 is not used
if (frame[i-1].volume != -1 && frame[i].volume == -1 && frame[i+1].volume != -1)
j++;
if (j>0)
Debug(9,"Single frames with missing audio: %d\n",j);
if (non_uniformity < min_uniform + 100)
non_uniformity = min_uniform + 100;
if (framearray) // Find minumum volume around black frame
{
for (k = black_count - 1; k >= 0; k--)
{
if (black[k].cause == C_s || black[k].cause == C_c || black[k].cause == (C_c|C_s) )
{
i = black[k].frame-volume_slip; // Find quality of silence around black frame
if (i < 0) i = 0;
j = black[k].frame+volume_slip;
if (j>frame_count) j = frame_count;
count = 0;
for (a=i; a<j; a++)
{
if (frame[a].volume < max_volume/4)
count += volume_slip;
else if (frame[a].volume < max_volume)
count++;
}
if (count > volume_slip/4)
{
black[k].volume = max_volume /2;
}
else
{
black[k].volume = max_volume *10 ;
}
}
else
{
i = black[k].frame-volume_slip; // Find minimum volume around black frame
if (i < 0) i = 0;
j = black[k].frame+volume_slip;
if (j>frame_count) j = frame_count;
for (a=i; a<j; a++)
if (frame[a].volume >= 0)
if (black[k].volume > frame[a].volume)
black[k].volume = frame[a].volume;
}
}
for (k = ar_block_count - 1; k >= 0; k--)
{
i = ar_block[k].end-volume_slip;
if (i < 0) i = 0;
j = ar_block[k].end+volume_slip;
if (j>frame_count) j = frame_count;
ar_block[k].volume = frame[i].volume;
for (a=i; a<j; a++)
if (frame[a].volume >= 0)
if (ar_block[k].volume > frame[a].volume)
{
ar_block[k].volume = frame[a].volume;
ar_block[k].end = a;
if (k < ar_block_count - 1)
ar_block[k+1].start = a;
}
}
}
for (k = 1; k < 255; k++)
{
if (volumeHistogram[k] > 10)
{
min_volume = (k-1)*volumeScale;
break;
}
}
for (k = 1; k < 255; k++)
{
if (uniformHistogram[k] > 10)
{
min_uniform = (k-1)*UNIFORMSCALE;
break;
}
}
for (k = 0; k < 255; k++)
{
if (brightHistogram[k] > 1)
{
min_brightness_found = k;
break;
}
}
if (max_volume > 0)
{
for (k = black_count - 1; k >= 0; k--)
{
if ((black[k].cause & C_t) != 0)
continue;
if (black[k].volume > max_volume
// &&
// ( black[k].frame > 10 && (int)frame[black[k].frame-2].brightness < (int)frame[black[k].frame].brightness + 50 &&
// black[k].frame < frame_count - 10 && (int)frame[black[k].frame+2].brightness < (int) frame[black[k].frame].brightness + 50 )
// || black[k].volume > max_volume * 1.5
)
{
Debug
(
12,
"%i - Removing black frame %i, from black frame list because volume %i is more than %i, brightness %i, uniform %i\n",
k,
black[k].frame,
black[k].volume,
max_volume,
black[k].brightness,
black[k].uniform
);
for (j = k; j < black_count - 1; j++)
{
black[j] = black[j + 1];
}
black_count--;
}
}
}
for (k = black_count - 1; k >= 0; k--)
{
if ((black[k].cause & C_t) != 0)
continue;
if ((black[k].cause & C_r) != 0)
continue;
if ((black[k].cause & C_b) && black[k].brightness > max_avg_brightness)
{
Debug
(
12,
"%i - Removing black frame %i, from black frame list because %i is more than %i, uniform %i\n",
k,
black[k].frame,
black[k].brightness,
max_avg_brightness,
black[k].uniform
);
for (j = k; j < black_count - 1; j++)
{
black[j] = black[j + 1];
}
black_count--;
}
}
if (non_uniformity > 0)
{
for (k = black_count - 1; k >= 0; k--)
{
if ((black[k].cause & C_t) != 0)
continue;
if ((black[k].cause & C_u) && black[k].uniform > non_uniformity)
{
Debug
(
12,
"%i - Removing uniform frame %i, from black frame list because %i is more than %i, brightness %i\n",
k,
black[k].frame,
black[k].uniform,
non_uniformity,
black[k].brightness
);
for (j = k; j < black_count - 1; j++)
{
black[j] = black[j + 1];
}
black_count--;
}
}
}
if (cut_on_ar_change==2)
{
if (logoPercentage > logo_fraction && logoPercentage < logo_percentile)
cut_on_ar_change = 1;
// else
// ar_wrong_modifier=1;
}
if (cut_on_ar_change==1)
{
// if (logoPercentage < logo_fraction || logoPercentage > logo_percentile)
// cut_on_ar_change = 2;
}
if (((commDetectMethod & LOGO) && cut_on_ar_change ) || cut_on_ar_change >= 2)
{
// if (cut_on_ar_change ) {
for (i = 0; i < ar_block_count; i++)
{
if ((cut_on_ar_change == 1 || ar_block[i].volume < max_volume) &&
ar_block[i].ar_ratio != AR_UNDEF && ar_block[i+1].ar_ratio != AR_UNDEF)
{
a = ar_block[i].end;
// if (a > 20 * fps)
InsertBlackFrame(a,frame[a].brightness,frame[a].uniform,frame[a].volume, C_a);
}
}
}
if ( cut_on_ac_change )
{
for (i = 0; i < ac_block_count; i++)
{
a = ac_block[i].end;
InsertBlackFrame(a,frame[a].brightness,frame[a].uniform,frame[a].volume, C_r);
}
}
if (ValidateBlackFrames(C_b, 3.0, false) < 1 / 3.0)
Debug(8, "Black Frame cutting too low\n");
if (validate_scenechange /* || (logoPercentage < logo_fraction || logoPercentage > logo_percentile) */)
ValidateBlackFrames(C_s, ((logoPercentage < logo_fraction || logoPercentage > logo_percentile) ? 1.2 : 3.5), true);
// ValidateBlackFrames(C_c, 3.0, true);
if (validate_uniform)
ValidateBlackFrames(C_u, ((logoPercentage < logo_fraction || logoPercentage > logo_percentile) ? 1.2 : 3.0), true);
if (commDetectMethod & SILENCE)
{
k = 0;
for (i = 0; i < frame_count; i++)
{
if (frame[i].volume < max_volume) k++;
}
/*
if (k * 100 / frame_count > 25) {
Debug(8, "Too mutch Silence Frames (%d%%), disabling silence detection\n", k * 100 / frame_count);
ValidateBlackFrames(C_v, 1.0, true);
commDetectMethod &= ~SILENCE;
validate_silence = 0;
} else
*/ if (validate_silence)
ValidateBlackFrames(C_v, 3.0, true);
}
// if (logoPercentage < logo_fraction)
// if (cut_on_ar_change == 2)
// ValidateBlackFrames(C_a, 3.0, true);
Debug(8, "Black Frame List\n---------------------------\nBlack Frame Count = %i\nnr \tframe\tpts\tbright\tuniform\tvolume\t\tcause\tdimcount bright type\n", black_count);
for (k = 0; k < black_count; k++)
{
Debug(8, "%3i\t%6i\t%8.3f\t%6i\t%6i\t%6i\t%6s\t%6i\t%6i\t%c\n", k, black[k].frame, get_frame_pts(black[k].frame), black[k].brightness, black[k].uniform, black[k].volume,&(CauseString(black[k].cause)[10]), frame[black[k].frame].dimCount, frame[black[k].frame].hasBright, frame[black[k].frame].pict_type);
if (k+1 < black_count && black[k].frame+1 != black[k+1].frame)
Debug(8, "-----------------------------\n");
}
// add black frame at end to enable usage of last cblock
InsertBlackFrame(framesprocessed,0,0,0,C_b);
/*
InitializeBlackArray(black_count);
black[black_count].frame = framesprocessed;
black[black_count].brightness = 0;
black[black_count].uniform = 0;
black[black_count].volume = 0;
black[black_count].cause = 0;
black_count++;
*/
//Create blocks
i = 0;
j = 0;
// if (((commDetectMethod & LOGO) && cut_on_ar_change ) || cut_on_ar_change == 2)
// a = 0;
// else
a = ar_block_count; // Don't cut on AR when logo disabled
cause = 0;
block_count = 0;
prev_start = 1;
prev_head = 0;
while(i < black_count || a < ar_block_count)
{
if (!(commDetectMethod & LOGO) && i < black_count && (black[i].cause & (C_s | C_l)))
{
// i++; // Skip logo cuts and brighness cuts when not enough logo detected
// goto again;
}
cause = 0;
b_start = black[i].frame;
cause |= black[i].cause;
b_end = b_start;
j = i + 1;
v_count = 0;
b_count = 0;
black_start = 0;
black_end = 0;
//Find end of next black cblock
while(j < black_count && (F2T(black[j].frame) - F2T(b_end) < 1.0 )) //Allow for 2 missing black frames
{
if (black[j].frame - b_end > 2 &&
(((black[j].cause & (C_v)) != 0 && (cause & (C_v)) == 0) ||
((black[j].cause & (C_v)) == 0 && (cause & (C_v)) != 0)))
{
Debug
(
6,
"At frame %i there is a gap of %i frames in the blackframe list\n",
black[j].frame,
black[j].frame - b_end
);
}
if ((black[j].cause & (C_b | C_s | C_u | C_r)) != 0)
{
b_count++;
if (black_start == 0)
black_start = black[j].frame;
black_end = black[j].frame;
}
if ((black[j].cause & (C_v)) != 0)
v_count++;
if (black[j].cause == C_a)
{
cause |= black[j].cause;
j++;
}
else if (cause == C_a)
{
cause |= black[j].cause;
b_start = b_end = black[j++].frame;
}
else
{
cause |= black[j].cause;
b_end = black[j++].frame;
}
}
i = j;
if (b_count > 0 && v_count > 1.5*b_count)
{
b_start = black_start;
b_end = black_end;
b_counted = b_count;
}
else if (b_count == 0 && v_count > 5)
{
b_start = b_start - 1 + v_count / 2;
b_end = b_end + 1 - v_count / 2;
b_counted = 3;
}
cblock[block_count].cause = cause;
//Do it this way for in roundoff problems
b_counted = (b_end - b_start + 1)/2;
cblock[block_count].b_head = prev_head;
cblock[block_count].f_start = prev_start - cblock[block_count].b_head;
if (b_end == framesprocessed)
cblock[block_count].f_end = framesprocessed;
else
cblock[block_count].f_end = b_start + b_counted - 1;
cblock[block_count].b_tail = b_counted; //half on the tail of this cblock
cblock[block_count].bframe_count = cblock[block_count].b_head + cblock[block_count].b_tail;
cblock[block_count].length = F2T(cblock[block_count].f_end) - F2T(cblock[block_count].f_start);
//If first cblock is < 1 sec. throw it away
if( block_count > 0 ||
F2L( cblock[block_count].f_end, cblock[block_count].f_start) > 1.0 ||
cblock[block_count].f_end == framesprocessed
)
{
Debug(12, "Creating cblock %i From %i (%i) to %i (%i) because of %s with %i head and %i tail\n",
block_count, cblock[block_count].f_start, (cblock[block_count].f_start + cblock[block_count].b_head),
cblock[block_count].f_end, (cblock[block_count].f_end - cblock[block_count].b_tail),
CauseString(cause),
cblock[block_count].b_head, cblock[block_count].b_tail);
block_count++;
InitializeBlockArray(block_count);
prev_start = b_end + 1; //cblock starts at end of black initially
prev_head = b_end - b_start - b_counted + 1; //remaining black from previous cblock tail
}
}
#if 1
//Combine blocks with less than minimum black between them
for (i = block_count-1; i >= 1; i--)
{
unsigned int bfcount = cblock[i].b_head + cblock[i-1].b_tail;
if (bfcount < min_black_frames_for_break && cblock[i-1].cause == C_b)
{
Debug(10, "Combining blocks %i and %i at %i because there are only %i black frames separating them.\n",
i-1, i, cblock[i-1].f_end , bfcount);
cblock[i-1].f_end = cblock[i].f_end;
cblock[i-1].b_tail = cblock[i].b_tail;
cblock[i-1].length = F2L(cblock[i-1].f_end, cblock[i-1].f_start);
cblock[i-1].cause = cblock[i].cause;
for (k = i; k < block_count-1; k++)
{
cblock[k] = cblock[k+1];
}
block_count--;
}
}
#endif
return (true);
}
void FindLogoThreshold()
{
int i;
int buckets = 20;
int counter = 0;
if (framearray)
{
for (i = 1; i < frame_count; i += 1 /*(int) fps */ )
{
logoHistogram[(int)(frame[i].currentGoodEdge * (buckets - 1))]++;
}
OutputLogoHistogram(buckets);
counter = 0;
for (i = 0; i < buckets; i++)
{
counter += logoHistogram[i];
if (100 * counter / frame_count > 40)
break;
}
if (i < buckets/2)
i = buckets * 3 / 4;
else
{
if (logoHistogram[i - 2] < logoHistogram[i])
i -= 2;
if (logoHistogram[i - 1] < logoHistogram[i])
i -= 1;
if (logoHistogram[i - 1] < logoHistogram[i])
i -= 1;
if (logoHistogram[i - 1] < logoHistogram[i])
i -= 1;
}
logo_quality = ((double) i + 0.5) / (double) buckets;
Debug(8, "Set Logo Quality = %.5f\n", logo_quality);
/*
j = 0;
for (i = 0; i < buckets/2; i++) {
j += logoHistogram[i];
}
k = 0;
for (i = buckets/2; i < buckets; i++) {
k += logoHistogram[i];
}
if (k < j * 1.3) {
logo_quality = 0.9;
} else {
k = logoHistogram[0];
for (i = 0; i < buckets; i++) {
if (logoHistogram[k] < logoHistogram[i]) {
k = i;
}
}
if (k < buckets * 2 / 3) {
logo_quality = 0.9;
} else {
i = 0;
j = logoHistogram[0];
for (i = buckets/2; i < k; i++) {
if (j * 10 / 8 >= logoHistogram[i]) {
j = logoHistogram[i];
logo_quality = ((double) i + 0.5) / (double) buckets;
}
}
}
}
*/
}
if (logo_threshold == 0)
{
logo_threshold = logo_quality;
}
}
void CleanLogoBlocks()
{
int i,k,n;
// double stdev;
int sum_brightness,v,b, sum_volume,s,sum_silence,sum_uniform;
double sum_brightness2;
int sum_delta;
#if 1
if ((commDetectMethod & LOGO /* || startOverAfterLogoInfoAvail==0 */ ) &&! reverseLogoLogic && connect_blocks_with_logo)
{
//Combine blocks with both logo
for (i = block_count-1; i >= 1; i--)
{
if (CheckFrameForLogo(cblock[i-1].f_end) &&
CheckFrameForLogo(cblock[i].f_start) )
{
Debug(6, "Joining blocks %i and %i at frame %i because they both have a logo.\n",
i-1, i, cblock[i-1].f_end);
cblock[i-1].f_end = cblock[i].f_end;
cblock[i-1].b_tail = cblock[i].b_tail;
if (cblock[i].length > cblock[i-1].length)
cblock[i-1].ar_ratio = cblock[i].ar_ratio; // Use AR of longest cblock
cblock[i-1].length = F2L(cblock[i-1].f_end, cblock[i-1].f_start);
cblock[i-1].cause = cblock[i].cause;
for (k = i; k < block_count-1; k++)
{
cblock[k] = cblock[k+1];
}
block_count--;
}
}
}
#endif
k = -1;
//Checking cblock size ratio
/*
for (i = 0; i < block_count; i++) {
if (F2L(cblock[i].f_end, cblock[i].f_start) > (int) min_show_segment_length )
{
if (k != -1 && i > k+1)
{
a = cblock[k].f_end - cblock[k].f_start;
j = cblock[i].f_start - cblock[k].f_end;
Debug(1, "Long/Short cblock ratio for cblock %i till %i is %i percent\n",k, i-1 , (int)(100 * a)/(a+j));
}
k = i;
}
}
*/
avg_brightness = 0;
avg_volume = 0;
maxi_volume = 0;
avg_silence = 0;
avg_uniform = 0;
avg_schange = 0.0;
for (i = 0; i < block_count; i++)
{
sum_brightness = 0;
sum_volume = 0;
sum_silence = 0;
sum_uniform = 0;
sum_brightness2 = 0.0;
sum_delta = 0;
if (framearray)
{
for (k = cblock[i].f_start+1; k < cblock[i].f_end; k++)
{
// b = frame[k].brightness;
b = abs(frame[k].brightness - frame[k-1].brightness);
v = frame[k].volume;
if (maxi_volume < v)
maxi_volume = v;
s = (frame[k].volume < max_volume ? 0 : 99);
sum_brightness += b;
sum_volume += v;
sum_silence += s;
sum_uniform += abs(frame[k].uniform - frame[k-1].uniform);
sum_brightness2 += b*b;
sum_delta += abs(frame[k].brightness - frame[k-1].brightness);
}
}
n = cblock[i].f_end - cblock[i].f_start+1;
if (n>0) {
cblock[i].brightness = sum_brightness * 1000 / n;
cblock[i].volume = sum_volume / n;
cblock[i].silence = sum_silence / n;
cblock[i].uniform = sum_uniform / n;
}
if ((cblock[i].schange_count = CountSceneChanges(cblock[i].f_start, cblock[i].f_end)))
{
cblock[i].schange_rate = (double)cblock[i].schange_count / n;
}
else
cblock[i].schange_rate = 0.0;
cblock[i].stdev =
// sqrt( (n*sum_brightness2 - sum_brightness*sum_brightness)/ (n * (n-1)));
100* sum_delta / n;
avg_brightness += sum_brightness * 1000;
avg_volume += sum_volume;
avg_silence += sum_silence;
avg_uniform += sum_uniform;
avg_schange += cblock[i].schange_rate*n;
}
n = cblock[block_count-1].f_end - cblock[0].f_start;
if (n>0) {
avg_brightness /= n;
avg_volume /= n;
avg_silence /= n;
avg_uniform /= n;
avg_schange /= n;
}
// Debug(1, "Average brightness is %i\n",avg_brightness);
// Debug(1, "Average volume is %i\n",avg_volume);
}
#define LOGO_BORDER 5
void InitScanLines()
{
int i;
for (i = 0; i < height; i++)
{
if (i < clogoMinY - LOGO_BORDER || i > clogoMaxY + LOGO_BORDER)
{
lineStart[i] = border;
lineEnd[i] = videowidth-1-border;
}
else
{
if ( clogoMinX > videowidth - clogoMaxX) // Most pixels left of the logo
{
lineStart[i] = border;
lineEnd[i] = MAX(0,clogoMinX-LOGO_BORDER);
}
else
{
lineStart[i] = MIN(videowidth-1,clogoMaxX+LOGO_BORDER);
lineEnd[i] = videowidth-1-border;
}
}
}
for (i = height; i < MAXHEIGHT; i++)
{
lineStart[i] = 0;
lineEnd[i] = 0;
}
}
void InitHasLogo()
{
int x,y;
memset(haslogo, 0, MAXWIDTH*MAXHEIGHT*sizeof(char));
for (y = MAX(0,clogoMinY - LOGO_BORDER); y < MIN(MAXHEIGHT,clogoMaxY + LOGO_BORDER); y++)
{
for (x = MAX(0,clogoMinX-LOGO_BORDER); x < MIN(MAXWIDTH,clogoMaxX + LOGO_BORDER) ; x++)
{
haslogo[y*width+x] = 1;
}
}
}
#define DEBUGFRAMES 80000
#ifdef _WIN32
#define GRAPH_P(X,Y,P) graph[3*(((oheight+30)-(Y))*owidth+(X))+P]
#else
#define GRAPH_P(X,Y,P) graph[3*((Y)*owidth+(X))+P]
#endif
#define GRAPH_R(X,Y) GRAPH_P(X,Y,2)
#define GRAPH_G(X,Y) GRAPH_P(X,Y,1)
#define GRAPH_B(X,Y) GRAPH_P(X,Y,0)
#define PIXEL(X,Y) GRAPH_R(X,Y) = GRAPH_G(X,Y) = GRAPH_B(X,Y)
#define SETPIXEL(X,Y, R,G,B) { GRAPH_R(X,Y) = (R); GRAPH_G(X,Y) = (G); GRAPH_B(X,Y) = (B); }
#define PLOT(S, I, X, Y, MAX, L, R,G,B) { int y, o; o = oheight - (oheight/(S))* (I); y = (Y)*(oheight/(S)-5)/(MAX); if (y < 0) y = 0; if (y > (oheight/(S)-1)) y = (oheight/(S)-1); SETPIXEL((X),(o - y),((Y) < (L) ? 255: R ) , ((Y) < (L) ? 255: G ) ,((Y) < (L) ? 255: B));}
#define LOGOBORDER 4*edge_step
#define LOGO_Y_LOOP int y_max_test = (subtitles? height/2 : (height - edge_radius - border - LOGOBORDER)); \
int y_step_test = height/3; \
for (y = (logo_at_bottom ? height/2 : edge_radius + border + LOGOBORDER); y < y_max_test; y = (y==y_step_test ? 2*height/3 : y+edge_step))
// #define LOGO_X_LOOP for (x = max(edge_radius + (int)(width * borderIgnore), minX+AR_DIST); x < min((width - edge_radius - (int)(width * borderIgnore)),maxX-AR_DIST); x += edge_step)
#define LOGO_X_LOOP for (x = edge_radius + border + LOGOBORDER; x < (videowidth - edge_radius - border- LOGOBORDER); x = (x==videowidth/3 ? 2*videowidth/3 : x+edge_step))
int oheight = 0;
int owidth = 0;
double divider = 1;
int oldfrm = -1;
int zstart = 0;
int zfactor = 1;
int show_XDS=0;
int show_silence=0;
int preMarkerFrame = 0;
int postMarkerFrame = 0;
void OutputDebugWindow(bool showVideo, int frm, int grf, bool forceRefresh)
{
#if defined(_WIN32) || defined(HAVE_SDL)
int i,j,x,y,a=0,c=0,r,s=0,g,gc,lb=0,e=0,n=0,bl,xd;
int v,w;
int bartop = 0;
int b,cb;
int barh = 32;
char t[1024];
char x1[80];
char x2[80];
char x3[80];
char x4[80];
char x5[80];
char *tt[40];
char tbuf[80][80];
char frametext[80];
bool blackframe, bothtrue, haslogo, uniformframe;
int silence=0;
// frm++;
if (!forceRefresh && oldfrm == frm)
return;
oldfrm = frm;
if (output_debugwindow && frame_count )
{
if (!vo_init_done)
{
if (width == 0 /*|| (loadingCSV && !showVideo) */)
videowidth = width = 800; // MAXWIDTH;
if (height == 0 /*|| (loadingCSV && !showVideo) */)
height = 600-barh; // MAXHEIGHT-30;
if (edge_step == 0) {
edge_step = 1;
}
if (height > 600 || width > 800)
{
oheight = height / 2;
owidth = width / 2 ;
owidth = videowidth / 2 ;
divider = 2;
}
else
if (height < 150 || width < 200)
{
divider = 0.25;
oheight = height / divider;
owidth = width / divider;
owidth = videowidth / divider;
} else
if (height < 300 || width < 400)
{
divider = 0.5;
oheight = height / divider;
owidth = width / divider;
owidth = videowidth / divider;
} else
{
oheight = height;
owidth = width;
owidth = videowidth;
divider = 1;
}
oheight = (oheight + 31) & -32;
owidth = (owidth + 31) & -32;
sprintf(t, windowtitle, filename);
vo_init(owidth, oheight+barh,t);
// vo_init(owidth, oheight+barh,"Comskip");
vo_init_done++;
}
// bartop = oheight;
if (frm >= frame_count)
frm = frame_count-1;
if (frm < 1)
frm = 1;
v = frame_count/zfactor;
if ( frm < zstart + v / 10) zstart = frm - v / 10;
if ( zstart < 0 ) zstart = 0;
if ( frm > v + zstart - v / 10) zstart = frm - v + v / 10;
if (zstart + v > frame_count) zstart = frame_count - v;
// if ( frm > v + zstart) zstart = frm - v;
w = ((frm - zstart)* owidth / v);
if (showVideo && frame_ptr)
{
memset(graph, 0, owidth*oheight*3);
/*
for (x = 0; x < border; x++) {
for (y = 0; y < oheight; y++) {
PIXEL(x,y+barh) = 0;
PIXEL(owidth - 1 - x,y+barh) = 0;
}
}
*/
for (x = 0+border; x < owidth-border; x++)
{
// for (y = 0; y < border; y++) {
// PIXEL(x,y+barh) = 0;
// PIXEL(x,oheight - 1 - (y+barh)) = 0;
// }
for (y = 0+border; y < oheight-border; y++)
{
if (x*divider < width && y*divider < height)
PIXEL(x,y+barh) = frame_ptr[((int)(y*divider))*(width)+(int)(x*divider)] >> (grf?1:0);
// PIXEL(x,y+barh) = min_br[(y*divider)*width+(x*divider)]; //MAXMIN Logo search
// PIXEL(x,y+barh) = vert_edges[(y*divider)*width+(x*divider)]; //Edge detect
// PIXEL(x,y+barh) = (ver_edgecount[(y*divider)*width+(x*divider)]* 4); // Edge count
/*
PIXEL(x,y+barh) =(abs((frame_ptr[(y*divider)*width+(x*divider)] +
frame_ptr[(y*divider)*width+((x+1)*divider)])/2
-
(frame_ptr[(y*divider)*width+((x+2)*divider)]+
frame_ptr[(y*divider)*width+((x+3)*divider)])/2
) > edge_level_threshold ? 200 : 0);
*/
// graph[((oheight - y)*owidth+x)*3+0] = frame_ptr[y*owidth+x];
// graph[((oheight - y)*owidth+x)*3+1] = frame_ptr[y*owidth+x];
// graph[((oheight - y)*owidth+x)*3+2] = frame_ptr[y*owidth+x];
}
}
// memcpy(&graph[owidth*oheight * 0], frame_ptr, owidth*oheight);
// memcpy(&graph[owidth*oheight * 1], frame_ptr, owidth*oheight);
// memcpy(&graph[owidth*oheight * 2], frame_ptr, owidth*oheight);
if (framearray && grf && ((commDetectMethod & LOGO) || logoInfoAvailable ))
{
if (aggressive_logo_rejection)
s = edge_radius/2; // Cater of mask offset
else
s = 0;
// w = 0;
// v = 0;
if (logoInfoAvailable) // Show logo mask
{
if (frame[frm].currentGoodEdge > logo_threshold)
{
e = (int)(frame[frm].currentGoodEdge * 250);
for (y = clogoMinY; y <= clogoMaxY ; y += edge_step)
{
for (x = clogoMinX; x <= clogoMaxX ; x += edge_step)
{
if (choriz_edgemask[y * width + x]) r = 255;
else r = 0;
if (cvert_edgemask[y * width + x]) g = 255;
else g = 0;
if (r || g) SETPIXEL(((int)((x-s)/divider)),((int)((y-s)/divider))+barh,r,g,0);
}
}
}
}
else // Show detected logo pixels only while scanning input
{
if (frm+1 == frame_count)
{
LOGO_X_LOOP
{
LOGO_Y_LOOP
{
if (edgemask_filled) {
r = 255 * thoriz_edgemask[(y) * width + (x)];
g = 255 * tvert_edgemask[(y) * width + (x)];
} else {
r = 255 * hor_edgecount[(y) * width + (x)] / num_logo_buffers;
g = 255 * ver_edgecount[(y) * width + (x)] / num_logo_buffers;
}
if (r > 255) r = 255;
if (g > 255) g = 255;
//if (r > 128 || g > 128)
SETPIXEL(((int)(x/divider)),((int)(y/divider))+barh,r,g,0);
#ifdef xxxxxx
for (y = s; y < oheight; y++)
{
for (x = s ; x < owidth; x++)
{
/*
if (hor_edgecount[(y*divider) * width + (x*divider)] >= num_logo_buffers*2/3) r = 255;
else r = 0;
if (ver_edgecount[(y*divider) * width + (x*divider)] >= num_logo_buffers*2/3) g = 255;
else g = 0;
*/
if (edgemask_filled) {
r = 255 * thoriz_edgemask[((int)((y*divider))) * width + ((int)((x*divider)))] / num_logo_buffers;
g = 255 * tvert_edgemask[((int)((y*divider))) * width + ((int)((x*divider)))] / num_logo_buffers;
} else {
r = 255 * hor_edgecount[((int)((y*divider))) * width + ((int)((x*divider)))] / num_logo_buffers;
g = 255 * ver_edgecount[((int)((y*divider))) * width + ((int)((x*divider)))] / num_logo_buffers;
}
if (r > 255) r = 255;
if (g > 255) g = 255;
if (r > 128 || g > 128) SETPIXEL(x-s,y-s+barh,r,g,0);
}
}
#endif
}
}
}
}
}
}
else
{
memset(graph, 0, owidth*oheight*3);
}
if (framearray && grf)
{
for (y=0; y < oheight; y++)
{
SETPIXEL(w,y,100,100,100);
}
bl = 0;
for (x=0 ; x < owidth; x++) // debug bar
{
a = 0;
b = 0;
s = 0;
c = 0;
n = 0;
if (block_count && grf == 2)
{
while (bl < block_count && cblock[bl].f_end < zstart+(int)((double)x * v /owidth))
bl++;
#define PLOTS 9
PLOT(PLOTS, 0, x, cblock[bl].brightness, 2550, (int)(avg_brightness*punish_threshold), 0, 255, 0); // RED
PLOT(PLOTS, 1, x, cblock[bl].volume/100, 100000, (int)(avg_volume*punish_threshold)/100, 255, 0, 0); // Green
PLOT(PLOTS, 2, x, cblock[bl].uniform, 3000, (int)(avg_uniform*punish_threshold), 255, 0,0); // RED
PLOT(PLOTS, 3, x, (int)(cblock[bl].schange_rate*1000), 1000, (int)(avg_schange*punish_threshold*1000), 255, 0, 0); // PURPLE
}
for (i = zstart+(int)((double)x * v /owidth); i < zstart+(int)((double)(x+1) * v /owidth ); i++)
{
if (i <= frame_count)
{
b += frame[i].brightness;
PLOT(PLOTS, 0, x, frame[i].brightness, 255, max_avg_brightness, 255, 0,0); // RED
s += frame[i].volume;
PLOT(PLOTS, 1, x, frame[i].volume, 10000, max_volume, (frame[i].audio_channels * 40), 255, 0); // GREEN
e += frame[i].uniform;
PLOT(PLOTS, 2, x, frame[i].uniform, 30000, non_uniformity, 0, 255, 255); // LIGHT BLUE
c += (int)(frame[i].currentGoodEdge*100);
PLOT(PLOTS, 4, x, (int)(frame[i].currentGoodEdge*100), 100, 0, 255, 255, 0); // YELLOW
PLOT(PLOTS, 4, x, (int)(frame[i].logo_filter*50+50), 100, 0, (frame[i].logo_filter < 0.0 ?255:0) , (frame[i].logo_filter < 0.0 ?0:255), 0);
PLOT(PLOTS, 5, x, (int)((frame[i].ar_ratio-0.5) * 100), 250, 0, 0, 0, 255); // BLUE
if (commDetectMethod & CUTSCENE)
{
PLOT(PLOTS, 3, x, (int)(frame[i].cutscenematch), 100, cutscenedelta, 255, 0, 255); // PURPLE
}
else
{
PLOT(PLOTS, 3, x, (int)(frame[i].schange_percent), 100, schange_cutlevel, 255, 0, 255); // PURPLE
}
a += frame[i].maxY;
PLOT(PLOTS, 6, x, frame[i].maxY, height, 0, 0, 128, 128);
b += frame[i].minY;
PLOT(PLOTS, 6, x, frame[i].minY, height, 0, 0, 128, 128);
n++;
PLOT(PLOTS, 7, x, frame[i].maxX, width, 0, 0, 0, 255);
PLOT(PLOTS, 7, x, frame[i].minX, width, 0, 0, 0, 255);
}
}
if (n > 0)
{
a /= n;
// f /= n;
b /= n;
s /= n;
c /= n;
e /= n;
}
// PLOT(S, I, X, Y, MAX, L, R,G,B)
}
}
if (frame_ptr && framearray)
{
// for (x=0; x < owidth; x++) { // Edge counter indicator
// graph[2* owidth + x] = (x < edge_count /8 ? 255 : 0);
// }
x = frame[frm].maxX/divider;
if (x == 0) x = owidth;
for (i=frame[frm].minX/divider; i < x; i++) // AR lines
{
SETPIXEL(i, ((int)((frame[frm].minY/divider)))+barh, 0,0,255);
SETPIXEL(i, ((int)((frame[frm].maxY/divider)+barh)), 0,0,255);
// graph[frame[frm].minY* owidth + i] = 255;
// graph[frame[frm].maxY* owidth + i] = 255;
}
for (i=(frame[frm].minY/divider); i < (frame[frm].maxY/divider); i++) // AR lines
{
SETPIXEL(((int)((frame[frm].minX/divider))), i+barh, 0,0,255);
SETPIXEL(((int)((frame[frm].maxX/divider))), i+barh, 0,0,255);
}
}
if (framearray /* && commDetectMethod & LOGO */ )
{
#define SHOWLOGOBOXWHILESCANNING
#ifdef SHOWLOGOBOXWHILESCANNING
for (x = tlogoMinX/divider; x < tlogoMaxX/divider; x++) // Logo box X
{
SETPIXEL(x,((int)(tlogoMinY/divider))+barh,255,e,e);
SETPIXEL(x,((int)(tlogoMaxY/divider))+barh,255,e,e);
}
for (y = tlogoMinY/divider; y < tlogoMaxY/divider; y++) // Logo box Y
{
SETPIXEL(((int)(tlogoMinX/divider)),y+barh,255,e,e);
SETPIXEL(((int)(tlogoMaxX/divider)),y+barh,255,e,e);
}
#else
for (x = clogoMinX/divider; x < clogoMaxX/divider; x++) // Logo box X
{
SETPIXEL(x,clogoMinY/divider+barh,255,e,e);
SETPIXEL(x,clogoMaxY/divider+barh,255,e,e);
}
for (y = clogoMinY/divider; y < clogoMaxY/divider; y++) // Logo box Y
{
SETPIXEL(clogoMinX/divider,y+barh,255,e,e);
SETPIXEL(clogoMaxX/divider,y+barh,255,e,e);
}
#endif // SHOWLOGOBOXWHILESCANNING
}
b = 0;
for (i = 0; i < block_count; i++)
{
if (cblock[i].f_start <= frm && frm <= cblock[i].f_end)
{
b = i;
break;
}
}
/*
memset(graph,20,owidth*(oheight+30)*3);
for (i=0; i<oheight/2;i++) {
graph[(i*(owidth+0))*3] = 255;
graph[(i*(owidth+0))*3+1] = 0;
graph[(i*(owidth+0))*3+2] = 0;
}
*/
// if (0) // disable debug bar
for (x=0 ; x < owidth; x++) // debug bar
{
blackframe = false;
uniformframe = false;
silence = 0;
haslogo = false;
bothtrue = false;
g = 0;
gc = 0;
xd = 0;
// v = max(frame_count, DEBUGFRAMES);
if (framearray)
{
xd = XDS_block_count-1;
while (xd > 0 && XDS_block[xd].frame > zstart+(int)((double)(x+1) * v /owidth) )
xd--;
if (!(xd > 0 && XDS_block[xd].frame >= zstart+(int)((double)x * v /owidth)))
xd = 0;
for (i = zstart+(int)((double)x * v /owidth); i < zstart+(int)((double)(x+1) * v /owidth ); i++)
{
if (i <= frame_count)
{
if ((frame[i].isblack & C_b) || (frame[i].isblack & C_r))
{
blackframe = true;
for (j = 0; j < block_count; j++)
{
if (cblock[j].f_end == i)
bothtrue = true;
}
}
if (frame[i].isblack & C_u)
{
uniformframe = true;
}
if (frame[i].volume < max_volume && silence < 1) silence = 1;
if ((frame[i].volume < 50 || frame[i].volume < max_silence) && silence < 2) silence = 2;
if (frame[i].volume < 9) silence = 3;
if (frame[i].volume < 9) silence = 3;
if ((frame[i].isblack & C_v)) silence = 3;
if (frame[i].volume == 0) silence = 4;
if ((frame[i].isblack & C_b) && frame[i].volume < max_volume) bothtrue = true;
if ((frame[i].isblack & C_r)) bothtrue = true;
if (frm+1 == frame_count) // Show details of logo while scanning
{
if (frame[i].logo_present) haslogo = true;
}
else
{
while (lb < logo_block_count && i > logo_block[lb].end) // Show logo blocks when finished
lb++;
if (lb < logo_block_count && i >= logo_block[lb].start)
{
haslogo=true;
}
}
// if (frame[i].currentGoodEdge > logo_threshold) haslogo = true;
a = (int)((frame[i].ar_ratio - 0.5 - 0.1)*6); // Position of AR line
g += (int)(frame[i].currentGoodEdge * 5);
gc++;
}
}
}
if (gc > 0)
g /= gc;
c = 255;
if (c == 255)
{
for (i = 0; i <= commercial_count; i++) // Inside commercial?
{
if (zstart+(int)((double)x * v /owidth ) >= commercial[i].start_frame &&
zstart+(int)((double)x * v /owidth ) <= commercial[i].end_frame )
{
c = 128;
break;
}
}
}
if (c == 255) // not in a commercial but score above threshold
{
for (i = 0; i < block_count; i++)
{
if (zstart+(int)((double)x * v /owidth ) >= cblock[i].f_start &&
zstart+(int)((double)x * v /owidth ) <= cblock[i].f_end &&
cblock[i].score > global_threshold )
{
c = 220;
break;
}
}
}
r = 255;
for (i = 0; i <= reffer_count; i++) // Inside reference?
{
if (zstart+(int)((double)x * v /owidth ) >= reffer[i].start_frame &&
zstart+(int)((double)x * v /owidth ) <= reffer[i].end_frame )
{
r = 0;
break;
}
}
a = bartop + 14 - a;
for (y = bartop+5; y < bartop+15 ; y++) // Commercial / AR bar
if (y == a)
{
SETPIXEL(x,y,0,0,255);
}
else
{
SETPIXEL(x,y,c,c,c);
// PIXEL(x,y) = c;
}
g = 5; // Disable goodEdge graph
for (i = 0; i < block_count; i++)
{
if (zstart+(int)((double)x * v /owidth ) >= cblock[i].f_start &&
zstart+(int)((double)x * v /owidth ) <= cblock[i].f_end &&
cblock[i].correlation > 0 ) // if inside a correlated cblock
{
g=2;
break;
}
}
for (y = bartop + 15; y < bartop+20 ; y++) // Logo bar
{
if (haslogo) PIXEL(x,y) = ((y - (bartop + 15) == g)?255:((commDetectMethod & LOGO)? 0 : 128));
else PIXEL(x,y) = ((y - (bartop + 15) == g)?0:255);
// if (y - (bartop + 15) == g) graph[y * owidth + x] = 128;
}
cb = 255;
if (block_count && cblock[b].f_start <= zstart+(int)((double)x * v /owidth ) && zstart+(int)((double)x * v /owidth ) <= cblock[b].f_end)
cb = 0;
if (bothtrue)
c = 0;
else
c = 255;
for (y = bartop + 20; y < bartop+25 ; y++) // Blackframe bar
{
if (blackframe)
{
SETPIXEL(x,y,c,0,0);
}
else if (uniformframe)
{
SETPIXEL(x,y,0,0,c);
}
else
{
SETPIXEL(x,y,255,cb,255);
}
}
c = 255;
for (y = bartop + 25; y < bartop+30 ; y++) // Silence bar
{
if (silence == 1)
{
SETPIXEL(x,y,0,c,0);
}
else if (silence == 2)
{
SETPIXEL(x,y,0,0,c);
}
else if (silence == 3)
{
SETPIXEL(x,y,c,0,0);
}
else if (silence == 4)
{
SETPIXEL(x,y,c,c,0);
}
else
PIXEL(x,y) = 255;
}
if (w < owidth) // Progress indicator
for (y = bartop; y < bartop+30 ; y++) SETPIXEL(w,y,255,0,0);
if (preMarkerFrame > 0)
{
int showMkrX = ((preMarkerFrame - zstart)* owidth / v);
for (y = bartop; y < bartop+30 ; y++) SETPIXEL(showMkrX,y,0,255,0);
}
if (postMarkerFrame > 0)
{
int comMkrX = ((postMarkerFrame - zstart)* owidth / v);
for (y = bartop; y < bartop+30 ; y++) SETPIXEL(comMkrX,y,0,0,255);
}
for (y = bartop; y < bartop+(loadingTXT?20:5) ; y++)
{
// Reference bar
if (xd)
{
SETPIXEL(x,y,128,128,128);
}
else if (reffer_count >= 0) PIXEL(x,y) = r;
}
}
vo_draw(graph);
// sprintf(t, "%8i %8i %1s %1s", frm, framenum_infer, (frame[frm].isblack?"B":" "), (frame[frm].volume<max_volume?"S":" "));
b = 0;
for (i = 0; i < block_count; i++)
{
if (cblock[i].f_start <= frm && frm <= cblock[i].f_end)
{
b = i;
break;
}
}
if (timeflag == 2 && framearray)
{
sprintf(frametext, "%8.2f", F2T(frm));
}
else
if (timeflag == 1 && framearray)
{
sprintf(frametext, "%s", dblSecondsToStrMinutes(F2T(frm)));
}
else
{
sprintf(frametext, "%8i", frm);
}
if (recalculate)
{
sprintf(t, "max_volume=%d, non_uniformity=%d, max_avg_brightness=%d", max_volume, non_uniformity, max_avg_brightness);
}
else
{
if (framearray)
{
if (b < block_count)
sprintf(t, "%s B=%i%1s V=%i%1s U=%i%1s AR=%4.2f Block #%i Length=%6.2fs Score=%6.2f Logo=%6.2f %s ", frametext, frame[frm].brightness, (frame[frm].isblack & C_b?"B":" "), frame[frm].volume, (frame[frm].volume<max_volume?"S":" "),frame[frm].uniform, (frame[frm].uniform<non_uniformity?"U":" "), frame[frm].ar_ratio, b, cblock[b].length, cblock[b].score, cblock[b].logo, CauseString(cblock[b].cause)
);
else
sprintf(t, "%s B=%i%1s V=%i%1s U=%i%1s AR=%4.2f ", frametext, frame[frm].brightness, (frame[frm].isblack & C_b?"B":" "), frame[frm].volume, (frame[frm].volume<max_volume?"S":" "),frame[frm].uniform, (frame[frm].uniform<non_uniformity?"U":" "), frame[frm].ar_ratio);
}
else
sprintf(t, "%s", frametext);
}
if (soft_seeking)
{
tt[0] = t;
tt[1] = "WARNING: Seeking inaccurate, do not use for cutpoint review!";
tt[2] = 0;
ShowHelp(tt);
}
else if (helpflag)
ShowHelp(helptext);
else if (show_XDS && XDS_block_count)
{
tt[0] = t;
i = (XDS_block_count > 0 ? XDS_block_count-1 : 0);
while (i > 0 && XDS_block[i].frame > frm)
i--;
sprintf(x1,"Program Name : %s", XDS_block[i].name);
tt[1] = x1;
sprintf(x2,"Program V-Chip : %4x", XDS_block[i].v_chip);
tt[2] = x2;
sprintf(x3,"Program duration: %2d:%02d", (XDS_block[i].duration & 0x3f00)/256, (XDS_block[i].duration & 0x3f) % 256);
tt[3] = x3;
sprintf(x4,"Program position: %2d:%02d", (XDS_block[i].position & 0x3f00)/256, (XDS_block[i].position & 0x3f) % 256);
tt[4] = x4;
sprintf(x5,"Composite Packet: %2d:%02d, %2d/%2d, ", (XDS_block[i].composite1 & 0x3f00)/256, (XDS_block[i].composite1 & 0x1f) % 256, (XDS_block[i].composite2 & 0x1f00)/256, (XDS_block[i].composite2 & 0x0f) % 256);
tt[5] = x5;
tt[6] = 0;
ShowHelp(tt);
}
else if (show_silence)
{
for (i=0; i<25; i++)
{
tt[i] = tbuf[i];
sprintf(tt[i],"volume[%i] = %i", i, silenceHistogram[i]);
}
tt[i] = 0;
ShowHelp(tt);
}
else
ShowDetails(t);
}
if (key == 0x20)
{
subsample_video = 0;
key = 0;
}
if (key == 27)
{
exit(1);
}
if (key == 'G')
{
subsample_video = 0x3f;
key = 0;
}
if (subsample_video == 0)
{
// Enable for single stepping trough the video
if (!vo_init_done)
{
if (width == 0 /*|| (loadingCSV && !showVideo) */)
videowidth = width = 800; // MAXWIDTH;
if (height == 0 /*|| (loadingCSV && !showVideo) */)
height = 600-barh; // MAXHEIGHT-30;
if (height > 600 || width > 800)
{
oheight = height / 2;
owidth = width / 2;
divider = 2;
}
else
{
oheight = height;
owidth = width;
divider = 1;
}
owidth = (owidth + 31) & -32;
sprintf(t, windowtitle, filename);
vo_init(owidth, oheight+barh,t);
// vo_init(owidth, oheight+barh,"Comskip");
vo_init_done++;
}
while(key == 0)
vo_draw(graph);
if (key == 27)
{
exit(1);
}
if (key == 'G')
{
subsample_video = 0x3f;
}
key = 0;
}
recalculate = 0;
#endif
}
static int shift = 0;
void Recalc()
{
BuildBlocks(true);
if (commDetectMethod & LOGO)
{
PrintLogoFrameGroups();
}
WeighBlocks();
OutputBlocks();
}
bool ReviewResult()
{
FILE *review_file = NULL;
int curframe = 1;
int lastcurframe = -1;
int bartop = 0;
int grf = 2;
int i,j;
long prev;
char tsfilename[MAX_PATH];
if (!framearray) grf = 0;
output_demux = 0;
output_data = 0;
output_srt = 0;
output_smi = 0;
if (!review_file && mpegfilename[0])
review_file = myfopen(mpegfilename, "rb");
if (review_file == 0 )
{
strcpy(tsfilename, mpegfilename);
i = strlen(tsfilename);
while (i > 0 && tsfilename[i-1] != '.') i--;
tsfilename[i] = 't';
tsfilename[i+1] = 's';
tsfilename[i+2] = 0;
review_file = myfopen(tsfilename, "rb");
if (review_file)
{
demux_pid = 1;
strcpy(mpegfilename, tsfilename);
}
}
if (review_file == 0 )
{
strcpy(tsfilename, mpegfilename);
i = strlen(tsfilename);
while (i > 0 && tsfilename[i-1] != '.') i--;
strcpy(&tsfilename[i], "dvr-ms");
review_file = myfopen(tsfilename, "rb");
if (review_file)
{
demux_asf = 1;
strcpy(mpegfilename, tsfilename);
}
}
while(true)
{
// Indicates whether to force the debug window to refresh even if the current frame does not change.
bool forceRefresh = false;
if (key != 0)
{
if (key == 27) if (!helpflag) exit(0);
if (key == 112)
{
helpflag = 1; // F1 Key
oldfrm = -1;
}
else
{
if (helpflag == 1)
{
helpflag = 0;
oldfrm = -1;
}
}
if (key == 16)
{
shift = 1;
}
if (key == 37) curframe -= 1;
if (key == 39) curframe += 1;
if (key == 38) curframe -= (int)fps;
if (key == 40) curframe += (int)fps;
if (key == 33) curframe -= (int)(20*fps);
if (key == 34) curframe += (int)(20*fps);
if (key == 78 || (key == 39 && shift)) // Next key
{
curframe += 5;
if (framearray)
{
i = 0;
while (i <= commercial_count && curframe > commercial[i].end_frame) i++;
// if (i > 0)
curframe = commercial[i].end_frame+5;
// while (curframe < frame_count && frame[curframe].isblack) curframe++;
// while (curframe < frame_count && !frame[curframe].isblack) curframe++;
// while (curframe < frame_count && frame[curframe].isblack) curframe++;
}
else
{
i = 0;
while (i <= reffer_count && curframe > reffer[i].end_frame) i++;
// if (i > 0)
curframe = reffer[i].end_frame+5;
}
curframe -= 5;
}
if (key == 80 || (key == 37 && shift)) // Prev key
{
curframe -= 5;
if (framearray)
{
i = commercial_count;
while (i >= 0 && curframe < commercial[i].start_frame) i--;
// if (i > 0)
curframe = commercial[i].start_frame-5;
// while (curframe > 1 && frame[curframe].isblack) curframe--;
// while (curframe > 1 && !frame[curframe].isblack) curframe--;
// while (curframe > 1 && frame[curframe].isblack) curframe--;
}
else
{
i = reffer_count;
while (i >= 0 && curframe < reffer[i].start_frame) i--;
// if (i > 0)
curframe = reffer[i].start_frame-5;
}
curframe += 5;
}
if (key == 'S')
{
if (framearray)
{
curframe = 0;
}
else
{
curframe = 0;
}
}
if (key == 'F')
{
if (framearray)
{
curframe = frame_count;
}
else
{
curframe = frame_count;
}
}
if (key == 'E') // End key
{
if (framearray)
{
curframe += 10;
i = 0;
while (i < block_count && curframe > cblock[i].f_end) i++;
// if (i > 0)
curframe = cblock[i].f_end+5;
curframe -= 10;
}
else
{
i = reffer_count;
while (i >= 0 && curframe < reffer[i].start_frame) i--;
if (i >= 0)
reffer[i].end_frame = curframe;
oldfrm = -1;
}
}
if (key == 'B') // begin key
{
if (framearray)
{
curframe -= 10;
i = block_count-1;
while (i > 0 && curframe < cblock[i].f_start) i--;
// if (i > 0)
curframe = cblock[i].f_start-5;
curframe += 10;
}
else
{
i = 0;
while (i <= reffer_count && curframe > reffer[i].end_frame) i++;
if (i <= reffer_count)
reffer[i].start_frame = curframe;
oldfrm = -1;
}
}
if (key == 'T') // Toggle key
{
if (framearray)
{
i = 0;
while (i < block_count && curframe > cblock[i].f_end) i++;
if (i < block_count)
{
if (cblock[i].score < global_threshold)
cblock[i].score = 99.99;
else
cblock[i].score = 0.01;
cblock[i].cause |= C_F;
oldfrm = -1;
BuildCommercial();
key = 'W'; // Trick to cause writing of the new commercial list
}
}
}
if (key == 68) // Delete key
{
if (framearray)
{
i = 0;
while (i < block_count && curframe > cblock[i].f_end) i++;
if (i < block_count)
{
cblock[i].score = 99.99;
cblock[i].cause |= C_F;
oldfrm = -1;
BuildCommercial();
}
}
else
{
i = reffer_count;
while (i >= 0 && curframe < reffer[i].start_frame) i--;
if (i >= 0 && reffer[i].start_frame <= curframe && curframe <= reffer[i].end_frame )
{
while (i < reffer_count)
{
reffer[i] = reffer[i+1];
i++;
}
reffer_count--;
oldfrm = -1;
}
}
}
if (key == 73) // Insert key
{
if (framearray)
{
i = 0;
while (i < block_count && curframe > cblock[i].f_end) i++;
if (i < block_count)
{
cblock[i].score = 0.01;
cblock[i].cause |= C_F;
oldfrm = -1;
BuildCommercial();
}
}
else
{
i = reffer_count;
while (i >= 0 && curframe < reffer[i].start_frame) i--;
if (i == -1 || curframe > reffer[i].end_frame ) //Insert BEFORE i
{
j = reffer_count;
while (j > i)
{
reffer[j+1] = reffer[j];
j--;
}
reffer[i+1].start_frame = max(curframe-1000,1);
reffer[i+1].end_frame = min(curframe+1000,frame_count);
reffer_count++;
oldfrm = -1;
}
}
}
if (key == 'W') // W key
{
output_default = true;
OpenOutputFiles();
if (framearray)
{
prev = -1;
for (i = 0; i <= commercial_count; i++)
{
OutputCommercialBlock(i, prev, commercial[i].start_frame, commercial[i].end_frame, (commercial[i].end_frame < frame_count-2 ? false : true));
prev = commercial[i].end_frame;
}
if (commercial[commercial_count].end_frame < frame_count-2)
OutputCommercialBlock(commercial_count, prev, frame_count-2, frame_count-1, true);
}
else
{
prev = -1;
for (i = 0; i <= reffer_count; i++)
{
OutputCommercialBlock(i, prev, reffer[i].start_frame, reffer[i].end_frame, (reffer[i].end_frame < frame_count-2 ? false : true));
prev = reffer[i].end_frame;
}
if (reffer[reffer_count].end_frame < frame_count-2)
OutputCommercialBlock(reffer_count, prev, frame_count-2, frame_count-1, true);
}
output_default = false;
oldfrm = -1;
}
if (key == 'Z')
{
if (zfactor < 256 && frame_count / zfactor > owidth)
{
// i = (curframe - zstart) * zfactor * owidth/ frame_count;
zfactor = zfactor << 1;
// zstart = i * frame_count / owidth / zfactor;
zstart = (curframe + zstart) / 2;
oldfrm = -1;
}
}
if (key == 'U')
{
if (zfactor > 1)
{
// i = (curframe - zstart) * zfactor * owidth/ frame_count;
zfactor = zfactor >> 1;
// zstart = i * frame_count / owidth / zfactor;
zstart = zstart - (curframe - zstart);
if (zstart < 0)
zstart = 0;
oldfrm = -1;
}
}
if (key == 'C')
{
RecordCutScene(curframe, frame[curframe].brightness);
}
if (key == 'X')
{
show_XDS = !show_XDS;
oldfrm = -1;
}
if (key == 'V')
{
show_silence = !show_silence;
oldfrm = -1;
}
if (key == 'G')
{
grf++;
if (grf > 2)
grf = 0;
oldfrm = -1;
}
if (key == 113) // F2 key
{
max_volume = (int)(max_volume / 1.1);
Recalc();
oldfrm = -1;
}
if (key == 114) // F3 key
{
non_uniformity = (int)(non_uniformity / 1.1);
Recalc();
oldfrm = -1;
}
if (key == 115) // F4 key
{
max_avg_brightness = (int)(max_avg_brightness / 1.1);
Recalc();
oldfrm = -1;
}
if (key == 116) // F5 key
{
timeflag++;
if (timeflag > MAXTIMEFLAG)
timeflag = 0;
oldfrm = -1;
}
if (key == '.')
{
oldfrm = -1;
}
if (key == 'J')
{
// Handle the user setting the before marker frame.
preMarkerFrame = curframe;
if (postMarkerFrame > 0 && preMarkerFrame > 0)
{
long midpoint = ((long)postMarkerFrame + (long)preMarkerFrame) / 2l;
curframe = (int)midpoint;
}
forceRefresh = true;
}
if (key == 'K')
{
// Handle the user setting the after marker frame.
postMarkerFrame = curframe;
if (postMarkerFrame > 0 && preMarkerFrame > 0)
{
long midpoint = ((long)postMarkerFrame + (long)preMarkerFrame) / 2l;
curframe = (int)midpoint;
}
forceRefresh = true;
}
if (key == 'L')
{
// Handle the user clearing the markers.
preMarkerFrame = 0;
postMarkerFrame = 0;
forceRefresh = true;
}
if (key == 82) return(true);
if (key != 16) shift = 0;
key = 0;
}
if (lMouseDown)
{
if (yPos >= bartop && yPos < bartop + 30)
curframe = zstart+frame_count * xPos / owidth / zfactor + 1;
lMouseDown = 0;
}
if (curframe < 1) curframe = 1;
if (frame_count > 0)
{
if (curframe >= frame_count) curframe = frame_count-1;
}
if (frame_count > 0 && review_file)
if (curframe!= lastcurframe)
{
DecodeOnePicture(review_file, (framearray ? F2T(curframe) : (double)curframe / fps));
lastcurframe = curframe;
}
OutputDebugWindow((review_file ? true : false),curframe, grf, forceRefresh);
#if defined(_WIN32) || defined(HAVE_SDL)
vo_wait();
#endif
}
return false;
}
int DetectCommercials(int f, double pts)
{
bool isBlack = 0; /*Gil*/
int i,j;
long oldBlack_count;
if (loadingTXT)
return(0);
if (loadingCSV)
return(0);
if (!initialized)
InitComSkip();
// frame_count++;
frame_count = framenum_real = framenum+1;
//Debug(1, "Frame info f=%d, framenum=%d, framenum_real=%d, frame_count=%d\n",f, framenum, framenum_real, frame_count, max_frame_count);
avg_fps = 1.0/ (pts / frame_count);
if (framenum_real < 0) return 0;
if (play_nice) Sleep(play_nice_sleep);
if (framearray) InitializeFrameArray(framenum_real);
// curvolume = RetreiveVolume(framenum_real);
//curvolume = RetreiveVolume(frame_count);
if (pts < 0.0)
pts = 0.0;
frame[frame_count].pts = pts;
frame[frame_count].pict_type = pict_type;
if (frame_count == 1)
frame[0].pts = pts;
// curvolume = retreive_frame_volume(get_frame_pts(frame_count-1), get_frame_pts(frame_count));
frame[frame_count].volume = -1;
backfill_frame_volumes();
curvolume = frame[frame_count].volume;
// if (frame_count != framenum_real)
// Debug(0, "Inconsistent frame numbers\n");
if (framearray)
{
frame[frame_count].volume = curvolume;
frame[frame_count].goppos = headerpos;
frame[frame_count].cur_segment = debug_cur_segment;
frame[frame_count].audio_channels = audio_channels;
}
if (curvolume > 0)
{
volumeHistogram[(curvolume/volumeScale < 255 ? curvolume/volumeScale : 255)]++;
}
if (ticker_tape_percentage>0)
ticker_tape = ticker_tape_percentage * height / 100;
if (ticker_tape)
{
memset(&frame_ptr[width*(height - ticker_tape)], 0, width*ticker_tape);
}
if (ignore_side)
{
for (i = 0; i < height; i++)
{
for (j = 0; j < ignore_side; j++)
{
frame_ptr[width*i + j] = 0;
frame_ptr[width*i + (width -1) - j] = 0;
}
}
}
if (ignore_left_side)
{
for (i = 0; i < height; i++)
{
for (j = 0; j < ignore_left_side; j++)
{
frame_ptr[width*i + j] = 0;
}
}
}
if (ignore_right_side)
{
for (i = 0; i < height; i++)
{
for (j = 0; j < ignore_right_side; j++)
{
frame_ptr[width*i + (width -1) - j] = 0;
}
}
}
oldBlack_count = black_count; /*Gil*/
CheckSceneHasChanged();
isBlack = oldBlack_count != black_count; /*Gil*/
if ((commDetectMethod & LOGO) && ((frame_count % (int)(fps * logoFreq)) == 0))
{
if (!logoInfoAvailable || (!lastLogoTest && !startOverAfterLogoInfoAvail) )
{
if (delay_logo_search == 0 ||
(delay_logo_search == 1 && F2T(frame_count) > added_recording * 60) ||
(F2T(frame_count) > delay_logo_search))
{
FillLogoBuffer();
if (logoBuffersFull)
{
Debug(6, "\nLooking For Logo in frames %i to %i.\n", logoFrameNum[oldestLogoBuffer], frame_count);
if(!SearchForLogoEdges())
{
InitComSkip();
return 1;
}
}
if (logoInfoAvailable)
{
// logoTrendCounter = num_logo_buffers;
// lastLogoTest = true;
// curLogoTest = true;
// logoTrendStartFrame = logoFrameNum[oldestLogoBuffer];
// curLogoTest = true;
// lastRealLogoChange = logoFrameNum[oldestLogoBuffer];
// if (num_logo_buffers >= minHitsForTrend) {
// hindsightLogoState = true;
// } else {
// hindsightLogoState = false;
// }
}
}
}
if (logoInfoAvailable)
{
// EdgeCount(frame_ptr);
// curLogoTest = logoBuffersFull;
currentGoodEdge = CheckStationLogoEdge(frame_ptr);
curLogoTest = (currentGoodEdge > logo_threshold);
lastLogoTest = ProcessLogoTest(frame_count, curLogoTest, false);
if (!lastLogoTest && !startOverAfterLogoInfoAvail && logoBuffersFull) // Lost logo
{
// logoInfoAvailable = false;
// secondLogoSearch = true;
logoBuffersFull = false;
InitLogoBuffers();
newestLogoBuffer = -1;
}
if (startOverAfterLogoInfoAvail && !loadingCSV && !secondLogoSearch && logo_block_count > 0 &&
!lastLogoTest &&
F2L(frame_count,logo_block[logo_block_count-1].end) > ( max_commercialbreak * 1.2 ) &&
(double)frames_with_logo / (double)frame_count < 0.5
)
{
Debug(6, "\nNo Logo in frames %i to %i, restarting Logo search.\n", logo_block[logo_block_count-1].end, frame_count);
// First logo found but no logo found after first commercial cblock so search new logo
logoInfoAvailable = false;
secondLogoSearch = true;
logoBuffersFull = false;
InitLogoBuffers();
newestLogoBuffer = -1;
}
}
}
// EdgeCount(frame_ptr);
// currentGoodEdge = ((double) edge_count) / 750;
if (logoInfoAvailable && framearray) {
frame[frame_count].logo_present = lastLogoTest;
} else if (framearray) {
frame[frame_count].logo_present = 0.0;
}
if (lastLogoTest)
frames_with_logo++;
if (framearray) frame[frame_count].currentGoodEdge = currentGoodEdge;
if (((frame_count) & subsample_video) == 0)
OutputDebugWindow(true,frame_count,true, false);
// key = 0;
// while (key==0)
// vo_wait();
framesprocessed++;
scr += 1;
if (live_tv && !isBlack)
{
BuildCommListAsYouGo();
}
return 0;
}
int Max(int i,int j)
{
return(i>j?i:j);
}
int Min(int i,int j)
{
return(i<j?i:j);
}
double AverageARForBlock(int start, int end)
{
int i, maxSize;
double Ar;
int f,t;
maxSize = 0;
Ar = 0.0;
for (i = 0; i < ar_block_count; i++)
{
f = max(ar_block[i].start, start);
t = min(ar_block[i].end, end);
if (maxSize < t-f+1)
{
Ar = ar_block[i].ar_ratio;
maxSize = t-f+1;
}
if (ar_block[i].start > end)
break;
}
return(Ar);
}
int AverageACForBlock(int start, int end)
{
int i, maxSize;
int Ac;
int f,t;
maxSize = 0;
Ac = 0;
for (i = 0; i < ac_block_count; i++)
{
f = max(ac_block[i].start, start);
t = min(ac_block[i].end, end);
if (maxSize < t-f+1)
{
Ac = ac_block[i].audio_channels;
maxSize = t-f+1;
}
if (ac_block[i].start > end)
break;
}
return(Ac);
}
double FindARFromHistogram(double ar_ratio)
{
int i;
for (i = 0; i < MAX_ASPECT_RATIOS; i++)
{
if (ar_ratio > ar_histogram[i].ar_ratio - ar_delta &&
ar_ratio < ar_histogram[i].ar_ratio + ar_delta)
return (ar_histogram[i].ar_ratio);
}
for (i = 0; i < MAX_ASPECT_RATIOS; i++)
{
if (ar_ratio > ar_histogram[i].ar_ratio - 2*ar_delta &&
ar_ratio < ar_histogram[i].ar_ratio + 2*ar_delta)
return (ar_histogram[i].ar_ratio);
}
for (i = 0; i < MAX_ASPECT_RATIOS; i++)
{
if (ar_ratio > ar_histogram[i].ar_ratio - 4*ar_delta &&
ar_ratio < ar_histogram[i].ar_ratio + 4*ar_delta)
return (ar_histogram[i].ar_ratio);
}
return (0.0);
}
void FillARHistogram(bool refill)
{
int i;
bool hadToSwap;
long tempFrames;
double tempRatio;
long totalFrames = 0;
long tempCount;
long counter;
int hi;
if (refill)
{
for (i = 0; i < MAX_ASPECT_RATIOS; i++)
{
ar_histogram[i].frames = 0;
ar_histogram[i].ar_ratio = 0.0;
}
for (i = 0; i < ar_block_count; i++)
{
hi = (int)((ar_block[i].ar_ratio - 0.5)*100);
if (hi >= 0 && hi < MAX_ASPECT_RATIOS)
{
ar_histogram[hi].frames += ar_block[i].end - ar_block[i].start + 1;
ar_histogram[hi].ar_ratio = ar_block[i].ar_ratio;
}
}
}
counter = 0;
do
{
hadToSwap = false;
counter++;
for (i = 0; i < MAX_ASPECT_RATIOS - 1; i++)
{
if (ar_histogram[i].frames < ar_histogram[i + 1].frames)
{
hadToSwap = true;
tempFrames = ar_histogram[i].frames;
tempRatio = ar_histogram[i].ar_ratio;
ar_histogram[i] = ar_histogram[i + 1];
ar_histogram[i+1].frames = tempFrames;
ar_histogram[i+1].ar_ratio = tempRatio;
}
}
}
while (hadToSwap);
for (i = 0; i < MAX_ASPECT_RATIOS; i++)
{
totalFrames += ar_histogram[i].frames;
}
tempCount = 0;
Debug(10, "\n\nAfter Sorting - %i\n--------------\n", counter);
i = 0;
while (i < MAX_ASPECT_RATIOS && ar_histogram[i].frames > 0)
{
tempCount += ar_histogram[i].frames;
Debug(10, "Aspect Ratio %5.2f found on %6i frames totalling \t%3.1f%c\n", ar_histogram[i].ar_ratio, ar_histogram[i].frames, ((double)tempCount / (double)totalFrames)*100,'%');
i++;
}
}
void FillACHistogram(bool refill)
{
int i;
bool hadToSwap;
long tempFrames;
int tempAC;
long totalFrames = 0;
long tempCount;
long counter;
int hi;
if (refill)
{
for (i = 0; i < MAX_AUDIO_CHANNELS; i++)
{
ac_histogram[i].frames = 0;
ac_histogram[i].audio_channels = 0.0;
}
for (i = 0; i < ac_block_count; i++)
{
hi = ac_block[i].audio_channels;
if (hi >= 0 && hi < MAX_AUDIO_CHANNELS)
{
ac_histogram[hi].frames += ac_block[i].end - ac_block[i].start + 1;
ac_histogram[hi].audio_channels = ac_block[i].audio_channels;
}
}
}
counter = 0;
do
{
hadToSwap = false;
counter++;
for (i = 0; i < MAX_AUDIO_CHANNELS - 1; i++)
{
if (ac_histogram[i].frames < ac_histogram[i + 1].frames)
{
hadToSwap = true;
tempFrames = ac_histogram[i].frames;
tempAC = ac_histogram[i].audio_channels;
ac_histogram[i] = ac_histogram[i + 1];
ac_histogram[i+1].frames = tempFrames;
ac_histogram[i+1].audio_channels = tempAC;
}
}
}
while (hadToSwap);
for (i = 0; i < MAX_AUDIO_CHANNELS; i++)
{
totalFrames += ac_histogram[i].frames;
}
tempCount = 0;
Debug(10, "\n\nAfter Sorting - %i\n--------------\n", counter);
i = 0;
while (i < MAX_AUDIO_CHANNELS && ac_histogram[i].frames > 0)
{
tempCount += ac_histogram[i].frames;
Debug(10, "Audio channels %3i found on %6i frames totalling \t%3.1f%c\n", ac_histogram[i].audio_channels, ac_histogram[i].frames, ((double)tempCount / (double)totalFrames)*100,'%');
i++;
}
}
void InsertBlackFrame(int f, int b, int u, int v, int c)
{
int i;
// if ((black_count==0 || black[black_count-1].frame < logo_block[logo_block_count-1].end )) {
i = 0;
while (i < black_count && black[i].frame != f)
i++;
if (i < black_count && black[i].frame == f)
{
black[i].cause |= c;
}
else
{
if (black_count >= max_black_count)
{
max_black_count += 500;
black = realloc(black, (max_black_count + 1) * sizeof(black_frame_info));
Debug(9, "Resizing black frame array to accomodate %i frames.\n", max_black_count);
}
// InitializeBlackArray(black_count);
black_count++;
i = black_count-2;
while (i >= 0 && black[i].frame > f)
{
black[i+1] = black[i];
i--;
}
i++;
black[i].frame = f;
black[i].brightness = b;
black[i].uniform = u;
black[i].volume = v;
black[i].cause = c;
}
}
bool BuildMasterCommList(void)
{
int i, j, t, c;
int a = 0,k,count = 0;
int cp=0,cpf, maxsc,rsc;
int silence_count = 0;
int silence_start = 0;
int summed_volume1 = 0;
int summed_volume2 = 0;
int schange_found = false;
int schange_frame;
int low_volume_count;
int very_low_volume_count;
int schange_max;
int mv=0,ms=0;
int volume_delta;
int p_vol, n_vol;
int plataus = 0;
int platauHistogram[256];
double length;
double new_ar_ratio;
FILE* logo_file = NULL;
bool foundCommercials = false;
time_t ltime;
if (frame_count == 0)
{
Debug(1, "No video found\n");
return(false);
}
Debug(7, "Finished scanning file. Starting to build Commercial List.\n");
// if (fabs(avg_fps - fps)> 0.01)
// Debug(1,"WARNING: Actual framerate (%6.3f) different from specified framerate (%6.3f)\n", avg_fps, fps);
length = F2L(frame_count-1, 1);
if (fabs( length - (frame_count -1)/fps) > 0.5) {
if (fabs(avg_fps - fps)> 1)
Debug(1,"WARNING: Actual framerate (%6.3f) different from specified framerate (%6.3f)\nInternal frame numbers will be different from .txt frame numbers\n", avg_fps, fps);
Debug(1,"WARNING: Complex timeline or errors in the recording!!!!\nResults may be wrong, .ref input will be misaligned. .txt editing will produce wrong results\nUse .edl output if possible\n");
}
frame[frame_count].pts = frame[frame_count-1].pts + 1.0 / fps;
for (i = 1; i < 255; i++)
{
if (volumeHistogram[i] > 10)
{
min_volume = (i-1)*volumeScale;
break;
}
}
for (k = 1; k < 255; k++)
{
if (uniformHistogram[k] > 10)
{
min_uniform = (k-1)*UNIFORMSCALE;
break;
}
}
for (i = 0; i < 255; i++)
{
if (brightHistogram[i] > 1)
{
min_brightness_found = i;
break;
}
}
logoPercentage = (double) frames_with_logo / (double) framenum_real;
// if (max_volume == 0)
{
#define VOLUME_DELTA 10
#define VOLUME_MAXIMUM 300
#define VOLUME_PLATAU_SIZE 6
volume_delta = VOLUME_DELTA;
try_again:
if (framearray) // Find silence volume level
{
for (i = 0; i < 255; i++)
platauHistogram[i] = 0;
plataus = 0;
j = 1;
for (i = VOLUME_PLATAU_SIZE; i < frame_count-VOLUME_PLATAU_SIZE;)
{
if (frame[i].volume > VOLUME_MAXIMUM || frame[i].volume < 0)
{
i++;
continue;
}
while (i+1 < frame_count-VOLUME_PLATAU_SIZE && frame[i+1].volume < frame[i].volume)
i++;
k = 1;
while (i-k - VOLUME_PLATAU_SIZE > 1 &&
(abs(frame[i-k].volume - frame[i].volume) < volume_delta
//|| frame[i-k].volume < 50
))
{
k++;
}
if (frame[i-k].volume < frame[i].volume)
{
i++;
continue;
}
a = 1;
while (i+a +VOLUME_PLATAU_SIZE < frame_count &&
(abs(frame[i+a].volume - frame[i].volume) < volume_delta
//|| frame[i+a].volume < 50
))
{
a++;
}
if (frame[i+a].volume < frame[i].volume)
{
i = i+a;
continue;
}
// i=8 k=1 a=11
if (a+k > VOLUME_PLATAU_SIZE && i-k-VOLUME_PLATAU_SIZE > 0 && i+a+VOLUME_PLATAU_SIZE < frame_count)
{
p_vol = (frame[i-k-4].volume +
frame[i-k-VOLUME_PLATAU_SIZE+3].volume +
frame[i-k-VOLUME_PLATAU_SIZE+2].volume +
frame[i-k-VOLUME_PLATAU_SIZE+1].volume +
frame[i-k-VOLUME_PLATAU_SIZE].volume) / 5;
n_vol = (frame[i+a+4].volume +
frame[i+a+VOLUME_PLATAU_SIZE-3].volume +
frame[i+a+VOLUME_PLATAU_SIZE-2].volume +
frame[i+a+VOLUME_PLATAU_SIZE-1].volume +
frame[i+a+VOLUME_PLATAU_SIZE].volume) / 5;
if ( p_vol > frame[i].volume + 220 || n_vol > frame[i].volume + 220 )
//if ( abs(frame[i-k-2].volume - frame[i].volume) > VOLUME_DELTA*2 ||
// abs(frame[i+a+2].volume - frame[i].volume) > VOLUME_DELTA*2)
{
Debug(8, "Platau@[%d] frames %d, volume %d, distance %d seconds\n", i, k+a, frame[i].volume, (int)F2L(i,j));
j = i;
// for (j = i-k; j < i + a; j++)
// frame[j].isblack |= C_v;
plataus++;
platauHistogram[frame[i].volume/10]++;
}
}
i += a;
}
a = 0;
Debug(9, "Vol : #Frames\n");
for (i = 0; i < 255; i++)
{
a += platauHistogram[i];
if (platauHistogram[i] > 0)
Debug(9, "%3d : %d\n", i*10, platauHistogram[i]);
}
a = a * 6 / 10;
j = 0;
i = 0;
while (j < a)
{
j += platauHistogram[i++];
}
ms = i*10;
Debug(7, "Calculated silence level = %d\n", ms);
if (ms > 0 && ms < 10)
ms = 10;
if (ms < 50)
mv = 1.5 * ms;
else if (ms < 100)
mv = 2 * ms;
else if (ms < 200)
mv = 2 * ms;
else
mv = 2 * ms;
}
if (mv == 0 || plataus < 5)
{
volume_delta *= 2;
if (volume_delta < VOLUME_MAXIMUM)
goto try_again;
}
}
if (max_volume == 0)
{
max_volume = mv;
max_silence = ms;
}
/*
if (max_silence < min_volume + 30)
max_silence = min_volume + 30;
if (max_volume < 100) {
if ( max_volume < min_volume + 30)
max_volume = min_volume + 30;
}
else
if (max_volume < min_volume + 100)
max_volume = min_volume + 100;
*/
if (max_volume == 0)
{
if (framearray) // Find silence volume level
{
#define START_VOLUME 500
count = 21;
scanagain:
a = START_VOLUME;
k = 0;
if (frame[i].volume > 0)
j = frame[i].volume;
els