diff options
Diffstat (limited to 'command')
33 files changed, 2194 insertions, 3263 deletions
diff --git a/command/Makefile b/command/Makefile index 95d7da4..e1ab816 100644 --- a/command/Makefile +++ b/command/Makefile @@ -37,7 +37,7 @@ INCLUDES += -I.. ### The object files (add further files here): -OBJS = markad-standalone.o decoder.o marks.o streaminfo.o video.o audio.o audio_gain_analysis.o demux.o queue.o ts2pkt.o pes2es.o +OBJS = markad-standalone.o decoder.o marks.o streaminfo.o video.o audio.o demux.o ### The main target: diff --git a/command/audio.cpp b/command/audio.cpp index 46fa57a..0a9c133 100644 --- a/command/audio.cpp +++ b/command/audio.cpp @@ -11,182 +11,73 @@ #include <stdlib.h> #include <string.h> -extern "C" -{ -#include "debug.h" -} - #include "audio.h" cMarkAdAudio::cMarkAdAudio(MarkAdContext *maContext) { macontext=maContext; - mark.Comment=NULL; mark.Position=0; mark.Type=0; - result.CommentBefore=NULL; - result.CommentAfter=NULL; Clear(); } cMarkAdAudio::~cMarkAdAudio() { + resetmark(); Clear(); - ResetMark(); } -void cMarkAdAudio::ResetMark() -{ - if (mark.Comment) free(mark.Comment); - mark.Comment=NULL; - mark.Position=0; - mark.Type=0; -} - -bool cMarkAdAudio::AddMark(int Type, int Position, const char *Comment) +void cMarkAdAudio::Clear() { - if (!Comment) return false; - if (mark.Comment) - { - int oldlen=strlen(mark.Comment); - mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment)); - if (!mark.Comment) - { - mark.Position=0; - return false; - } - strcat(mark.Comment," ["); - strcat(mark.Comment,Comment); - strcat(mark.Comment,"]"); - } - else - { - mark.Comment=strdup(Comment); - } - mark.Position=Position; - mark.Type=Type; - return true; + channels=0; } -bool cMarkAdAudio::AnalyzeGain(int FrameNumber) +void cMarkAdAudio::resetmark() { - if (!macontext->Audio.Data.Valid) return false; - - int samples=macontext->Audio.Data.SampleBufLen/ - sizeof(*macontext->Audio.Data.SampleBuf)/ - macontext->Audio.Info.Channels; - - double left[samples]; - double right[samples]; - - for (int i=0; i<samples; i++) - { - left[i]=macontext->Audio.Data.SampleBuf[0+(i*2)]; - right[i]=macontext->Audio.Data.SampleBuf[1+(i*2)]; - } - - if (FrameNumber!=lastframe_gain) - { - if ((lastframe_gain>0) && (audiogain.AnalyzedSamples()>=(3*samples))) - { - double gain = audiogain.GetGain(); - printf("%05i %+.2f db\n",lastframe_gain,gain); - } - audiogain.Init(macontext->Audio.Info.SampleRate); - lastframe_gain=-1; - } - - if (audiogain.AnalyzeSamples(left,right,samples,2)!=GAIN_ANALYSIS_OK) - { - lastframe_gain=-1; - return false; - } - else - { - lastframe_gain=FrameNumber; - } - return true; + if (!mark.Type) return; + memset(&mark,0,sizeof(mark)); } -void cMarkAdAudio::Clear() +void cMarkAdAudio::setmark(int type, int position, int channelsbefore, int channelsafter) { - channels=0; - lastframe_gain=-1; - if (result.CommentBefore) free(result.CommentBefore); - if (result.CommentAfter) free(result.CommentAfter); - memset(&result,0,sizeof(result)); + mark.ChannelsBefore=channelsbefore; + mark.ChannelsAfter=channelsafter; + mark.Position=position; + mark.Type=type; } -bool cMarkAdAudio::ChannelChange(int a, int b) +bool cMarkAdAudio::channelchange(int a, int b) { if ((a==0) || (b==0)) return false; if (a!=b) return true; return false; } -MarkAdPos *cMarkAdAudio::Process2ndPass(int FrameNumber) -{ - if (!FrameNumber) return NULL; -#if 0 - if (AnalyzeGain(FrameNumber)) - { - if (result.CommentBefore) free(result.CommentBefore); - if (asprintf(&result.CommentBefore,"audio silence detection (%i)",FrameNumber)==-1) - { - result.CommentBefore=NULL; - } - result.FrameNumberBefore=FrameNumber; - return &result; - } -#endif - return NULL; -} - MarkAdMark *cMarkAdAudio::Process(int FrameNumber, int FrameNumberNext) { if ((!FrameNumber) || (!FrameNumberNext)) return NULL; - ResetMark(); + resetmark(); - if (ChannelChange(macontext->Audio.Info.Channels,channels)) + if (channelchange(macontext->Audio.Info.Channels,channels)) { - char *buf=(char *) calloc(1,256); - if (!buf) return NULL; - - snprintf(buf,255,"audio channel change from %i to %i (", channels, - macontext->Audio.Info.Channels); - - if (macontext->Info.Channels) + if (macontext->Audio.Info.Channels>2) { - if (macontext->Info.Channels==macontext->Audio.Info.Channels) - { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)*",FrameNumberNext); - nbuf[19]=0; - strcat(buf,nbuf); - AddMark(MT_CHANNELSTART,FrameNumberNext,buf); - } - else - { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)",framelast); - nbuf[19]=0; - strcat(buf,nbuf); - AddMark(MT_CHANNELSTOP,framelast,buf); - } + setmark(MT_CHANNELSTART,FrameNumberNext,channels,macontext->Audio.Info.Channels); } else { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)?",FrameNumber); - nbuf[19]=0; - strcat(buf,nbuf); - AddMark(MT_CHANNELCHANGE,FrameNumber,buf); + setmark(MT_CHANNELSTOP,framelast,channels,macontext->Audio.Info.Channels); } - free(buf); } channels=macontext->Audio.Info.Channels; framelast=FrameNumber; - return &mark; + if (mark.Position) + { + return &mark; + } + else + { + return NULL; + } } - diff --git a/command/audio.h b/command/audio.h index 651d056..4c6c819 100644 --- a/command/audio.h +++ b/command/audio.h @@ -10,35 +10,22 @@ #include "global.h" -#include "audio_gain_analysis.h" - class cMarkAdAudio { private: - //int framenumber; MarkAdContext *macontext; MarkAdMark mark; - void ResetMark(); - bool AddMark(int Type, int Position, const char *Comment); - - int lastframe_gain; - double lastgain; - cMarkAdAudioGainAnalysis audiogain; - bool AnalyzeGain(int FrameNumber); - + void resetmark(); + void setmark(int type, int position, int channelsbefore, int channelsafter); int channels; - bool ChannelChange(int a, int b); + bool channelchange(int a, int b); int framelast; - - MarkAdPos result; public: cMarkAdAudio(MarkAdContext *maContext); ~cMarkAdAudio(); MarkAdMark *Process(int FrameNumber, int FrameNumberBefore); - MarkAdPos *Process2ndPass(int FrameNumber); void Clear(); }; - #endif diff --git a/command/audio_gain_analysis.cpp b/command/audio_gain_analysis.cpp deleted file mode 100644 index b5abf9b..0000000 --- a/command/audio_gain_analysis.cpp +++ /dev/null @@ -1,423 +0,0 @@ -/* - * ReplayGainAnalysis - analyzes input samples and give the recommended dB change - * Copyright (C) 2001-2009 David Robinson and Glen Sawyer - * Improvements and optimizations added by Frank Klemm, and by Marcel Müller - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * concept and filter values by David Robinson (David@Robinson.org) - * -- blame him if you think the idea is flawed - * original coding by Glen Sawyer (mp3gain@hotmail.com) - * -- blame him if you think this runs too slowly, or the coding is otherwise flawed - * - * lots of code improvements by Frank Klemm ( http://www.uni-jena.de/~pfk/mpp/ ) - * -- credit him for all the _good_ programming ;) - * - * - * For an explanation of the concepts and the basic algorithms involved, go to: - * http://www.replaygain.org/ - */ - -/* - * Here's the deal. Call - * - * InitGainAnalysis ( long samplefreq ); - * - * to initialize everything. Call - * - * AnalyzeSamples ( const Float_t* left_samples, - * const Float_t* right_samples, - * size_t num_samples, - * int num_channels ); - * - * as many times as you want, with as many or as few samples as you want. - * If mono, pass the sample buffer in through left_samples, leave - * right_samples NULL, and make sure num_channels = 1. - * - * GetGain() - * - * will return the recommended dB level change for all samples analyzed - * SINCE THE LAST TIME you called InitGainAnalysis(). - * - */ - -/* - * So here's the main source of potential code confusion: - * - * The filters applied to the incoming samples are IIR filters, - * meaning they rely on up to <filter order> number of previous samples - * AND up to <filter order> number of previous filtered samples. - * - * I set up the AnalyzeSamples routine to minimize memory usage and interface - * complexity. The speed isn't compromised too much (I don't think), but the - * internal complexity is higher than it should be for such a relatively - * simple routine. - * - * Optimization/clarity suggestions are welcome. - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "audio_gain_analysis.h" - -const Float_t cMarkAdAudioGainAnalysis::ABYule[12][2*YULE_ORDER + 1] = -{ - {0.006471345933032, -7.22103125152679, -0.02567678242161, 24.7034187975904, 0.049805860704367, -52.6825833623896, -0.05823001743528, 77.4825736677539, 0.040611847441914, -82.0074753444205, -0.010912036887501, 63.1566097101925, -0.00901635868667, -34.889569769245, 0.012448886238123, 13.2126852760198, -0.007206683749426, -3.09445623301669, 0.002167156433951, 0.340344741393305, -0.000261819276949}, - {0.015415414474287, -7.19001570087017, -0.07691359399407, 24.4109412087159, 0.196677418516518, -51.6306373580801, -0.338855114128061, 75.3978476863163, 0.430094579594561, -79.4164552507386, -0.415015413747894, 61.0373661948115, 0.304942508151101, -33.7446462547014, -0.166191795926663, 12.8168791146274, 0.063198189938739, -3.01332198541437, -0.015003978694525, 0.223619893831468, 0.001748085184539}, - {0.021776466467053, -5.74819833657784, -0.062376961003801, 16.246507961894, 0.107731165328514, -29.9691822642542, -0.150994515142316, 40.027597579378, 0.170334807313632, -40.3209196052655, -0.157984942890531, 30.8542077487718, 0.121639833268721, -17.5965138737281, -0.074094040816409, 7.10690214103873, 0.031282852041061, -1.82175564515191, -0.00755421235941, 0.223619893831468, 0.00117925454213 }, - {0.03857599435200, -3.84664617118067, -0.02160367184185, 7.81501653005538, -0.00123395316851, -11.34170355132042, -0.00009291677959, 13.05504219327545, -0.01655260341619, -12.28759895145294, 0.02161526843274, 9.48293806319790, -0.02074045215285, -5.87257861775999, 0.00594298065125, 2.75465861874613, 0.00306428023191, -0.86984376593551, 0.00012025322027, 0.13919314567432, 0.00288463683916 }, - {0.05418656406430, -3.47845948550071, -0.02911007808948, 6.36317777566148, -0.00848709379851, -8.54751527471874, -0.00851165645469, 9.47693607801280, -0.00834990904936, -8.81498681370155, 0.02245293253339, 6.85401540936998, -0.02596338512915, -4.39470996079559, 0.01624864962975, 2.19611684890774, -0.00240879051584, -0.75104302451432, 0.00674613682247, 0.13149317958808, -0.00187763777362 }, - {0.15457299681924, -2.37898834973084, -0.09331049056315, 2.84868151156327, -0.06247880153653, -2.64577170229825, 0.02163541888798, 2.23697657451713, -0.05588393329856, -1.67148153367602, 0.04781476674921, 1.00595954808547, 0.00222312597743, -0.45953458054983, 0.03174092540049, 0.16378164858596, -0.01390589421898, -0.05032077717131, 0.00651420667831, 0.02347897407020, -0.00881362733839 }, - {0.30296907319327, -1.61273165137247, -0.22613988682123, 1.07977492259970, -0.08587323730772, -0.25656257754070, 0.03282930172664, -0.16276719120440, -0.00915702933434, -0.22638893773906, -0.02364141202522, 0.39120800788284, -0.00584456039913, -0.22138138954925, 0.06276101321749, 0.04500235387352, -0.00000828086748, 0.02005851806501, 0.00205861885564, 0.00302439095741, -0.02950134983287 }, - {0.33642304856132, -1.49858979367799, -0.25572241425570, 0.87350271418188, -0.11828570177555, 0.12205022308084, 0.11921148675203, -0.80774944671438, -0.07834489609479, 0.47854794562326, -0.00469977914380, -0.12453458140019, -0.00589500224440, -0.04067510197014, 0.05724228140351, 0.08333755284107, 0.00832043980773, -0.04237348025746, -0.01635381384540, 0.02977207319925, -0.01760176568150 }, - {0.44915256608450, -0.62820619233671, -0.14351757464547, 0.29661783706366, -0.22784394429749, -0.37256372942400, -0.01419140100551, 0.00213767857124, 0.04078262797139, -0.42029820170918, -0.12398163381748, 0.22199650564824, 0.04097565135648, 0.00613424350682, 0.10478503600251, 0.06747620744683, -0.01863887810927, 0.05784820375801, -0.03193428438915, 0.03222754072173, 0.00541907748707 }, - {0.56619470757641, -1.04800335126349, -0.75464456939302, 0.29156311971249, 0.16242137742230, -0.26806001042947, 0.16744243493672, 0.00819999645858, -0.18901604199609, 0.45054734505008, 0.30931782841830, -0.33032403314006, -0.27562961986224, 0.06739368333110, 0.00647310677246, -0.04784254229033, 0.08647503780351, 0.01639907836189, -0.03788984554840, 0.01807364323573, -0.00588215443421 }, - {0.58100494960553, -0.51035327095184, -0.53174909058578, -0.31863563325245, -0.14289799034253, -0.20256413484477, 0.17520704835522, 0.14728154134330, 0.02377945217615, 0.38952639978999, 0.15558449135573, -0.23313271880868, -0.25344790059353, -0.05246019024463, 0.01628462406333, -0.02505961724053, 0.06920467763959, 0.02442357316099, -0.03721611395801, 0.01818801111503, -0.00749618797172 }, - {0.53648789255105, -0.25049871956020, -0.42163034350696, -0.43193942311114, -0.00275953611929, -0.03424681017675, 0.04267842219415, -0.04678328784242, -0.10214864179676, 0.26408300200955, 0.14590772289388, 0.15113130533216, -0.02459864859345, -0.17556493366449, -0.11202315195388, -0.18823009262115, -0.04060034127000, 0.05477720428674, 0.04788665548180, 0.04704409688120, -0.02217936801134 } -}; - -const Float_t cMarkAdAudioGainAnalysis::ABButter[12][2*BUTTER_ORDER + 1] = -{ - {0.99308203517541, -1.98611621154089, -1.98616407035082, 0.986211929160751, 0.99308203517541 }, - {0.992472550461293,-1.98488843762334, -1.98494510092258, 0.979389350028798, 0.992472550461293}, - {0.989641019334721,-1.97917472731008, -1.97928203866944, 0.979389350028798, 0.989641019334721}, - {0.98621192462708, -1.97223372919527, -1.97242384925416, 0.97261396931306, 0.98621192462708 }, - {0.98500175787242, -1.96977855582618, -1.97000351574484, 0.97022847566350, 0.98500175787242 }, - {0.97938932735214, -1.95835380975398, -1.95877865470428, 0.95920349965459, 0.97938932735214 }, - {0.97531843204928, -1.95002759149878, -1.95063686409857, 0.95124613669835, 0.97531843204928 }, - {0.97316523498161, -1.94561023566527, -1.94633046996323, 0.94705070426118, 0.97316523498161 }, - {0.96454515552826, -1.92783286977036, -1.92909031105652, 0.93034775234268, 0.96454515552826 }, - {0.96009142950541, -1.91858953033784, -1.92018285901082, 0.92177618768381, 0.96009142950541 }, - {0.95856916599601, -1.91542108074780, -1.91713833199203, 0.91885558323625, 0.95856916599601 }, - {0.94597685600279, -1.88903307939452, -1.89195371200558, 0.89487434461664, 0.94597685600279 } -}; - -void cMarkAdAudioGainAnalysis::filterYule (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel) -{ - // for each filter: - // [0] 48 kHz, [1] 44.1 kHz, [2] 32 kHz, [3] 24 kHz, [4] 22050 Hz, - // [5] 16 kHz, [6] 12 kHz, [7] is 11025 Hz, [8] 8 kHz - - // When calling these filter procedures, make sure that ip[-order] and op[-order] point to real data! - - while (nSamples--) - { - *output = 1e-10 /* 1e-10 is a hack to avoid slowdown because of denormals */ - + input [0] * kernel[0] - - output[-1] * kernel[1] - + input [-1] * kernel[2] - - output[-2] * kernel[3] - + input [-2] * kernel[4] - - output[-3] * kernel[5] - + input [-3] * kernel[6] - - output[-4] * kernel[7] - + input [-4] * kernel[8] - - output[-5] * kernel[9] - + input [-5] * kernel[10] - - output[-6] * kernel[11] - + input [-6] * kernel[12] - - output[-7] * kernel[13] - + input [-7] * kernel[14] - - output[-8] * kernel[15] - + input [-8] * kernel[16] - - output[-9] * kernel[17] - + input [-9] * kernel[18] - - output[-10]* kernel[19] - + input [-10]* kernel[20]; - ++output; - ++input; - } -} - -void cMarkAdAudioGainAnalysis::filterButter (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel) -{ - - while (nSamples--) - { - *output = - input [0] * kernel[0] - - output[-1] * kernel[1] - + input [-1] * kernel[2] - - output[-2] * kernel[3] - + input [-2] * kernel[4]; - ++output; - ++input; - } -} - -int cMarkAdAudioGainAnalysis::ResetSampleFrequency ( long samplefreq ) -{ - // returns a INIT_GAIN_ANALYSIS_OK if successful, INIT_GAIN_ANALYSIS_ERROR if not - int i; - - // zero out initial values - for ( i = 0; i < MAX_ORDER; i++ ) - linprebuf[i] = lstepbuf[i] = loutbuf[i] = rinprebuf[i] = rstepbuf[i] = routbuf[i] = 0.; - - switch ( (int)(samplefreq) ) - { - case 96000: - freqindex = 0; - break; - case 88200: - freqindex = 1; - break; - case 64000: - freqindex = 2; - break; - case 48000: - freqindex = 3; - break; - case 44100: - freqindex = 4; - break; - case 32000: - freqindex = 5; - break; - case 24000: - freqindex = 6; - break; - case 22050: - freqindex = 7; - break; - case 16000: - freqindex = 8; - break; - case 12000: - freqindex = 9; - break; - case 11025: - freqindex = 10; - break; - case 8000: - freqindex = 11; - break; - default: - return INIT_GAIN_ANALYSIS_ERROR; - } - - sampleWindow = (int) ceil (samplefreq * RMS_WINDOW_TIME); - - lsum = 0.; - rsum = 0.; - totsamp = 0; - - memset ( A, 0, sizeof(A) ); - - return INIT_GAIN_ANALYSIS_OK; -} - -int cMarkAdAudioGainAnalysis::Init(long samplefreq) -{ - if (ResetSampleFrequency(samplefreq) != INIT_GAIN_ANALYSIS_OK) - { - return INIT_GAIN_ANALYSIS_ERROR; - } - - linpre = linprebuf + MAX_ORDER; - rinpre = rinprebuf + MAX_ORDER; - lstep = lstepbuf + MAX_ORDER; - rstep = rstepbuf + MAX_ORDER; - lout = loutbuf + MAX_ORDER; - rout = routbuf + MAX_ORDER; - - memset ( B, 0, sizeof(B) ); - - gnum_samples=0; - - return INIT_GAIN_ANALYSIS_OK; -} - -static __inline double fsqr(const double d) -{ - return d*d; -} - -int cMarkAdAudioGainAnalysis::AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ) -{ - // returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not - const Float_t* curleft; - const Float_t* curright; - long batchsamples; - long cursamples; - long cursamplepos; - int i; - - if ( num_samples == 0 ) - return GAIN_ANALYSIS_OK; - - gnum_samples+=num_samples; - - cursamplepos = 0; - batchsamples = (long)num_samples; - - switch ( num_channels) - { - case 1: - right_samples = left_samples; - case 2: - break; - default: - return GAIN_ANALYSIS_ERROR; - } - - if ( num_samples < MAX_ORDER ) - { - memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) ); - memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) ); - } - else - { - memcpy ( linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t) ); - memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t) ); - } - - while ( batchsamples > 0 ) - { - cursamples = batchsamples > sampleWindow-totsamp ? sampleWindow - totsamp : batchsamples; - if ( cursamplepos < MAX_ORDER ) - { - curleft = linpre+cursamplepos; - curright = rinpre+cursamplepos; - if (cursamples > MAX_ORDER - cursamplepos ) - cursamples = MAX_ORDER - cursamplepos; - } - else - { - curleft = left_samples + cursamplepos; - curright = right_samples + cursamplepos; - } - - YULE_FILTER ( curleft , lstep + totsamp, cursamples, ABYule[freqindex]); - YULE_FILTER ( curright, rstep + totsamp, cursamples, ABYule[freqindex]); - - BUTTER_FILTER ( lstep + totsamp, lout + totsamp, cursamples, ABButter[freqindex]); - BUTTER_FILTER ( rstep + totsamp, rout + totsamp, cursamples, ABButter[freqindex]); - - curleft = lout + totsamp; // Get the squared values - curright = rout + totsamp; - - i = cursamples % 16; - while (i--) - { - lsum += fsqr(*curleft++); - rsum += fsqr(*curright++); - } - i = cursamples / 16; - while (i--) - { - lsum += fsqr(curleft[0]) - + fsqr(curleft[1]) - + fsqr(curleft[2]) - + fsqr(curleft[3]) - + fsqr(curleft[4]) - + fsqr(curleft[5]) - + fsqr(curleft[6]) - + fsqr(curleft[7]) - + fsqr(curleft[8]) - + fsqr(curleft[9]) - + fsqr(curleft[10]) - + fsqr(curleft[11]) - + fsqr(curleft[12]) - + fsqr(curleft[13]) - + fsqr(curleft[14]) - + fsqr(curleft[15]); - curleft += 16; - rsum += fsqr(curright[0]) - + fsqr(curright[1]) - + fsqr(curright[2]) - + fsqr(curright[3]) - + fsqr(curright[4]) - + fsqr(curright[5]) - + fsqr(curright[6]) - + fsqr(curright[7]) - + fsqr(curright[8]) - + fsqr(curright[9]) - + fsqr(curright[10]) - + fsqr(curright[11]) - + fsqr(curright[12]) - + fsqr(curright[13]) - + fsqr(curright[14]) - + fsqr(curright[15]); - curright += 16; - } - - batchsamples -= cursamples; - cursamplepos += cursamples; - totsamp += cursamples; - if ( totsamp == sampleWindow ) // Get the Root Mean Square (RMS) for this set of samples - { - double val = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 ); - int ival = (int) val; - if ( ival < 0 ) ival = 0; - if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = sizeof(A)/sizeof(*A) - 1; - A [ival]++; - lsum = rsum = 0.; - memmove ( loutbuf , loutbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); - memmove ( routbuf , routbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); - memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); - memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) ); - totsamp = 0; - } - if ( totsamp > sampleWindow ) - // somehow I really screwed up: Error in programming! - // Contact author about totsamp > sampleWindow - return GAIN_ANALYSIS_ERROR; - } - if ( num_samples < MAX_ORDER ) - { - memmove ( linprebuf,linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); - memmove ( rinprebuf,rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) ); - memcpy ( linprebuf + MAX_ORDER - num_samples, left_samples,num_samples * sizeof(Float_t) ); - memcpy ( rinprebuf + MAX_ORDER - num_samples, right_samples,num_samples * sizeof(Float_t) ); - } - else - { - memcpy ( linprebuf, left_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); - memcpy ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) ); - } - - return GAIN_ANALYSIS_OK; -} - -Float_t cMarkAdAudioGainAnalysis::analyzeResult ( Uint32_t* Array, size_t len ) -{ - Uint32_t elems; - Int32_t upper; - size_t i; - - elems = 0; - for ( i = 0; i < len; i++ ) - elems += Array[i]; - if ( elems == 0 ) - return GAIN_NOT_ENOUGH_SAMPLES; - - upper = (Int32_t) ceil (elems * (1. - RMS_PERCENTILE)); - for ( i = len; i-- > 0; ) - { - if ( (upper -= Array[i]) <= 0 ) - break; - } - - return (Float_t) ((Float_t)PINK_REF - (Float_t)i / (Float_t)STEPS_per_dB); -} - -Float_t cMarkAdAudioGainAnalysis::GetGain ( void ) -{ - return analyzeResult ( A, sizeof(A)/sizeof(*A) ); -} diff --git a/command/audio_gain_analysis.h b/command/audio_gain_analysis.h deleted file mode 100644 index abed123..0000000 --- a/command/audio_gain_analysis.h +++ /dev/null @@ -1,102 +0,0 @@ -/* - * ReplayGainAnalysis - analyzes input samples and give the recommended dB change - * Copyright (C) 2001-2009 David Robinson and Glen Sawyer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * concept and filter values by David Robinson (David@Robinson.org) - * -- blame him if you think the idea is flawed - * coding by Glen Sawyer (mp3gain@hotmail.com) 735 W 255 N, Orem, UT 84057-4505 USA - * -- blame him if you think this runs too slowly, or the coding is otherwise flawed - * - * For an explanation of the concepts and the basic algorithms involved, go to: - * http://www.replaygain.org/ - */ - -#ifndef GAIN_ANALYSIS_H -#define GAIN_ANALYSIS_H - -#include <stddef.h> - -#define GAIN_NOT_ENOUGH_SAMPLES -24601 -#define GAIN_ANALYSIS_ERROR 0 -#define GAIN_ANALYSIS_OK 1 - -#define INIT_GAIN_ANALYSIS_ERROR 0 -#define INIT_GAIN_ANALYSIS_OK 1 - -typedef double Float_t; // Type used for filtering - -class cMarkAdAudioGainAnalysis -{ -private: - typedef unsigned int Uint32_t; - typedef signed int Int32_t; - -#define YULE_ORDER 10 -#define BUTTER_ORDER 2 -#define YULE_FILTER filterYule -#define BUTTER_FILTER filterButter -#define RMS_PERCENTILE 0.95 // percentile which is louder than the proposed level -#define MAX_SAMP_FREQ 96000. // maximum allowed sample frequency [Hz] -#define RMS_WINDOW_TIME 0.050 // Time slice size [s] -#define STEPS_per_dB 100. // Table entries per dB -#define MAX_dB 120. // Table entries for 0...MAX_dB (normal max. values are 70...80 dB) - -#define MAX_ORDER (BUTTER_ORDER > YULE_ORDER ? BUTTER_ORDER : YULE_ORDER) -#define MAX_SAMPLES_PER_WINDOW (size_t) (MAX_SAMP_FREQ * RMS_WINDOW_TIME + 1) // max. Samples per Time slice -#define PINK_REF 64.82 //298640883795 // calibration value - - Float_t linprebuf [MAX_ORDER * 2]; - Float_t* linpre; // left input samples, with pre-buffer - Float_t lstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; - Float_t* lstep; // left "first step" (i.e. post first filter) samples - Float_t loutbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; - Float_t* lout; // left "out" (i.e. post second filter) samples - Float_t rinprebuf [MAX_ORDER * 2]; - Float_t* rinpre; // right input samples ... - Float_t rstepbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; - Float_t* rstep; - Float_t routbuf [MAX_SAMPLES_PER_WINDOW + MAX_ORDER]; - Float_t* rout; - long sampleWindow; // number of samples required to reach number of milliseconds required for RMS window - long totsamp; - int gnum_samples; - double lsum; - double rsum; - int freqindex; - int first; - Uint32_t A [(size_t)(STEPS_per_dB * MAX_dB)]; - Uint32_t B [(size_t)(STEPS_per_dB * MAX_dB)]; - - static const Float_t ABButter[12][2*BUTTER_ORDER + 1]; - static const Float_t ABYule[12][2*YULE_ORDER + 1]; - - void filterButter (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel); - void filterYule (const Float_t* input, Float_t* output, size_t nSamples, const Float_t* kernel); - - int ResetSampleFrequency ( long samplefreq ); - Float_t analyzeResult ( Uint32_t* Array, size_t len ); -public: - int Init( long samplefreq ); - int AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels ); - int AnalyzedSamples() - { - return (int) gnum_samples; - }; - Float_t GetGain(void); -}; - -#endif /* GAIN_ANALYSIS_H */ diff --git a/command/demux.cpp b/command/demux.cpp index 6f58dde..9f634bd 100644 --- a/command/demux.cpp +++ b/command/demux.cpp @@ -5,282 +5,1248 @@ * */ +#include <string.h> #include "demux.h" +extern "C" +{ +#include "debug.h" +} -#include <string.h> +#include <stdlib.h> -cMarkAdDemux::cMarkAdDemux() +#ifndef TS_SIZE +#define TS_SIZE 188 +#endif + +cPaketQueue::cPaketQueue(const char *Name, int Size) { - ts2pkt=NULL; - pes2audioes=NULL; - pes2videoes=NULL; - pause=false; - pause_retval=0; - min_needed=0; - skip=0; - queue = new cMarkAdPaketQueue(NULL,2176); + maxqueue=Size; + if (Name) + { + name=strdup(Name); + } + else + { + name=NULL; + } + buffer=(uchar *) malloc(Size+8); + if (!buffer) maxqueue=0; + memset(&pktinfo,0,sizeof(pktinfo)); + percent=-1; + mpercent=0; + Clear(); } -cMarkAdDemux::~cMarkAdDemux() +void cPaketQueue::Clear() { - if (ts2pkt) delete ts2pkt; - if (pes2audioes) delete pes2audioes; - if (pes2videoes) delete pes2videoes; - if (queue) delete queue; + inptr=outptr=0; + pktinfo.pkthdr=-1; + scanner=0xFFFFFFFF; + scannerstart=-1; + skipped=0; } -void cMarkAdDemux::Clear() +cPaketQueue::~cPaketQueue() { - if (ts2pkt) ts2pkt->Clear(); - if (pes2audioes) pes2audioes->Clear(); - if (pes2videoes) pes2videoes->Clear(); - if (queue) queue->Clear(); - pause=false; - pause_retval=0; - min_needed=0; - skip=0; + if (name) + { + tsyslog("buffer usage : %-15s %3i%%",name,mpercent); + free((void *) name); + } + if (buffer) free(buffer); +} + +void cPaketQueue::Resize(int NewSize, const char *NewName) +{ + if (NewName) + { + if (name) free((void *) name); + name=strdup(NewName); + } + if (inptr<NewSize) + { + buffer=(uchar *) realloc(buffer,NewSize+8); + if (buffer) + { + maxqueue=NewSize; + } + else + { + maxqueue=0; + Clear(); + } + } } -void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +int cPaketQueue::findpktheader(int start, int *streamsize,int *headersize, bool longstartcode, bool pesonly=false) { - if (!Pkt) return; + if ((!streamsize) || (!headersize)) return -1; + if (!start) start=outptr; + if (start>=inptr) return -1; + *streamsize=0; + if (longstartcode) + { + *headersize=4; // 0x0 0x0 0x0 0x1 + } + else + { + *headersize=3; // 0x0 0x0 0x1 + } + int i; + + if (scanner!=0xFFFFFFFF) + { + scanner<<=8; + scanner|=buffer[start++]; + } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) + bool found=false; + for (i=start; i<inptr; i++) + { + if (longstartcode) + { + if (scanner==1L) + { + found=true; + break; + } + if ((scanner & 0xFFFFFFF0)==0x1E0L) + { + found=true; + break; + } + } + else + { + if ((scanner & 0x00FFFFFF)==1L) + { + if (pesonly) + { + if (buffer[i]>=0xBC) + { + found=true; + break; + } + } + else + { + found=true; + break; + } + } + } + scanner<<=8; + scanner|=buffer[i]; + } + if (!found) { - if (!pes2audioes) pes2audioes=new cMarkAdPES2ES((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) ? - "PES2ES AC3" : "PES2ES MP2"); - if (!pes2audioes) return; - pes2audioes->Process(Pid,Data,Count,Pkt); + if (longstartcode) + { + if (scanner==1L) + { + found=true; + } + if ((scanner & 0xFFFFFFF0)==0x1E0L) + { + found=true; + } + } + else + { + if (((scanner & 0x00FFFFFF)==1L) && (!pesonly)) + { + found=true; + } + } } - if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) + if (i==inptr) + { + if (found) + { + if (!start) + { + scanner=0xFFFFFFFF; + return -1; + } + } + else + { + return -1; + } + } + if (longstartcode) i--; + if (buffer[i]>=0xBC) // do we have a PES packet? { - if (!pes2videoes) +#define PESHDRSIZE 6 + if ((i+PESHDRSIZE)>inptr) + { + return -1; // we need more data (for streamsize and headersize) + } + + *streamsize=(buffer[i+1]<<8)+buffer[i+2]; + if (*streamsize) (*streamsize)+=PESHDRSIZE; // 6 Byte PES-Header + if (longstartcode) { - if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) + struct PESHDROPT *peshdropt=(struct PESHDROPT *) &buffer[i+3]; + if (peshdropt->MarkerBits==0x2) { - pes2videoes=new cMarkAdPES2ES("PES2H264ES",425984); + *headersize=PESHDRSIZE+sizeof(struct PESHDROPT)+ + peshdropt->Length; } else { - pes2videoes=new cMarkAdPES2ES("PES2H262ES",65536); + *headersize=PESHDRSIZE; } } - if (!pes2videoes) return; - pes2videoes->Process(Pid,Data,Count,Pkt); } - return; + return i-3; } -void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +int cPaketQueue::findaudioheader(int start, int *framesize, int *headersize, bool ac3) { - if (!Pkt) return; + if ((!framesize) || (!headersize)) return -1; + if (!start) start=outptr; + if (start>=inptr) return -1; + (*framesize)=0; + if (ac3) + { + (*headersize)=2; + } + else + { + (*headersize)=3; + } + int i; + + if (scanner!=0xFFFFFFFF) + { + scanner<<=8; + scanner|=buffer[start++]; + } + else + { + scanner<<=8; + scanner|=buffer[start++]; + scanner<<=8; + scanner|=buffer[start++]; + } + + for (i=start; i<inptr; i++) + { + + if (ac3) + { + if ((scanner & 0x0000FFFF)==0xB77L) break; + } + else + { + if ((scanner & 0x0000FFE0)==0xFFE0L) break; + } + + scanner<<=8; + scanner|=buffer[i]; + } + if (i==inptr) return -1; - MarkAdPacket pkt; - memset(&pkt,0,sizeof(pkt)); + i-=2; - if (!ts2pkt) + if (ac3) { - switch (Pid.Type) + struct AC3HDR *ac3hdr = (struct AC3HDR *) &buffer[i]; + + if (ac3hdr->SampleRateIndex==3) return -1; // reserved + if (ac3hdr->FrameSizeIndex>=38) return -1; // reserved + + if (framesize) { - case MARKAD_PIDTYPE_VIDEO_H264: - ts2pkt=new cMarkAdTS2Pkt("TS2H264",819200); - break; + int bitRatesAC3[3][38] = // all values are specified as kbits/s + { + { 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, + 224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, + 640, 640, 768, 768, 896, 896, 1024, 1024, 1152, 1152, 1280, 1280 }, // 48kHz - case MARKAD_PIDTYPE_VIDEO_H262: - ts2pkt=new cMarkAdTS2Pkt("TS2H262",262144); - break; + { 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, + 243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, + 696, 697, 835, 836, 975, 976, 1114, 1115, 1253, 1254, 1393, 1394 }, // 44.1kHz - case MARKAD_PIDTYPE_AUDIO_AC3: - ts2pkt=new cMarkAdTS2Pkt("TS2PES AC3",32768); - break; + { 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, + 288, 336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768, + 768, 960, 960, 1152, 1152, 1344, 1344, 1536, 1536, 1728, 1728, 1920,1920 } // 32kHz + }; - case MARKAD_PIDTYPE_AUDIO_MP2: - ts2pkt=new cMarkAdTS2Pkt("TS2PES MP2",16384); + *framesize=2*bitRatesAC3[ac3hdr->SampleRateIndex][ac3hdr->FrameSizeIndex]; + } + return i; + } + else + { + struct MP2HDR *mp2hdr = (struct MP2HDR *) &buffer[i]; + if (mp2hdr->MpegID==1) return -1; // reserved + if (mp2hdr->Layer==0) return -1; // reserved + if (mp2hdr->BitRateIndex==0xF) return -1; // forbidden + if (mp2hdr->SampleRateIndex==3) return -1; //reserved + if (mp2hdr->Emphasis==2) return -1; // reserved + + if (framesize) + { + int bitRates[3][3][16] = // all values are specified as kbits/s + { + { + { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // M1, L1 + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // M1, L2 + { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // M1, L3 + }, + { + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2, L1 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2, L2 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2, L3 + }, + { + { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2.5, L1 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2.5, L2 + { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2.5, L3 + } + }; + + int samplingFrequencies[3][4] = // all values are specified in Hz + { + { 44100, 48000, 32000, -1 }, // MPEG 1 + { 22050, 24000, 16000, -1 }, // MPEG 2 + { 32000, 16000, 8000, -1 } // MPEG 2.5 + }; + + + int slots_per_frame[3][3] = + { + { 12, 144, 144 }, // MPEG 1, Layer I, II, III + { 12, 144, 72 }, // MPEG 2, Layer I, II, III + { 12, 144, 72 } // MPEG 2.5, Layer I, II, III + }; + + int mpegIndex; + switch (mp2hdr->MpegID) + { + case 0: + mpegIndex=2; + break; + case 2: + mpegIndex=1; + break; + case 3: + mpegIndex=0; + break; + default: + mpegIndex=0; // just to get rid of compiler warnings ;) + } + int layerIndex = 3 - mp2hdr->Layer; + + // Layer I (i. e., layerIndex == 0) has a larger slot size + int slotSize = (layerIndex == 0) ? 4 : 1; // bytes + int sf = samplingFrequencies[mpegIndex][mp2hdr->SampleRateIndex]; + + if (mp2hdr->BitRateIndex == 0) + *framesize = 0; // "free" Bitrate -> we don't support this! + else + { + int br = 1000 * bitRates[mpegIndex][layerIndex][mp2hdr->BitRateIndex]; // bits/s + int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots + + *framesize = (N + mp2hdr->Padding) * slotSize; // bytes + } + } + return i; + } +} + +int cPaketQueue::FindPesHeader(int Start) +{ + int start=outptr+Start; + int ssize,hsize; + int pos=findpktheader(start,&ssize,&hsize,false,true); + if (pos==-1) return -1; + pos-=outptr; + return pos; +} + +bool cPaketQueue::Put(uchar *Data, int Size) +{ + if (!buffer) return false; + if ((inptr) && (inptr==outptr)) inptr=outptr=0; + + if (outptr) + { + if (outptr>(inptr-outptr)) + { + memcpy(buffer,&buffer[outptr],inptr-outptr); + if (scannerstart==inptr) scannerstart-=outptr; + inptr-=outptr; + if (pktinfo.pkthdr==outptr) pktinfo.pkthdr=0; + outptr=0; + } + } + + if ((inptr+Size)>maxqueue) + { + if (name) + { + esyslog("buffer %s full",name); + } + else + { + esyslog("buffer full"); + } + mpercent=100; + Clear(); + return false; + } + + memcpy(&buffer[inptr],Data,Size); + inptr+=Size; + + int npercent=(int) ((inptr*100)/maxqueue); + if (npercent>mpercent) mpercent=npercent; + + if ((npercent>90) && (name) && (npercent!=percent)) + { + dsyslog("buffer %s usage: %3i%%", + name,npercent); + percent=npercent; + } + + return true; +} + +uchar *cPaketQueue::Get(int *Size) +{ + if (!buffer) return NULL; + if (!Size) return NULL; + if (Length()<*Size) + { + *Size=0; + return NULL; + } + uchar *ret=&buffer[outptr]; + outptr+=*Size; + return ret; +} + +uchar *cPaketQueue::Peek(int Size) +{ + if (!buffer) return NULL; + if (!Size) return NULL; + if (Length()<Size) return NULL; + uchar *ret=&buffer[outptr]; + return ret; +} + +uchar *cPaketQueue::GetPacket(int *Size, int Type) +{ + if (!Size) return NULL; + *Size=0; + if (Length()<4) return NULL; + + skipped=0; + if (pktinfo.pkthdr==-1) + { + scanner=0xFFFFFFFF; + scannerstart=outptr; + switch (Type) + { + case PACKET_AC3: + pktinfo.pkthdr=findaudioheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); + break; + case PACKET_MP2: + pktinfo.pkthdr=findaudioheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); break; + case PACKET_H264: + pktinfo.pkthdr=findpktheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); + break; + default: + pktinfo.pkthdr=findpktheader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); + break; + } + + if (pktinfo.pkthdr==-1) + { + return NULL; + } + else + { + if (pktinfo.pkthdr!=outptr) + { + skipped=pktinfo.pkthdr-outptr; + } } + scannerstart=pktinfo.pkthdr+pktinfo.pktsyncsize; } - if (!ts2pkt) return; - if (!ts2pkt->Process(Pid,Data,Count,&pkt)) + int streamsize,pktsyncsize,pkthdr=-1; + + if (pktinfo.streamsize) { - if (pes2audioes) pes2audioes->Clear(); - return; + if ((pktinfo.pkthdr+pktinfo.streamsize)>inptr) + { + return NULL; // need more data + } + else + { + scannerstart=pktinfo.pkthdr+pktinfo.streamsize; + scanner=0xFFFFFFFF; + } } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) + switch (Type) { - if (!pes2audioes) pes2audioes=new cMarkAdPES2ES((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) ? - "PES2ES AC3" : "PES2ES MP2"); - if (!pes2audioes) return; - pes2audioes->Process(Pid,pkt.Data,pkt.Length,Pkt); + case PACKET_AC3: + pkthdr=findaudioheader(scannerstart,&streamsize,&pktsyncsize,true); + break; + case PACKET_MP2: + pkthdr=findaudioheader(scannerstart,&streamsize,&pktsyncsize,false); + break; + case PACKET_H264: + pkthdr=findpktheader(scannerstart,&streamsize,&pktsyncsize,true); + break; + default: + pkthdr=findpktheader(scannerstart,&streamsize,&pktsyncsize,false); + break; } - if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) + if (pkthdr==-1) { - if ((pkt.Data) && ((pkt.Data[3] & 0xF0)==0xE0) && (pkt.Data[4]!=0) && (pkt.Data[5]!=0)) + if ((pktinfo.streamsize) && ((inptr-outptr)>pktinfo.streamsize)) + { + // no startcode right after streamsize? + // output streamsize packet + scannerstart=pktinfo.pkthdr+pktinfo.streamsize; + streamsize=pktsyncsize=0; + pkthdr=-1; + } + else { - ts2pkt->InjectVideoPES(pkt.Data,pkt.Length); - pkt.Data=NULL; - pkt.Length=0; + scannerstart=inptr; + return NULL; } - Pkt->Data=pkt.Data; - Pkt->Length=pkt.Length; - Pkt->Skipped=pkt.Skipped; } - return; + else + { + scannerstart=pkthdr+pktsyncsize; + } + + uchar *ptr=&buffer[pktinfo.pkthdr]; + + if (pktinfo.streamsize) + { + *Size=pktinfo.streamsize; + } + else + { + *Size=pkthdr-pktinfo.pkthdr; + } + if (pkthdr==-1) + { + outptr=pktinfo.pkthdr+pktinfo.streamsize; + } + else + { + outptr=pkthdr; + } + + pktinfo.pkthdr=pkthdr; + pktinfo.streamsize=streamsize; + pktinfo.pktsyncsize=pktsyncsize; + + return ptr; } -int cMarkAdDemux::GetMinNeeded(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt) +// ---------------------------------------------------------------------------- + +cTS2Pkt::cTS2Pkt(int Pid, const char *QueueName, int QueueSize, bool H264) { - if (Pid.Num>=0) return TS_SIZE; + queue=new cPaketQueue(QueueName,QueueSize); + pid=Pid; + h264=H264; + firstsync=false; + Clear(); +} - uchar *qData=queue->Peek(PESHDRSIZE); - if (!qData) +cTS2Pkt::~cTS2Pkt() +{ + if (queue) { - int len=PESHDRSIZE-queue->Length(); - int cnt=(Count>len) ? len : Count; - queue->Put(Data,cnt); - return -cnt; + if (skipped) tsyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + delete queue; } +} - int stream=qData[3]; +void cTS2Pkt::Clear() +{ + sync=false; + counter=-1; + skipped=0; + noticeFILLER=false; + noticeSEQUENCE=false; + noticeSTREAM=false; + if (queue) queue->Clear(); +} - if ((qData[0]==0) && (qData[1]==0) && (qData[2]==1) && (stream>0xBC)) +bool cTS2Pkt::Process(uchar *TSData, int TSSize, AvPacket *Pkt) +{ + if (!Pkt) return false; + if (TSData) { - int needed=qData[4]*256+qData[5]; + if (TSSize!=TS_SIZE) return false; + + struct TSHDR *tshdr = (struct TSHDR *) TSData; - if (((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || - (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) && ((stream & 0xF0)!=0xE0)) + int ppid=(tshdr->PidH << 8) | tshdr->PidL; + if (ppid!=pid) { - // ignore 6 header bytes from queue->Put above - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + return false; } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2) && ((stream & 0xF0)!=0xC0)) + + if ((counter!=-1) && (((counter+1) & 0xF)!=tshdr->Counter)) { - // ignore 6 header bytes from queue->Put above - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + if (counter==(int) tshdr->Counter) + { + skipped+=TS_SIZE; + return true; // duplicate paket -> just ignore + } + // sequence error + if (!noticeSEQUENCE) + { + noticeSEQUENCE=true; + isyslog("sequence error"); + } + if (queue) + { + skipped+=queue->Length(); + queue->Clear(); + } + counter=-1; + sync=false; + if (!tshdr->PayloadStart) return true; } - if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) && (stream!=0xBD)) + counter=tshdr->Counter; + + if (tshdr->PayloadStart) { - // ignore 6 header bytes from queue->Put above - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -needed; + firstsync=sync=true; + } + if (!sync) + { + if (firstsync) skipped+=TS_SIZE; // only count skipped bytes after first sync + return true; // not synced + } + + // we just ignore the infos in the adaption field (e.g. OPCR/PCR) + if ((tshdr->AFC!=1) && (tshdr->AFC!=3)) + { + return true; + } + + int buflen=TS_SIZE+1; + uchar *buf=NULL; + + if (tshdr->AFC==1) + { + // payload only + buflen=TS_SIZE-sizeof(struct TSHDR); + buf=&TSData[sizeof(struct TSHDR)]; + } + + if (tshdr->AFC==3) + { + // adaption field + payload + int alen=TSData[4]+1; + if (alen>(TS_SIZE-(int) sizeof(struct TSHDR))) alen=TS_SIZE-(int) sizeof(struct TSHDR); + buflen=TS_SIZE-(sizeof(struct TSHDR)+alen); + buf=&TSData[sizeof(struct TSHDR)+alen]; + } + + if (!buflen) + { + // no data? -> impossible? + return false; + } + + if (tshdr->PayloadStart) + { + if ((buf[0]!=0) && (buf[1]!=0)) + { + if (!noticeSTREAM) + { + isyslog("stream error"); + noticeSTREAM=true; + } + skipped+=TS_SIZE; + sync=false; + if (buflen<7) return false; + // add a pseudo padding stream + buf[0]=0; + buf[1]=0; + buf[2]=1; + buf[3]=0xbe; + buf[4]=0; + buf[5]=buflen-6; + } + } + queue->Put(buf,buflen); + } + + Pkt->Data=queue->GetPacket(&Pkt->Length,h264 ? PACKET_H264 : PACKET_H262); + if (Pkt->Data) + { + Pkt->Type=h264 ? PACKET_H264 : PACKET_H262; + if ((h264) && ((Pkt->Data[4] & 0x1F)==0x0C)) + { + if (!noticeFILLER) + { + isyslog("H264 video stream with filler nalu"); + noticeFILLER=true; + } + skipped+=Pkt->Length; // thats not accurate! + Pkt->Data=NULL; + Pkt->Length=0; + Pkt->Type=0; } - return needed+PESHDRSIZE; } else { - queue->Clear(); - if (Offcnt) *Offcnt=true; - return -1; // skip one byte (maybe we get another header!) + Pkt->Length=0; + Pkt->Type=0; } + return true; } -void cMarkAdDemux::VDRTSAddPATPMT2Offset(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt) +// ---------------------------------------------------------------------------- + +cPES2ES::cPES2ES(int PacketType, const char *QueueName, int QueueSize) { - if (Pid.Num<0) return; - if (Count<2) return; - if ((Data[0]==0x47) && (Data[1]==0x40) && ((Data[2]==0) || (Data[2]==0x84))) *Offcnt=false; + queue = new cPaketQueue(QueueName,QueueSize); + ptype=PacketType; + Clear(); } -int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +cPES2ES::~cPES2ES() { - if ((!Data) && (!Count) && (!Pkt)) return -1; + if (queue) + { + if (skipped) tsyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + delete queue; + } +} - uchar *in=NULL; - int inlen=0; - int retval=0; +void cPES2ES::Clear() +{ + skipped=0; + if (queue) queue->Clear(); +} - if (!pause) +bool cPES2ES::Process(uchar *PESData, int PESSize, AvPacket *ESPkt) +{ + if (!ESPkt) return false; + if (PESData) { - Pkt->Data=NULL; - Pkt->Length=0; - Pkt->Skipped=0; - Pkt->Offcnt=false; + struct PESHDR *peshdr=(struct PESHDR *) PESData; + + // first check some simple things + if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1)) return false; + if (peshdr->StreamID<=0xBC) return false; - if (!min_needed) + int Length=(peshdr->LenH<<8)+peshdr->LenL; + if (Length) { - if (skip) + Length+=sizeof(PESHDR); + if (Length!=PESSize) { - int t_skip=skip; - skip=0; - Pkt->Offcnt=true; - return t_skip; + skipped+=Length; + return true; } + } + + if (peshdr->StreamID==0xBE) + { + queue->Clear(); + return true; + } + + switch (ptype) + { + case PACKET_H262: + if (peshdr->StreamID!=0xE0) return true; // ignore packets not for us! + break; + case PACKET_H264: + if (peshdr->StreamID!=0xE0) return true; // ignore packets not for us! + break; + case PACKET_AC3: + if (peshdr->StreamID!=0xBD) return true; // ignore packets not for us! + break; + case PACKET_MP2: + if (peshdr->StreamID!=0xC0) return true; // ignore packets not for us! + break; + default: + break; + } + + struct PESHDROPT *peshdropt=(struct PESHDROPT *) &PESData[sizeof(struct PESHDR)]; - int t_min_needed=GetMinNeeded(Pid,Data,Count,&Pkt->Offcnt); - if (t_min_needed==0) + uchar *buf; + int buflen; + + if (peshdropt->MarkerBits==0x2) + { + // we have an optional PES header + int bpos=sizeof(struct PESHDR)+sizeof(struct PESHDROPT)+ + peshdropt->Length; + buf=&PESData[bpos]; + buflen=PESSize-bpos; + } + else + { + int bpos=sizeof(struct PESHDR); + buf=&PESData[bpos]; + buflen=PESSize-bpos; + } + if ((ptype==PACKET_AC3) && (buflen>6)) + { + if ((buf[4]==0x0B) && (buf[5]==0x77)) { - return -1; + buf+=4; + buflen-=4; } - if (t_min_needed<0) + } + queue->Put(buf,buflen); + } + ESPkt->Data=queue->GetPacket(&ESPkt->Length,ptype); + if (ESPkt->Data) + { + ESPkt->Type=ptype; + } + else + { + ESPkt->Type=0; + ESPkt->Length=0; + } + skipped+=queue->Skipped(); + return true; +} + +// ---------------------------------------------------------------------------- + +cDemux::cDemux(int VPid, int DPid, int APid, bool H264, bool VDRCount) +{ + TS=false; + if ((VPid>0) || (DPid>0) || (APid>0)) TS=true; + + vpid=VPid; + dpid=DPid; + apid=APid; + if (TS) + { + if (!vpid) vpid=-1; + if (!dpid) dpid=-1; + if (!apid) apid=-1; + } + + pes2videoes=NULL; + pes2audioes_mp2=NULL; + pes2audioes_ac3=NULL; + ts2pkt_vpid=NULL; + ts2pkt_dpid=NULL; + ts2pkt_apid=NULL; + h264=H264; + vdrcount=VDRCount; + queue = new cPaketQueue("DEMUX",5640); + skipped=0; + Clear(); +} + +cDemux::~cDemux() +{ + if (skipped) tsyslog("buffer skipped: %-15s %i bytes",queue->Name(),skipped); + if (queue) delete queue; + + if (ts2pkt_vpid) delete ts2pkt_vpid; + if (pes2videoes) delete pes2videoes; + + if (ts2pkt_dpid) delete ts2pkt_dpid; + if (pes2audioes_ac3) delete pes2audioes_ac3; + + if (ts2pkt_apid) delete ts2pkt_apid; + if (pes2audioes_mp2) delete pes2audioes_mp2; + +} + +int cDemux::Skipped() +{ + int val=skipped; + if (pes2videoes) val+=pes2videoes->Skipped(); + if (pes2audioes_mp2) val+=pes2audioes_mp2->Skipped(); + if (pes2audioes_ac3) val+=pes2audioes_ac3->Skipped(); + if (ts2pkt_vpid) val+=ts2pkt_vpid->Skipped(); + if (ts2pkt_dpid) val+=ts2pkt_dpid->Skipped(); + if (ts2pkt_apid) val+=ts2pkt_apid->Skipped(); + return val; +} + +void cDemux::Clear() +{ + if (pes2videoes) pes2videoes->Clear(); + if (pes2audioes_mp2) pes2audioes_mp2->Clear(); + if (pes2audioes_ac3) pes2audioes_ac3->Clear(); + if (ts2pkt_vpid) ts2pkt_vpid->Clear(); + if (ts2pkt_dpid) ts2pkt_dpid->Clear(); + if (ts2pkt_apid) ts2pkt_apid->Clear(); + + if (queue) queue->Clear(); + offset=0; + vdroffset=0; + last_bplen=0; + from_oldfile=0; + stream_or_pid=0; +} + +bool cDemux::isvideopes(uchar *data, int count) +{ + if (!data) return false; + if (count<6) return false; + if ((data[0]==0) && (data[1]==0) && (data[2]==1) && + ((data[3] & 0xF0)==0xE0) && + ((data[4]!=0) || (data[5]!=0))) return true; + return false; +} + +int cDemux::checkts(uchar *data, int count, int &pid) +{ + if (count<(int) sizeof(struct TSHDR)) return -1; + if (data[0]!=0x47) return 1; + + struct TSHDR *tshdr = (struct TSHDR *) data; + if ((tshdr->AFC<=0) || (tshdr->AFC>3)) return 1; + + pid = (tshdr->PidH << 8) | tshdr->PidL; + return 0; +} + +int cDemux::fillqueue(uchar *data, int count, int &stream_or_pid, int &packetsize, int &readout) +{ +#define PEEKBUF 6 + stream_or_pid=packetsize=readout=0; + + uchar *qData=NULL; + + while (!(qData=queue->Peek(PEEKBUF))) + { + int len=PEEKBUF-queue->Length(); + int cnt=(count>len) ? len : count; + if (!queue->Put(data,cnt)) return -1; + readout+=cnt; + data+=cnt; + count-=cnt; + if (queue->Length()<PEEKBUF) return cnt; // we need more data! + } + + if (!TS) + { + if ((qData[0]==0) && (qData[1]==0) && (qData[2]==1) && (qData[3]>=0xBC)) + { + stream_or_pid=qData[3]; + packetsize=PEEKBUF+(qData[4]*256+qData[5]); + } + else + { + skipped++; + stream_or_pid=0; + packetsize=1; + return 0; + } + } + else + { + int error_skipbytes=checkts(qData,PEEKBUF,stream_or_pid); + if (error_skipbytes==-1) return -1; + if (error_skipbytes) + { + skipped+=error_skipbytes; + stream_or_pid=0; + packetsize=1; + return error_skipbytes; // no useable data found, try next! + } + packetsize=TS_SIZE; + } + + int needed=packetsize+PEEKBUF; + while (!(qData=queue->Peek(needed))) + { + int len=needed-queue->Length(); + int cnt=(count>len) ? len : count; + if (!queue->Put(data,cnt)) return -1; + readout+=cnt; + data+=cnt; + count-=cnt; + if (queue->Length()<needed) return cnt; // we need more data! + } + if (!TS) + { + // check length of PES-paket + qData=queue->Peek(packetsize+PEEKBUF); + if (qData) + { + int start=packetsize; + if ((qData[start]!=0) || (qData[start+1]!=0) || (qData[start+2]!=1) || (qData[start+3]<0xBC)) { - if (-t_min_needed>Count) + int start=queue->FindPesHeader(1); + if (start) + { + // broken PES in queue, skip it + packetsize=start; + skipped+=start; + stream_or_pid=0; + return 0; + } + else { - skip=-t_min_needed-Count; - return Count; + return -1; } - if (t_min_needed==-1) Pkt->Skipped++; - return -t_min_needed; } - min_needed=t_min_needed; } + } + else + { + qData=queue->Peek(packetsize+PEEKBUF); + if (qData) + { + int start=packetsize; + int pid; + int ret=checkts(&qData[start],PEEKBUF,pid); + if (ret==-1) + { + return -1; + } + if (ret) + { + return -1; + } + } + } + return 0; +} - int needed=min_needed-queue->Length(); +bool cDemux::needmoredata() +{ + if (!stream_or_pid) return true; + + if (!TS) + { + switch (stream_or_pid) + { + case 0xE0: + if (pes2videoes) return pes2videoes->NeedMoreData(); + break; + case 0xC0: + if ((pes2audioes_mp2) && (apid)) return pes2audioes_mp2->NeedMoreData(); + break; + case 0xBD: + if ((pes2audioes_ac3) && (dpid)) return pes2audioes_ac3->NeedMoreData(); + break; + } + } - if (Count>needed) + if (TS) + { + if ((stream_or_pid==vpid) && (ts2pkt_vpid)) { - queue->Put(Data,needed); - retval=needed; + if (pes2videoes) return pes2videoes->NeedMoreData(); + return ts2pkt_vpid->NeedMoreData(); } - else + if ((stream_or_pid==dpid) && (ts2pkt_dpid)) { - queue->Put(Data,Count); - retval=Count; + if (pes2audioes_ac3) return pes2audioes_ac3->NeedMoreData(); + return ts2pkt_dpid->NeedMoreData(); } - if (queue->Length()<min_needed) + if ((stream_or_pid==apid) && (ts2pkt_apid)) { - return Count; + if (pes2audioes_mp2) return pes2audioes_mp2->NeedMoreData(); + return ts2pkt_apid->NeedMoreData(); } - inlen=min_needed; - in=queue->Get(&inlen); - min_needed=0; } + return false; +} - if (Pid.Num>=0) +bool cDemux::vdraddpatpmt(uchar *data, int count) +{ + // TS-VDR adds pat/pmt to the output, e.g. if + // a picture starts @376, vdr outputs 0 (!) + int pid; + if (checkts(data,count,pid)!=0) return false; + if ((!pid) || (pid==132)) { - ProcessTS(Pid, in, inlen, Pkt); + last_bplen=0; + vdroffset+=count; } else { - ProcessVDR(Pid, in, inlen, Pkt); + last_bplen=vdroffset+count; + vdroffset=0; } + return true; +} - if (Pkt->Data) +void cDemux::addoffset() +{ + offset+=last_bplen; + if (from_oldfile) { - if (!pause_retval) pause_retval=retval; - pause=true; - return 0; + from_oldfile-=last_bplen; + if (!from_oldfile) offset=0; } + last_bplen=0; +} - if (pause) +void cDemux::NewFile() +{ + from_oldfile=queue->Length(); +} + +void cDemux::DisableDPid() +{ + if (pes2audioes_ac3) delete pes2audioes_ac3; + if (ts2pkt_dpid) delete ts2pkt_dpid; + pes2audioes_ac3=NULL; + ts2pkt_dpid=NULL; + if (TS) + { + dpid=-1; + } + else + { + dpid=0; + } + stream_or_pid=0; +} + +int cDemux::Process(uchar *Data, int Count, AvPacket *pkt) +{ + if (!pkt) return -1; + pkt->Data=NULL; + pkt->Length=0; + + bool add=needmoredata(); + + int bplen=0,readout=0; + uchar *bpkt=NULL; + if (add) + { + addoffset(); + int advance=fillqueue(Data,Count,stream_or_pid,bplen,readout); + if (advance<0) return -1; + if (advance) return advance; + bpkt=queue->Get(&bplen); + if (!bpkt) return -1; + last_bplen=bplen; + if (vdrcount) vdraddpatpmt(bpkt,bplen); + } + + if (!TS) { - if (pause_retval) + switch (stream_or_pid) { - retval=pause_retval; - pause_retval=0; + case 0xE0: + if (!pes2videoes) + { + if (h264) + { + pes2videoes=new cPES2ES(PACKET_H264,"PES2H264ES",524288); + } + else + { + pes2videoes=new cPES2ES(PACKET_H262,"PES2H262ES",65536); + } + } + if (!pes2videoes->Process(bpkt,bplen,pkt)) return -1; + break; + case 0xC0: + if (apid) + { + if (!pes2audioes_mp2) pes2audioes_mp2=new cPES2ES(PACKET_MP2,"PES2MP2",16384); + if (!pes2audioes_mp2->Process(bpkt,bplen,pkt)) return -1; + } + else + { + stream_or_pid=0; + } + break; + case 0xBD: + if (dpid) + { + if (!pes2audioes_ac3) pes2audioes_ac3=new cPES2ES(PACKET_AC3,"PES2AC3"); + if (!pes2audioes_ac3->Process(bpkt,bplen,pkt)) return -1; + } + else + { + stream_or_pid=0; + } + break; + default: + stream_or_pid=0; + break; } - pause=false; } - - Pkt->Offcnt=true; - VDRTSAddPATPMT2Offset(Pid, in, inlen, &Pkt->Offcnt); - return retval; + if (TS) + { + if (stream_or_pid==vpid) + { + if (!ts2pkt_vpid) + { + if (h264) + { + ts2pkt_vpid=new cTS2Pkt(vpid,"TS2H264",524288,true); + } + else + { + ts2pkt_vpid=new cTS2Pkt(vpid,"TS2H262",65536); + } + } + if (!ts2pkt_vpid->Process(bpkt,bplen,pkt)) return -1; + if (isvideopes(pkt->Data,pkt->Length) || pes2videoes) + { + AvPacket tpkt; + memcpy(&tpkt,pkt,sizeof(AvPacket)); + memset(pkt,0,sizeof(AvPacket)); + if (!pes2videoes) + { + ts2pkt_vpid->Resize(3*tpkt.Length,"TS2PES"); + if (h264) + { + pes2videoes=new cPES2ES(PACKET_H264,"PES2H264ES",524288); + } + else + { + pes2videoes=new cPES2ES(PACKET_H262,"PES2H262ES",65536); + } + } + if (!pes2videoes->Process(tpkt.Data,tpkt.Length,pkt)) return -1; + } + } + else if (stream_or_pid==dpid) + { + AvPacket tpkt={NULL,0,0}; + if (!ts2pkt_dpid) ts2pkt_dpid=new cTS2Pkt(dpid,"TS2PES AC3"); + if (!ts2pkt_dpid->Process(bpkt,bplen,&tpkt)) return -1; + if (!pes2audioes_ac3) pes2audioes_ac3=new cPES2ES(PACKET_AC3,"PES2AC3"); + if (!pes2audioes_ac3->Process(tpkt.Data,tpkt.Length,pkt)) return -1; + } + else if (stream_or_pid==apid) + { + AvPacket tpkt={NULL,0,0}; + if (!ts2pkt_apid) ts2pkt_apid=new cTS2Pkt(apid,"TS2PES MP2",16384); + if (!ts2pkt_apid->Process(bpkt,bplen,&tpkt)) return -1; + if (!pes2audioes_mp2) pes2audioes_mp2=new cPES2ES(PACKET_MP2,"PES2MP2",16384); + if (!pes2audioes_mp2->Process(tpkt.Data,tpkt.Length,pkt)) return -1; + } + else stream_or_pid=0; + } + return add ? readout : 0; } diff --git a/command/demux.h b/command/demux.h index 0c91c7a..2299e6b 100644 --- a/command/demux.h +++ b/command/demux.h @@ -8,39 +8,323 @@ #ifndef __demux_h_ #define __demux_h_ -#ifndef TS_SIZE -#define TS_SIZE 188 +#include <stdint.h> + +#ifndef uchar +typedef unsigned char uchar; #endif -#define PESHDRSIZE 6 +#define PACKET_MASK 0xF0 +#define PACKET_VIDEO 0x10 +#define PACKET_H262 0x10 // 0x00 0x00 0x01 (PES / H262) +#define PACKET_H264 0x11 // 0x00 0x00 0x00 0x01 (H264) +#define PACKET_AUDIO 0x20 +#define PACKET_AC3 0x20 +#define PACKET_MP2 0x21 + +typedef struct AvPacket +{ + uchar *Data; + int Length; + int Type; +} AvPacket; + +struct TSHDR +{ +unsigned Sync: + 8; +unsigned PidH: + 5; +unsigned Priority: + 1; +unsigned PayloadStart: + 1; +unsigned TError: + 1; +unsigned PidL: + 8; +unsigned Counter: + 4; +unsigned AFC: + 2; +unsigned TSC: + 2; +}; + +struct PESHDR +{ + uchar Sync1; + uchar Sync2; + uchar Sync3; + uchar StreamID; + uchar LenH; + uchar LenL; +}; + +#pragma pack(1) +struct PESHDROPT +{ +unsigned OOC: + 1; +unsigned CY: + 1; +unsigned DAI: + 1; +unsigned PESP: + 1; +unsigned PESSC: + 2; +unsigned MarkerBits: + 2; +unsigned EXT: + 1; +unsigned CRC: + 1; +unsigned ACI: + 1; +unsigned TM: + 1; +unsigned RATE: + 1; +unsigned ESCR: + 1; +unsigned PTSDTS: + 2; +unsigned Length: + 8; +}; +#pragma pack() + +// ---------------------------------------------------------------------------- + +class cPaketQueue +{ + struct MP2HDR + { +unsigned Sync1: + 8; +unsigned Protection: + 1; +unsigned Layer: + 2; +unsigned MpegID: + 2; +unsigned Sync2: + 3; +unsigned Private: + 1; +unsigned Padding: + 1; +unsigned SampleRateIndex: + 2; +unsigned BitRateIndex: + 4; +unsigned Emphasis: + 2; +unsigned Original: + 1; +unsigned Copyright: + 1; +unsigned ModeExt: + 2; +unsigned Mode: + 2; + }; + +#pragma pack(1) + struct AC3HDR + { +unsigned Sync1: + 8; +unsigned Sync2: + 8; +unsigned CRC1: + 8; +unsigned CRC2: + 8; +unsigned FrameSizeIndex: + 6; +unsigned SampleRateIndex: + 2; + }; +#pragma pack() + +private: + const char *name; + struct pktinfo + { + int pkthdr; + int pktsyncsize; + int streamsize; + bool ispes; + } pktinfo; + + int percent; + int mpercent; // max percentage use + + uchar *buffer; + int maxqueue; + int inptr; + int outptr; + + int skipped; -#include "queue.h" -#include "ts2pkt.h" -#include "pes2es.h" + uint32_t scanner; + int scannerstart; -class cMarkAdDemux + int findaudioheader(int start, int *framesize, int *headersize, bool ac3); + int findpktheader(int start, int *streamsize,int *headersize, bool longstartcode, bool pesonly); +public: + cPaketQueue(const char *Name, int Size=32768); + ~cPaketQueue(); + int Length() + { + return inptr-outptr; + } + const char *Name() + { + return name; + } + void Clear(); + int Skipped() + { + return skipped; + } + bool Put(uchar *Data, int Size); + uchar *Get(int *Size); + uchar *Peek(int Size); + uchar *GetPacket(int *Size, int Type); + void Resize(int NewSize, const char *NewName=NULL); + bool NeedMoreData() + { + if (scannerstart==-1) return true; + if (pktinfo.streamsize) + { + return (((inptr-pktinfo.pkthdr)-outptr)<=(pktinfo.streamsize+6)); + } + else + { + return ((scannerstart==inptr) || (scannerstart==outptr)); + } + } + int FindPesHeader(int Start); +}; + +// ---------------------------------------------------------------------------- + +class cTS2Pkt { +private: + cPaketQueue *queue; + int counter; + bool sync; + bool firstsync; + int skipped; + int pid; + bool h264; + bool noticeFILLER; + bool noticeSEQUENCE; + bool noticeSTREAM; +public: + cTS2Pkt(int Pid, const char *QueueName="TS2Pkt", int QueueSize=32768, bool H264=false); + ~cTS2Pkt(); + void Clear(); + int Skipped() + { + return skipped; + } + bool Process(uchar *TSData, int TSSize, AvPacket *Pkt); + void Resize(int NewQueueSize, const char *NewQueueName) + { + queue->Resize(NewQueueSize, NewQueueName); + } + bool NeedMoreData() + { + if (queue) + { + return queue->NeedMoreData(); + } + else + { + return false; + } + } +}; +// ---------------------------------------------------------------------------- + +class cPES2ES +{ private: - cMarkAdTS2Pkt *ts2pkt; - cMarkAdPES2ES *pes2audioes; - cMarkAdPES2ES *pes2videoes; - cMarkAdPaketQueue *queue; - - bool pause; - int pause_retval; - int min_needed; - int skip; - - int GetMinNeeded(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt); - void ProcessTS(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); - void ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); - void VDRTSAddPATPMT2Offset(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt); + cPaketQueue *queue; + int skipped; + int ptype; + bool h264; +public: + cPES2ES(int PacketType, const char *QueueName="PES2ES", int QueueSize=32768); + ~cPES2ES(); + void Clear(); + bool Process(uchar *PESData, int PESSize, AvPacket *ESPkt); + int Skipped() + { + return skipped; + } + bool NeedMoreData() + { + if (queue) + { + return queue->NeedMoreData(); + } + else + { + return false; + } + } +}; + +// ---------------------------------------------------------------------------- + +class cDemux +{ +private: + int vpid,dpid,apid; + int stream_or_pid; + int skipped; + bool h264; + bool TS; + uint64_t offset; + int from_oldfile; + int last_bplen; + + bool vdrcount; + int vdroffset; + bool vdraddpatpmt(uchar *data, int count); + + void addoffset(); + + cPaketQueue *queue; + cPES2ES *pes2videoes; + cPES2ES *pes2audioes_mp2; + cPES2ES *pes2audioes_ac3; + cTS2Pkt *ts2pkt_vpid; + cTS2Pkt *ts2pkt_dpid; + cTS2Pkt *ts2pkt_apid; + bool needmoredata(); + int checkts(uchar *data, int count, int &pid); + bool isvideopes(uchar *data, int count); + int fillqueue(uchar *data, int count, int &stream_or_pid, int &packetsize, int &readout); public: - cMarkAdDemux(); - ~cMarkAdDemux(); + cDemux(int VPid, int DPid, int APid, bool H264=false, bool VDRCount=false); + ~cDemux(); + void DisableDPid(); void Clear(); - int Process(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); + int Skipped(); + void NewFile(); + uint64_t Offset() + { + return offset; + } + int Process(uchar *Data, int Count, AvPacket *pkt); }; #endif diff --git a/command/global.h b/command/global.h index 7ac42b6..af2d3a1 100644 --- a/command/global.h +++ b/command/global.h @@ -22,38 +22,35 @@ typedef unsigned char uchar; #define MA_SP_TYPE 6 #define MA_BI_TYPE 7 -#define MT_UNCERTAIN 0 -#define MT_START 1 -#define MT_STOP 2 +#define MT_START 1 +#define MT_STOP 2 -#define MT_COMMON 0x10 -#define MT_COMMONSTART 0x11 -#define MT_COMMONSTOP 0x12 +#define MT_ASSUMED 0x10 +#define MT_ASSUMEDSTART 0x11 +#define MT_ASSUMEDSTOP 0x12 -#define MT_ASSUMED 0x20 -#define MT_ASSUMEDSTART 0x21 -#define MT_ASSUMEDSTOP 0x22 +#define MT_LOGOCHANGE 0x20 +#define MT_LOGOSTART 0x21 +#define MT_LOGOSTOP 0x22 -#define MT_ASPECTCHANGE 0x30 -#define MT_ASPECTSTART 0x31 -#define MT_ASPECTSTOP 0x32 +#define MT_HBORDERCHANGE 0x30 +#define MT_HBORDERSTART 0x31 +#define MT_HBORDERSTOP 0x32 -#define MT_CHANNELCHANGE 0x40 -#define MT_CHANNELSTART 0x41 -#define MT_CHANNELSTOP 0x42 +#define MT_VBORDERCHANGE 0x40 +#define MT_VBORDERSTART 0x41 +#define MT_VBORDERSTOP 0x42 -#define MT_LOGOCHANGE 0x50 -#define MT_LOGOSTART 0x51 -#define MT_LOGOSTOP 0x52 +#define MT_ASPECTCHANGE 0x50 +#define MT_ASPECTSTART 0x51 +#define MT_ASPECTSTOP 0x52 -#define MT_BORDERCHANGE 0x60 -#define MT_BORDERSTART 0x61 -#define MT_BORDERSTOP 0x62 +#define MT_CHANNELCHANGE 0x60 +#define MT_CHANNELSTART 0x61 +#define MT_CHANNELSTOP 0x62 -#define MT_SILENCECHANGE 0x90 - -#define MT_MOVED 0xE0 -#define MT_ALL 0xFF +#define MT_MOVED 0xE0 +#define MT_ALL 0xFF typedef struct config { @@ -84,15 +81,22 @@ typedef struct MarkAdPos { int FrameNumberBefore; int FrameNumberAfter; - char *CommentBefore; - char *CommentAfter; } MarkAdPos; +typedef struct MarkAdAspectRatio +{ + int Num; + int Den; +} MarkAdAspectRatio; + typedef struct MarkAdMark { char Type; int Position; - char *Comment; + int ChannelsBefore; + int ChannelsAfter; + MarkAdAspectRatio AspectRatioBefore; + MarkAdAspectRatio AspectRatioAfter; } MarkAdMark; typedef struct MarkAdMarks @@ -102,12 +106,6 @@ typedef struct MarkAdMarks int Count; } MarkAdMarks; -typedef struct MarkAdAspectRatio -{ - int Num; - int Den; -} MarkAdAspectRatio; - #define MARKAD_PIDTYPE_VIDEO_H262 0x10 #define MARKAD_PIDTYPE_VIDEO_H264 0x11 #define MARKAD_PIDTYPE_AUDIO_AC3 0x20 @@ -125,9 +123,11 @@ typedef struct MarkAdContext struct Info { + + MarkAdAspectRatio AspectRatio; int Channels; + char *ChannelName; - MarkAdAspectRatio AspectRatio; MarkAdPid VPid; MarkAdPid APid; MarkAdPid DPid; diff --git a/command/logos/ANTENA_3-A16_9-P0.pgm b/command/logos/ANTENA_3-A16_9-P0.pgm Binary files differnew file mode 100644 index 0000000..8ad9602 --- /dev/null +++ b/command/logos/ANTENA_3-A16_9-P0.pgm diff --git a/command/logos/ANTENA_3-A16_9-P1.pgm b/command/logos/ANTENA_3-A16_9-P1.pgm Binary files differnew file mode 100644 index 0000000..0c147a7 --- /dev/null +++ b/command/logos/ANTENA_3-A16_9-P1.pgm diff --git a/command/logos/ANTENA_3-A16_9-P2.pgm b/command/logos/ANTENA_3-A16_9-P2.pgm Binary files differnew file mode 100644 index 0000000..77c175b --- /dev/null +++ b/command/logos/ANTENA_3-A16_9-P2.pgm diff --git a/command/logos/Antena_3_HD-A16_9-P0.pgm b/command/logos/Antena_3_HD-A16_9-P0.pgm Binary files differindex c742986..0d97c2c 100644 --- a/command/logos/Antena_3_HD-A16_9-P0.pgm +++ b/command/logos/Antena_3_HD-A16_9-P0.pgm diff --git a/command/logos/CUATRO-A16_9-P0.pgm b/command/logos/CUATRO-A16_9-P0.pgm Binary files differnew file mode 100644 index 0000000..6192ecf --- /dev/null +++ b/command/logos/CUATRO-A16_9-P0.pgm diff --git a/command/logos/La_1-A16_9-P0.pgm b/command/logos/La_1-A16_9-P0.pgm Binary files differnew file mode 100644 index 0000000..9aed19c --- /dev/null +++ b/command/logos/La_1-A16_9-P0.pgm diff --git a/command/logos/La_2-A4_3-P0.pgm b/command/logos/La_2-A4_3-P0.pgm Binary files differnew file mode 100644 index 0000000..90d5413 --- /dev/null +++ b/command/logos/La_2-A4_3-P0.pgm diff --git a/command/logos/Telecinco-A16_9-P0.pgm b/command/logos/Telecinco-A16_9-P0.pgm Binary files differnew file mode 100644 index 0000000..d33075d --- /dev/null +++ b/command/logos/Telecinco-A16_9-P0.pgm diff --git a/command/logos/Telecinco-A16_9-P1.pgm b/command/logos/Telecinco-A16_9-P1.pgm Binary files differnew file mode 100644 index 0000000..0c1119e --- /dev/null +++ b/command/logos/Telecinco-A16_9-P1.pgm diff --git a/command/logos/Telecinco-A16_9-P2.pgm b/command/logos/Telecinco-A16_9-P2.pgm Binary files differnew file mode 100644 index 0000000..be29a09 --- /dev/null +++ b/command/logos/Telecinco-A16_9-P2.pgm diff --git a/command/logos/Telecinco_HD-A16_9-P0.pgm b/command/logos/Telecinco_HD-A16_9-P0.pgm new file mode 100644 index 0000000..f0a92c9 --- /dev/null +++ b/command/logos/Telecinco_HD-A16_9-P0.pgm @@ -0,0 +1,5 @@ +P5 +#C3 +380 170 +255 +ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ diff --git a/command/logos/laSexta-A16_9-P0.pgm b/command/logos/laSexta-A16_9-P0.pgm Binary files differnew file mode 100644 index 0000000..6a25f77 --- /dev/null +++ b/command/logos/laSexta-A16_9-P0.pgm diff --git a/command/logos/laSexta_HD-A4_3-P0.pgm b/command/logos/laSexta_HD-A4_3-P0.pgm Binary files differnew file mode 100644 index 0000000..67a842b --- /dev/null +++ b/command/logos/laSexta_HD-A4_3-P0.pgm diff --git a/command/markad-standalone.cpp b/command/markad-standalone.cpp index cccbf0f..f99ae92 100644 --- a/command/markad-standalone.cpp +++ b/command/markad-standalone.cpp @@ -26,6 +26,8 @@ #include <execinfo.h> #include <mntent.h> #include <utime.h> +#include <math.h> +#include <limits.h> #include <errno.h> #include "markad-standalone.h" @@ -179,625 +181,326 @@ int cOSDMessage::Send(const char *format, ...) return 0; } -void cMarkAdStandalone::CalculateStopPosition(int startframe, int delta) +void cMarkAdStandalone::CalculateCheckPositions(int startframe) { if (!length) return; + if (!startframe) return; if (!macontext.Video.Info.FramesPerSecond) return; - int len_in_frames=length*macontext.Video.Info.FramesPerSecond; + int delta=macontext.Video.Info.FramesPerSecond*MAXRANGE; + int len_in_frames=macontext.Video.Info.FramesPerSecond*length; - if (startframe) - { - iStop=-(startframe+len_in_frames); - chkLEFT=startframe+delta+macontext.Video.Info.FramesPerSecond; - chkRIGHT=startframe+(len_in_frames*2/3); - } - else - { - chkLEFT=(int) (len_in_frames*2/5); - chkRIGHT=(int) (len_in_frames*2/3); - } + iStart=-startframe; + iStop=-(startframe+len_in_frames); + chkSTART=-iStart+delta; + chkSTOP=-iStop+(3*delta); } -void cMarkAdStandalone::AddStartMark() +void cMarkAdStandalone::CheckStop() { - if (tStart<2) - { - char *buf; - if (asprintf(&buf,"start of recording (0)")!=-1) - { - marks.Add(MT_COMMONSTART,0,buf); - isyslog("%s",buf); - free(buf); - } - } - if (tStart) + dsyslog("checking stop"); + int delta=macontext.Video.Info.FramesPerSecond*MAXRANGE; + clMark *end=marks.GetAround(delta,iStop,MT_STOP,0x0F); + + if (end) { - iStart=-(tStart*macontext.Video.Info.FramesPerSecond); + marks.DelTill(end->position,false); + isyslog("using mark on position %i as stop mark",end->position); } - CalculateStopPosition(-iStart,macontext.Video.Info.FramesPerSecond*MAXRANGE); - if (tStart==1) + else { - tStart=iStart=0; + //fallback, shouldn't be reached + MarkAdMark mark; + memset(&mark,0,sizeof(mark)); + mark.Position=iStop; + mark.Type=MT_ASSUMEDSTOP; + AddMark(&mark); + marks.DelTill(iStop,false); } + iStop=0; + gotendmark=true; } -void cMarkAdStandalone::CheckStartStop(int frame, bool checkend) +void cMarkAdStandalone::CheckStart() { - MarkAdMark mark; - char *buf; + dsyslog("checking start"); + clMark *begin=NULL; - if ((iStartCheck>0) && (frame>=iStartCheck)) + if ((macontext.Info.Channels) && (macontext.Audio.Info.Channels) && + (macontext.Info.Channels!=macontext.Audio.Info.Channels)) { - // check if we have a start mark which is near iStart - clMark *before_iStart=marks.GetPrev(iStart,MT_START,0xF); - clMark *after_iStart=marks.GetNext(iStart,MT_START,0xF); - - int MAXMARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*MAXRANGE); - int type=0; - int newpos=0; - int delta_before=MAXMARKDIFF; - int delta_after=MAXMARKDIFF; - - if (before_iStart) - { - delta_before=abs(iStart-before_iStart->position); - if (delta_before>MAXMARKDIFF) delta_before=MAXMARKDIFF; - } - if (after_iStart) + char as[20]; + switch (macontext.Info.Channels) { - delta_after=abs(after_iStart->position-iStart); - if (delta_after>MAXMARKDIFF) delta_after=MAXMARKDIFF; - } - - if (delta_before>delta_after) - { - // use after_iStart - newpos=after_iStart->position; - type=after_iStart->type; + case 1: + strcpy(as,"mono"); + break; + case 2: + strcpy(as,"stereo"); + break; + case 6: + strcpy(as,"dd5.1"); + break; + default: + strcpy(as,"??"); + break; } - - if (delta_before<delta_after) + char ad[20]; + switch (macontext.Audio.Info.Channels) { - // use before_iStart - newpos=before_iStart->position; - type=before_iStart->type; - } - - if (newpos) - { - if (type==MT_BORDERSTART) - { - // check if we have an MT_ASPECTCHANGE in low distance - clMark *aspectmark=marks.GetPrev(newpos,MT_ASPECTSTART); - if (!aspectmark) aspectmark=marks.GetNext(newpos,MT_ASPECTSTART); - if (aspectmark) - { - int MAXMARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*20); - if (abs(newpos-aspectmark->position)<=MAXMARKDIFF) - { - newpos=aspectmark->position; - } - } - } - isyslog("using this mark instead of assumed start mark (%i->%i)", - newpos,iStart); - marks.Del(iStart); - marks.DelTill(newpos); - CalculateStopPosition(newpos,frame-newpos); + case 1: + strcpy(ad,"mono"); + break; + case 2: + strcpy(ad,"stereo"); + break; + case 6: + strcpy(ad,"dd5.1"); + break; + default: + strcpy(ad,"??"); + break; } - iStart=0; - iStartCheck=0; + isyslog("audio description in info (%s) wrong, we have %s",as,ad); } - if ((iStopCheck>0) && ((frame>=iStopCheck) || (checkend))) + macontext.Info.Channels=macontext.Audio.Info.Channels; + if (macontext.Info.Channels==6) { - // check if we have a stop mark which is near iStop - clMark *before_iStop=marks.GetPrev(iStop,MT_STOP,0xF); - clMark *after_iStop=marks.GetNext(iStop,MT_STOP,0xF); - - int MAXMARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*MAXRANGE); - int newpos=0; - int delta_before=MAXMARKDIFF; - int delta_after=MAXMARKDIFF; - - if (before_iStop) - { - delta_before=abs(iStop-before_iStop->position); - if (delta_before>MAXMARKDIFF) delta_before=MAXMARKDIFF; - } - if (after_iStop) - { - delta_after=abs(after_iStop->position-iStop); - if (delta_after>MAXMARKDIFF) delta_after=MAXMARKDIFF; - } - - if (delta_before>delta_after) - { - // use after_iStop - newpos=after_iStop->position; - } - - if (delta_before<delta_after) - { - // use before_iStop - newpos=before_iStop->position; - } - - if (newpos) - { - isyslog("using this mark instead of assumed stop mark (%i->%i)", - newpos,iStop); - marks.Del(iStop); - marks.DelTill(newpos,false); - } - else - { - marks.DelTill(iStop,false); - } + isyslog("DolbyDigital5.1 audio detected. logo/border/aspect detection disabled"); bDecodeVideo=false; - gotendmark=true; - iStop=0; - iStopCheck=0; - } - - if ((iStart<0) && (frame>-iStart)) - { - iStart=frame; - if (asprintf(&buf,"assumed start of broadcast (%i)",iStart)!=-1) - { - mark.Type=MT_ASSUMEDSTART; - mark.Position=iStart; - mark.Comment=buf; - AddMark(&mark); - free(buf); - } - - int MARKDIFF=length/6; - if (MARKDIFF>MAXRANGE) MARKDIFF=MAXRANGE; - MARKDIFF=(int) (MARKDIFF*macontext.Video.Info.FramesPerSecond); - iStartCheck=iStart+MARKDIFF; - CalculateStopPosition(iStart,MARKDIFF); + macontext.Video.Options.IgnoreAspectRatio=true; + macontext.Video.Options.IgnoreLogoDetection=true; + marks.Del(MT_ASPECTSTART); + marks.Del(MT_ASPECTSTOP); + // start mark must be around istart + begin=marks.GetAround(INT_MAX,iStart,MT_CHANNELSTART); } - if ((iStop<0) && (lastiframe>-iStop)) + else { - iStop=frame; - if (asprintf(&buf,"assumed stop of broadcast (%i)",iStop)!=-1) - { - mark.Type=MT_ASSUMEDSTOP; - mark.Position=iStop; - mark.Comment=buf; - AddMark(&mark); - free(buf); - } - - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*MAXRANGE); - clMark *before_iStop=marks.GetPrev(iStart,MT_STOP,0xF); - if (before_iStop) + if (macontext.Info.DPid.Num) { - int tmpdiff=abs(iStop-before_iStop->position); - if (tmpdiff<MARKDIFF) MARKDIFF=tmpdiff; + if (macontext.Info.Channels) + isyslog("broadcast with %i audio channels, disabling AC3 decoding",macontext.Info.Channels); + macontext.Info.DPid.Num=0; + demux->DisableDPid(); } - iStopCheck=iStop+MARKDIFF; } -} -void cMarkAdStandalone::CheckLogoMarks(clMark *last) -{ - clMark *mark=marks.GetFirst(); - while (mark) + if ((macontext.Info.AspectRatio.Num) && ((macontext.Info.AspectRatio.Num!= + macontext.Video.Info.AspectRatio.Num) || (macontext.Info.AspectRatio.Den!= + macontext.Video.Info.AspectRatio.Den))) { - if ((mark->type==MT_LOGOSTOP) && mark->Next() && mark->Next()->type==MT_LOGOSTART) - { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*55); - if (abs(mark->Next()->position-mark->position)<=MARKDIFF) - { - double distance=(mark->Next()->position-mark->position)/ - macontext.Video.Info.FramesPerSecond; - isyslog("logo distance too short (%.1fs), deleting (%i,%i)",distance, - mark->position,mark->Next()->position); - clMark *tmp=mark; - mark=mark->Next()->Next(); - marks.Del(tmp->Next()); - marks.Del(tmp); - continue; - } - } - mark=mark->Next(); - if ((last) && (mark==last)) return; + isyslog("video aspect description in info (%i:%i) wrong", + macontext.Info.AspectRatio.Num, + macontext.Info.AspectRatio.Den); } -} -void cMarkAdStandalone::CheckLastMark() -{ - if (marks.Count()<=2) return; // just two marks -> do nothing - clMark *last=marks.GetLast(); - if (!last) return; + macontext.Info.AspectRatio.Num=macontext.Video.Info.AspectRatio.Num; + macontext.Info.AspectRatio.Den=macontext.Video.Info.AspectRatio.Den; - clMark *nexttolast=last->Prev(); - if (!nexttolast) return; + isyslog("aspectratio of %i:%i detected. %s", + macontext.Video.Info.AspectRatio.Num, + macontext.Video.Info.AspectRatio.Den, + ((macontext.Video.Info.AspectRatio.Num==4) && + (macontext.Video.Info.AspectRatio.Den==3)) ? + "logo/border detection disabled" : ""); - if ((last->type & 0xF)==(nexttolast->type & 0xF)) + if ((macontext.Video.Info.AspectRatio.Num==4) && + (macontext.Video.Info.AspectRatio.Den==3)) { - isyslog("removing double stop mark (%i)",last->position); - marks.Del(last); - return; - } -} - -void cMarkAdStandalone::CheckFirstMark() -{ - if (marksAligned) return; - clMark *first=marks.GetFirst(); - if (!first) return; - - // Check the second mark - clMark *second=first->Next(); - if (!second) return; - - if ((marks.Count(MT_BORDERCHANGE,0xF0)>0) && (marks.Count(MT_ASPECTCHANGE,0xF0)>0)) - { - // wait till its clear, if we use ASPECT or BORDER - return; - } - - if ((second->type & 0xF)==MT_START) - { - bool delsec=false; - clMark *third=second->Next(); - if (third) - { - if ((third->type & 0xF)==MT_START) delsec=true; - } - if (delsec) - { - isyslog("removing double start marks (%i,%i)",first->position,second->position); - marks.Del(second); - } - else - { - isyslog("removing double start mark (%i)",first->position); - } - marks.Del(first); - marksAligned=true; - first=marks.GetFirst(); - if (first) - { - CalculateStopPosition(first->position,macontext.Video.Info.FramesPerSecond*MAXRANGE); - } - return; - } - - if ((second->type & 0xF)==MT_STOP) - { - marksAligned=true; - return; + bDecodeVideo=false; + macontext.Video.Options.IgnoreLogoDetection=true; + marks.Del(MT_CHANNELSTART); + marks.Del(MT_CHANNELSTOP); + // start mark must be around iStart + begin=marks.GetAround(macontext.Video.Info.FramesPerSecond*(MAXRANGE*4),iStart,MT_ASPECTSTART); } - // If we have an aspectchange, check the next aspectchange mark - // and the difference between - if ((second->type==MT_ASPECTCHANGE) && (length)) + if (!bDecodeVideo) { - clMark *next=marks.GetNext(second->position,MT_ASPECTCHANGE); - if (next) - { - int maxlen=length*(13*60)/(90*60); // max 13 minutes ads on 90 minutes program - if (maxlen>(13*60)) maxlen=(13*60); // maximum ad block = 13 minutes - int MAXPOSDIFF=(int) (macontext.Video.Info.FramesPerSecond*maxlen); - if ((next->position-second->position)>MAXPOSDIFF) - { - clMark *first=marks.GetFirst(); - if (first) - { - isyslog("removing unaligned start mark (%i)",first->position); - marks.Del(first); - marksAligned=true; - return; - } - } - } + macontext.Video.Data.Valid=false; + marks.Del(MT_LOGOSTART); + marks.Del(MT_LOGOSTOP); + marks.Del(MT_HBORDERSTART); + marks.Del(MT_HBORDERSTOP); + marks.Del(MT_VBORDERSTART); + marks.Del(MT_VBORDERSTOP); } - return; -} - -void cMarkAdStandalone::CheckAspectRatio_and_AudioChannels() -{ - if (aspectChecked) return; - dsyslog("checking aspectratio and audio channels"); - - if (!macontext.Info.Channels) + if (!begin) { - macontext.Info.Channels=macontext.Audio.Info.Channels; - if (macontext.Info.Channels==2) setAudio20=true; - if (macontext.Info.Channels==6) - { - isyslog("DolbyDigital5.1 audio detected. logo/border detection disabled"); - bDecodeVideo=false; - setAudio20=false; - setAudio51=true; - reprocess=true; - } + begin=marks.GetAround(macontext.Video.Info.FramesPerSecond*(MAXRANGE*2),iStart,MT_START,0x0F); } - - bool aSet=false; - if (!macontext.Info.AspectRatio.Num) + if (begin) { - isyslog("assuming aspectratio of %i:%i", - macontext.Video.Info.AspectRatio.Num,macontext.Video.Info.AspectRatio.Den); - aSet=true; + marks.DelTill(begin->position); + CalculateCheckPositions(begin->position); + isyslog("using mark on position %i as start mark",begin->position); } else { - if (!bIgnoreVideoInfo) - { - if ((macontext.Info.AspectRatio.Num!=macontext.Video.Info.AspectRatio.Num) && - (macontext.Info.AspectRatio.Den!=macontext.Video.Info.AspectRatio.Den)) - { - isyslog("aspectratio in info wrong %i:%i instead of %i:%i", - macontext.Video.Info.AspectRatio.Num,macontext.Video.Info.AspectRatio.Den, - macontext.Info.AspectRatio.Num,macontext.Info.AspectRatio.Den); - aSet=true; - } - } - } - - if (aSet) - { - macontext.Info.AspectRatio.Num=macontext.Video.Info.AspectRatio.Num; - macontext.Info.AspectRatio.Den=macontext.Video.Info.AspectRatio.Den; - - if ((macontext.Info.AspectRatio.Num==16) && - (macontext.Info.AspectRatio.Den==9)) - { - macontext.Video.Options.IgnoreAspectRatio=true; - setVideo169=true; - setVideo43=false; - setVideo43LB=false; - } - - if ((macontext.Info.AspectRatio.Num==4) && - (macontext.Info.AspectRatio.Den==3)) - { - setVideo43=true; - setVideo169=false; - } - reprocess=true; + //fallback, shouldn't be reached + MarkAdMark mark; + memset(&mark,0,sizeof(mark)); + mark.Position=iStart; + mark.Type=MT_ASSUMEDSTART; + AddMark(&mark); + marks.DelTill(iStart); + CalculateCheckPositions(iStart); } - - aspectChecked=true; + iStart=0; return; } void cMarkAdStandalone::AddMark(MarkAdMark *Mark) { - if (gotendmark) return; if (!Mark) return; if (!Mark->Type) return; - if (!macontext.Video.Info.FramesPerSecond) return; - - bool loggedAlready=false; + if (gotendmark) return; - if (Mark->Type==MT_ASPECTSTOP) + char *comment=NULL; + switch (Mark->Type) { - // check if last mark is an stop mark in short distance - clMark *prev=marks.GetLast(); - if (prev) + case MT_ASSUMEDSTART: + if (asprintf(&comment,"assuming start (%i)",Mark->Position)==-1) comment=NULL; + break; + case MT_ASSUMEDSTOP: + if (asprintf(&comment,"assuming stop (%i)",Mark->Position)==-1) comment=NULL; + break; + case MT_LOGOSTART: + if (asprintf(&comment,"detected logo start (%i)*",Mark->Position)==-1) comment=NULL; + break; + case MT_LOGOSTOP: + if (asprintf(&comment,"detected logo stop (%i)",Mark->Position)==-1) comment=NULL; + break; + case MT_HBORDERSTART: + if (asprintf(&comment,"detected start of horiz. borders (%i)*", + Mark->Position)==-1) comment=NULL; + break; + case MT_HBORDERSTOP: + if (asprintf(&comment,"detected stop of horiz. borders (%i)", + Mark->Position)==-1) comment=NULL; + break; + case MT_VBORDERSTART: + if (asprintf(&comment,"detected start of vert. borders (%i)*", + Mark->Position)==-1) comment=NULL; + break; + case MT_VBORDERSTOP: + if (asprintf(&comment,"detected stop of vert. borders (%i)", + Mark->Position)==-1) comment=NULL; + break; + case MT_ASPECTSTART: + if (!Mark->AspectRatioBefore.Num) { - if ((prev->type & 0xF)==MT_STOP) - { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*15); - if ((Mark->Position-prev->position)<MARKDIFF) - { - if (Mark->Comment) isyslog("%s",Mark->Comment); - isyslog("double stop mark in short distance, deleting this mark (%i)",prev->position); - marks.Del(prev); - loggedAlready=true; - } - } - if (prev->type==MT_ASPECTSTART) - { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*240); - if ((Mark->Position-prev->position)<MARKDIFF) - { - if (Mark->Comment) isyslog("%s",Mark->Comment); - double distance=(Mark->Position-prev->position)/macontext.Video.Info.FramesPerSecond; - isyslog("aspect mark distance too short (%.1fs), deleting (%i,%i)",distance, - prev->position,Mark->Position); - marks.Del(prev); - return; - } - } + if (asprintf(&comment,"aspectratio start with %i:%i (%i)*", + Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, + Mark->Position)==-1) comment=NULL; } - } - - if (Mark->Type==MT_CHANNELSTART) - { - clMark *prev=marks.GetPrev(Mark->Position,MT_CHANNELSTOP); - if (prev) + else { - int MARKDIFF=(int) macontext.Video.Info.FramesPerSecond; - if ((Mark->Position-prev->position)<MARKDIFF) - { - if (Mark->Comment) isyslog("%s",Mark->Comment); - double distance=(Mark->Position-prev->position)/macontext.Video.Info.FramesPerSecond; - isyslog("channel distance too short (%.1fs), deleting (%i,%i)",distance, - prev->position,Mark->Position); - marks.Del(prev); - return; - } + if (asprintf(&comment,"aspectratio change from %i:%i to %i:%i (%i)*", + Mark->AspectRatioBefore.Num,Mark->AspectRatioBefore.Den, + Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, + Mark->Position)==-1) comment=NULL; } + break; + case MT_ASPECTSTOP: + if (asprintf(&comment,"aspectratio change from %i:%i to %i:%i (%i)", + Mark->AspectRatioBefore.Num,Mark->AspectRatioBefore.Den, + Mark->AspectRatioAfter.Num,Mark->AspectRatioAfter.Den, + Mark->Position)==-1) comment=NULL; + break; + case MT_CHANNELSTART: + if (asprintf(&comment,"audio channel change from %i to %i (%i)*", + Mark->ChannelsBefore,Mark->ChannelsAfter, + Mark->Position)==-1) comment=NULL; + break; + case MT_CHANNELSTOP: + if (asprintf(&comment,"audio channel change from %i to %i (%i)", + Mark->ChannelsBefore,Mark->ChannelsAfter, + Mark->Position)==-1) comment=NULL; + break; } - clMark *old=marks.Get(Mark->Position); - if ((old) && (((old->type & 0xF0)==MT_ASPECTCHANGE) || ((old->type & 0xF0)==MT_CHANNELCHANGE))) - { - // Aspect- / Channelchange wins over Logo/Border - return; - } + if (comment) isyslog("%s",comment); - if (Mark->Type==MT_LOGOSTOP) + if ((Mark->Type & 0x0F)==MT_STOP) { - // check if last mark is an audiochannel stop - clMark *prev=marks.GetLast(); - if ((prev) && (prev->type==MT_CHANNELSTOP)) + clMark *prev=marks.GetPrev(Mark->Position,(Mark->Type & 0xF0)|MT_START); + if (prev) { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*15); - if ((Mark->Position-prev->position)<MARKDIFF) + int MARKDIFF; + if ((Mark->Type & 0xF0)==MT_LOGOCHANGE) { - if (Mark->Comment) isyslog("%s",Mark->Comment); - isyslog("audiochannel change in short distance, using this mark (%i->%i)",Mark->Position,prev->position); - return; + MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*240); + } + else + { + MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*10); } - } - - prev=marks.GetPrev(Mark->Position,MT_LOGOSTART); - if (prev) - { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*240); if ((Mark->Position-prev->position)<MARKDIFF) { - if (Mark->Comment) isyslog("%s",Mark->Comment); double distance=(Mark->Position-prev->position)/macontext.Video.Info.FramesPerSecond; - isyslog("logo distance too short (%.1fs), deleting (%i,%i)",distance, + isyslog("mark distance too short (%.1fs), deleting %i,%i",distance, prev->position,Mark->Position); + if ((prev->type & 0x0F)==MT_START) inBroadCast=false; marks.Del(prev); return; } } } - if (Mark->Type==MT_LOGOSTART) + clMark *prev=marks.GetLast(); + if (prev) { - // check if last mark is an aspectratio change - clMark *prev=marks.GetLast(); - if (prev) + if ((Mark->Type==MT_LOGOSTART) && (!iStart)) { - if ((prev->type & 0xF0)==MT_ASPECTCHANGE) + if (prev->type==MT_LOGOSTOP) { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*5); + int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*30); if ((Mark->Position-prev->position)<MARKDIFF) { - if (Mark->Comment) isyslog("%s",Mark->Comment); - isyslog("aspectratio change in short distance, deleting this mark (%i)", - Mark->Position); - return; - } - } - - if (prev->type==MT_CHANNELSTART) - { - int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*5); - if ((Mark->Position-prev->position)<MARKDIFF) - { - if (Mark->Comment) isyslog("%s",Mark->Comment); - isyslog("audiochannel change in short distance, deleting this mark (%i)", - Mark->Position); + double distance=(Mark->Position-prev->position)/macontext.Video.Info.FramesPerSecond; + isyslog("mark distance too short (%.1fs), deleting %i,%i",distance, + prev->position,Mark->Position); + if ((prev->type & 0x0F)==MT_START) inBroadCast=false; + marks.Del(prev); return; } } } - } - if (length>0) - { - if ((Mark->Type==MT_BORDERSTART) && (Mark->Position>chkLEFT) && - (Mark->Position<chkRIGHT) && (!macontext.Video.Options.IgnoreLogoDetection)) + if ((prev->type & 0x0F)==(Mark->Type & 0x0F)) { - if (Mark->Comment) + int MARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*30); + int diff=abs(Mark->Position-prev->position); + if (diff<MARKDIFF) { - isyslog("%s",Mark->Comment); - loggedAlready=true; - } - isyslog("border changes detected. logo detection disabled"); - macontext.Video.Options.IgnoreLogoDetection=true; - marks.Del((uchar) MT_LOGOSTART); - marks.Del((uchar) MT_LOGOSTOP); - } - - bool deleteLogoBorder=false; - if (((Mark->Type & 0xF0)==MT_CHANNELCHANGE) && (Mark->Position>chkLEFT) && - (Mark->Position<chkRIGHT) && (macontext.Info.Channels!=6)) - { - if (!loggedAlready) - { - if (Mark->Comment) - { - isyslog("%s",Mark->Comment); - loggedAlready=true; - } - } - isyslog("audio channel changes detected. logo/border detection disabled"); - if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H262) - { - if (!macontext.Info.AspectRatio.Num) + if (prev->type>Mark->Type) { - isyslog("assuming broadcast aspectratio is 16:9"); - macontext.Info.AspectRatio.Num=16; - macontext.Info.AspectRatio.Den=9; - macontext.Video.Options.IgnoreAspectRatio=true; - } - } - macontext.Info.Channels=6; - setAudio51=true; - setAudio20=false; - reprocess=true; - deleteLogoBorder=true; - } - - if (((Mark->Type & 0xF0)==MT_ASPECTCHANGE) && (Mark->Position>chkLEFT) && - (Mark->Position<chkRIGHT) && (!macontext.Video.Options.IgnoreLogoDetection)) - { - if (!loggedAlready) - { - if (Mark->Comment) - { - isyslog("%s",Mark->Comment); - loggedAlready=true; + isyslog("previous mark (%i) stronger than actual mark, deleting %i", + prev->position, Mark->Position); + return; } - } - isyslog("aspectratio changes detected. logo/border detection disabled"); - - if (!macontext.Info.AspectRatio.Num) - { - isyslog("assuming broadcast aspectratio is 4:3"); - macontext.Info.AspectRatio.Num=4; - macontext.Info.AspectRatio.Den=3; - reprocess=true; - setVideo43=true; - } - else - { - if ((macontext.Info.AspectRatio.Num==4) && - (macontext.Info.AspectRatio.Den==3)) + else { - if (marks.Count(MT_BORDERSTART)>0) - { - isyslog("assuming broadcast is in letterbox format"); - setVideo43LB=true; - setVideo43=false; - } + isyslog("actual mark stronger then previous mark, deleting %i",prev->position); + marks.Del(prev); } } - deleteLogoBorder=true; - } - - if (deleteLogoBorder) - { - bDecodeVideo=false; - macontext.Video.Options.IgnoreLogoDetection=true; - macontext.Video.Data.Valid=false; - marks.Del((uchar) MT_LOGOSTART); - marks.Del((uchar) MT_LOGOSTOP); - marks.Del((uchar) MT_BORDERSTART); - marks.Del((uchar) MT_BORDERSTOP); } } - if (Mark->Position>chkLEFT) CheckFirstMark(); - if (marksAligned) CheckLogoMarks(marks.GetLast()); - if ((Mark->Comment) && (!loggedAlready)) isyslog("%s",Mark->Comment); - marks.Add(Mark->Type,Mark->Position,Mark->Comment); + if ((Mark->Type & 0x0F)==MT_START) + { + inBroadCast=true; + } + else + { + inBroadCast=false; + } + marks.Add(Mark->Type,Mark->Position,comment); } void cMarkAdStandalone::SaveFrame(int frame) @@ -825,21 +528,7 @@ void cMarkAdStandalone::SaveFrame(int frame) fclose(pFile); } -void cMarkAdStandalone::CheckBroadcastLength() -{ - if (length) return; - if (!macontext.Video.Info.FramesPerSecond) return; - /* get broadcastlength from length of indexFile */ - int tframecnt=1; - int iIndexError; - marks.CheckIndex(directory,isTS,&tframecnt,&iIndexError); - if (iIndexError!=0) return; - length=tframecnt/macontext.Video.Info.FramesPerSecond; - isyslog("got broadcast length of %im from index",length/60); - reprocess=true; -} - -bool cMarkAdStandalone::CheckIndexGrowing() +void cMarkAdStandalone::CheckIndexGrowing() { // Here we check if the index is more // advanced than our framecounter. @@ -848,15 +537,15 @@ bool cMarkAdStandalone::CheckIndexGrowing() #define WAITTIME 15 - if (!indexFile) return false; - if (macontext.Config->logoExtraction!=-1) return false; - if (sleepcnt>=2) return false; // we already slept too much + if (!indexFile) return; + if (macontext.Config->logoExtraction!=-1) return; + if (sleepcnt>=2) return; // we already slept too much bool notenough=true; do { struct stat statbuf; - if (stat(indexFile,&statbuf)==-1) return false; + if (stat(indexFile,&statbuf)==-1) return; int maxframes=statbuf.st_size/8; if (maxframes<(framecnt+200)) @@ -868,7 +557,7 @@ bool cMarkAdStandalone::CheckIndexGrowing() if (time(NULL)>(startTime+(time_t) length)) { // "old" recording - return false; + return; } else { @@ -880,13 +569,13 @@ bool cMarkAdStandalone::CheckIndexGrowing() else { // "old" recording - return false; + return; } } marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS); sleep(WAITTIME); // now we sleep and hopefully the index will grow waittime+=WAITTIME; - if (errno==EINTR) return false; + if (errno==EINTR) return; sleepcnt++; if (sleepcnt>=2) { @@ -907,8 +596,7 @@ bool cMarkAdStandalone::CheckIndexGrowing() } } while (notenough); - if (!sleepcnt) return true; - else return false; + return; } void cMarkAdStandalone::ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *NewPos) @@ -920,17 +608,25 @@ void cMarkAdStandalone::ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *N if ((*Mark1)->position!=NewPos->FrameNumberBefore) { + char *buf=NULL; + if (asprintf(&buf,"overlap before %i, moved to %i",(*Mark1)->position, + NewPos->FrameNumberBefore)==-1) return; + isyslog("%s",buf); marks.Del(*Mark1); - *Mark1=marks.Add(MT_MOVED,NewPos->FrameNumberBefore,NewPos->CommentBefore); + *Mark1=marks.Add(MT_MOVED,NewPos->FrameNumberBefore,buf); + free(buf); save=true; } - if (NewPos->CommentBefore) isyslog("%s",NewPos->CommentBefore); if (Mark2 && (*Mark2) && (*Mark2)->position!=NewPos->FrameNumberAfter) { + char *buf=NULL; + if (asprintf(&buf,"overlap after %i, moved to %i",(*Mark2)->position, + NewPos->FrameNumberAfter)==-1) return; + isyslog("%s",buf); marks.Del(*Mark2); - *Mark2=marks.Add(MT_MOVED,NewPos->FrameNumberAfter,NewPos->CommentAfter); - if (NewPos->CommentAfter) isyslog("%s",NewPos->CommentAfter); + *Mark2=marks.Add(MT_MOVED,NewPos->FrameNumberAfter,buf); + free(buf); save=true; } if (save) marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS,true); @@ -950,12 +646,12 @@ bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Nu if (Mark1 && Mark2) { if (!(*Mark1) || !(*Mark2)) return false; - if (*Mark1==*Mark2) pn=1; - if (*Mark1!=*Mark2) pn=3; + if (*Mark1==*Mark2) pn=mSTART; + if (*Mark1!=*Mark2) pn=mAFTER; } else { - pn=2; + pn=mBEFORE; } if (!Reset(false)) @@ -992,13 +688,20 @@ bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Nu if (f==-1) return false; int dataread; - if (pn==1) + if (pn==mSTART) { dsyslog("processing file %05i (start mark)",Number); } else { - dsyslog("processing file %05i %s",Number,(pn==3) ? "(after mark)" : "(before mark)"); + if (pn==mBEFORE) + { + dsyslog("processing file %05i (before mark %i)",Number,(*Mark1)->position); + } + else + { + dsyslog("processing file %05i (after mark %i)",Number,(*Mark2)->position); + } } if (lseek(f,Offset,SEEK_SET)!=Offset) @@ -1007,32 +710,30 @@ bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Nu return false; } - uint64_t offset=Offset; - int offset_add=0; while ((dataread=read(f,data,datalen))>0) { if (abort) break; - if ((video_demux) && (video) && (decoder) && (streaminfo)) + if ((demux) && (video) && (decoder) && (streaminfo)) { uchar *tspkt = data; int tslen = dataread; while (tslen>0) { - int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&vpkt); + int len=demux->Process(tspkt,tslen,&pkt); if (len<0) { - esyslog("error demuxing video"); + esyslog("error demuxing file"); abort=true; break; } else { - if (vpkt.Data) + if ((pkt.Data) && ((pkt.Type & PACKET_MASK)==PACKET_VIDEO)) { bool dRes=false; - if (streaminfo->FindVideoInfos(&macontext,vpkt.Data,vpkt.Length)) + if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) { actframe++; framecnt2++; @@ -1051,16 +752,15 @@ bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Nu dRes=true; } } - if (pn>1) dRes=decoder->DecodeVideo(&macontext,vpkt.Data,vpkt.Length); + if (pn>mSTART) dRes=decoder->DecodeVideo(&macontext,pkt.Data,pkt.Length); if (dRes) { if ((actframe-iframe)<=3) { - if (pn>1) pos=video->Process2ndPass(lastiframe,Frames,(pn==2)); - //SaveFrame(lastiframe); + if (pn>mSTART) pos=video->ProcessOverlap(lastiframe,Frames,(pn==mBEFORE)); framecounter++; } - if ((pos) && (pn==3)) + if ((pos) && (pn==mAFTER)) { // found overlap ChangeMarks(Mark1,Mark2,pos); @@ -1071,54 +771,10 @@ bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Nu } tspkt+=len; tslen-=len; - if (!vpkt.Offcnt) - { - offset_add+=len; - } - else - { - offset+=offset_add; - offset+=len; - offset_add=0; - } } } } -#if 0 - if ((mp2_demux) && (audio) && (pn!=3)) - { - uchar *tspkt = data; - int tslen = dataread; - while (tslen>0) - { - int len=mp2_demux->Process(macontext.Info.APid,tspkt,tslen,&apkt); - if (len<0) - { - esyslog("error demuxing mp2-audio"); - break; - } - else - { - if (apkt.Data) - { - if (apkt.Timestamp) audiotime=apkt.Timestamp; - - if (abs(audiotime-lastiframetime)<DELTATIME) - { - if (decoder->DecodeMP2(&macontext,apkt.Data,apkt.length)) - { - pos=audio->Process2ndPass(iframe); - if (pos) ChangeMarks(Mark1,NULL,pos); - } - } - } - tspkt+=len; - tslen-=len; - } - } - } -#endif if (abort) { close(f); @@ -1161,24 +817,7 @@ void cMarkAdStandalone::Process2ndPass() bool infoheader=false; clMark *p1=NULL,*p2=NULL; -#if 0 - p1=marks.GetFirst(); - if (!p1) return; - if (p1->position>0) - { - isyslog("2nd pass"); - infoheader=true; - off_t offset; - int number,frame,iframes; - int frange=macontext.Video.Info.FramesPerSecond*120; - - if (marks.ReadIndex(directory,isTS,p1->position-(frange/10),frange,&number,&offset,&frame,&iframes)) - { - ProcessFile2ndPass(&p1,&p1,number,offset,frame,iframes); - } - } -#endif - if (marks.Count()<4) return; // here we cannot do much + if (marks.Count()<4) return; // we cannot do much without marks p1=marks.GetFirst(); if (!p1) return; @@ -1196,24 +835,22 @@ void cMarkAdStandalone::Process2ndPass() off_t offset; int number,frame,iframes; int frange=macontext.Video.Info.FramesPerSecond*120; // 40s + 80s - int overlap=macontext.Video.Info.FramesPerSecond*10; // 10s - if (((p1->type & 0xF0)==MT_ASPECTCHANGE) || (p1->type==0)) - { - dsyslog("ignoring additional overlap"); - overlap=0; - } - - if (marks.ReadIndex(directory,isTS,p1->position-frange,frange+overlap,&number,&offset,&frame,&iframes)) + if (marks.ReadIndex(directory,isTS,p1->position-frange,frange,&number,&offset,&frame,&iframes)) { if (!ProcessFile2ndPass(&p1,NULL,number,offset,frame,iframes)) break; frange=macontext.Video.Info.FramesPerSecond*320; // 160s + 160s - if (marks.ReadIndex(directory,isTS,p2->position-overlap,frange+overlap,&number,&offset,&frame,&iframes)) + if (marks.ReadIndex(directory,isTS,p2->position,frange,&number,&offset,&frame,&iframes)) { if (!ProcessFile2ndPass(&p1,&p2,number,offset,frame,iframes)) break; } } + else + { + esyslog("error reading index"); + return; + } p1=p2->Next(); if (p1) @@ -1232,7 +869,7 @@ bool cMarkAdStandalone::ProcessFile(int Number) if (!directory) return false; if (!Number) return false; - if (!CheckIndexGrowing()) CheckBroadcastLength(); + CheckIndexGrowing(); if (abort) return false; @@ -1256,154 +893,123 @@ bool cMarkAdStandalone::ProcessFile(int Number) int dataread; dsyslog("processing file %05i",Number); - uint64_t offset=0; - int offset_add=0; + demux->NewFile(); while ((dataread=read(f,data,datalen))>0) { if (abort) break; - - if ((video_demux) && (video) && (streaminfo)) + if ((demux) && (video) && (streaminfo)) { uchar *tspkt = data; int tslen = dataread; - while (tslen>0) { - int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&vpkt); + int len=demux->Process(tspkt,tslen,&pkt); if (len<0) { - esyslog("error demuxing video"); + esyslog("error demuxing"); abort=true; break; } else { - if (vpkt.Data) + if (pkt.Data) { - if ((macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) && (!noticeFILLER)) + if ((pkt.Type & PACKET_MASK)==PACKET_VIDEO) { - if ((vpkt.Data[4] & 0x1F)==0x0C) + bool dRes=false; + if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) { - isyslog("H264 video stream with filler nalu"); - noticeFILLER=true; - } - } + if ((macontext.Video.Info.Height) && (!noticeHEADER)) + { + isyslog("%s %ix%i%c%0.f",(macontext.Video.Info.Height>576) ? "HDTV" : "SDTV", + macontext.Video.Info.Width, + macontext.Video.Info.Height, + macontext.Video.Info.Interlaced ? 'i' : 'p', + macontext.Video.Info.FramesPerSecond); + noticeHEADER=true; + } - bool dRes=false; - if (streaminfo->FindVideoInfos(&macontext,vpkt.Data,vpkt.Length)) - { - if ((macontext.Video.Info.Height) && (!noticeHEADER)) - { - isyslog("%s %ix%i%c%0.f",(macontext.Video.Info.Height>576) ? "HDTV" : "SDTV", - macontext.Video.Info.Width, - macontext.Video.Info.Height, - macontext.Video.Info.Interlaced ? 'i' : 'p', - macontext.Video.Info.FramesPerSecond); - noticeHEADER=true; - } + if (!framecnt) + { + CalculateCheckPositions(tStart*macontext.Video.Info.FramesPerSecond); + } - if (!framecnt) - { - AddStartMark(); - } + if (macontext.Config->GenIndex) + { + marks.WriteIndex(directory,isTS,demux->Offset(),macontext.Video.Info.Pict_Type,Number); + } + framecnt++; - if (macontext.Config->GenIndex) - { - marks.WriteIndex(directory,isTS,offset,macontext.Video.Info.Pict_Type,Number); + if (macontext.Video.Info.Pict_Type==MA_I_TYPE) + { + lastiframe=iframe; + if ((iStart<0) && (lastiframe>-iStart)) iStart=lastiframe; + if ((iStop<0) && (lastiframe>-iStop)) iStop=lastiframe; + if (iStart>0) + { + if ((inBroadCast) && (lastiframe>chkSTART)) CheckStart(); + } + if (iStop>0) + { + if (lastiframe>chkSTOP) CheckStop(); + } + iframe=framecnt-1; + dRes=true; + } } - framecnt++; - if (macontext.Video.Info.Pict_Type==MA_I_TYPE) + if ((decoder) && (bDecodeVideo)) + dRes=decoder->DecodeVideo(&macontext,pkt.Data,pkt.Length); + if (dRes) { - lastiframe=iframe; - CheckStartStop(lastiframe); - if (lastiframe>chkLEFT) CheckAspectRatio_and_AudioChannels(); - iframe=framecnt-1; - dRes=true; + if ((framecnt-iframe)<=3) + { + MarkAdMarks *vmarks=video->Process(lastiframe,iframe); + if (vmarks) + { + for (int i=0; i<vmarks->Count; i++) + { + AddMark(&vmarks->Number[i]); + } + } + //SaveFrame(lastiframe); // TODO: JUST FOR DEBUGGING! + } } } - if ((decoder) && (bDecodeVideo)) - dRes=decoder->DecodeVideo(&macontext,vpkt.Data,vpkt.Length); - if (dRes) + if ((pkt.Type & PACKET_MASK)==PACKET_AC3) { - if ((framecnt-iframe)<=3) + if (streaminfo->FindAC3AudioInfos(&macontext,pkt.Data,pkt.Length)) { - MarkAdMarks *vmarks; - vmarks=video->Process(lastiframe,iframe); - if (vmarks) + if ((!isTS) && (!noticeVDR_AC3)) + { + isyslog("found AC3"); + noticeVDR_AC3=true; + } + if ((framecnt-iframe)<=3) { - for (int i=0; i<vmarks->Count; i++) + MarkAdMark *amark=audio->Process(lastiframe,iframe); + if (amark) { - AddMark(&vmarks->Number[i]); + AddMark(amark); } } - //SaveFrame(lastiframe); // TODO: JUST FOR DEBUGGING! } } } - if (vpkt.Skipped) - { - errcnt+=vpkt.Skipped; - } - tspkt+=len; - tslen-=len; - if (!vpkt.Offcnt) - { - offset_add+=len; - } - else - { - offset+=offset_add; - offset+=len; - offset_add=0; - } - } - } - } - if ((ac3_demux) && (streaminfo) && (audio)) - { - uchar *tspkt = data; - int tslen = dataread; - - while (tslen>0) - { - int len=ac3_demux->Process(macontext.Info.DPid,tspkt,tslen,&apkt); - if (len<0) - { - esyslog("error demuxing ac3-audio"); - break; - } - else - { - if (apkt.Data) - { - if (streaminfo->FindAC3AudioInfos(&macontext,apkt.Data,apkt.Length)) - { - if ((!isTS) && (!noticeVDR_AC3)) - { - isyslog("found AC3"); - noticeVDR_AC3=true; - } - MarkAdMark *amark; - amark=audio->Process(lastiframe,iframe); - if (amark) AddMark(amark); - } - } tspkt+=len; tslen-=len; } } } - - if (((gotendmark) && (!macontext.Config->GenIndex)) || (reprocess)) + if ((gotendmark) && (!macontext.Config->GenIndex)) { if (f!=-1) close(f); return true; } - if (!CheckIndexGrowing()) CheckBroadcastLength(); + CheckIndexGrowing(); if (abort) { if (f!=-1) close(f); @@ -1417,23 +1023,18 @@ bool cMarkAdStandalone::ProcessFile(int Number) bool cMarkAdStandalone::Reset(bool FirstPass) { bool ret=true; - reprocess=false; if (FirstPass) framecnt=0; lastiframe=0; iframe=0; gotendmark=false; - memset(&vpkt,0,sizeof(vpkt)); - memset(&apkt,0,sizeof(apkt)); + memset(&pkt,0,sizeof(pkt)); - iStart=iStartCheck=iStop=iStopCheck=0; - chkLEFT=INT_MAX; - chkRIGHT=INT_MIN; + chkSTART=chkSTOP=INT_MAX; if (FirstPass) { - marksAligned=false; marks.DelAll(); marks.CloseIndex(directory,isTS); } @@ -1448,9 +1049,7 @@ bool cMarkAdStandalone::Reset(bool FirstPass) ret=decoder->Clear(); } if (streaminfo) streaminfo->Clear(); - if (video_demux) video_demux->Clear(); - if (ac3_demux) ac3_demux->Clear(); - if (mp2_demux) mp2_demux->Clear(); + if (demux) demux->Clear(); if (video) video->Clear(); if (audio) audio->Clear(); return ret; @@ -1463,39 +1062,22 @@ void cMarkAdStandalone::ProcessFile() if (abort) break; if (!ProcessFile(i)) break; if ((gotendmark) && (!macontext.Config->GenIndex)) break; - if (reprocess) - { - isyslog("restarting from scratch"); - i=0; - Reset(); - } } if (!abort) { if (lastiframe) { - CheckStartStop(lastiframe,true); - - if ((!gotendmark) && ((iStop<0) || (!tStart))) + if ((inBroadCast) && (!gotendmark)) { - char *buf; MarkAdMark tempmark; - tempmark.Type=MT_COMMONSTOP; + tempmark.Type=MT_ASSUMEDSTOP; tempmark.Position=lastiframe; - - if (asprintf(&buf,"stop of recording (%i)",lastiframe)!=-1) - { - tempmark.Comment=buf; - AddMark(&tempmark); - free(buf); - } + AddMark(&tempmark); } } - - CheckLastMark(); - CheckLogoMarks(); } + skipped=demux->Skipped(); } void cMarkAdStandalone::Process() @@ -1581,8 +1163,7 @@ bool cMarkAdStandalone::SetFileUID(char *File) bool cMarkAdStandalone::SaveInfo() { - if ((!setVideo43) && (!setVideo169) && (!setAudio20) && (!setAudio51) && (!setVideo43LB)) return true; - + isyslog("writing info file"); char *src,*dst; if (asprintf(&src,"%s/info%s",directory,isTS ? "" : ".vdr")==-1) return false; @@ -1594,7 +1175,6 @@ bool cMarkAdStandalone::SaveInfo() FILE *r,*w; r=fopen(src,"r"); - w=fopen(dst,"w+"); if ((!r) || (!w)) @@ -1608,12 +1188,6 @@ bool cMarkAdStandalone::SaveInfo() char *lline=NULL; size_t length=0; - bool setVideo43LB_done=false; - bool setVideo43_done=false; - bool setVideo169_done=false; - bool setAudio20_done=false; - bool setAudio51_done=false; - char lang[4]=""; int component_type_add=0; @@ -1653,47 +1227,46 @@ bool cMarkAdStandalone::SaveInfo() case 5: if (stream==stream_content) { - if ( (((type==1) || (type==5)) && (setVideo169)) || - (((type==3) || (type==7)) && ((setVideo43) || (setVideo43LB)))) + if ((macontext.Info.AspectRatio.Num==4) && (macontext.Info.AspectRatio.Den==3)) { - if (setVideo43) - { - if (fprintf(w,"X %i %02i %s 4:3\n",stream_content, - component_type_43+component_type_add,lang)<=0) err=true; - setVideo43_done=true; - } - if (setVideo43LB) - { - if (fprintf(w,"X %i %02i %s 4:3 LetterBox\n",stream_content, - component_type_43+component_type_add,lang)<=0) err=true; - setVideo43_done=true; - setVideo43LB_done=true; - } - if (setVideo169) - { - if (fprintf(w,"X %i %02i %s 16:9\n",stream_content, - component_type_169+component_type_add,lang)<=0) err=true; - setVideo169_done=true; - } + if (fprintf(w,"X %i %02i %s 4:3\n",stream_content, + component_type_43+component_type_add,lang)<=0) err=true; + macontext.Info.AspectRatio.Num=0; + macontext.Info.AspectRatio.Den=0; + } + else if ((macontext.Info.AspectRatio.Num==16) && (macontext.Info.AspectRatio.Den==9)) + { + if (fprintf(w,"X %i %02i %s 16:9\n",stream_content, + component_type_169+component_type_add,lang)<=0) err=true; + macontext.Info.AspectRatio.Num=0; + macontext.Info.AspectRatio.Den=0; } else { if (fprintf(w,"%s",line)<=0) err=true; } } + else + { + if (fprintf(w,"%s",line)<=0) err=true; + } break; case 2: - if ((type==5) && ((setAudio51) || (setAudio20))) + if (type==5) { - if (setAudio51) + if (macontext.Info.Channels==6) { if (fprintf(w,"X 2 05 %s Dolby Digital 5.1\n",lang)<=0) err=true; - setAudio51_done=true; + macontext.Info.Channels=0; } - if (setAudio20) + else if (macontext.Info.Channels==2) { if (fprintf(w,"X 2 05 %s Dolby Digital 2.0\n",lang)<=0) err=true; - setAudio20_done=true; + macontext.Info.Channels=0; + } + else + { + if (fprintf(w,"%s",line)<=0) err=true; } } else @@ -1733,28 +1306,23 @@ bool cMarkAdStandalone::SaveInfo() if (stream_content) { - if ((setVideo43LB) && (!setVideo43LB_done) && (!err)) - { - if (fprintf(w,"X %i %02i %s 4:3 LetterBox\n",stream_content, - component_type_43+component_type_add,lang)<=0) err=true; - } - if ((setVideo43) && (!setVideo43_done) && (!err)) + if ((macontext.Info.AspectRatio.Num==4) && (macontext.Info.AspectRatio.Den==3) && (!err)) { if (fprintf(w,"X %i %02i %s 4:3\n",stream_content, component_type_43+component_type_add,lang)<=0) err=true; } - if ((setVideo169) && (!setVideo169_done) && (!err)) + if ((macontext.Info.AspectRatio.Num==16) && (macontext.Info.AspectRatio.Den==9) && (!err)) { if (fprintf(w,"X %i %02i %s 16:9\n",stream_content, component_type_169+component_type_add,lang)<=0) err=true; } } - if ((setAudio20) && (!setAudio20_done) && (!err)) + if ((macontext.Info.Channels==2) && (!err)) { if (fprintf(w,"X 2 05 %s Dolby Digital 2.0\n",lang)<=0) err=true; } - if ((setAudio51) && (!setAudio51_done) && (!err)) + if ((macontext.Info.Channels==6) && (!err)) { if (fprintf(w,"X 2 05 %s Dolby Digital 5.1\n",lang)<=0) err=true; } @@ -1800,23 +1368,36 @@ time_t cMarkAdStandalone::GetBroadcastStart(time_t start, int fd) struct mntent *ent; struct stat statbuf; FILE *mounts=setmntent(_PATH_MOUNTED,"r"); + int mlen=0; + int oldmlen=0; + bool useatime=false; while ((ent=getmntent(mounts))!=NULL) { if (strstr(directory,ent->mnt_dir)) { - if (strstr(ent->mnt_opts,"noatime")) + mlen=strlen(ent->mnt_dir); + if (mlen>oldmlen) { - if (stat(directory,&statbuf)!=-1) + if (strstr(ent->mnt_opts,"noatime")) { - endmntent(mounts); - isyslog("getting broadcast start from directory atime"); - return statbuf.st_atime; + useatime=true; + } + else + { + useatime=false; } } + oldmlen=mlen; } } endmntent(mounts); + if ((useatime) && (stat(directory,&statbuf)!=-1)) + { + isyslog("getting broadcast start from directory atime"); + return statbuf.st_atime; + } + // try to get from mtime // (and hope info.vdr has not changed after the start of the recording) if (fstat(fd,&statbuf)!=-1) @@ -1930,7 +1511,7 @@ bool cMarkAdStandalone::LoadInfo() int result=sscanf(line,"%*c %i %i %250c",&stream,&type,(char *) &descr); if ((result!=0) && (result!=EOF)) { - if (((stream==1) || (stream==5)) && (!bIgnoreVideoInfo)) + if ((stream==1) || (stream==5)) { if ((type!=1) && (type!=5) && (type!=9) && (type!=13)) { @@ -1946,26 +1527,22 @@ bool cMarkAdStandalone::LoadInfo() } } - if ((stream==2) && (!bIgnoreAudioInfo)) + if (stream==2) { if (type==5) { // if we have DolbyDigital 2.0 disable AC3 if (strchr(descr,'2')) { - isyslog("broadcast with DolbyDigital2.0, disabling AC3 decoding"); - macontext.Info.DPid.Num=0; + isyslog("broadcast with DolbyDigital2.0 (from info)"); macontext.Info.Channels=2; } - else - // if we have DolbyDigital 5.1 disable video decoding - if (strchr(descr,'5')) - { - bDecodeVideo=false; - macontext.Info.Channels=6; - macontext.Video.Options.IgnoreAspectRatio=true; - isyslog("broadcast with DolbyDigital5.1, disabling video decoding"); - } + // if we have DolbyDigital 5.1 disable video decoding + if (strchr(descr,'5')) + { + isyslog("broadcast with DolbyDigital5.1 (from info)"); + macontext.Info.Channels=6; + } } } } @@ -2011,8 +1588,6 @@ bool cMarkAdStandalone::LoadInfo() esyslog("cannot read broadcast length from info, marks can be wrong!"); macontext.Info.AspectRatio.Num=0; macontext.Info.AspectRatio.Den=0; - bIgnoreAudioInfo=true; - bIgnoreVideoInfo=true; bDecodeVideo=macontext.Config->DecodeVideo; macontext.Video.Options.IgnoreAspectRatio=false; } @@ -2131,7 +1706,11 @@ bool cMarkAdStandalone::CheckPATPMT(off_t Offset) free(buf); if (fd==-1) return false; - if (lseek(fd,Offset,SEEK_SET)==(off_t)-1) return false; + if (lseek(fd,Offset,SEEK_SET)==(off_t)-1) + { + close(fd); + return false; + } uchar patpmt_buf[564]; uchar *patpmt; @@ -2324,39 +1903,27 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * directory=Directory; abort=false; gotendmark=false; - reprocess=false; indexFile=NULL; streaminfo=NULL; - video_demux=NULL; - ac3_demux=NULL; - mp2_demux=NULL; + demux=NULL; decoder=NULL; video=NULL; audio=NULL; osd=NULL; - memset(&vpkt,0,sizeof(vpkt)); - memset(&apkt,0,sizeof(apkt)); - - setAudio51=false; - setAudio20=false; - setVideo43=false; - setVideo43LB=false; - setVideo169=false; - aspectChecked=false; + memset(&pkt,0,sizeof(pkt)); noticeVDR_MP2=false; noticeVDR_AC3=false; noticeHEADER=false; noticeFILLER=false; - errcnt=0; + skipped=0; sleepcnt=0; waittime=iwaittime=0; duplicate=false; - marksAligned=false; title[0]=0; memset(&macontext,0,sizeof(macontext)); @@ -2365,25 +1932,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * bDecodeVideo=config->DecodeVideo; bDecodeAudio=config->DecodeAudio; - tStart=iStart=iStop=iStartCheck=iStopCheck=0; - - if ((config->ignoreInfo & IGNORE_VIDEOINFO)==IGNORE_VIDEOINFO) - { - bIgnoreVideoInfo=true; - } - else - { - bIgnoreVideoInfo=false; - } - - if ((config->ignoreInfo & IGNORE_AUDIOINFO)==IGNORE_AUDIOINFO) - { - bIgnoreAudioInfo=true; - } - else - { - bIgnoreAudioInfo=false; - } + tStart=iStart=iStop=0; if ((config->ignoreInfo & IGNORE_TIMERINFO)==IGNORE_TIMERINFO) { @@ -2408,14 +1957,6 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * { isyslog("video decoding disabled by user"); } - if (bIgnoreAudioInfo) - { - isyslog("audio info usage disabled by user"); - } - if (bIgnoreVideoInfo) - { - isyslog("video info usage disabled by user"); - } if (bIgnoreTimerInfo) { isyslog("timer info usage disabled by user"); @@ -2425,8 +1966,6 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * { // just to be sure extraction works bDecodeVideo=true; - bIgnoreAudioInfo=true; - bIgnoreVideoInfo=true; } if (!config->NoPid) @@ -2465,6 +2004,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * } if (asprintf(&indexFile,"%s/index.vdr",Directory)==-1) indexFile=NULL; } + macontext.Info.APid.Num=0; // till now we do just nothing with stereo-sound if (!LoadInfo()) { @@ -2473,7 +2013,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * esyslog("failed loading info - logo %s%sdisabled", (config->logoExtraction!=-1) ? "extraction" : "detection", bIgnoreTimerInfo ? " " : " and pre-/post-timer "); - tStart=0; + tStart=iStart=iStop=0; } } @@ -2505,42 +2045,33 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * { if (isTS) { - dsyslog("using %s-video (0x%04x)", + dsyslog("found %s-video (0x%04x)", macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262", macontext.Info.VPid.Num); } else { - dsyslog("using %s-video", + dsyslog("found %s-video", macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262"); } - video_demux = new cMarkAdDemux(); + demux=new cDemux(macontext.Info.VPid.Num,macontext.Info.DPid.Num,macontext.Info.APid.Num, + macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264,true); } else { - video_demux=NULL; + demux=NULL; } if (macontext.Info.APid.Num) { if (macontext.Info.APid.Num!=-1) - dsyslog("using MP2 (0x%04x)",macontext.Info.APid.Num); - mp2_demux = new cMarkAdDemux(); - } - else - { - mp2_demux=NULL; + dsyslog("found MP2 (0x%04x)",macontext.Info.APid.Num); } if (macontext.Info.DPid.Num) { if (macontext.Info.DPid.Num!=-1) - dsyslog("using AC3 (0x%04x)",macontext.Info.DPid.Num); - ac3_demux = new cMarkAdDemux(); - } - else - { - ac3_demux=NULL; + dsyslog("found AC3 (0x%04x)",macontext.Info.DPid.Num); } if (!abort) @@ -2551,15 +2082,14 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, const MarkAdConfig * audio = new cMarkAdAudio(&macontext); streaminfo = new cMarkAdStreamInfo; if (macontext.Info.ChannelName) - dsyslog("channel %s",macontext.Info.ChannelName); + isyslog("channel %s",macontext.Info.ChannelName); } framecnt=0; framecnt2=0; lastiframe=0; iframe=0; - chkLEFT=INT_MAX; - chkRIGHT=INT_MIN; + chkSTART=chkSTOP=INT_MAX; gettimeofday(&tv1,&tz); } @@ -2567,9 +2097,9 @@ cMarkAdStandalone::~cMarkAdStandalone() { if ((!abort) && (!duplicate)) { - if (errcnt>20) + if (skipped) { - isyslog("skipped %i bytes in video stream",errcnt); + isyslog("skipped %i bytes",skipped); } gettimeofday(&tv2,&tz); @@ -2606,9 +2136,7 @@ cMarkAdStandalone::~cMarkAdStandalone() if (macontext.Info.ChannelName) free(macontext.Info.ChannelName); if (indexFile) free(indexFile); - if (video_demux) delete video_demux; - if (ac3_demux) delete ac3_demux; - if (mp2_demux) delete mp2_demux; + if (demux) delete demux; if (decoder) delete decoder; if (video) delete video; if (audio) delete audio; @@ -2642,10 +2170,7 @@ int usage(int svdrpport) " decoding, 3 = disable video and audio decoding\n" "-i --ignoreinfo=<info>\n" " ignores hints from info(.vdr) file\n" - " <info> 1 = ignore audio info, 2 = ignore video info,\n" - " 4 = ignore timer info\n" - " (options can be added together, e.g. 5 = ignore\n" - " audio and timer info)\n" + " <info> 4 = ignore timer info\n" "-l --logocachedir\n" " directory where logos stored, default /var/lib/markad\n" "-p --priority=<priority>\n" @@ -2655,7 +2180,7 @@ int usage(int svdrpport) " io priority of markad when running in background\n" " <class> 1 = realtime, <level> from 0..7, default 4\n" " 2 = besteffort, <level> from 0..7, default 4\n" - " 3 = idle\n" + " 3 = idle (default)\n" "-v --verbose\n" " increments loglevel by one, can be given multiple times\n" "-B --backupmarks\n" @@ -2681,8 +2206,13 @@ int usage(int svdrpport) " (default is the number of cpus)\n" "-V --version\n" " print version-info and exit\n" + " --loglevel=<level>\n" + " sets loglevel to the specified value\n" + " <level> 1=error 2=info 3=debug 4=trace\n" " --markfile=<markfilename>\n" " set a different markfile-name\n" + " --nopid\n" + " disables creation of markad.pid file in recdir\n" " --online[=1|2] (default is 1)\n" " start markad immediately when called with \"before\" as cmd\n" " if online is 1, markad starts online for live-recordings\n" @@ -2766,8 +2296,8 @@ int main(int argc, char *argv[]) bool bAfter=false,bEdited=false; bool bFork=false,bNice=false,bImmediateCall=false; int niceLevel = 19; - int ioprio_class=2; - int ioprio=4; + int ioprio_class=3; + int ioprio=7; char *tok,*str; int ntok; int online=0; @@ -2780,6 +2310,7 @@ int main(int argc, char *argv[]) // set defaults config.DecodeVideo=true; config.DecodeAudio=true; + config.SaveInfo=false; config.logoExtraction=-1; config.logoWidth=-1; config.logoHeight=-1; @@ -2834,7 +2365,7 @@ int main(int argc, char *argv[]) {"backupmarks", 0, 0, 'B'}, {"scenechangedetection", 0, 0, 'C'}, {"genindex",0, 0, 'G'}, - {"saveinfo",0,0,'I'}, + {"saveinfo",0, 0, 'I'}, {"extractlogo", 1, 0, 'L'}, {"OSD",0,0,'O' }, {"log2rec",0,0,'R'}, @@ -2845,7 +2376,7 @@ int main(int argc, char *argv[]) {0, 0, 0, 0} }; - c = getopt_long (argc, argv, "abcd:i:jl:nop:r:s:vBCGL:ORST:V", + c = getopt_long (argc, argv, "abcd:i:jl:nop:r:s:vBCGIL:ORST:V", long_options, &option_index); if (c == -1) break; @@ -2890,7 +2421,7 @@ int main(int argc, char *argv[]) case 'i': // --ignoreinfo config.ignoreInfo=atoi(optarg); - if ((config.ignoreInfo<0) || (config.ignoreInfo>255)) + if ((config.ignoreInfo<1) || (config.ignoreInfo>255)) { fprintf(stderr, "markad: invalid ignoreinfo option: %s\n", optarg); return 2; @@ -3071,7 +2602,14 @@ int main(int argc, char *argv[]) break; case 4: // --online - online=atoi(optarg); + if (optarg) + { + online=atoi(optarg); + } + else + { + online=1; + } if ((online!=1) && (online!=2)) { fprintf(stderr, "markad: invalid online value: %s\n", optarg); diff --git a/command/markad-standalone.h b/command/markad-standalone.h index 78f0ea9..de91225 100644 --- a/command/markad-standalone.h +++ b/command/markad-standalone.h @@ -24,8 +24,7 @@ #define DELTATIME 20000 /* equals to 222ms (base is 90kHz PTS) */ -#define MAXRANGE 420 /* range to search for start/stop marks in seconds */ - +#define MAXRANGE 120 /* range to search for start/stop marks in seconds */ class cOSDMessage { @@ -156,19 +155,19 @@ unsigned Descriptor_Length: 8; }; + enum { mSTART=0x1, mBEFORE, mAFTER }; + static const char frametypes[8]; const char *directory; - cMarkAdDemux *video_demux; - cMarkAdDemux *ac3_demux; - cMarkAdDemux *mp2_demux; + cDemux *demux; cMarkAdDecoder *decoder; cMarkAdVideo *video; cMarkAdAudio *audio; cMarkAdStreamInfo *streaminfo; cOSDMessage *osd; - MarkAdPacket vpkt,apkt; + AvPacket pkt; MarkAdContext macontext; @@ -189,7 +188,6 @@ unsigned Descriptor_Length: bool abort; bool gotendmark; - bool reprocess; int waittime; int iwaittime; struct timeval tv1,tv2; @@ -202,49 +200,32 @@ unsigned Descriptor_Length: bool bDecodeVideo; bool bDecodeAudio; - bool bIgnoreAudioInfo; - bool bIgnoreVideoInfo; bool bIgnoreTimerInfo; - time_t startTime; // StartTime of broadcast - int length; // Length in seconds + time_t startTime; // starttime of broadcast + int length; // length of broadcast in seconds int tStart; // pretimer in seconds - int iStart; // pretimer as index value - int iStartCheck; // check position for iStart - int iStop; // posttimer as index value - int iStopCheck; // check position for iStop + int iStart; // pretimer in frames (negative if unset) + int iStop; // endposition in frames (negative if unset) - int errcnt; // Skipped bytes in stream + void CheckStop(); + void CheckStart(); + void CalculateCheckPositions(int startframe); + int chkSTART; + int chkSTOP; - bool setAudio51; // set audio to 5.1 in info - bool setAudio20; // set audio to 2.0 in info - bool setVideo43; // set video to 4:3 in info - bool setVideo43LB; // set video to 4:3 letterbox in info - bool setVideo169; // set video to 16:9 in info - - int chkLEFT; - int chkRIGHT; + int skipped; // skipped bytes in whole file + bool inBroadCast; // are we in a broadcast (or ad)? time_t GetBroadcastStart(time_t start, int fd); - void CheckBroadcastLength(); - bool CheckIndexGrowing(); + void CheckIndexGrowing(); char *indexFile; int sleepcnt; void SaveFrame(int Frame); - bool aspectChecked; - bool marksAligned; - clMarks marks; char *IndexToHMSF(int Index); - void CalculateStopPosition(int startframe, int delta); - void CheckFirstMark(); - void CheckLastMark(); - void CheckStartStop(int frame, bool checkend=false); - void CheckAspectRatio_and_AudioChannels(); - void CheckLogoMarks(clMark *last=NULL); - void AddStartMark(); void AddMark(MarkAdMark *Mark); bool Reset(bool FirstPass=true); void ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *NewPos); diff --git a/command/marks.cpp b/command/marks.cpp index 130ff42..8909e3c 100644 --- a/command/marks.cpp +++ b/command/marks.cpp @@ -186,6 +186,29 @@ clMark *clMarks::Get(int Position) return mark; } +clMark *clMarks::GetAround(int Frames, int Position, int Type, int Mask) +{ + clMark *m1=GetPrev(Position,Type,Mask); + clMark *m2=GetNext(Position,Type,Mask); + + if (!m1 && !m2) return NULL; + if (!m1 && m2) return m2; + if (m1 && !m2) + { + if (abs(Position-m1->position)>Frames) return NULL; + return m1; + } + + if (abs(m1->position-Position)>abs(m2->position-Position)) + { + return m2; + } + else + { + return m1; + } +} + clMark *clMarks::GetPrev(int Position, int Type, int Mask) { if (!first) return NULL; // no elements yet @@ -485,7 +508,6 @@ void clMarks::WriteIndex(const char *Directory, bool isTS, uint64_t Offset, indexfd=open(ipath,O_WRONLY|O_CREAT|O_TRUNC,0644); free(ipath); if (indexfd==-1) return; - if (Offset>376) WriteIndex(isTS,0,1,Number); } WriteIndex(isTS,Offset,FrameType,Number); return; diff --git a/command/marks.h b/command/marks.h index 7c31ba9..18e6cc4 100644 --- a/command/marks.h +++ b/command/marks.h @@ -100,6 +100,7 @@ public: void Del(unsigned char Type); void Del(int Position); clMark *Get(int Position); + clMark *GetAround(int Frames, int Position, int Type=0xFF, int Mask=0xFF); clMark *GetPrev(int Position,int Type=0xFF, int Mask=0xFF); clMark *GetNext(int Position,int Type=0xFF, int Mask=0xFF); clMark *GetFirst() diff --git a/command/pes2es.cpp b/command/pes2es.cpp deleted file mode 100644 index c78afd6..0000000 --- a/command/pes2es.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * pes2es.cpp: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#include <inttypes.h> -#include <stdlib.h> -#include <string.h> - -#include "pes2es.h" -#include <stdio.h> -cMarkAdPES2ES::cMarkAdPES2ES(const char *QueueName, int QueueSize) -{ - queue = new cMarkAdPaketQueue(QueueName,QueueSize); - type=0; -} - -cMarkAdPES2ES::~cMarkAdPES2ES() -{ - if (queue) delete queue; -} - -void cMarkAdPES2ES::Clear() -{ - if (queue) queue->Clear(); -} - -void cMarkAdPES2ES::Process(MarkAdPid Pid, uchar *PESData, int PESSize, MarkAdPacket *ESPkt) -{ - if (!ESPkt) return; - - if (PESData) - { - struct PESHDR *peshdr=(struct PESHDR *) PESData; - - // first check some simple things - if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1)) - { - Clear(); - return; - } - - if (peshdr->StreamID<=0xBC) return; - - int Length=(peshdr->LenH<<8)+peshdr->LenL; - if (Length) Length+=sizeof(PESHDR); - if (Length!=PESSize) - { - if ((peshdr->StreamID & 0xF0)==0xE0) return; - Clear(); - return; - } - - switch (Pid.Type) - { - case MARKAD_PIDTYPE_VIDEO_H262: - if ((peshdr->StreamID & 0xF0)!=0xE0) return; - type=MA_PACKET_PKT; - break; - case MARKAD_PIDTYPE_VIDEO_H264: - if ((peshdr->StreamID & 0xF0)!=0xE0) return; - type=MA_PACKET_H264; - break; - case MARKAD_PIDTYPE_AUDIO_AC3: - if (peshdr->StreamID!=0xBD) return; - type=MA_PACKET_AC3; - break; - case MARKAD_PIDTYPE_AUDIO_MP2: - if ((peshdr->StreamID<0xC0) || (peshdr->StreamID>0xDF)) return; - type=MA_PACKET_MP2; - break; - default: - Clear(); - return; - } - - struct PESHDROPT *peshdropt=(struct PESHDROPT *) &PESData[sizeof(struct PESHDR)]; - - uchar *buf; - int buflen; - - if (peshdropt->MarkerBits==0x2) - { - // we have an optional PES header - int bpos=sizeof(struct PESHDR)+sizeof(struct PESHDROPT)+ - peshdropt->Length; - buf=&PESData[bpos]; - buflen=PESSize-bpos; - } - else - { - int bpos=sizeof(struct PESHDR); - buf=&PESData[bpos]; - buflen=PESSize-bpos; - } - queue->Put(buf,buflen); - } - if (type) ESPkt->Data=queue->GetPacket(&ESPkt->Length,type); - return; -} diff --git a/command/pes2es.h b/command/pes2es.h deleted file mode 100644 index bfeaaaa..0000000 --- a/command/pes2es.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * pes2es.h: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#ifndef __pes2es_h_ -#define __pes2es_h_ - -#ifndef uchar -typedef unsigned char uchar; -#endif - -#include "global.h" -#include "queue.h" - -struct PESHDR -{ - uchar Sync1; - uchar Sync2; - uchar Sync3; - uchar StreamID; - uchar LenH; - uchar LenL; -}; - -#pragma pack(1) -struct PESHDROPT -{ -unsigned OOC: - 1; -unsigned CY: - 1; -unsigned DAI: - 1; -unsigned PESP: - 1; -unsigned PESSC: - 2; -unsigned MarkerBits: - 2; -unsigned EXT: - 1; -unsigned CRC: - 1; -unsigned ACI: - 1; -unsigned TM: - 1; -unsigned RATE: - 1; -unsigned ESCR: - 1; -unsigned PTSDTS: - 2; -unsigned Length: - 8; -}; - -struct PESHDROPTPTS -{ -unsigned Marker1: - 1; -unsigned PTS32_30: - 3; -unsigned Fixed: - 4; -unsigned PTS29_15_H: - 8; -unsigned Marker2: - 1; -unsigned PTS29_15_L: - 7; -unsigned PTS14_0_H: - 8; -unsigned Marker3: - 1; -unsigned PTS14_0_L: - 7; -}; -#pragma pack() - -class cMarkAdPES2ES -{ -private: - cMarkAdPaketQueue *queue; - int type; -public: - cMarkAdPES2ES(const char *QueueName="PES2ES", int QueueSize=32768); - ~cMarkAdPES2ES(); - void Clear(); - void Process(MarkAdPid Pid, uchar *PESData, int PESSize, MarkAdPacket *ESPkt); -}; - -#endif diff --git a/command/queue.cpp b/command/queue.cpp deleted file mode 100644 index 65188e4..0000000 --- a/command/queue.cpp +++ /dev/null @@ -1,504 +0,0 @@ -/* - * queue.cpp: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#include <stdlib.h> -#include <string.h> - -extern "C" -{ -#include "debug.h" -} - -#include "queue.h" - -cMarkAdPaketQueue::cMarkAdPaketQueue(const char *Name, int Size) -{ - inptr=0; - outptr=0; - memset(&pktinfo,0,sizeof(pktinfo)); - pktinfo.pkthdr=-1; - maxqueue=Size; - if (Name) - { - name=strdup(Name); - } - else - { - name=NULL; - } - buffer=(uchar *) malloc(Size+1); - if (!buffer) maxqueue=0; - scanner=0xFFFFFFFF; - scannerstart=-1; - percent=-1; - mpercent=0; -} - -cMarkAdPaketQueue::~cMarkAdPaketQueue() -{ - if (name) - { - tsyslog("buffer usage: %-15s %3i%%",name,mpercent); - free(name); - } - if (buffer) free(buffer); -} - -bool cMarkAdPaketQueue::Inject(uchar *Data, int Size) -{ - if (!buffer) return false; - isyslog("inject was called, please report this"); - - if (outptr>Size) - { - uchar *temp=(uchar *) alloca(Size+1); - if (!temp) return false; - memcpy(temp,Data,Size); - outptr-=Size; - memcpy(&buffer[outptr],temp,Size); - pktinfo.pkthdr=-1; - } - else - { - int oldSize=Length(); - uchar *tempold=(uchar *) alloca(oldSize+1); - if (!tempold) return false; - uchar *temp=(uchar *) alloca(Size+1); - if (!temp) return false; - - memcpy(tempold,&buffer[outptr],oldSize); - memcpy(temp,Data,Size); - memcpy(buffer,temp,Size); - memcpy(buffer+Size,tempold,oldSize); - - inptr=Size+oldSize; - outptr=0; - pktinfo.pkthdr=-1; - } - return true; -} - -bool cMarkAdPaketQueue::Put(uchar *Data, int Size) -{ - if (!buffer) return false; - if ((inptr) && (inptr==outptr)) inptr=outptr=0; - - if ((inptr+Size)>maxqueue) - { - if (name) - { - esyslog("buffer %s full",name); - } - else - { - esyslog("buffer full"); - } - mpercent=100; - Clear(); - return false; - } - - memcpy(&buffer[inptr],Data,Size); - inptr+=Size; - - int npercent=(int) ((inptr*100)/maxqueue); - if (npercent>mpercent) mpercent=npercent; - - if ((npercent>90) && (name) && (npercent!=percent)) - { - dsyslog("buffer %s usage: %3i%%", - name,npercent); - percent=npercent; - } - - return true; -} - -uchar *cMarkAdPaketQueue::Get(int *Size) -{ - if (!buffer) return NULL; - if (!Size) return NULL; - if (Length()<*Size) - { - *Size=0; - return NULL; - } - uchar *ret=&buffer[outptr]; - outptr+=*Size; - return ret; -} - -uchar *cMarkAdPaketQueue::Peek(int Size) -{ - if (!buffer) return NULL; - if (!Size) return NULL; - if (Length()<Size) return NULL; - uchar *ret=&buffer[outptr]; - return ret; -} - -int cMarkAdPaketQueue::FindPktHeader(int Start, int *StreamSize,int *HeaderSize, bool LongStartCode) -{ - if ((!StreamSize) || (!HeaderSize)) return -1; - if (!Start) Start=outptr; - if (Start>=inptr) return -1; - *StreamSize=0; - if (LongStartCode) - { - *HeaderSize=4; // 0x0 0x0 0x0 0x1 - } - else - { - *HeaderSize=3; // 0x0 0x0 0x1 - } - int i; - - if (scanner!=0xFFFFFFFF) - { - scanner<<=8; - scanner|=buffer[Start++]; - } - - bool found=false; - for (i=Start; i<inptr; i++) - { - if (LongStartCode) - { - if (scanner==1L) - { - found=true; - break; - } - if ((scanner & 0xFFFFFFF0)==0x1E0L) - { - found=true; - break; - } - } - else - { - if ((scanner & 0x00FFFFFF)==1L) - { - found=true; - break; - } - } - scanner<<=8; - scanner|=buffer[i]; - } - if (!found) - { - if (LongStartCode) - { - if (scanner==1L) - { - found=true; - } - if ((scanner & 0xFFFFFFF0)==0x1E0L) - { - found=true; - } - } - else - { - if ((scanner & 0x00FFFFFF)==1L) - { - found=true; - } - } - } - - if (i==inptr) - { - if (found) - { - if (!Start) - { - scanner=0xFFFFFFFF; - return -1; - } - } - else - { - return -1; - } - } - if (LongStartCode) i--; - if ((buffer[i]>=0xBC) && (!Start)) // do we have a PES packet? - { -#define PESHDRSIZE 6 - if ((i+PESHDRSIZE)>inptr) - { - return -1; // we need more data (for streamsize and headersize) - } - - *StreamSize=(buffer[i+1]<<8)+buffer[i+2]; - if (*StreamSize) (*StreamSize)+=PESHDRSIZE; // 6 Byte PES-Header - if (LongStartCode) - { - struct PESHDROPT *peshdropt=(struct PESHDROPT *) &buffer[i+3]; - if (peshdropt->MarkerBits==0x2) - { - *HeaderSize=PESHDRSIZE+sizeof(struct PESHDROPT)+ - peshdropt->Length; - } - else - { - *HeaderSize=PESHDRSIZE; - } - } - } - - return i-3; -} - -int cMarkAdPaketQueue::FindAudioHeader(int Start, int *FrameSize, int *HeaderSize, bool AC3) -{ - if ((!FrameSize) || (!HeaderSize)) return -1; - if (!Start) Start=outptr; - if (Start>=inptr) return -1; - (*FrameSize)=0; - if (AC3) - { - (*HeaderSize)=2; - } - else - { - (*HeaderSize)=3; - } - int i; - - if (scanner!=0xFFFFFFFF) - { - scanner<<=8; - scanner|=buffer[Start++]; - } - - for (i=Start; i<inptr; i++) - { - - if (AC3) - { - if ((scanner & 0x0000FFFF)==0xB77L) break; - } - else - { - if ((scanner & 0x00000FFE)==0xFFEL) break; - } - - scanner<<=8; - scanner|=buffer[i]; - } - if (i==inptr) return -1; - if (AC3) i-=2; - - if (AC3) - { - struct AC3HDR *ac3hdr = (struct AC3HDR *) &buffer[i]; - - if (ac3hdr->SampleRateIndex==3) return -1; // reserved - if (ac3hdr->FrameSizeIndex>=38) return -1; // reserved - - if (FrameSize) - { - int bitRatesAC3[3][38] = // all values are specified as kbits/s - { - { 64, 64, 80, 80, 96, 96, 112, 112, 128, 128, 160, 160, 192, 192, - 224, 224, 256, 256, 320, 320, 384, 384, 448, 448, 512, 512, - 640, 640, 768, 768, 896, 896, 1024, 1024, 1152, 1152, 1280, 1280 }, // 48kHz - - { 69, 70, 87, 88, 104, 105, 121, 122, 139, 140, 174, 175, 208, 209, - 243, 244, 278, 279, 348, 349, 417, 418, 487, 488, 557, 558, - 696, 697, 835, 836, 975, 976, 1114, 1115, 1253, 1254, 1393, 1394 }, // 44.1kHz - - { 96, 96, 120, 120, 144, 144, 168, 168, 192, 192, 240, 240, 288, - 288, 336, 336, 384, 384, 480, 480, 576, 576, 672, 672, 768, - 768, 960, 960, 1152, 1152, 1344, 1344, 1536, 1536, 1728, 1728, 1920,1920 } // 32kHz - }; - - *FrameSize=2*bitRatesAC3[ac3hdr->SampleRateIndex][ac3hdr->FrameSizeIndex]; - } - return i; - } - else - { - struct MP2HDR *mp2hdr = (struct MP2HDR *) &buffer[i]; - if (mp2hdr->MpegID==1) return -1; // reserved - if (mp2hdr->Layer==0) return -1; // reserved - if (mp2hdr->BitRateIndex==0xF) return -1; // forbidden - if (mp2hdr->SampleRateIndex==3) return -1; //reserved - if (mp2hdr->Emphasis==2) return -1; // reserved - - if (FrameSize) - { - int bitRates[3][3][16] = // all values are specified as kbits/s - { - { - { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1 }, // M1, L1 - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1 }, // M1, L2 - { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 } // M1, L3 - }, - { - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2, L1 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2, L2 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2, L3 - }, - { - { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, -1 }, // M2.5, L1 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 }, // M2.5, L2 - { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, -1 } // M2.5, L3 - } - }; - - int samplingFrequencies[3][4] = // all values are specified in Hz - { - { 44100, 48000, 32000, -1 }, // MPEG 1 - { 22050, 24000, 16000, -1 }, // MPEG 2 - { 32000, 16000, 8000, -1 } // MPEG 2.5 - }; - - - int slots_per_frame[3][3] = - { - { 12, 144, 144 }, // MPEG 1, Layer I, II, III - { 12, 144, 72 }, // MPEG 2, Layer I, II, III - { 12, 144, 72 } // MPEG 2.5, Layer I, II, III - }; - - int mpegIndex; - switch (mp2hdr->MpegID) - { - case 0: - mpegIndex=2; - break; - case 2: - mpegIndex=1; - break; - case 3: - mpegIndex=0; - break; - default: - mpegIndex=0; // just to get rid of compiler warnings ;) - } - int layerIndex = 3 - mp2hdr->Layer; - - // Layer I (i. e., layerIndex == 0) has a larger slot size - int slotSize = (layerIndex == 0) ? 4 : 1; // bytes - int sf = samplingFrequencies[mpegIndex][mp2hdr->SampleRateIndex]; - - if (mp2hdr->BitRateIndex == 0) - *FrameSize = 0; // "free" Bitrate -> we don't support this! - else - { - int br = 1000 * bitRates[mpegIndex][layerIndex][mp2hdr->BitRateIndex]; // bits/s - int N = slots_per_frame[mpegIndex][layerIndex] * br / sf; // slots - - *FrameSize = (N + mp2hdr->Padding) * slotSize; // bytes - } - } - return i; - } -} - -uchar *cMarkAdPaketQueue::GetPacket(int *Size, int Type) -{ - if (!Size) return NULL; - *Size=0; - if (Length()<4) return NULL; - - if (pktinfo.pkthdr==-1) - { - scanner=0xFFFFFFFF; - switch (Type) - { - case MA_PACKET_AC3: - pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); - break; - case MA_PACKET_MP2: - pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); - break; - case MA_PACKET_H264: - pktinfo.pkthdr=FindPktHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); - break; - default: - pktinfo.pkthdr=FindPktHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); - break; - } - - if (pktinfo.pkthdr==-1) - { - return NULL; - } - scannerstart=pktinfo.pkthdr+pktinfo.pktsyncsize; - } - - int streamsize,pktsyncsize,pkthdr=-1; - - if (pktinfo.streamsize) - { - if ((pktinfo.pkthdr+pktinfo.streamsize)>inptr) - { - return NULL; // need more data - } - else - { - scannerstart=pktinfo.pkthdr+pktinfo.streamsize; - scanner=0xFFFFFFFF; - } - } - - switch (Type) - { - case MA_PACKET_AC3: - pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize,true); - break; - case MA_PACKET_MP2: - pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize,false); - break; - case MA_PACKET_H264: - pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize,true); - break; - default: - pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize,false); - break; - } - - if (pkthdr==-1) - { - scannerstart=inptr; - return NULL; - } - scannerstart=pkthdr+pktsyncsize; - - uchar *ptr=&buffer[pktinfo.pkthdr]; - - if (pktinfo.streamsize) - { - *Size=pktinfo.streamsize; - } - else - { - *Size=pkthdr-pktinfo.pkthdr; - } - outptr=pkthdr; - - int bytesleft=inptr-outptr; - if (pktinfo.pkthdr>(4096+bytesleft)) - { - memcpy(buffer,&buffer[pkthdr],bytesleft); - scannerstart-=outptr; - inptr=bytesleft; - outptr=0; - pkthdr=0; - } - - pktinfo.pkthdr=pkthdr; - pktinfo.streamsize=streamsize; - pktinfo.pktsyncsize=pktsyncsize; - - return ptr; -} diff --git a/command/queue.h b/command/queue.h deleted file mode 100644 index 4efd197..0000000 --- a/command/queue.h +++ /dev/null @@ -1,161 +0,0 @@ -/* - * queue.h: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#ifndef __queue_h_ -#define __queue_h_ - -#include <stdint.h> - -#ifndef uchar -typedef unsigned char uchar; -#endif - -typedef struct MarkAdPacket -{ - uchar *Data; - int Length; - int Skipped; - bool Offcnt; -} MarkAdPacket; - -class cMarkAdPaketQueue -{ - struct MP2HDR - { -unsigned Sync1: - 8; -unsigned Protection: - 1; -unsigned Layer: - 2; -unsigned MpegID: - 2; -unsigned Sync2: - 3; -unsigned Private: - 1; -unsigned Padding: - 1; -unsigned SampleRateIndex: - 2; -unsigned BitRateIndex: - 4; -unsigned Emphasis: - 2; -unsigned Original: - 1; -unsigned Copyright: - 1; -unsigned ModeExt: - 2; -unsigned Mode: - 2; - }; - -#pragma pack(1) - struct AC3HDR - { -unsigned Sync1: - 8; -unsigned Sync2: - 8; -unsigned CRC1: - 8; -unsigned CRC2: - 8; -unsigned FrameSizeIndex: - 6; -unsigned SampleRateIndex: - 2; - }; -#pragma pack() - -#pragma pack(1) - struct PESHDROPT - { -unsigned OOC: - 1; -unsigned CY: - 1; -unsigned DAI: - 1; -unsigned PESP: - 1; -unsigned PESSC: - 2; -unsigned MarkerBits: - 2; -unsigned EXT: - 1; -unsigned CRC: - 1; -unsigned ACI: - 1; -unsigned TM: - 1; -unsigned RATE: - 1; -unsigned ESCR: - 1; -unsigned TSF: - 2; -unsigned Length: - 8; - }; -#pragma pack() - -private: - char *name; - struct pktinfo - { - int pkthdr; - int pktsyncsize; - int streamsize; - bool ispes; - } pktinfo; - - int percent; - int mpercent; // max percentage use - - uchar *buffer; - int maxqueue; - int inptr; - int outptr; - - uint32_t scanner; - int scannerstart; - - int FindPktHeader(int Start, int *StreamSize,int *SyncSize, bool LongStartCode); - int FindAudioHeader(int Start, int *FrameSize, int *SyncSize, bool AC3); -public: - cMarkAdPaketQueue(const char *Name, int Size=32768); - ~cMarkAdPaketQueue(); - int Length() - { - return inptr-outptr; - } - void Clear() - { - inptr=outptr=0; - pktinfo.pkthdr=-1; - scanner=0xFFFFFFFF; - scannerstart=-1; - } - bool Inject(uchar *Data, int Size); - bool Put(uchar *Data, int Size); - uchar *Get(int *Size); - uchar *Peek(int Size); - -#define MA_PACKET_PKT 0x10 // 0x00 0x00 0x01 (PES / H262) -#define MA_PACKET_H264 0x11 // 0x00 0x00 0x00 0x01 (H264) -#define MA_PACKET_AC3 0x20 -#define MA_PACKET_MP2 0x30 - - uchar *GetPacket(int *Size, int Type); -}; - -#endif diff --git a/command/ts2pkt.cpp b/command/ts2pkt.cpp deleted file mode 100644 index c3bbd15..0000000 --- a/command/ts2pkt.cpp +++ /dev/null @@ -1,224 +0,0 @@ -/* - * ts2pkt.cpp: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#include <stdlib.h> -#include <string.h> - -extern "C" -{ -#include "debug.h" -} - -#include "ts2pkt.h" - -cMarkAdTS2Pkt::cMarkAdTS2Pkt(const char *QueueName, int QueueSize) -{ - queue=new cMarkAdPaketQueue(QueueName,QueueSize); - Reset(); -} - -cMarkAdTS2Pkt::~cMarkAdTS2Pkt() -{ - if (queue) delete queue; -} - -void cMarkAdTS2Pkt::Clear() -{ - Reset(); -} - -bool cMarkAdTS2Pkt::Reset(int ErrIndex) -{ - sync=false; - switch (ErrIndex) - { - case MA_ERR_TSSIZE: - dsyslog("inbuf not 188 bytes"); - break; - case MA_ERR_NOSYNC: - dsyslog("found no sync"); - break; - case MA_ERR_SEQ: - dsyslog("sequence error"); - break; - case MA_ERR_AFC: - dsyslog("wrong AFC value"); - break; - case MA_ERR_TOBIG: - dsyslog("buflen > 188 bytes"); - break; - case MA_ERR_NEG: - dsyslog("buflen negative"); - break; - } - counter=-1; - if (queue) queue->Clear(); - return false; -} - -bool cMarkAdTS2Pkt::InjectVideoPES(uchar *PESData, int PESSize) -{ - if ((!PESData) || (!PESSize)) return false; - - struct PESHDR *peshdr=(struct PESHDR *) PESData; - - // first check some simple things - if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1)) return false; - if ((peshdr->StreamID & 0xF0)!=0xE0) return false; - - int Length=(peshdr->LenH<<8)+peshdr->LenL; - if (Length) Length+=sizeof(PESHDR); - if (Length!=PESSize) return false; - - struct PESHDROPT *peshdropt=(struct PESHDROPT *) &PESData[sizeof(struct PESHDR)]; - - uchar *buf; - int buflen; - - if (peshdropt->MarkerBits==0x2) - { - // we have an optional PES header - int bpos=sizeof(struct PESHDR)+sizeof(struct PESHDROPT)+ - peshdropt->Length; - buf=&PESData[bpos]; - buflen=PESSize-bpos; - } - else - { - int bpos=sizeof(struct PESHDR); - buf=&PESData[bpos]; - buflen=PESSize-bpos; - } - queue->Inject(buf,buflen); - return true; -} - -bool cMarkAdTS2Pkt::Process(MarkAdPid Pid, uchar *TSData, int TSSize, MarkAdPacket *Pkt) -{ - if ((!Pkt) || (!queue)) return false; - - bool ret=true; - - if (TSData) - { - if (TSSize!=TS_SIZE) - { - return Reset(MA_ERR_TSSIZE); // we need a full packet - } - - // check TS packet sync - if (TSData[0]!=0x47) - { - return Reset(MA_ERR_NOSYNC); // no sync - } - - struct TSHDR *tshdr = (struct TSHDR *) TSData; - - int pid = (tshdr->PidH << 8) | tshdr->PidL; - if (Pid.Num!=pid) - { - return true; // not for us, but this is ok - } - - if ((counter!=-1) && (((counter+1) & 0xF)!=tshdr->Counter)) - { - if (counter==(int) tshdr->Counter) - { - Pkt->Skipped+=TS_SIZE; - return true; // duplicate paket -> just ignore - } - // sequence error - ret=Reset(MA_ERR_SEQ); - if (!tshdr->PayloadStart) return ret; - } - counter=tshdr->Counter; - - if (tshdr->PayloadStart) - { - sync=true; - } - if (!sync) - { - Pkt->Skipped+=TS_SIZE; - return false; // not synced - } - - if ((tshdr->AFC<=0) || (tshdr->AFC>3)) - { - return Reset(MA_ERR_AFC); - } - - // we just ignore the infos in the adaption field (e.g. OPCR/PCR) - if ((tshdr->AFC!=1) && (tshdr->AFC!=3)) - { - return true; - } - - int buflen=TS_SIZE+1; - uchar *buf=NULL; - - if (tshdr->AFC==1) - { - // payload only - buflen=TS_SIZE-sizeof(struct TSHDR); - buf=&TSData[sizeof(struct TSHDR)]; - } - - if (tshdr->AFC==3) - { - // adaption field + payload - struct TSADAPT *tsadapt = (struct TSADAPT *) &TSData[4]; - int alen=tsadapt->Len+1; - buflen=TS_SIZE-(sizeof(struct TSHDR)+alen); - buf=&TSData[sizeof(struct TSHDR)+alen]; - } - - if (buflen>TS_SIZE) - { - // size to large - return Reset(MA_ERR_TOBIG); - } - if (buflen<0) - { - // error in size - return Reset(MA_ERR_NEG); - } - if (buflen==0) - { - // no data? - return false; - } - - if (tshdr->PayloadStart) - { - if ((buf[0]!=0) && (buf[1]!=0)) - { - Pkt->Skipped+=TS_SIZE; - sync=false; - if (buflen<7) return false; - // add a pseudo padding stream - buf[0]=0; - buf[1]=0; - buf[2]=1; - buf[3]=0xbe; - buf[4]=buflen-6; - buf[5]=0; - } - } - queue->Put(buf,buflen); - } - if (!ret) return ret; - if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) - { - Pkt->Data=queue->GetPacket(&Pkt->Length,MA_PACKET_H264); - } - else - { - Pkt->Data=queue->GetPacket(&Pkt->Length,MA_PACKET_PKT); - } - return ret; -} diff --git a/command/ts2pkt.h b/command/ts2pkt.h deleted file mode 100644 index 367a007..0000000 --- a/command/ts2pkt.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * ts2pkt.h: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#ifndef __ts2pkt_h_ -#define __ts2pkt_h_ - -#ifndef TS_SIZE -#define TS_SIZE 188 -#endif - -#ifndef uchar -typedef unsigned char uchar; -#endif - -#include "global.h" -#include "queue.h" - -class cMarkAdTS2Pkt -{ -private: - struct TSHDR - { -unsigned Sync: - 8; -unsigned PidH: - 5; -unsigned Priority: - 1; -unsigned PayloadStart: - 1; -unsigned TError: - 1; -unsigned PidL: - 8; -unsigned Counter: - 4; -unsigned AFC: - 2; -unsigned TSC: - 2; - }; - - struct TSADAPT - { -unsigned Len: - 8; -unsigned Discontinuity_indicator: - 1; -unsigned Random_access_indicator: - 1; -unsigned Elementary_stream_priority_indicator: - 1; -unsigned PCR_flag: - 1; -unsigned OPCR_flag: - 1; -unsigned Splicing_point_flag: - 1; -unsigned Transport_private_data_flag: - 1; -unsigned Adaption_field_extension_flag: - 1; -uint64_t PCR_base: - 33; -unsigned reserved: - 6; -unsigned PCR_ext: - 9; - }; - - struct PESHDR - { - uchar Sync1; - uchar Sync2; - uchar Sync3; - uchar StreamID; - uchar LenH; - uchar LenL; - }; - -#pragma pack(1) - struct PESHDROPT - { -unsigned OOC: - 1; -unsigned CY: - 1; -unsigned DAI: - 1; -unsigned PESP: - 1; -unsigned PESSC: - 2; -unsigned MarkerBits: - 2; -unsigned EXT: - 1; -unsigned CRC: - 1; -unsigned ACI: - 1; -unsigned TM: - 1; -unsigned RATE: - 1; -unsigned ESCR: - 1; -unsigned TSF: - 2; -unsigned Length: - 8; - }; -#pragma pack() - - int counter; - bool sync; - - cMarkAdPaketQueue *queue; - -#define MA_ERR_STARTUP 0 -#define MA_ERR_TSSIZE 1 -#define MA_ERR_NOSYNC 2 -#define MA_ERR_SEQ 3 -#define MA_ERR_AFC 4 -#define MA_ERR_TOBIG 5 -#define MA_ERR_NEG 6 - bool Reset(int ErrIndex=MA_ERR_STARTUP); -public: - cMarkAdTS2Pkt(const char *QueueName="TS2Pkt", int QueueSize=32768); - ~cMarkAdTS2Pkt(); - void Clear(); - bool Process(MarkAdPid Pid,uchar *TSData, int TSSize, MarkAdPacket *Pkt); - bool InjectVideoPES(uchar *PESData, int PESSize); -}; - -#endif diff --git a/command/video.cpp b/command/video.cpp index cf5f649..be2cb5c 100644 --- a/command/video.cpp +++ b/command/video.cpp @@ -47,13 +47,13 @@ cMarkAdLogo::cMarkAdLogo(MarkAdContext *maContext) if (maContext->Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) { - LOGOWIDTH=LOGO_DEFHDWIDTH; LOGOHEIGHT=LOGO_DEFHDHEIGHT; + LOGOWIDTH=LOGO_DEFHDWIDTH; } else { - LOGOWIDTH=LOGO_DEFWIDTH; LOGOHEIGHT=LOGO_DEFHEIGHT; + LOGOWIDTH=LOGO_DEFWIDTH; } pixfmt_info=false; @@ -63,7 +63,7 @@ cMarkAdLogo::cMarkAdLogo(MarkAdContext *maContext) void cMarkAdLogo::Clear() { memset(&area,0,sizeof(area)); - area.status=UNINITIALIZED; + area.status=LOGO_UNINITIALIZED; } int cMarkAdLogo::Load(const char *directory, char *file, int plane) @@ -105,7 +105,11 @@ int cMarkAdLogo::Load(const char *directory, char *file, int plane) return -2; } - if (fread(&area.mask[plane],1,width*height,pFile)!=(size_t) (width*height)) return -2; + if (fread(&area.mask[plane],1,width*height,pFile)!=(size_t) (width*height)) + { + fclose(pFile); + return -2; + } if (!area.mpixel[plane]) { @@ -218,8 +222,9 @@ int cMarkAdLogo::SobelPlane(int plane) return 0; } - int boundary=15; + int boundary=6; int cutval=127; + //int cutval=60; int width=LOGOWIDTH; if (plane>0) @@ -327,36 +332,36 @@ int cMarkAdLogo::Detect(int framenumber, int *logoframenumber) mpixel+=area.mpixel[plane]; } } - if (extract) return NOCHANGE; - if (!processed) return ERROR; + if (extract) return LOGO_NOCHANGE; + if (!processed) return LOGO_ERROR; if (processed==1) { - if ((area.intensity>100) || (area.status!=LOGO) && - (area.intensity>180)) return NOCHANGE; + if ((area.intensity>100) || (area.status!=LOGO_VISIBLE) && + (area.intensity>180)) return LOGO_NOCHANGE; } - int ret=NOCHANGE; - if (area.status==UNINITIALIZED) + int ret=LOGO_NOCHANGE; + if (area.status==LOGO_UNINITIALIZED) { // Initialize - if (rpixel>(mpixel*LOGO_VMARK)) + if (rpixel>=(mpixel*LOGO_VMARK)) { - area.status=LOGO; + area.status=ret=LOGO_VISIBLE; } else { - area.status=NOLOGO; + area.status=LOGO_INVISIBLE; } } if (rpixel>=(mpixel*LOGO_VMARK)) { - if (area.status==NOLOGO) + if (area.status==LOGO_INVISIBLE) { if (area.counter>=LOGO_VMAXCOUNT) { - area.status=ret=LOGO; + area.status=ret=LOGO_VISIBLE; *logoframenumber=area.framenumber; area.counter=0; } @@ -375,11 +380,11 @@ int cMarkAdLogo::Detect(int framenumber, int *logoframenumber) if (rpixel<(mpixel*LOGO_IMARK)) { - if (area.status==LOGO) + if (area.status==LOGO_VISIBLE) { if (area.counter>=LOGO_IMAXCOUNT) { - area.status=ret=NOLOGO; + area.status=ret=LOGO_INVISIBLE; *logoframenumber=area.framenumber; area.counter=0; } @@ -404,12 +409,16 @@ int cMarkAdLogo::Detect(int framenumber, int *logoframenumber) int cMarkAdLogo::Process(int FrameNumber, int *LogoFrameNumber) { - if (!macontext) return ERROR; - if (!macontext->Video.Data.Valid) return ERROR; - if (!macontext->Video.Info.Width) return ERROR; - if (!macontext->Video.Info.Height) return ERROR; - if (!macontext->Config->logoDirectory[0]) return ERROR; - if (!macontext->Info.ChannelName) return ERROR; + if (!macontext) return LOGO_ERROR; + if (!macontext->Video.Data.Valid) + { + area.status=LOGO_UNINITIALIZED; + return LOGO_ERROR; + } + if (!macontext->Video.Info.Width) return LOGO_ERROR; + if (!macontext->Video.Info.Height) return LOGO_ERROR; + if (!macontext->Config->logoDirectory[0]) return LOGO_ERROR; + if (!macontext->Info.ChannelName) return LOGO_ERROR; if (macontext->Config->logoExtraction==-1) { @@ -469,7 +478,7 @@ cMarkAdBlackBordersHoriz::cMarkAdBlackBordersHoriz(MarkAdContext *maContext) void cMarkAdBlackBordersHoriz::Clear() { - borderstatus=UNINITIALIZED; + borderstatus=HBORDER_UNINITIALIZED; borderframenumber=-1; } @@ -537,23 +546,23 @@ int cMarkAdBlackBordersHoriz::Process(int FrameNumber, int *BorderIFrame) #define MINSECS 240 switch (borderstatus) { - case UNINITIALIZED: + case HBORDER_UNINITIALIZED: if (FrameNumber>(borderframenumber+macontext->Video.Info.FramesPerSecond*MINSECS)) { - borderstatus=BORDER; + borderstatus=HBORDER_VISIBLE; } break; - case NOBORDER: + case HBORDER_INVISIBLE: if (FrameNumber>(borderframenumber+macontext->Video.Info.FramesPerSecond*MINSECS)) { *BorderIFrame=borderframenumber; - borderstatus=BORDER; + borderstatus=HBORDER_VISIBLE; return 1; // detected start of black border } break; - case BORDER: + case HBORDER_VISIBLE: borderframenumber=FrameNumber; break; } @@ -563,10 +572,10 @@ int cMarkAdBlackBordersHoriz::Process(int FrameNumber, int *BorderIFrame) { if (borderframenumber!=-1) { - if (borderstatus==BORDER) + if (borderstatus==HBORDER_VISIBLE) { *BorderIFrame=borderframenumber; - borderstatus=NOBORDER; + borderstatus=HBORDER_INVISIBLE; borderframenumber=-1; return -1; // detected stop of black border } @@ -578,7 +587,7 @@ int cMarkAdBlackBordersHoriz::Process(int FrameNumber, int *BorderIFrame) else { borderframenumber=-1; - borderstatus=NOBORDER; + borderstatus=HBORDER_INVISIBLE; } } return 0; @@ -588,10 +597,8 @@ cMarkAdOverlap::cMarkAdOverlap(MarkAdContext *maContext) { macontext=maContext; - histbuf[BEFORE]=NULL; - histbuf[AFTER]=NULL; - result.CommentBefore=NULL; - result.CommentAfter=NULL; + histbuf[OV_BEFORE]=NULL; + histbuf[OV_AFTER]=NULL; Clear(); } @@ -602,22 +609,20 @@ cMarkAdOverlap::~cMarkAdOverlap() void cMarkAdOverlap::Clear() { - histcnt[BEFORE]=0; - histcnt[AFTER]=0; - histframes[BEFORE]=0; - histframes[AFTER]=0; - if (histbuf[BEFORE]) + histcnt[OV_BEFORE]=0; + histcnt[OV_AFTER]=0; + histframes[OV_BEFORE]=0; + histframes[OV_AFTER]=0; + if (histbuf[OV_BEFORE]) { - delete[] histbuf[BEFORE]; - histbuf[BEFORE]=NULL; + delete[] histbuf[OV_BEFORE]; + histbuf[OV_BEFORE]=NULL; } - if (histbuf[AFTER]) + if (histbuf[OV_AFTER]) { - delete[] histbuf[AFTER]; - histbuf[AFTER]=NULL; + delete[] histbuf[OV_AFTER]; + histbuf[OV_AFTER]=NULL; } - if (result.CommentBefore) free(result.CommentBefore); - if (result.CommentAfter) free(result.CommentAfter); memset(&result,0,sizeof(result)); similarCutOff=0; similarMaxCnt=0; @@ -656,12 +661,12 @@ MarkAdPos *cMarkAdOverlap::Detect() int tmpA=0,tmpB=0; if (result.FrameNumberBefore==-1) return NULL; result.FrameNumberBefore=-1; - for (int B=0; B<histcnt[BEFORE]; B++) + for (int B=0; B<histcnt[OV_BEFORE]; B++) { - for (int A=start; A<histcnt[AFTER]; A++) + for (int A=start; A<histcnt[OV_AFTER]; A++) { - //printf("%6i %6i ",histbuf[BEFORE][B].framenumber,histbuf[AFTER][A].framenumber); - bool simil=areSimilar(histbuf[BEFORE][B].histogram,histbuf[AFTER][A].histogram); + //printf("%6i %6i ",histbuf[OV_BEFORE][B].framenumber,histbuf[OV_AFTER][A].framenumber); + bool simil=areSimilar(histbuf[OV_BEFORE][B].histogram,histbuf[OV_AFTER][A].histogram); if (simil) { tmpA=A; @@ -675,15 +680,15 @@ MarkAdPos *cMarkAdOverlap::Detect() } else { - //if (simcnt) printf("%i %i %i\n",simcnt,histbuf[BEFORE][B].framenumber,histbuf[AFTER][A].framenumber); + //if (simcnt) printf("simcnt=%i\n",simcnt); if (simcnt>similarMaxCnt) { - if ((histbuf[BEFORE][tmpB].framenumber>result.FrameNumberBefore) && - (histbuf[AFTER][tmpA].framenumber>result.FrameNumberAfter)) + if ((histbuf[OV_BEFORE][tmpB].framenumber>result.FrameNumberBefore) && + (histbuf[OV_AFTER][tmpA].framenumber>result.FrameNumberAfter)) { - result.FrameNumberBefore=histbuf[BEFORE][tmpB].framenumber; - result.FrameNumberAfter=histbuf[AFTER][tmpA].framenumber; + result.FrameNumberBefore=histbuf[OV_BEFORE][tmpB].framenumber; + result.FrameNumberAfter=histbuf[OV_AFTER][tmpA].framenumber; } } else @@ -698,23 +703,14 @@ MarkAdPos *cMarkAdOverlap::Detect() { if (simcnt>similarMaxCnt) { - result.FrameNumberBefore=histbuf[BEFORE][tmpB].framenumber; - result.FrameNumberAfter=histbuf[AFTER][tmpA].framenumber; + result.FrameNumberBefore=histbuf[OV_BEFORE][tmpB].framenumber; + result.FrameNumberAfter=histbuf[OV_AFTER][tmpA].framenumber; } else { return NULL; } } - - if (asprintf(&result.CommentBefore,"detected overlap before (%i)",result.FrameNumberBefore)==-1) - { - result.CommentBefore=NULL; - } - if (asprintf(&result.CommentAfter,"detected overlap after (%i)",result.FrameNumberAfter)==-1) - { - result.CommentAfter=NULL; - } return &result; } @@ -722,13 +718,13 @@ MarkAdPos *cMarkAdOverlap::Process(int FrameNumber, int Frames, bool BeforeAd) { if ((lastframenumber>0) && (!similarMaxCnt)) { - similarCutOff=60000; // lower is harder! + similarCutOff=50000; // lower is harder! similarMaxCnt=4; } if (BeforeAd) { - if ((histframes[BEFORE]) && (histcnt[BEFORE]>=histframes[BEFORE])) + if ((histframes[OV_BEFORE]) && (histcnt[OV_BEFORE]>=histframes[OV_BEFORE])) { if (result.FrameNumberBefore) { @@ -739,31 +735,31 @@ MarkAdPos *cMarkAdOverlap::Process(int FrameNumber, int Frames, bool BeforeAd) return NULL; } } - if (!histbuf[BEFORE]) + if (!histbuf[OV_BEFORE]) { - histframes[BEFORE]=Frames; - histbuf[BEFORE]=new histbuffer[Frames+1]; + histframes[OV_BEFORE]=Frames; + histbuf[OV_BEFORE]=new histbuffer[Frames+1]; } - getHistogram(histbuf[BEFORE][histcnt[BEFORE]].histogram); - histbuf[BEFORE][histcnt[BEFORE]].framenumber=FrameNumber; - histcnt[BEFORE]++; + getHistogram(histbuf[OV_BEFORE][histcnt[OV_BEFORE]].histogram); + histbuf[OV_BEFORE][histcnt[OV_BEFORE]].framenumber=FrameNumber; + histcnt[OV_BEFORE]++; } else { - if (!histbuf[AFTER]) + if (!histbuf[OV_AFTER]) { - histframes[AFTER]=Frames; - histbuf[AFTER]=new histbuffer[Frames+1]; + histframes[OV_AFTER]=Frames; + histbuf[OV_AFTER]=new histbuffer[Frames+1]; } - if (histcnt[AFTER]>=histframes[AFTER]-1) + if (histcnt[OV_AFTER]>=histframes[OV_AFTER]-1) { if (result.FrameNumberBefore) return NULL; return Detect(); } - getHistogram(histbuf[AFTER][histcnt[AFTER]].histogram); - histbuf[AFTER][histcnt[AFTER]].framenumber=FrameNumber; - histcnt[AFTER]++; + getHistogram(histbuf[OV_AFTER][histcnt[OV_AFTER]].histogram); + histbuf[OV_AFTER][histcnt[OV_AFTER]].framenumber=FrameNumber; + histcnt[OV_AFTER]++; } lastframenumber=FrameNumber; return NULL; @@ -783,7 +779,7 @@ cMarkAdVideo::cMarkAdVideo(MarkAdContext *maContext) cMarkAdVideo::~cMarkAdVideo() { - ResetMarks(); + resetmarks(); if (hborder) delete hborder; if (logo) delete logo; if (overlap) delete overlap; @@ -793,41 +789,57 @@ void cMarkAdVideo::Clear() { aspectratio.Num=0; aspectratio.Den=0; + framelast=0; + framebeforelast=0; if (hborder) hborder->Clear(); if (logo) logo->Clear(); } -void cMarkAdVideo::ResetMarks() +void cMarkAdVideo::resetmarks() { - for (int i=0; i<marks.maxCount; i++) - { - if (marks.Number[i].Comment) free(marks.Number[i].Comment); - } memset(&marks,0,sizeof(marks)); } -bool cMarkAdVideo::AddMark(int Type, int Position, const char *Comment) +bool cMarkAdVideo::addmark(int type, int position, MarkAdAspectRatio *before, + MarkAdAspectRatio *after) { - if (!Comment) return false; if (marks.Count>marks.maxCount) return false; - marks.Number[marks.Count].Comment=strdup(Comment); - marks.Number[marks.Count].Position=Position; - marks.Number[marks.Count].Type=Type; + if (before) + { + marks.Number[marks.Count].AspectRatioBefore.Num=before->Num; + marks.Number[marks.Count].AspectRatioBefore.Den=before->Den; + } + if (after) + { + marks.Number[marks.Count].AspectRatioAfter.Num=after->Num; + marks.Number[marks.Count].AspectRatioAfter.Den=after->Den; + } + marks.Number[marks.Count].Position=position; + marks.Number[marks.Count].Type=type; marks.Count++; return true; } -bool cMarkAdVideo::AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b) +bool cMarkAdVideo::aspectratiochange(MarkAdAspectRatio &a, MarkAdAspectRatio &b, bool &start) { - if ((!a) || (!b)) return false; - - if (a->Num==0 || a->Den==0 || b->Num==0 || b->Den==0) return false; - if ((a->Num!=b->Num) && (a->Den!=b->Den)) return true; + start=false; + if (a.Num==0 || a.Den==0 || b.Num==0 || b.Den==0) + { + if (((a.Num==4) || (b.Num==4)) && ((a.Den==3) || (b.Den==3))) + { + start=true; + } + else + { + return false; + } + } + if ((a.Num!=b.Num) && (a.Den!=b.Den)) return true; return false; } -MarkAdPos *cMarkAdVideo::Process2ndPass(int FrameNumber, int Frames, bool BeforeAd) +MarkAdPos *cMarkAdVideo::ProcessOverlap(int FrameNumber, int Frames, bool BeforeAd) { if (!FrameNumber) return NULL; if (!overlap) overlap=new cMarkAdOverlap(macontext); @@ -840,7 +852,7 @@ MarkAdMarks *cMarkAdVideo::Process(int FrameNumber, int FrameNumberNext) { if ((!FrameNumber) && (!FrameNumberNext)) return NULL; - ResetMarks(); + resetmarks(); if (!macontext->Video.Options.IgnoreLogoDetection) { @@ -848,97 +860,69 @@ MarkAdMarks *cMarkAdVideo::Process(int FrameNumber, int FrameNumberNext) int lret=logo->Process(FrameNumber,&logoframenumber); if ((lret>=-1) && (lret!=0)) { - char *buf=NULL; if (lret>0) { - if (asprintf(&buf,"detected logo start (%i)",logoframenumber)!=-1) - { - AddMark(MT_LOGOSTART,logoframenumber,buf); - free(buf); - } + addmark(MT_LOGOSTART,logoframenumber); } else { - if (asprintf(&buf,"detected logo stop (%i)",logoframenumber)!=-1) - { - AddMark(MT_LOGOSTOP,logoframenumber,buf); - free(buf); - } + addmark(MT_LOGOSTOP,logoframenumber); } } } + else + { + logo->SetStatusUninitialized(); + } - int borderframenumber; - int hret=hborder->Process(FrameNumber,&borderframenumber); + int hborderframenumber; + int hret=hborder->Process(FrameNumber,&hborderframenumber); - if ((hret>0) && (borderframenumber)) + if ((hret>0) && (hborderframenumber)) { - char *buf=NULL; - if (asprintf(&buf,"detected start of horiz. borders (%i [%i])", - borderframenumber,FrameNumber)!=-1) - { - AddMark(MT_BORDERSTART,borderframenumber,buf); - free(buf); - } + addmark(MT_HBORDERSTART,hborderframenumber); } - if ((hret<0) && (borderframenumber)) + if ((hret<0) && (hborderframenumber)) { - char *buf=NULL; - if (asprintf(&buf,"detected stop of horiz. borders (%i [%i])", - borderframenumber,FrameNumber)!=-1) - { - AddMark(MT_BORDERSTOP,borderframenumber,buf); - free(buf); - } + addmark(MT_HBORDERSTOP,hborderframenumber); } if (!macontext->Video.Options.IgnoreAspectRatio) { - if (AspectRatioChange(&macontext->Video.Info.AspectRatio,&aspectratio)) + bool start; + if (aspectratiochange(macontext->Video.Info.AspectRatio,aspectratio,start)) { - char *buf=(char *) calloc(1,256); - if (!buf) return NULL; - snprintf(buf,255,"aspect ratio change from %i:%i to %i:%i (", - aspectratio.Num,aspectratio.Den, - macontext->Video.Info.AspectRatio.Num, - macontext->Video.Info.AspectRatio.Den); + if ((logo->Status()==LOGO_VISIBLE) && (!start)) + { + addmark(MT_LOGOSTOP,framebeforelast); + logo->SetStatusLogoInvisible(); + } - if ((macontext->Info.AspectRatio.Num) && (macontext->Info.AspectRatio.Den)) + if ((macontext->Video.Info.AspectRatio.Num==4) && + (macontext->Video.Info.AspectRatio.Den==3)) { - if ((macontext->Video.Info.AspectRatio.Num==macontext->Info.AspectRatio.Num) && - (macontext->Video.Info.AspectRatio.Den==macontext->Info.AspectRatio.Den)) - { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)*",FrameNumberNext); - nbuf[19]=0; - strcat(buf,nbuf); - AddMark(MT_ASPECTSTART,FrameNumberNext,buf); - } - else - { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)",framelast); - nbuf[19]=0; - strcat(buf,nbuf); - AddMark(MT_ASPECTSTOP,framelast,buf); - } + addmark(MT_ASPECTSTART,start ? FrameNumber : FrameNumberNext, + &aspectratio,&macontext->Video.Info.AspectRatio); } else { - char nbuf[20]; - snprintf(nbuf,sizeof(nbuf),"%i)?",FrameNumber); - nbuf[19]=0; - strcat(buf,nbuf); - - AddMark(MT_ASPECTCHANGE,FrameNumber,buf); + addmark(MT_ASPECTSTOP,framelast,&aspectratio, + &macontext->Video.Info.AspectRatio); } - free(buf); } aspectratio.Num=macontext->Video.Info.AspectRatio.Num; aspectratio.Den=macontext->Video.Info.AspectRatio.Den; } framelast=FrameNumberNext; - return &marks; + framebeforelast=FrameNumber; + if (marks.Count) + { + return &marks; + } + else + { + return NULL; + } } diff --git a/command/video.h b/command/video.h index 9767006..f4abbec 100644 --- a/command/video.h +++ b/command/video.h @@ -23,11 +23,33 @@ #define LOGO_VMARK 0.5 // percantage of pixels for visible #define LOGO_IMARK 0.15 // percentage of pixels for invisible +enum +{ + LOGO_ERROR=-3, + LOGO_UNINITIALIZED=-2, + LOGO_INVISIBLE=-1, + LOGO_NOCHANGE=0, + LOGO_VISIBLE=1 +}; + +enum +{ + HBORDER_ERROR=-3, + HBORDER_UNINITIALIZED=-2, + HBORDER_INVISIBLE=-1, + HBORDER_NOCHANGE=0, + HBORDER_VISIBLE=1 +}; + +enum +{ + OV_BEFORE=0, + OV_AFTER=1 +}; + class cMarkAdOverlap { private: -#define BEFORE 0 -#define AFTER 1 MarkAdContext *macontext; typedef int simpleHistogram[256]; @@ -68,15 +90,6 @@ private: BOTTOM_RIGHT }; - enum - { - ERROR=-3, - UNINITIALIZED=-2, - NOLOGO=-1, - NOCHANGE=0, - LOGO=1 - }; - int LOGOHEIGHT; // max. 140 int LOGOWIDTH; // 192-288 @@ -114,27 +127,36 @@ private: public: cMarkAdLogo(MarkAdContext *maContext); int Process(int FrameNumber, int *LogoFrameNumber); + int Status() + { + return area.status; + } + void SetStatusLogoInvisible() + { + if (area.status==LOGO_VISIBLE) + area.status=LOGO_INVISIBLE; + } + void SetStatusUninitialized() + { + if (area.status!=LOGO_UNINITIALIZED) + area.status=LOGO_UNINITIALIZED; + } void Clear(); }; class cMarkAdBlackBordersHoriz { private: - enum - { - ERROR=-3, - UNINITIALIZED=-2, - NOBORDER=-1, - NOCHANGE=0, - BORDER=1 - }; - int borderstatus; int borderframenumber; MarkAdContext *macontext; public: cMarkAdBlackBordersHoriz(MarkAdContext *maContext); int Process(int FrameNumber,int *BorderFrameNumber); + int Status() + { + return borderstatus; + } void Clear(); }; @@ -149,16 +171,18 @@ private: cMarkAdLogo *logo; cMarkAdOverlap *overlap; - void ResetMarks(); - bool AddMark(int Type, int Position, const char *Comment); - bool AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b); + void resetmarks(); + bool addmark(int type, int position, MarkAdAspectRatio *before=NULL, + MarkAdAspectRatio *after=NULL); + bool aspectratiochange(MarkAdAspectRatio &a, MarkAdAspectRatio &b, bool &start); int framelast; + int framebeforelast; public: cMarkAdVideo(MarkAdContext *maContext); ~cMarkAdVideo(); - MarkAdPos *Process2ndPass(int FrameNumber, int Frames, bool BeforeAd); + MarkAdPos *ProcessOverlap(int FrameNumber, int Frames, bool BeforeAd); MarkAdMarks *Process(int FrameNumber, int FrameNumberNext); void Clear(); }; |