diff options
author | Jochen Dolze <vdr@dolze.de> | 2010-09-16 21:37:36 +0200 |
---|---|---|
committer | Jochen Dolze <vdr@dolze.de> | 2010-09-16 21:37:36 +0200 |
commit | 57df9917d4626d93323c7b0d2368fbf5d4748627 (patch) | |
tree | ebfe76fe280db47e223335266eab1bd34afa4226 | |
parent | 03fc6351fce571b5ff7454bdfad9d4a0f0fb7679 (diff) | |
download | vdr-plugin-markad-57df9917d4626d93323c7b0d2368fbf5d4748627.tar.gz vdr-plugin-markad-57df9917d4626d93323c7b0d2368fbf5d4748627.tar.bz2 |
Added second pass processing (overlap, audio silence detection)
-rw-r--r-- | command/Makefile | 2 | ||||
-rw-r--r-- | command/audio.cpp | 103 | ||||
-rw-r--r-- | command/audio.h | 25 | ||||
-rw-r--r-- | command/audio_gain_analysis.cpp | 423 | ||||
-rw-r--r-- | command/audio_gain_analysis.h | 102 | ||||
-rw-r--r-- | command/decoder.cpp | 121 | ||||
-rw-r--r-- | command/decoder.h | 7 | ||||
-rw-r--r-- | command/demux.cpp | 101 | ||||
-rw-r--r-- | command/demux.h | 9 | ||||
-rw-r--r-- | command/global.h | 8 | ||||
-rw-r--r-- | command/markad-standalone.cpp | 678 | ||||
-rw-r--r-- | command/markad-standalone.h | 43 | ||||
-rw-r--r-- | command/marks.cpp | 167 | ||||
-rw-r--r-- | command/marks.h | 5 | ||||
-rw-r--r-- | command/pes2es.cpp | 24 | ||||
-rw-r--r-- | command/pes2es.h | 82 | ||||
-rw-r--r-- | command/queue.cpp | 25 | ||||
-rw-r--r-- | command/queue.h | 8 | ||||
-rw-r--r-- | command/streaminfo.cpp | 213 | ||||
-rw-r--r-- | command/streaminfo.h | 10 | ||||
-rw-r--r-- | command/ts2pkt.cpp | 58 | ||||
-rw-r--r-- | command/ts2pkt.h | 28 | ||||
-rw-r--r-- | command/video.cpp | 318 | ||||
-rw-r--r-- | command/video.h | 52 |
24 files changed, 1977 insertions, 635 deletions
diff --git a/command/Makefile b/command/Makefile index e6e1f2f..77f4f3e 100644 --- a/command/Makefile +++ b/command/Makefile @@ -28,7 +28,7 @@ INCLUDES += -I.. ### The object files (add further files here): -OBJS = markad-standalone.o decoder.o marks.o streaminfo.o video.o audio.o demux.o queue.o ts2pkt.o pes2es.o +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 ### The main target: diff --git a/command/audio.cpp b/command/audio.cpp index a5fc357..f0457b5 100644 --- a/command/audio.cpp +++ b/command/audio.cpp @@ -11,6 +11,11 @@ #include <stdlib.h> #include <string.h> +extern "C" +{ +#include "debug.h" +} + #include "audio.h" cMarkAdAudio::cMarkAdAudio(MarkAdContext *maContext) @@ -19,11 +24,14 @@ cMarkAdAudio::cMarkAdAudio(MarkAdContext *maContext) mark.Comment=NULL; mark.Position=0; mark.Type=0; + result.CommentBefore=NULL; + result.CommentAfter=NULL; Clear(); } cMarkAdAudio::~cMarkAdAudio() { + Clear(); ResetMark(); } @@ -60,11 +68,18 @@ bool cMarkAdAudio::AddMark(int Type, int Position, const char *Comment) return true; } -bool cMarkAdAudio::SilenceDetection() +bool cMarkAdAudio::SilenceDetection(int FrameNumber) { + // function taken from noad + if (!FrameNumber) return false; if (!macontext->Audio.Data.Valid) return false; - if (lastiframe_silence==lastiframe) return false; // we already detected silence for this frame - + if (lastframe_silence==FrameNumber) return false; // we already detected silence for this frame + if (lastframe_silence==-1) + { + // ignore first detection + lastframe_silence=FrameNumber; + return false; + } int samples=macontext->Audio.Data.SampleBufLen/ sizeof(*macontext->Audio.Data.SampleBuf)/ macontext->Audio.Info.Channels; @@ -81,20 +96,23 @@ bool cMarkAdAudio::SilenceDetection() lowvalcount++; if (lowvalcount>MIN_LOWVALS) { - lastiframe_silence=lastiframe; - return true; + lastframe_silence=FrameNumber; } } else { + if (lastframe_silence==FrameNumber) + { +// isyslog("Silentium @%i (%i)!",FrameNumber,lowvalcount); + return true; + } lowvalcount=0; } } return false; } -#if 0 -bool cMarkAdAudio::AnalyzeGain() +bool cMarkAdAudio::AnalyzeGain(int FrameNumber) { if (!macontext->Audio.Data.Valid) return false; @@ -111,34 +129,37 @@ bool cMarkAdAudio::AnalyzeGain() right[i]=macontext->Audio.Data.SampleBuf[1+(i*2)]; } - if ((lastiframe-lastiframe_gain)>ANALYZEFRAMES) + if (FrameNumber!=lastframe_gain) { - if (lastiframe_gain>0) + if ((lastframe_gain>0) && (audiogain.AnalyzedSamples()>=(3*samples))) { - double dgain,gain = audiogain.GetGain(); - dgain=gain-lastgain; - printf("%05i %+.2f db %+.2f db\n",lastiframe_gain,gain,lastgain); - lastgain=gain; + double gain = audiogain.GetGain(); + printf("%05i %+.2f db\n",lastframe_gain,gain); } audiogain.Init(macontext->Audio.Info.SampleRate); - lastiframe_gain=lastiframe; + lastframe_gain=-1; } + if (audiogain.AnalyzeSamples(left,right,samples,2)!=GAIN_ANALYSIS_OK) { - lastiframe_gain=-ANALYZEFRAMES; + lastframe_gain=-1; + return false; + } + else + { + lastframe_gain=FrameNumber; } - return true; } -#endif void cMarkAdAudio::Clear() { channels=0; -#if 0 - lastiframe_gain=-ANALYZEFRAMES; -#endif - lastiframe_silence=-1; + lastframe_gain=-1; + lastframe_silence=-1; + if (result.CommentBefore) free(result.CommentBefore); + if (result.CommentAfter) free(result.CommentAfter); + memset(&result,0,sizeof(result)); } bool cMarkAdAudio::ChannelChange(int a, int b) @@ -148,47 +169,51 @@ bool cMarkAdAudio::ChannelChange(int a, int b) return false; } -MarkAdMark *cMarkAdAudio::Process(int LastIFrame) +MarkAdPos *cMarkAdAudio::Process2ndPass(int FrameNumber) { - ResetMark(); - if (!LastIFrame) return NULL; - lastiframe=LastIFrame; + if (!FrameNumber) return NULL; -#if 0 - AnalyzeGain(); -#endif - if (macontext->Audio.Options.AudioSilenceDetection) + if (SilenceDetection(FrameNumber)) { - if (SilenceDetection()) + if (result.CommentBefore) free(result.CommentBefore); + if (asprintf(&result.CommentBefore,"audio silence detection (%i)",FrameNumber)==-1) { - char *buf=NULL; - if (asprintf(&buf,"audio channel silence detecion (%i)",lastiframe)!=-1) - { - AddMark(MT_SILENCECHANGE,lastiframe,buf); - free(buf); - } + result.CommentBefore=NULL; } + result.FrameNumberBefore=FrameNumber; + return &result; } + return NULL; +} + +MarkAdMark *cMarkAdAudio::Process(int FrameNumber, int FrameNumberNext) +{ + if ((!FrameNumber) || (!FrameNumberNext)) return NULL; + ResetMark(); + if (ChannelChange(macontext->Audio.Info.Channels,channels)) { char *buf=NULL; if (asprintf(&buf,"audio channel change from %i to %i (%i)", channels, - macontext->Audio.Info.Channels,lastiframe)!=-1) + macontext->Audio.Info.Channels, + (macontext->Audio.Info.Channels>2) ? FrameNumberNext : + framelast)!=-1) { if (macontext->Audio.Info.Channels>2) { - AddMark(MT_CHANNELSTART,lastiframe,buf); + AddMark(MT_CHANNELSTART,FrameNumberNext,buf); } else { - AddMark(MT_CHANNELSTOP,lastiframe,buf); + AddMark(MT_CHANNELSTOP,framelast,buf); } free(buf); } } channels=macontext->Audio.Info.Channels; + framelast=FrameNumberNext; return &mark; } diff --git a/command/audio.h b/command/audio.h index bb751ae..56901a2 100644 --- a/command/audio.h +++ b/command/audio.h @@ -10,39 +10,38 @@ #include "global.h" -#if 0 #include "audio_gain_analysis.h" -#endif class cMarkAdAudio { private: - int lastiframe; + //int framenumber; MarkAdContext *macontext; MarkAdMark mark; void ResetMark(); bool AddMark(int Type, int Position, const char *Comment); -#define CUT_VAL 10 -#define MIN_LOWVALS 3 - bool SilenceDetection(); - int lastiframe_silence; +#define CUT_VAL 4 +#define MIN_LOWVALS 25 + bool SilenceDetection(int FrameNumber); + int lastframe_silence; -#if 0 -#define ANALYZEFRAMES 1 - int lastiframe_gain; + int lastframe_gain; double lastgain; cMarkAdAudioGainAnalysis audiogain; - bool AnalyzeGain(); -#endif + bool AnalyzeGain(int FrameNumber); int channels; bool ChannelChange(int a, int b); + int framelast; + + MarkAdPos result; public: cMarkAdAudio(MarkAdContext *maContext); ~cMarkAdAudio(); - MarkAdMark *Process(int LastIFrame); + MarkAdMark *Process(int FrameNumber, int FrameNumberBefore); + MarkAdPos *Process2ndPass(int FrameNumber); void Clear(); }; diff --git a/command/audio_gain_analysis.cpp b/command/audio_gain_analysis.cpp new file mode 100644 index 0000000..b5abf9b --- /dev/null +++ b/command/audio_gain_analysis.cpp @@ -0,0 +1,423 @@ +/* + * 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 new file mode 100644 index 0000000..abed123 --- /dev/null +++ b/command/audio_gain_analysis.h @@ -0,0 +1,102 @@ +/* + * 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/decoder.cpp b/command/decoder.cpp index e600278..74919ad 100644 --- a/command/decoder.cpp +++ b/command/decoder.cpp @@ -27,6 +27,7 @@ cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) avcodec_register_all(); last_qscale_table=NULL; + skipframes=true; cpu_set_t cpumask; uint len = sizeof(cpumask); @@ -131,7 +132,7 @@ cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) ac3_context=NULL; } - AVCodec *video_codec=NULL; + video_codec=NULL; CodecID video_codecid; if (useH264) @@ -156,11 +157,12 @@ cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) video_context = avcodec_alloc_context(); if (video_context) { + video_context->get_buffer=our_get_buffer; + video_context->release_buffer=our_release_buffer; video_context->thread_count=cpucount; if (video_codec->capabilities & CODEC_CAP_TRUNCATED) video_context->flags|=CODEC_FLAG_TRUNCATED; // we do not send complete frames - video_context->flags|=CODEC_FLAG_GRAY; // only decode grayscale video_context->flags2|=CODEC_FLAG2_FAST; // really? video_context->skip_idct=AVDISCARD_ALL; @@ -168,14 +170,12 @@ cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) if (video_codecid==CODEC_ID_H264) { video_context->flags2|=CODEC_FLAG2_CHUNKS; // needed for H264! - video_context->skip_loop_filter=AVDISCARD_ALL; // skip deblocking av_log_set_level(AV_LOG_FATAL); // H264 decoder is very chatty } else { video_context->skip_frame=AVDISCARD_NONKEY; // just I-frames } - video_context->codec_id = video_codecid; video_context->codec_type = CODEC_TYPE_VIDEO; int ret=avcodec_open(video_context, video_codec); @@ -271,11 +271,12 @@ cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) { audiobuf=NULL; } - + initial_coded_picture=-1; } cMarkAdDecoder::~cMarkAdDecoder() { + Clear(); if (video_context) { avcodec_close(video_context); @@ -297,6 +298,58 @@ cMarkAdDecoder::~cMarkAdDecoder() if (audiobuf) free(audiobuf); } +bool cMarkAdDecoder::Clear() +{ + bool ret=true; + if (video_context) + { + avcodec_flush_buffers(video_context); + AVCodecContext *dest; + dest=avcodec_alloc_context(); + if (dest) + { + if (avcodec_copy_context(dest,video_context)!=0) ret=false; + } + else + { + ret=false; + } + avcodec_close(video_context); + av_free(video_context); + if (ret) + { + video_context=dest; + if (avcodec_open(video_context,video_codec)<0) ret=false; + } + } + if (ac3_context) avcodec_flush_buffers(ac3_context); + if (mp2_context) avcodec_flush_buffers(mp2_context); + return ret; +} + +int cMarkAdDecoder::our_get_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + int ret=avcodec_default_get_buffer(c,pic); + return ret; +} + +void cMarkAdDecoder::our_release_buffer(struct AVCodecContext *c, AVFrame *pic) +{ + avcodec_default_release_buffer(c, pic); +} + +bool cMarkAdDecoder::SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context) +{ + if ((!maContext) || (!Audio_Context)) return false; + + maContext->Audio.Info.SampleRate = Audio_Context->sample_rate; + maContext->Audio.Info.Channels = Audio_Context->channels; + maContext->Audio.Data.SampleBuf=audiobuf; + maContext->Audio.Data.SampleBufLen=audiobufsize; + maContext->Audio.Data.Valid=true; + return true; +} + bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen) { if (!mp2_context) return false; @@ -314,14 +367,13 @@ bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen audiobufsize=AVCODEC_MAX_AUDIO_FRAME_SIZE; int ret=false; - int16_t Taudiobuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; while (avpkt.size>0) { #if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) - int len=avcodec_decode_audio2(mp2_context,Taudiobuf,&audiobufsize, + int len=avcodec_decode_audio2(mp2_context,audiobuf,&audiobufsize, avpkt.data,avpkt.size); #else - int len=avcodec_decode_audio3(mp2_context,Taudiobuf,&audiobufsize,&avpkt); + int len=avcodec_decode_audio3(mp2_context,audiobuf,&audiobufsize,&avpkt); #endif if (len<0) { @@ -330,7 +382,6 @@ bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen } if (audiobufsize>0) { - memcpy(audiobuf,Taudiobuf,audiobufsize); SetAudioInfos(maContext,mp2_context); ret=true; avpkt.size-=len; @@ -340,18 +391,6 @@ bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen return ret; } -bool cMarkAdDecoder::SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context) -{ - if ((!maContext) || (!Audio_Context)) return false; - - maContext->Audio.Info.SampleRate = Audio_Context->sample_rate; - maContext->Audio.Info.Channels = Audio_Context->channels; - maContext->Audio.Data.SampleBuf=audiobuf; - maContext->Audio.Data.SampleBufLen=audiobufsize; - maContext->Audio.Data.Valid=true; - return true; -} - bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen) { if (!ac3_context) return false; @@ -368,15 +407,14 @@ bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen avpkt.size=eslen; int ret=false; - int16_t Taudiobuf[AVCODEC_MAX_AUDIO_FRAME_SIZE]; while (avpkt.size>0) { audiobufsize=AVCODEC_MAX_AUDIO_FRAME_SIZE; #if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) - int len=avcodec_decode_audio2(ac3_context,Taudiobuf,&audiobufsize, + int len=avcodec_decode_audio2(ac3_context,audiobuf,&audiobufsize, avpkt.data,avpkt.size); #else - int len=avcodec_decode_audio3(ac3_context,Taudiobuf,&audiobufsize,&avpkt); + int len=avcodec_decode_audio3(ac3_context,audiobuf,&audiobufsize,&avpkt); #endif if (len<0) { @@ -385,7 +423,6 @@ bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen } if (audiobufsize>0) { - memcpy(audiobuf,Taudiobuf,audiobufsize); SetAudioInfos(maContext,ac3_context); ret=true; avpkt.size-=len; @@ -395,12 +432,6 @@ bool cMarkAdDecoder::DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen return ret; } -void cMarkAdDecoder::PAR2DAR(AVRational a, AVRational *erg) -{ - av_reduce(&erg->num,&erg->den,video_context->width*a.num, - video_context->height*a.den,1024*1024); -} - bool cMarkAdDecoder::SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Video_Context, AVFrame *Video_Frame) { if ((!maContext) || (!Video_Context) || (!Video_Frame)) return false; @@ -415,34 +446,35 @@ bool cMarkAdDecoder::SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Vide } maContext->Video.Info.Height=Video_Context->height; maContext->Video.Info.Width=Video_Context->width; - - AVRational dar; - PAR2DAR(Video_Context->sample_aspect_ratio,&dar); - - maContext->Video.Info.AspectRatio.Num=dar.num; - maContext->Video.Info.AspectRatio.Den=dar.den; - return true; } bool cMarkAdDecoder::DecodeVideo(MarkAdContext *maContext,uchar *pkt, int plen) { if (!video_context) return false; + if (!video_frame) return false; maContext->Video.Data.Valid=false; if ((video_context->codec_id==CODEC_ID_H264) && (!video_context->skip_frame)) { - if (maContext->Video.Info.Pict_Type) + // with H264 we cannot set skip_frame just to NONKEY, is depends on Interlaced... + if (maContext->Video.Info.Height) { if (maContext->Video.Info.Interlaced) { video_context->skip_frame=AVDISCARD_BIDIR; // just P/I-frames + video_context->skip_loop_filter=AVDISCARD_BIDIR; } else { video_context->skip_frame=AVDISCARD_NONKEY; // just I-frames + video_context->skip_loop_filter=AVDISCARD_NONKEY; } } + else + { + return false; + } } AVPacket avpkt; @@ -481,10 +513,17 @@ bool cMarkAdDecoder::DecodeVideo(MarkAdContext *maContext,uchar *pkt, int plen) } if (video_frame_ready) { - if (last_qscale_table!=video_frame->qscale_table) + if (video_context->skip_frame!=AVDISCARD_DEFAULT) + { + if (last_qscale_table!=video_frame->qscale_table) + { + if (SetVideoInfos(maContext,video_context,video_frame)) ret=true; + last_qscale_table=video_frame->qscale_table; + } + } + else { if (SetVideoInfos(maContext,video_context,video_frame)) ret=true; - last_qscale_table=video_frame->qscale_table; } } } diff --git a/command/decoder.h b/command/decoder.h index 12b398e..12773fc 100644 --- a/command/decoder.h +++ b/command/decoder.h @@ -31,25 +31,30 @@ extern "C" class cMarkAdDecoder { private: + bool skipframes; int16_t *audiobuf; int audiobufsize; + AVCodec *video_codec; AVCodecContext *ac3_context; AVCodecContext *mp2_context; AVCodecContext *video_context; AVFrame *video_frame; int8_t *last_qscale_table; + int initial_coded_picture; + static int our_get_buffer(struct AVCodecContext *c, AVFrame *pic); + static void our_release_buffer(struct AVCodecContext *c, AVFrame *pic); bool SetAudioInfos(MarkAdContext *maContext, AVCodecContext *Audio_Context); - void PAR2DAR(AVRational a, AVRational *erg); bool SetVideoInfos(MarkAdContext *maContext,AVCodecContext *Video_Context, AVFrame *Video_Frame); public: bool DecodeVideo(MarkAdContext *maContext, uchar *pkt, int plen); bool DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen); bool DecodeAC3(MarkAdContext *maContext, uchar *espkt, int eslen); + bool Clear(); cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3); ~cMarkAdDecoder(); }; diff --git a/command/demux.cpp b/command/demux.cpp index 23bd78e..91a5a54 100644 --- a/command/demux.cpp +++ b/command/demux.cpp @@ -7,6 +7,8 @@ #include "demux.h" +#include <string.h> + cMarkAdDemux::cMarkAdDemux() { ts2pkt=NULL; @@ -39,17 +41,15 @@ void cMarkAdDemux::Clear() skip=0; } -void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen) +void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) { - if ((!Pkt) || (!PktLen)) return; - *Pkt=NULL; - *PktLen=0; + if (!Pkt) return; if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) { if (!pes2audioes) pes2audioes=new cMarkAdPES2ES("PES2ES audio"); if (!pes2audioes) return; - pes2audioes->Process(Pid,Data,Count,Pkt,PktLen); + pes2audioes->Process(Pid,Data,Count,Pkt); } if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) @@ -66,20 +66,48 @@ void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt } } if (!pes2videoes) return; - pes2videoes->Process(Pid,Data,Count,Pkt,PktLen); + pes2videoes->Process(Pid,Data,Count,Pkt); } return; } -void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen) +void cMarkAdDemux::GetVideoPTS(uchar *Data, int Count, unsigned int *Timestamp) { - if ((!Pkt) || (!PktLen)) return; - *Pkt=NULL; - *PktLen=0; + if (!Data) return; + if (Count<=0) return; + if (!Timestamp) return; + struct PESHDR *peshdr=(struct PESHDR *) Data; + + if ((peshdr->Sync1!=0) && (peshdr->Sync2!=0) && (peshdr->Sync3!=1)) return; + if ((peshdr->StreamID & 0xF0)!=0xE0) return; + + struct PESHDROPT *peshdropt=(struct PESHDROPT *) &Data[sizeof(struct PESHDR)]; + if (peshdropt->MarkerBits!=0x2) return; - uchar *pkt; - int pktlen; + if (peshdropt->PTSDTS<2) return; + + struct PESHDROPTPTS *peshdroptpts=(struct PESHDROPTPTS *) &Data[sizeof(struct PESHDR)+ + sizeof(struct PESHDROPT)]; + + if (peshdroptpts->Marker1 && peshdroptpts->Marker2 && + peshdroptpts->Marker3) +{ + unsigned int pts=0; + pts|=((peshdroptpts->PTS29_15_H<<7|peshdroptpts->PTS29_15_L)<<15); + pts|=(peshdroptpts->PTS14_0_H<<7|peshdroptpts->PTS14_0_L); + pts|=(peshdroptpts->PTS32_30<<30); + *Timestamp=pts; + } + return; +} + +void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) +{ + if (!Pkt) return; + + MarkAdPacket pkt; + memset(&pkt,0,sizeof(pkt)); if (!ts2pkt) { @@ -94,31 +122,31 @@ void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, } if (!ts2pkt) return; - ts2pkt->Process(Pid,Data,Count,&pkt,&pktlen); + if (!ts2pkt->Process(Pid,Data,Count,&pkt)) + { + if (pes2audioes) pes2audioes->Clear(); + return; + } if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) { if (!pes2audioes) pes2audioes=new cMarkAdPES2ES("PES2ES audio"); if (!pes2audioes) return; - pes2audioes->Process(Pid,pkt,pktlen,Pkt,PktLen); + pes2audioes->Process(Pid,pkt.Data,pkt.Length,Pkt); } if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) { - if ((pkt) && ((pkt[3] & 0xF0)==0xE0) && (pkt[4]!=0) && (pkt[5]!=0)) + GetVideoPTS(pkt.Data,pkt.Length,&Pkt->Timestamp); + if ((pkt.Data) && ((pkt.Data[3] & 0xF0)==0xE0) && (pkt.Data[4]!=0) && (pkt.Data[5]!=0)) { - ts2pkt->InjectVideoPES(pkt,pktlen); - pkt=NULL; - pktlen=0; + ts2pkt->InjectVideoPES(pkt.Data,pkt.Length); + pkt.Data=NULL; + pkt.Length=0; } + Pkt->Data=pkt.Data; + Pkt->Length=pkt.Length; } - - if ((pkt) && (!*Pkt)) - { - *Pkt=pkt; - *PktLen=pktlen; - } - return; } @@ -171,32 +199,29 @@ int cMarkAdDemux::GetMinNeeded(MarkAdPid Pid, uchar *Data, int Count, bool *Offc } } -int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen, bool *Offcnt) +int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *Pkt) { - if ((!Data) && (!Count) && (!Pkt) || (!PktLen)) return -1; - - *Pkt=NULL; - *PktLen=0; + if ((!Data) && (!Count) && (!Pkt)) return -1; uchar *in=NULL; int inlen=0; int retval=0; - if (Offcnt) *Offcnt=false; - if (!pause) { + memset(Pkt,0,sizeof(MarkAdPacket)); + if (!min_needed) { if (skip) { int t_skip=skip; skip=0; - if (Offcnt) *Offcnt=true; + Pkt->Offcnt=true; return t_skip; } - int t_min_needed=GetMinNeeded(Pid,Data,Count,Offcnt); + int t_min_needed=GetMinNeeded(Pid,Data,Count,&Pkt->Offcnt); if (t_min_needed==0) { return -1; @@ -236,14 +261,14 @@ int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, in if (Pid.Num>=0) { - ProcessTS(Pid, in, inlen, Pkt, PktLen); + ProcessTS(Pid, in, inlen, Pkt); } else { - ProcessVDR(Pid, in, inlen, Pkt, PktLen); + ProcessVDR(Pid, in, inlen, Pkt); } - if (*Pkt) + if (Pkt->Data) { if (!pause_retval) pause_retval=retval; pause=true; @@ -260,6 +285,6 @@ int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, in pause=false; } - if (Offcnt) *Offcnt=true; + Pkt->Offcnt=true; return retval; } diff --git a/command/demux.h b/command/demux.h index f97fe08..489528b 100644 --- a/command/demux.h +++ b/command/demux.h @@ -14,13 +14,13 @@ #define PESHDRSIZE 6 -#include "global.h" #include "queue.h" #include "ts2pkt.h" #include "pes2es.h" class cMarkAdDemux { + private: cMarkAdTS2Pkt *ts2pkt; cMarkAdPES2ES *pes2audioes; @@ -32,14 +32,15 @@ private: int min_needed; int skip; + void GetVideoPTS(uchar *Data, int Count, unsigned int *Timestamp); int GetMinNeeded(MarkAdPid Pid, uchar *Data, int Count, bool *Offcnt); - void ProcessTS(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen); - void ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen); + void ProcessTS(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); + void ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); public: cMarkAdDemux(); ~cMarkAdDemux(); void Clear(); - int Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen, bool *Offcnt); + int Process(MarkAdPid Pid, uchar *Data, int Count, MarkAdPacket *pkt); }; #endif diff --git a/command/global.h b/command/global.h index 8508f30..6bc6cb4 100644 --- a/command/global.h +++ b/command/global.h @@ -51,6 +51,14 @@ typedef unsigned char uchar; #define MT_MOVED 0xE0 #define MT_ALL 0xFF +typedef struct MarkAdPos +{ + int FrameNumberBefore; + int FrameNumberAfter; + char *CommentBefore; + char *CommentAfter; +} MarkAdPos; + typedef struct MarkAdMark { char Type; diff --git a/command/markad-standalone.cpp b/command/markad-standalone.cpp index 3789290..51e0696 100644 --- a/command/markad-standalone.cpp +++ b/command/markad-standalone.cpp @@ -262,6 +262,14 @@ void cMarkAdStandalone::CheckFirstMark() clMark *second=first->Next(); if (!second) return; + // if first and second mark is ASPECT/BORDER + // wait till its clear, if we use ASPECT + // or BORDER + int ft=first->type & 0xF0; + int st=second->type & 0xF0; + if (((ft==MT_BORDERCHANGE) && (st==MT_ASPECTCHANGE)) || + ((st==MT_BORDERCHANGE) && (ft==MT_ASPECTCHANGE))) return; + if ((second->type & 0xF)==MT_START) { bool delsec=false; @@ -439,6 +447,8 @@ void cMarkAdStandalone::AddMark(MarkAdMark *Mark) { case MT_START: marks.DelAll(); + // calculate new stop position based on new start + iStop=-(Mark->Position+(macontext.Info.Length*macontext.Video.Info.FramesPerSecond)); iStart=0; break; case MT_STOP: @@ -640,7 +650,7 @@ void cMarkAdStandalone::SaveFrame(int frame) fclose(pFile); } -void cMarkAdStandalone::CheckIndex(const char *Directory) +void cMarkAdStandalone::CheckIndex() { // Here we check if the index is more // advanced than our framecounter. @@ -662,7 +672,7 @@ void cMarkAdStandalone::CheckIndex(const char *Directory) if (maxframes<(framecnt+200)) { if ((difftime(time(NULL),statbuf.st_mtime))>=10) return; // "old" file - marks.Save(Directory,macontext.Video.Info.FramesPerSecond,isTS); + 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; @@ -683,25 +693,338 @@ void cMarkAdStandalone::CheckIndex(const char *Directory) while (notenough); } -bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) +void cMarkAdStandalone::ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *NewPos) { - if (!Directory) return false; + if (!NewPos) return; + if (!Mark1) return; + if (!*Mark1) return; + bool save=false; + + if ((*Mark1)->position!=NewPos->FrameNumberBefore) + { + marks.Del(*Mark1); + *Mark1=marks.Add(MT_COMMON,NewPos->FrameNumberBefore,NewPos->CommentBefore); + save=true; + } + if (NewPos->CommentBefore) isyslog("%s",NewPos->CommentBefore); + + if (Mark2 && (*Mark2) && (*Mark2)->position!=NewPos->FrameNumberAfter) + { + marks.Del(*Mark2); + *Mark2=marks.Add(MT_COMMON,NewPos->FrameNumberAfter,NewPos->CommentAfter); + if (NewPos->CommentAfter) isyslog("%s",NewPos->CommentAfter); + save=true; + } + if (save) marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS,true); +} + +bool cMarkAdStandalone::ProcessFile2ndPass(clMark **Mark1, clMark **Mark2,int Number, off_t Offset, + int Frame, int Frames) +{ + if (!directory) return false; if (!Number) return false; + if (!Frames) return false; + if (!decoder) return false; + if (!Mark1) return false; + if (!*Mark1) return false; + + int pn; // process number 1=start mark, 2=before mark, 3=after mark + if (Mark1 && Mark2) + { + if (!(*Mark1) || !(*Mark2)) return false; + if (*Mark1==*Mark2) pn=1; + if (*Mark1!=*Mark2) pn=3; + } + else + { + pn=2; + } + + if (!Reset(false)) + { + // reset all, but marks + esyslog("failed resetting state"); + return false; + } + iframe=Frame; + int actframe=Frame; + int framecounter=0; + + MarkAdPos *pos=NULL; + + while (framecounter<Frames) + { + if (abort) return false; + + //const int datalen=8272; + const int datalen=319976; + uchar data[datalen]; + + char *fbuf; + if (isTS) + { + if (asprintf(&fbuf,"%s/%05i.ts",directory,Number)==-1) return false; + } + else + { + if (asprintf(&fbuf,"%s/%03i.vdr",directory,Number)==-1) return false; + } + + int f=open(fbuf,O_RDONLY); + free(fbuf); + if (f==-1) return false; + + int dataread; + if (pn==1) + { + dsyslog("processing file %05i (start mark)",Number); + } + else + { + dsyslog("processing file %05i %s",Number,(pn==3) ? "(after mark)" : "(before mark)"); + } + + if (lseek(f,Offset,SEEK_SET)!=Offset) + { + close(f); + 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)) + { + MarkAdPacket pkt; + + uchar *tspkt = data; + int tslen = dataread; + + while (tslen>0) + { + int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&pkt); + if (len<0) + { + esyslog("error demuxing video"); + abort=true; + break; + } + else + { + if (pkt.Data) + { + bool dRes=false; + if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) + { + actframe++; + framecnt2++; + + if (macontext.Video.Info.Pict_Type==MA_I_TYPE) + { + lastiframe=iframe; + lastiframetime=iframetime; + if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) + { + iframe=actframe; + } + else + { + iframe=actframe-1; + } + iframetime=pkt.Timestamp; + dRes=true; + } + } + if (pn>1) 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); + framecounter++; + } + if ((pos) && (pn==3)) + { + // found overlap + ChangeMarks(Mark1,Mark2,pos); + close(f); + return true; + } + } + } + tspkt+=len; + tslen-=len; + if (!pkt.Offcnt) + { + offset_add+=len; + } + else + { + offset+=offset_add; + offset+=len; + offset_add=0; + } + } + } + } + + if ((mp2_demux) && (audio) && (pn!=3)) + { + MarkAdPacket pkt; + + uchar *tspkt = data; + int tslen = dataread; + + while (tslen>0) + { + int len=mp2_demux->Process(macontext.Info.APid,tspkt,tslen,&pkt); + if (len<0) + { + esyslog("error demuxing mp2-audio"); + break; + } + else + { + if (pkt.Data) + { + if (pkt.Timestamp) audiotime=pkt.Timestamp; + + if (abs(audiotime-lastiframetime)<20000) + { + if (decoder->DecodeMP2(&macontext,pkt.Data,pkt.Length)) + { + pos=audio->Process2ndPass(iframe); + if (pos) ChangeMarks(Mark1,NULL,pos); + } + } + } + tspkt+=len; + tslen-=len; + } + } + } + + if (abort) + { + close(f); + return false; + } - CheckIndex(Directory); + if (framecounter>Frames) + { + break; + } + + } + close(f); + Number++; + Offset=0; + } + return true; +} + +void cMarkAdStandalone::Process2ndPass() +{ + if (abort) return; + if (duplicate) return; + if (!decoder) return; + + if (!macontext.Video.Info.FramesPerSecond) + macontext.Video.Info.FramesPerSecond=25; + + if (!marks.Count()) + { + marks.Load(directory,macontext.Video.Info.FramesPerSecond,isTS); + } + + isyslog("2nd pass"); + + clMark *p1=NULL,*p2=NULL; + + p1=marks.GetFirst(); + if (p1->position>0) + { + 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); + } + } + + if (marks.Count()<4) return; // here we cannot do much + + p1=p1->Next(); + if (p1) p2=p1->Next(); + + while ((p1) && (p2)) + { + off_t offset; + int number,frame,iframes; + int frange=macontext.Video.Info.FramesPerSecond*120; // 40s + 80s + + 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,frange,&number,&offset,&frame,&iframes)) + { + if (!ProcessFile2ndPass(&p1,&p2,number,offset,frame,iframes)) break; + } + } + + p1=p2->Next(); + if (p1) + { + p2=p1->Next(); + } + else + { + p2=NULL; + } + } +} + +char *cMarkAdStandalone::Timestamp2HMS(unsigned int Timestamp) +{ + double val=(double) Timestamp/90000; + int h,m; + double s_ns; + h=(int) val/3600; + m=(int) (val-(3600*h))/60; + s_ns=val-(h*3600+m*60); + static char buf[20]; + sprintf(buf,"%02d:%02d:%06.4f",h,m,s_ns); + return (char *) &buf; +} + +bool cMarkAdStandalone::ProcessFile(int Number) +{ + if (!directory) return false; + if (!Number) return false; + + CheckIndex(); if (abort) return false; - const int datalen=385024; + //const int datalen=8272; + const int datalen=319976; uchar data[datalen]; char *fbuf; if (isTS) { - if (asprintf(&fbuf,"%s/%05i.ts",Directory,Number)==-1) return false; + if (asprintf(&fbuf,"%s/%05i.ts",directory,Number)==-1) return false; } else { - if (asprintf(&fbuf,"%s/%03i.vdr",Directory,Number)==-1) return false; + if (asprintf(&fbuf,"%s/%03i.vdr",directory,Number)==-1) return false; } int f=open(fbuf,O_RDONLY); @@ -720,16 +1043,14 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) if ((video_demux) && (video) && (streaminfo)) { - uchar *pkt; - int pktlen; - bool offcnt=false; + MarkAdPacket pkt; uchar *tspkt = data; int tslen = dataread; while (tslen>0) { - int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&pkt,&pktlen,&offcnt); + int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&pkt); if (len<0) { esyslog("error demuxing video"); @@ -738,50 +1059,75 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) } else { - if (pkt) + if (pkt.Data) { bool dRes=false; - if (streaminfo->FindVideoInfos(&macontext,pkt,pktlen)) + if (streaminfo->FindVideoInfos(&macontext,pkt.Data,pkt.Length)) { - if (!framecnt) + if ((macontext.Video.Info.Height) && (!noticeHEADER)) { isyslog("%s %i%c",(macontext.Video.Info.Height>576) ? "HDTV" : "SDTV", macontext.Video.Info.Height, macontext.Video.Info.Interlaced ? 'i' : 'p'); + noticeHEADER=true; + } + + if (!framecnt) + { AddStartMark(); + nextPictType=MA_I_TYPE; } + if (bGenIndex) { - marks.WriteIndex(Directory,isTS,offset, - macontext.Video.Info.Pict_Type,Number); + if ((macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H262) || + ((macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) && + (!macontext.Video.Info.Interlaced))) + { + nextPictType=macontext.Video.Info.Pict_Type; + } + marks.WriteIndex(directory,isTS,offset,nextPictType,Number); } + nextPictType=macontext.Video.Info.Pict_Type; framecnt++; if (macontext.Video.Info.Pict_Type==MA_I_TYPE) { lastiframe=iframe; + lastiframetime=iframetime; CheckStartStop(lastiframe); if (lastiframe>chkLEFT) CheckInfoAspectRatio(); - iframe=framecnt-1; + + if (macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) + { + iframe=framecnt; + } + else + { + iframe=framecnt-1; + } + iframetime=pkt.Timestamp; dRes=true; } } - if ((decoder) && (bDecodeVideo)) dRes=decoder->DecodeVideo(&macontext,pkt,pktlen); + if ((decoder) && (bDecodeVideo)) dRes=decoder->DecodeVideo(&macontext,pkt.Data,pkt.Length); if (dRes) { if ((framecnt-iframe)<=3) { - mark=video->Process(lastiframe); - AddMark(mark); + //printf("VID %5i %s %x\n",lastiframe,Timestamp2HMS(lastiframetime),lastiframetime); + + mark=video->Process(lastiframe,iframe); + if (mark) AddMark(mark); //SaveFrame(lastiframe); // TODO: JUST FOR DEBUGGING! } } } tspkt+=len; tslen-=len; - if (!offcnt) + if (!pkt.Offcnt) { offset_add+=len; } @@ -797,15 +1143,14 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) if ((ac3_demux) && (streaminfo) && (audio)) { - uchar *pkt; - int pktlen; + MarkAdPacket pkt; uchar *tspkt = data; int tslen = dataread; while (tslen>0) { - int len=ac3_demux->Process(macontext.Info.DPid,tspkt,tslen,&pkt,&pktlen,NULL); + int len=ac3_demux->Process(macontext.Info.DPid,tspkt,tslen,&pkt); if (len<0) { esyslog("error demuxing ac3-audio"); @@ -813,59 +1158,23 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) } else { - if (pkt) + if (pkt.Data) { - if (streaminfo->FindAC3AudioInfos(&macontext,pkt,pktlen)) + if (streaminfo->FindAC3AudioInfos(&macontext,pkt.Data,pkt.Length)) { if ((!isTS) && (!noticeVDR_AC3)) { dsyslog("found AC3"); - if (mp2_demux) - { - delete mp2_demux; - mp2_demux=NULL; - } noticeVDR_AC3=true; } - mark=audio->Process(lastiframe); - AddMark(mark); - } - } - tspkt+=len; - tslen-=len; - } - } - } + if (pkt.Timestamp) audiotime=pkt.Timestamp; - if ((mp2_demux) && (decoder) && (audio) && (bDecodeAudio)) - { - uchar *pkt; - int pktlen; - - uchar *tspkt = data; - int tslen = dataread; - - while (tslen>0) - { - int len=mp2_demux->Process(macontext.Info.APid,tspkt,tslen,&pkt,&pktlen,NULL); - if (len<0) - { - esyslog("error demuxing mp2-audio"); - break; - } - else - { - if (pkt) - { - if (decoder->DecodeMP2(&macontext,pkt,pktlen)) - { - if ((!isTS) && (!noticeVDR_MP2)) + if (abs(audiotime-lastiframetime)<20000) { - dsyslog("found MP2"); - noticeVDR_MP2=true; + //printf("AC3 %5i %s %x\n",lastiframe,Timestamp2HMS(audiotime),audiotime); + mark=audio->Process(lastiframe,iframe); + if (mark) AddMark(mark); } - mark=audio->Process(lastiframe); - AddMark(mark); } } tspkt+=len; @@ -880,7 +1189,7 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) return true; } - CheckIndex(Directory); + CheckIndex(); if (abort) { if (f!=-1) close(f); @@ -891,55 +1200,60 @@ bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) return true; } -void cMarkAdStandalone::Reset() +bool cMarkAdStandalone::Reset(bool FirstPass) { + bool ret=true; reprocess=false; - framecnt=0; + if (FirstPass) framecnt=0; lastiframe=0; iframe=0; + + lastiframetime=0; + iframetime=0; + audiotime=0; + + fastexit=false; + iStart=0; iStop=0; chkLEFT=0; chkRIGHT=0; - marksAligned=false; - marks.DelAll(); - marks.CloseIndex(directory,isTS); + if (FirstPass) + { + marksAligned=false; + marks.DelAll(); + marks.CloseIndex(directory,isTS); + } - memset(&macontext.Video.Info,0,sizeof(macontext.Video.Info)); - memset(&macontext.Audio.Info,0,sizeof(macontext.Audio.Info)); + macontext.Video.Info.Pict_Type=0; + macontext.Video.Info.AspectRatio.Den=0; + macontext.Video.Info.AspectRatio.Num=0; + macontext.Audio.Info.Channels=0; if (decoder) { - delete decoder; - decoder = new cMarkAdDecoder(macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264, - macontext.Info.APid.Num!=0,macontext.Info.DPid.Num!=0); + 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 (video) video->Clear(); if (audio) audio->Clear(); + return ret; } -void cMarkAdStandalone::Process(const char *Directory) +void cMarkAdStandalone::Process() { if (abort) return; - struct timeval tv1,tv2; - struct timezone tz; - - gettimeofday(&tv1,&tz); - - if (bBackupMarks) marks.Backup(Directory,isTS); + if (bBackupMarks) marks.Backup(directory,isTS); for (int i=1; i<=MaxFiles; i++) { if (abort) break; - if (!ProcessFile(Directory,i)) + if (!ProcessFile(i)) { break; } @@ -954,7 +1268,7 @@ void cMarkAdStandalone::Process(const char *Directory) if (!abort) { - if ((lastiframe) && (!fastexit) && (iStop<0)) + if ((lastiframe) && (!fastexit) && ((iStop<0) || (!tStart))) { char *buf; MarkAdMark tempmark; @@ -972,21 +1286,11 @@ void cMarkAdStandalone::Process(const char *Directory) CheckLastMark(); CheckLogoMarks(); - gettimeofday(&tv2,&tz); - time_t sec; - suseconds_t usec; - sec=tv2.tv_sec-tv1.tv_sec; - usec=tv2.tv_usec-tv1.tv_usec; - if (usec<0) - { - usec+=1000000; - sec--; - } - marks.CloseIndex(Directory,isTS); - if (marks.Save(Directory,macontext.Video.Info.FramesPerSecond,isTS)) + marks.CloseIndex(directory,isTS); + if (marks.Save(directory,macontext.Video.Info.FramesPerSecond,isTS)) { int iIndexError=false; - if (marks.CheckIndex(Directory,isTS,bGenIndex ? framecnt : 0,&iIndexError)) + if (marks.CheckIndex(directory,isTS,bGenIndex ? framecnt : 0,&iIndexError)) { if (iIndexError) { @@ -996,11 +1300,9 @@ void cMarkAdStandalone::Process(const char *Directory) { case IERR_NOTFOUND: isyslog("no index found"); - break; case IERR_TOOSHORT: isyslog("index too short"); - break; default: isyslog("index contains errors"); @@ -1026,36 +1328,20 @@ void cMarkAdStandalone::Process(const char *Directory) } } } - if (bGenIndex) marks.RemoveGeneratedIndex(Directory,isTS); - - SaveInfo(Directory); - - double etime,ftime=0,ptime=0; - etime=sec+((double) usec/1000000)-waittime; - if (etime>0) ftime=framecnt/etime; - if (macontext.Video.Info.FramesPerSecond>0) - ptime=ftime/macontext.Video.Info.FramesPerSecond; - isyslog("processed time %.2fs, %i frames, %.1f fps, %.1f pps", - etime,framecnt,ftime,ptime); + if (bGenIndex) marks.RemoveGeneratedIndex(directory,isTS); + if ((!bIgnoreAudioInfo) && (!bIgnoreVideoInfo)) SaveInfo(); } } -bool cMarkAdStandalone::SaveInfo(const char *Directory) +bool cMarkAdStandalone::SaveInfo() { if ((!setVideo43) && (!setVideo169) && (!setAudio20) && (!setAudio51)) return true; char *src,*dst; - if (isTS) - { - if (asprintf(&src,"%s/info",Directory)==-1) return false; - } - else - { - if (asprintf(&src,"%s/info.vdr",Directory)==-1) return false; - } + if (asprintf(&src,"%s/info%s",directory,isTS ? "" : ".vdr")==-1) return false; - if (asprintf(&dst,"%s/info.bak",Directory)==-1) + if (asprintf(&dst,"%s/info.bak",directory)==-1) { free(src); return false; @@ -1082,6 +1368,8 @@ bool cMarkAdStandalone::SaveInfo(const char *Directory) bool setAudio20_done=false; bool setAudio51_done=false; + char lang[4]=""; + bool err=false; while (getline(&line,&length,r)!=-1) { @@ -1089,11 +1377,10 @@ bool cMarkAdStandalone::SaveInfo(const char *Directory) { int stream=0,type=0; char descr[256]=""; - char lang[4]=""; + int result=sscanf(line,"%*c %i %i %3c %250c",&stream,&type,(char *) &lang, (char *) &descr); if ((result!=0) && (result!=EOF)) { - switch (stream) { case 1: @@ -1163,21 +1450,23 @@ bool cMarkAdStandalone::SaveInfo(const char *Directory) if (line) free(line); line=lline; + if (lang[0]==0) strcpy(lang,"und"); + if ((setVideo43) && (!setVideo43_done) && (!err)) { - if (fprintf(w,"%s","X 1 01 und 4:3\n")<=0) err=true; + if (fprintf(w,"X 1 01 %s 4:3\n",lang)<=0) err=true; } if ((setVideo169) && (!setVideo169_done) && (!err)) { - if (fprintf(w,"%s","X 1 03 und 16:9\n")<=0) err=true; + if (fprintf(w,"X 1 03 %s 16:9\n",lang)<=0) err=true; } if ((setAudio20) && (!setAudio20_done) && (!err)) { - if (fprintf(w,"%s","X 2 05 und Dolby Digital 2.0\n")<=0) err=true; + if (fprintf(w,"X 2 05 %s Dolby Digital 2.0\n",lang)<=0) err=true; } if ((setAudio51) && (!setAudio51_done) && (!err)) { - if (fprintf(w,"%s","X 2 05 und Dolby Digital 5.1\n")<=0) err=true; + if (fprintf(w,"X 2 05 %s Dolby Digital 5.1\n",lang)<=0) err=true; } if (line) { @@ -1199,7 +1488,7 @@ bool cMarkAdStandalone::SaveInfo(const char *Directory) { // if we are root, set fileowner to owner of 001.vdr/00001.ts file char *spath=NULL; - if (asprintf(&spath,"%s/%s",Directory,isTS ? "00001.ts" : "001.vdr")!=-1) + if (asprintf(&spath,"%s/%s",directory,isTS ? "00001.ts" : "001.vdr")!=-1) { struct stat statbuf; if (!stat(spath,&statbuf)) @@ -1215,32 +1504,25 @@ bool cMarkAdStandalone::SaveInfo(const char *Directory) return (err==false); } -bool cMarkAdStandalone::LoadInfo(const char *Directory) +bool cMarkAdStandalone::LoadInfo() { char *buf; - if (isTS) - { - if (asprintf(&buf,"%s/info",Directory)==-1) return false; - } - else - { - if (asprintf(&buf,"%s/info.vdr",Directory)==-1) return false; - } + if (asprintf(&buf,"%s/info%s",directory,isTS ? "" : ".vdr")==-1) return false; FILE *f; f=fopen(buf,"r"); free(buf); if (!f) return false; - const char *timestr=strrchr(Directory,'/'); + const char *timestr=strrchr(directory,'/'); if (timestr) { timestr++; - if (!isdigit(*timestr)) timestr=Directory; + if (!isdigit(*timestr)) timestr=directory; } else { - timestr=Directory; + timestr=directory; } time_t now = time(NULL); @@ -1253,6 +1535,7 @@ bool cMarkAdStandalone::LoadInfo(const char *Directory) t.tm_year-=1900; t.tm_mon--; t.tm_sec=0; + t.tm_isdst=-1; rStart=mktime(&t); } @@ -1373,7 +1656,7 @@ bool cMarkAdStandalone::LoadInfo(const char *Directory) tStart=start-rStart; if (tStart<0) { - isyslog("%s","start of broadcast truncated"); + isyslog("broadcast start truncated by %ih %im",-tStart/3600,-tStart/60); macontext.Info.Length+=tStart; tStart=0; } @@ -1394,13 +1677,13 @@ bool cMarkAdStandalone::LoadInfo(const char *Directory) } } -bool cMarkAdStandalone::CheckTS(const char *Directory) +bool cMarkAdStandalone::CheckTS() { MaxFiles=0; isTS=false; - if (!Directory) return false; + if (!directory) return false; char *buf; - if (asprintf(&buf,"%s/00001.ts",Directory)==-1) return false; + if (asprintf(&buf,"%s/00001.ts",directory)==-1) return false; struct stat statbuf; if (stat(buf,&statbuf)==-1) { @@ -1410,7 +1693,7 @@ bool cMarkAdStandalone::CheckTS(const char *Directory) return false; } free(buf); - if (asprintf(&buf,"%s/001.vdr",Directory)==-1) return false; + if (asprintf(&buf,"%s/001.vdr",directory)==-1) return false; if (stat(buf,&statbuf)==-1) { free(buf); @@ -1429,10 +1712,10 @@ bool cMarkAdStandalone::CheckTS(const char *Directory) return true; } -bool cMarkAdStandalone::CheckVDRHD(const char *Directory) +bool cMarkAdStandalone::CheckVDRHD() { char *buf; - if (asprintf(&buf,"%s/001.vdr",Directory)==-1) return false; + if (asprintf(&buf,"%s/001.vdr",directory)==-1) return false; int fd=open(buf,O_RDONLY); free(buf); @@ -1460,10 +1743,10 @@ bool cMarkAdStandalone::CheckVDRHD(const char *Directory) return false; } -bool cMarkAdStandalone::CheckPATPMT(const char *Directory) +bool cMarkAdStandalone::CheckPATPMT() { char *buf; - if (asprintf(&buf,"%s/00001.ts",Directory)==-1) return false; + if (asprintf(&buf,"%s/00001.ts",directory)==-1) return false; int fd=open(buf,O_RDONLY); free(buf); @@ -1596,10 +1879,10 @@ bool cMarkAdStandalone::RegenerateIndex() return true; } -bool cMarkAdStandalone::CreatePidfile(const char *Directory) +bool cMarkAdStandalone::CreatePidfile() { char *buf=NULL; - if (asprintf(&buf,"%s/markad.pid",Directory)==-1) return false; + if (asprintf(&buf,"%s/markad.pid",directory)==-1) return false; // check for other running markad process FILE *oldpid=fopen(buf,"r"); @@ -1633,7 +1916,7 @@ bool cMarkAdStandalone::CreatePidfile(const char *Directory) { // if we are root, set fileowner to owner of directory struct stat statbuf; - if (!stat(Directory,&statbuf)) + if (!stat(directory,&statbuf)) { if (chown(buf,statbuf.st_uid, statbuf.st_gid)) {}; } @@ -1647,10 +1930,13 @@ bool cMarkAdStandalone::CreatePidfile(const char *Directory) return true; } -void cMarkAdStandalone::RemovePidfile(const char *Directory) +void cMarkAdStandalone::RemovePidfile() { + if (!directory) return; + if (duplicate) return; + char *buf; - if (asprintf(&buf,"%s/markad.pid",Directory)!=-1) + if (asprintf(&buf,"%s/markad.pid",directory)!=-1) { unlink(buf); free(buf); @@ -1662,7 +1948,7 @@ const char cMarkAdStandalone::frametypes[8]={'?','I','P','B','D','S','s','b'}; cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, int LogoExtraction, int LogoWidth, int LogoHeight, bool DecodeVideo, bool DecodeAudio, int IgnoreInfo, - const char *LogoDir, const char *MarkFileName, bool ASD, + const char *LogoDir, const char *MarkFileName, bool noPid, bool OSD, const char *SVDRPHost, int SVDRPPort, bool Before, bool GenIndex) { @@ -1690,6 +1976,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in noticeVDR_MP2=false; noticeVDR_AC3=false; + noticeHEADER=false; sleepcnt=0; waittime=0; @@ -1702,7 +1989,6 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in macontext.Options.LogoExtraction=LogoExtraction; macontext.Options.LogoWidth=LogoWidth; macontext.Options.LogoHeight=LogoHeight; - macontext.Audio.Options.AudioSilenceDetection=ASD; bDecodeVideo=DecodeVideo; bDecodeAudio=DecodeAudio; @@ -1774,37 +2060,31 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in if (!noPid) { - CreatePidfile(Directory); + CreatePidfile(); if (abort) return; } if (Before) sleep(10); - if (!CheckTS(Directory)) return; + if (!CheckTS()) return; if (isTS) { - if (!CheckPATPMT(Directory)) + if (!CheckPATPMT()) { esyslog("no PAT/PMT found -> nothing to process"); abort=true; - } - if (!macontext.Audio.Options.AudioSilenceDetection) - { - macontext.Info.APid.Num=0; + return; } if (asprintf(&indexFile,"%s/index",Directory)==-1) indexFile=NULL; } else { - if (macontext.Audio.Options.AudioSilenceDetection) - { - macontext.Info.APid.Num=-1; - } + macontext.Info.APid.Num=-1; macontext.Info.DPid.Num=-1; macontext.Info.VPid.Num=-1; - if (CheckVDRHD(Directory)) + if (CheckVDRHD()) { macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H264; } @@ -1815,7 +2095,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in if (asprintf(&indexFile,"%s/index.vdr",Directory)==-1) indexFile=NULL; } - if (!LoadInfo(Directory)) + if (!LoadInfo()) { if (bDecodeVideo) { @@ -1827,7 +2107,7 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in } if (tStart) isyslog("pre-timer %is",tStart); - if (macontext.Info.Length) isyslog("broadcast length %is",macontext.Info.Length); + if (macontext.Info.Length) isyslog("broadcast length %ih %im",macontext.Info.Length/3600,macontext.Info.Length/60); if (title[0]) { @@ -1904,14 +2184,40 @@ cMarkAdStandalone::cMarkAdStandalone(const char *Directory, bool BackupMarks, in } framecnt=0; + framecnt2=0; lastiframe=0; iframe=0; + lastiframetime=0; + iframetime=0; + audiotime=0; chkLEFT=0; chkRIGHT=0; + gettimeofday(&tv1,&tz); } cMarkAdStandalone::~cMarkAdStandalone() { + if ((!abort) && (!duplicate)) + { + gettimeofday(&tv2,&tz); + time_t sec; + suseconds_t usec; + sec=tv2.tv_sec-tv1.tv_sec; + usec=tv2.tv_usec-tv1.tv_usec; + if (usec<0) + { + usec+=1000000; + sec--; + } + double etime,ftime=0,ptime=0; + etime=sec+((double) usec/1000000)-waittime; + if (etime>0) ftime=(framecnt+framecnt2)/etime; + if (macontext.Video.Info.FramesPerSecond>0) + ptime=ftime/macontext.Video.Info.FramesPerSecond; + isyslog("processed time %.2fs, %i/%i frames, %.1f fps, %.1f pps", + etime,framecnt,framecnt2,ftime,ptime); + } + if (osd) { if (abort) @@ -1936,7 +2242,7 @@ cMarkAdStandalone::~cMarkAdStandalone() if (streaminfo) delete streaminfo; if (osd) delete osd; - if ((directory) && (!duplicate)) RemovePidfile(directory); + RemovePidfile(); } bool isnumber(const char *s) @@ -1989,8 +2295,6 @@ int usage() " markad sends an OSD-Message for start and end\n" "-V --version\n" " print version-info and exit\n" - " --asd\n" - " enable audio silence detecion\n" " --markfile=<markfilename>\n" " set a different markfile-name\n" " --online[=1|2] (default is 1)\n" @@ -2000,6 +2304,8 @@ int usage() " live-recordings are identified by having a '@' in the\n" " filename so the entry 'Mark instant recording' in the menu\n" " 'Setup - Recording' of the vdr should be set to 'yes'\n" + " --pass2only\n" + " process only second pass, fine adjustment or marks\n" " --svdrphost=<ip/hostname> (default is 127.0.0.1)\n" " ip/hostname of a remote VDR for OSD messages\n" " --svdrpport=<port> (default is 2001)\n" @@ -2064,7 +2370,6 @@ int main(int argc, char *argv[]) int c; bool bAfter=false,bBefore=false,bEdited=false; bool bFork=false,bNice=false,bImmediateCall=false; - bool bASD=false; int niceLevel = 19; char *recDir=NULL; char *tok,*str; @@ -2081,6 +2386,7 @@ int main(int argc, char *argv[]) bool bDecodeAudio=true; int ignoreInfo=0; bool bGenIndex=false; + bool bPass2Only=false; int online=0; strcpy(logoDirectory,"/var/lib/markad"); @@ -2109,10 +2415,14 @@ int main(int argc, char *argv[]) {"markfile",1,0,1}, {"nopid",0,0,5}, {"online",2,0,4}, + {"pass2only",0,0,10}, {"pass3only",0,0,7}, {"svdrphost",1,0,8}, {"svdrpport",1,0,9}, {"testmode",0,0,3}, + {"vpid",1,0,11}, + {"apid",1,0,12}, + {"dpid",1,0,13}, {"backupmarks", 0, 0, 'B'}, {"scenechangedetection", 0, 0, 'C'}, @@ -2324,7 +2634,6 @@ int main(int argc, char *argv[]) break; case 6: // --asd - bASD=true; break; case 7: // --pass3only @@ -2347,6 +2656,10 @@ int main(int argc, char *argv[]) } break; + case 10: // --pass2only + bPass2Only=true; + break; + default: printf ("? getopt returned character code 0%o ? (option_index %d)\n", c,option_index); } @@ -2521,11 +2834,12 @@ int main(int argc, char *argv[]) cmasta = new cMarkAdStandalone(recDir,bBackupMarks, logoExtraction, logoWidth, logoHeight, bDecodeVideo,bDecodeAudio,ignoreInfo, - logoDirectory,markFileName,bASD,bNoPid,bOSD,svdrphost, + logoDirectory,markFileName,bNoPid,bOSD,svdrphost, svdrpport,bBefore,bGenIndex); if (!cmasta) return -1; - cmasta->Process(recDir); + if (!bPass2Only) cmasta->Process(); + cmasta->Process2ndPass(); delete cmasta; return 0; } diff --git a/command/markad-standalone.h b/command/markad-standalone.h index dcf4769..3af97d7 100644 --- a/command/markad-standalone.h +++ b/command/markad-standalone.h @@ -166,22 +166,33 @@ unsigned Descriptor_Length: char title[80],*ptitle; - bool CreatePidfile(const char *Directory); - void RemovePidfile(const char *Directory); + bool CreatePidfile(); + void RemovePidfile(); bool duplicate; // are we a dup? bool isTS; int MaxFiles; + int lastiframe; int iframe; + + unsigned int iframetime; + unsigned int lastiframetime; + unsigned int audiotime; + int framecnt; + int framecnt2; // 2nd pass + bool abort; bool fastexit; bool reprocess; int waittime; + struct timeval tv1,tv2; + struct timezone tz; bool noticeVDR_MP2; bool noticeVDR_AC3; + bool noticeHEADER; bool bDecodeVideo; bool bDecodeAudio; @@ -198,10 +209,12 @@ unsigned Descriptor_Length: bool setVideo43; // set video to 4:3 in info bool setVideo169; // set video to 16:9 in info + int nextPictType; + int chkLEFT; int chkRIGHT; - void CheckIndex(const char *Directory); + void CheckIndex(); char *indexFile; int sleepcnt; @@ -219,25 +232,31 @@ unsigned Descriptor_Length: void CheckLogoMarks(); void AddStartMark(); void AddMark(MarkAdMark *Mark); - void Reset(); + bool Reset(bool FirstPass=true); + void ChangeMarks(clMark **Mark1, clMark **Mark2, MarkAdPos *NewPos); - bool CheckVDRHD(const char *Directory); - bool CheckPATPMT(const char *Directory); - bool CheckTS(const char *Directory); - bool LoadInfo(const char *Directory); - bool SaveInfo(const char *Directory); + bool CheckVDRHD(); + bool CheckPATPMT(); + bool CheckTS(); + bool LoadInfo(); + bool SaveInfo(); bool RegenerateIndex(); - bool ProcessFile(const char *Directory, int Number); + bool ProcessFile2ndPass(clMark **Mark1, clMark **Mark2, int Number, off_t Offset, int Frame, int Frames); + bool ProcessFile(int Number); + + char *Timestamp2HMS(unsigned int Timestamp); + public: void SetAbort() { abort=true; } - void Process(const char *Directory); + void Process2ndPass(); + void Process(); cMarkAdStandalone(const char *Directory, bool BackupMarks, int LogoExtraction, int LogoWidth, int LogoHeight, bool DecodeVideo, bool DecodeAudio, int IgnoreInfo, - const char *LogoDir, const char *MarkFileName, bool ASD, + const char *LogoDir, const char *MarkFileName, bool noPid, bool OSD, const char *SVDRPHost, int SVDRPPort, bool Before, bool GenIndex); diff --git a/command/marks.cpp b/command/marks.cpp index 5c9e25e..c7c8120 100644 --- a/command/marks.cpp +++ b/command/marks.cpp @@ -282,8 +282,20 @@ clMark *clMarks::Add(int Type, int Position,const char *Comment) // add between two marks newmark->Set(mark,mark->Next()); mark->SetNext(newmark); + newmark->Next()->SetPrev(newmark); break; } + else + { + if ((Position<mark->position) && (mark==first)) + { + // add as first mark + first=newmark; + mark->SetPrev(newmark); + newmark->SetNext(mark); + break; + } + } } mark=mark->Next(); } @@ -317,6 +329,111 @@ void clMarks::RemoveGeneratedIndex(const char *Directory, bool isTS) return; } +bool clMarks::ReadIndex(const char *Directory, bool isTS, int FrameNumber, int Range, int *Number, + off_t *Offset, int *Frame, int *iFrames) +{ + if (!Offset) return false; + if (!Number) return false; + if (!Frame) return false; + if (!iFrames) return false; + *Offset=0; + *Number=0; + *Frame=0; + *iFrames=0; + + char *ipath=NULL; + if (asprintf(&ipath,"%s/index%s",Directory,isTS ? "" : ".vdr")==-1) return false; + int ifd=open(ipath,O_RDONLY); + free(ipath); + if (ifd==-1) return false; + + if (isTS) + { + struct tIndexTS IndexTS; + off_t pos=FrameNumber*sizeof(IndexTS); + if (lseek(ifd,pos,SEEK_SET)!=pos) + { + close(ifd); + return false; + } + do + { + if (read(ifd,&IndexTS,sizeof(IndexTS))!=sizeof(IndexTS)) + { + close(ifd); + return false; + } + if (IndexTS.independent) + { + *Offset=IndexTS.offset; + *Number=IndexTS.number; + pos=lseek(ifd,0,SEEK_CUR); + *Frame=(int) (pos/sizeof(IndexTS))-1; + } + } + while (!IndexTS.independent); + + int cnt=0; + do + { + if (read(ifd,&IndexTS,sizeof(IndexTS))!=sizeof(IndexTS)) + { + close(ifd); + if (!*iFrames) return false; + (*iFrames)-=2; // just to be safe + return true; + } + if (IndexTS.independent) (*iFrames)++; + cnt++; + } + while (cnt<Range); + } + else + { + struct tIndexVDR IndexVDR; + off_t pos=FrameNumber*sizeof(IndexVDR); + if (lseek(ifd,pos,SEEK_SET)!=pos) + { + close(ifd); + return false; + } + do + { + if (read(ifd,&IndexVDR,sizeof(IndexVDR))!=sizeof(IndexVDR)) + { + close(ifd); + return false; + } + if (IndexVDR.type==1) + { + *Offset=IndexVDR.offset; + *Number=IndexVDR.number; + pos=lseek(ifd,0,SEEK_CUR); + *Frame=(int) (pos/sizeof(IndexVDR))-1; + } + } + while (IndexVDR.type!=1); + + int cnt=0; + do + { + if (read(ifd,&IndexVDR,sizeof(IndexVDR))!=sizeof(IndexVDR)) + { + close(ifd); + if (!*iFrames) return false; + (*iFrames)-=2; // just to be safe + return true; + } + if (IndexVDR.type==1) (*iFrames)++; + cnt++; + } + while (cnt<Range); + } + close(ifd); + if (!*iFrames) return false; + return true; +} + void clMarks::WriteIndex(const char *Directory, bool isTS, uint64_t Offset, int FrameType, int Number) { @@ -328,6 +445,7 @@ void clMarks::WriteIndex(const char *Directory, bool isTS, uint64_t Offset, free(ipath); if (indexfd==-1) return; Offset=0; + FrameType=1; } if (isTS) { @@ -481,10 +599,55 @@ bool clMarks::Backup(const char *Directory, bool isTS) return (ret==0); } -bool clMarks::Save(const char *Directory, double FrameRate, bool isTS) +bool clMarks::Load(const char *Directory, double FrameRate, bool isTS) +{ + char *fpath=NULL; + if (asprintf(&fpath,"%s/%s%s",Directory,filename,isTS ? "" : ".vdr")==-1) return false; + + FILE *mf; + mf=fopen(fpath,"r+"); + free(fpath); + if (!mf) return false; + + char *line=NULL; + size_t length; + int h, m, s, f; + + while (getline(&line,&length,mf)!=-1) + { + char descr[256]=""; + f=1; + int n=sscanf(line,"%d:%d:%d.%d %80c",&h,&m,&s,&f,(char *) &descr); + if (n==1) + { + Add(0,h); + } + if (n>=3) + { + int pos=int(round((h*3600+m*60+s)*FrameRate))+f-1; + if (n<=4) + { + Add(0,pos); + } + else + { + char *lf=strchr(descr,10); + if (lf) *lf=0; + char *cr=strchr(descr,13); + if (cr) *cr=0; + Add(0,pos,descr); + } + } + } + if (line) free(line); + fclose(mf); + return true; +} + +bool clMarks::Save(const char *Directory, double FrameRate, bool isTS, bool Force) { if (!first) return false; - if (savedcount==count) return false; + if ((savedcount==count) && (!Force)) return false; char *fpath=NULL; if (asprintf(&fpath,"%s/%s%s",Directory,filename,isTS ? "" : ".vdr")==-1) return false; diff --git a/command/marks.h b/command/marks.h index 94ebb91..8deae5b 100644 --- a/command/marks.h +++ b/command/marks.h @@ -107,13 +107,16 @@ public: return last; } bool Backup(const char *Directory, bool isTS); - bool Save(const char *Directory, double FrameRate, bool isTS); + bool Load(const char *Directory, double FrameRate, bool isTS); + bool Save(const char *Directory, double FrameRate, bool isTS, bool Force=false); #define IERR_NOTFOUND 1 #define IERR_TOOSHORT 2 #define IERR_SEEK 3 #define IERR_READ 4 #define IERR_FRAME 5 bool CheckIndex(const char *Directory, bool isTS, int FrameCnt, int *IndexError); + bool ReadIndex(const char *Directory, bool isTS, int FrameNumber, int Range, int *Number, + off_t *Offset, int *Frame, int *iFrames); void WriteIndex(const char *Directory, bool isTS, uint64_t Offset, int FrameType, int Number); void CloseIndex(const char *Directory, bool isTS); diff --git a/command/pes2es.cpp b/command/pes2es.cpp index d0287d0..4c74e5f 100644 --- a/command/pes2es.cpp +++ b/command/pes2es.cpp @@ -10,7 +10,7 @@ #include <string.h> #include "pes2es.h" - +#include <stdio.h> cMarkAdPES2ES::cMarkAdPES2ES(const char *QueueName, int QueueSize) { queue = new cMarkAdPaketQueue(QueueName,QueueSize); @@ -27,11 +27,9 @@ void cMarkAdPES2ES::Clear() if (queue) queue->Clear(); } -void cMarkAdPES2ES::Process(MarkAdPid Pid, uchar *PESData, int PESSize, uchar **ESData, int *ESSize) +void cMarkAdPES2ES::Process(MarkAdPid Pid, uchar *PESData, int PESSize, MarkAdPacket *ESPkt) { - if ((!ESData) || (!ESSize) || (!queue)) return; - *ESData=NULL; - *ESSize=0; + if (!ESPkt) return; if (PESData) { @@ -90,6 +88,20 @@ void cMarkAdPES2ES::Process(MarkAdPid Pid, uchar *PESData, int PESSize, uchar ** peshdropt->Length; buf=&PESData[bpos]; buflen=PESSize-bpos; + if (peshdropt->PTSDTS>1) + { + struct PESHDROPTPTS *peshdroptpts=(struct PESHDROPTPTS *) &PESData[sizeof(struct PESHDR)+ + sizeof(struct PESHDROPT)]; + + if (peshdroptpts->Marker1 && peshdroptpts->Marker2 && peshdroptpts->Marker3) + { + unsigned int pts=0; + pts|=((peshdroptpts->PTS29_15_H<<7|peshdroptpts->PTS29_15_L)<<15); + pts|=(peshdroptpts->PTS14_0_H<<7|peshdroptpts->PTS14_0_L); + pts|=(peshdroptpts->PTS32_30<<30); + ESPkt->Timestamp=pts; + } + } } else { @@ -99,6 +111,6 @@ void cMarkAdPES2ES::Process(MarkAdPid Pid, uchar *PESData, int PESSize, uchar ** } queue->Put(buf,buflen); } - if (type) *ESData=queue->GetPacket(ESSize,type); + if (type) ESPkt->Data=queue->GetPacket(&ESPkt->Length,type); return; } diff --git a/command/pes2es.h b/command/pes2es.h index c2c08e6..bfeaaaa 100644 --- a/command/pes2es.h +++ b/command/pes2es.h @@ -15,60 +15,82 @@ typedef unsigned char uchar; #include "global.h" #include "queue.h" -class cMarkAdPES2ES +struct PESHDR { -private: - struct PESHDR - { - uchar Sync1; - uchar Sync2; - uchar Sync3; - uchar StreamID; - uchar LenH; - uchar LenL; - }; + uchar Sync1; + uchar Sync2; + uchar Sync3; + uchar StreamID; + uchar LenH; + uchar LenL; +}; #pragma pack(1) - struct PESHDROPT - { +struct PESHDROPT +{ unsigned OOC: - 1; + 1; unsigned CY: - 1; + 1; unsigned DAI: - 1; + 1; unsigned PESP: - 1; + 1; unsigned PESSC: - 2; + 2; unsigned MarkerBits: - 2; + 2; unsigned EXT: - 1; + 1; unsigned CRC: - 1; + 1; unsigned ACI: - 1; + 1; unsigned TM: - 1; + 1; unsigned RATE: - 1; + 1; unsigned ESCR: - 1; -unsigned TSF: - 2; + 1; +unsigned PTSDTS: + 2; unsigned Length: - 8; - }; + 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, uchar **ESData, int *ESSize); + void Process(MarkAdPid Pid, uchar *PESData, int PESSize, MarkAdPacket *ESPkt); }; #endif diff --git a/command/queue.cpp b/command/queue.cpp index 81c0287..4f4eaa0 100644 --- a/command/queue.cpp +++ b/command/queue.cpp @@ -361,32 +361,19 @@ uchar *cMarkAdPaketQueue::GetPacket(int *Size, int Type) *Size=0; if (Length()<4) return NULL; - if ((Type==MA_PACKET_H264) && (pktinfo.pktsyncsize>5) && (pktinfo.pkthdr!=-1)) - { - // ignore PES paket - pktinfo.pkthdr=-1; - outptr+=pktinfo.pktsyncsize; - } - if (pktinfo.pkthdr==-1) { scanner=0xFFFFFFFF; switch (Type) { case MA_PACKET_AC3: - pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize, true); + pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); break; case MA_PACKET_MP2: - pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize, false); + pktinfo.pkthdr=FindAudioHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); break; case MA_PACKET_H264: pktinfo.pkthdr=FindPktHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,true); - if (pktinfo.pktsyncsize>5) - { - // ignore PES paket - pktinfo.pkthdr=-1; - outptr+=pktinfo.pktsyncsize; - } break; default: pktinfo.pkthdr=FindPktHeader(0,&pktinfo.streamsize,&pktinfo.pktsyncsize,false); @@ -418,16 +405,16 @@ uchar *cMarkAdPaketQueue::GetPacket(int *Size, int Type) switch (Type) { case MA_PACKET_AC3: - pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize, true); + pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize,true); break; case MA_PACKET_MP2: - pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize, false); + pkthdr=FindAudioHeader(scannerstart,&streamsize,&pktsyncsize,false); break; case MA_PACKET_H264: - pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize, true); + pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize,true); break; default: - pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize, false); + pkthdr=FindPktHeader(scannerstart,&streamsize,&pktsyncsize,false); break; } diff --git a/command/queue.h b/command/queue.h index 09257fd..8f40ac3 100644 --- a/command/queue.h +++ b/command/queue.h @@ -14,6 +14,14 @@ typedef unsigned char uchar; #endif +typedef struct MarkAdPacket +{ + uchar *Data; + int Length; + unsigned int Timestamp; + bool Offcnt; +} MarkAdPacket; + class cMarkAdPaketQueue { struct MP2HDR diff --git a/command/streaminfo.cpp b/command/streaminfo.cpp index 877eddd..583be70 100644 --- a/command/streaminfo.cpp +++ b/command/streaminfo.cpp @@ -17,8 +17,7 @@ cMarkAdStreamInfo::cMarkAdStreamInfo() void cMarkAdStreamInfo::Clear() { - memset(&H264,0,sizeof(H264)); - H264.frame_num=-1; + H264skiptoggle=false; } bool cMarkAdStreamInfo::FindAC3AudioInfos(MarkAdContext *maContext, uchar *espkt, int eslen) @@ -131,10 +130,29 @@ bool cMarkAdStreamInfo::FindH264VideoInfos(MarkAdContext *maContext, uchar *pkt, if (nalu==NAL_AUD) { + if (!maContext->Video.Info.Width) return false; if (pkt[5]==0x10) { - H264.primary_pic_typeI=true; + maContext->Video.Info.Pict_Type=MA_I_TYPE; + } + else + { + maContext->Video.Info.Pict_Type=0; } + if (!maContext->Video.Info.Interlaced) + { + // this is very crude, drop every second "frame" + if (H264skiptoggle) + { + H264skiptoggle=false; + return false; + } + else + { + H264skiptoggle=true; + } + } + return true; } if (nalu==NAL_SPS) @@ -181,7 +199,8 @@ bool cMarkAdStreamInfo::FindH264VideoInfos(MarkAdContext *maContext, uchar *pkt, } } } - H264.log2_max_frame_num=bs.getUeGolomb()+4; // log2_max_frame_num_minus4 + // H264.log2_max_frame_num=bs.getUeGolomb()+4; + bs.skipUeGolomb(); // log2_max_frame_num_minus4 pic_order_cnt_type = bs.getUeGolomb(); // pic_order_cnt_type if (pic_order_cnt_type == 0) bs.skipUeGolomb(); // log2_max_pic_order_cnt_lsb_minus4 @@ -257,60 +276,60 @@ bool cMarkAdStreamInfo::FindH264VideoInfos(MarkAdContext *maContext, uchar *pkt, frame_rate = time_scale / (2*num_units_in_tick); if (frame_mbs_only_flag) frame_rate/=2; } - //bs.skipBit(); // fixed_frame_rate_flag + bs.skipBit(); // fixed_frame_rate_flag } - /* - int nal_hrd_parameters_present_flag = bs.getBit(); // nal_hrd_parameters_present_flag - if (nal_hrd_parameters_present_flag) - { - int cpb_cnt_minus1; - cpb_cnt_minus1 = bs.getUeGolomb(); // cpb_cnt_minus1 - bs.skipBits(4); // bit_rate_scale - bs.skipBits(4); // cpb_size_scale - for (int i = 0; i <= cpb_cnt_minus1; i++) - { - bs.skipUeGolomb(); // bit_rate_value_minus1[i] - bs.skipUeGolomb(); // cpb_size_value_minus1[i] - bs.skipBit(); // cbr_flag[i] - } - bs.skipBits(5); // initial_cpb_removal_delay_length_minus1 - bs.skipBits(5); // cpb_removal_delay_length_minus1 - bs.skipBits(5); // dpb_output_delay_length_minus1 - bs.skipBits(5); // time_offset_length - } - int vlc_hrd_parameters_present_flag = bs.getBit(); // vlc_hrd_parameters_present_flag - if (vlc_hrd_parameters_present_flag) - { - int cpb_cnt_minus1; - cpb_cnt_minus1 = bs.getUeGolomb(); // cpb_cnt_minus1 - bs.skipBits(4); // bit_rate_scale - bs.skipBits(4); // cpb_size_scale - for (int i = 0; i <= cpb_cnt_minus1; i++) - { - bs.skipUeGolomb(); // bit_rate_value_minus1[i] - bs.skipUeGolomb(); // cpb_size_value_minus1[i] - bs.skipBit(); // cbr_flag[i] - } - bs.skipBits(5); // initial_cpb_removal_delay_length_minus1 - bs.skipBits(5); // cpb_removal_delay_length_minus1 - bs.skipBits(5); // dpb_output_delay_length_minus1 - bs.skipBits(5); // time_offset_length - } - cpb_dpb_delays_present_flag = (nal_hrd_parameters_present_flag | vlc_hrd_parameters_present_flag); - if (cpb_dpb_delays_present_flag) - bs.skipBit(); // low_delay_hrd_flag - bs.skipBit(); // pic_struct_present_flag - if (bs.getBit()) // bitstream_restriction_flag - { - bs.skipBit(); // motion_vectors_over_pic_boundaries_flag - bs.skipUeGolomb(); // max_bytes_per_pic_denom - bs.skipUeGolomb(); // max_bits_per_mb_denom - bs.skipUeGolomb(); // log2_max_mv_length_horizontal - bs.skipUeGolomb(); // log2_max_mv_length_vertical - bs.skipUeGolomb(); // num_reorder_frames - bs.skipUeGolomb(); // max_dec_frame_buffering - } - */ +#if 0 + int nal_hrd_parameters_present_flag = bs.getBit(); // nal_hrd_parameters_present_flag + if (nal_hrd_parameters_present_flag) + { + int cpb_cnt_minus1; + cpb_cnt_minus1 = bs.getUeGolomb(); // cpb_cnt_minus1 + bs.skipBits(4); // bit_rate_scale + bs.skipBits(4); // cpb_size_scale + for (int i = 0; i <= cpb_cnt_minus1; i++) + { + bs.skipUeGolomb(); // bit_rate_value_minus1[i] + bs.skipUeGolomb(); // cpb_size_value_minus1[i] + bs.skipBit(); // cbr_flag[i] + } + bs.skipBits(5); // initial_cpb_removal_delay_length_minus1 + bs.skipBits(5); // cpb_removal_delay_length_minus1 + bs.skipBits(5); // dpb_output_delay_length_minus1 + bs.skipBits(5); // time_offset_length + } + int vlc_hrd_parameters_present_flag = bs.getBit(); // vlc_hrd_parameters_present_flag + if (vlc_hrd_parameters_present_flag) + { + int cpb_cnt_minus1; + cpb_cnt_minus1 = bs.getUeGolomb(); // cpb_cnt_minus1 + bs.skipBits(4); // bit_rate_scale + bs.skipBits(4); // cpb_size_scale + for (int i = 0; i <= cpb_cnt_minus1; i++) + { + bs.skipUeGolomb(); // bit_rate_value_minus1[i] + bs.skipUeGolomb(); // cpb_size_value_minus1[i] + bs.skipBit(); // cbr_flag[i] + } + bs.skipBits(5); // initial_cpb_removal_delay_length_minus1 + bs.skipBits(5); // cpb_removal_delay_length_minus1 + bs.skipBits(5); // dpb_output_delay_length_minus1 + bs.skipBits(5); // time_offset_length + } + cpb_dpb_delays_present_flag = (nal_hrd_parameters_present_flag | vlc_hrd_parameters_present_flag); + if (cpb_dpb_delays_present_flag) + bs.skipBit(); // low_delay_hrd_flag + bs.skipBit(); // pic_struct_present_flag + if (bs.getBit()) // bitstream_restriction_flag + { + bs.skipBit(); // motion_vectors_over_pic_boundaries_flag + bs.skipUeGolomb(); // max_bytes_per_pic_denom + bs.skipUeGolomb(); // max_bits_per_mb_denom + bs.skipUeGolomb(); // log2_max_mv_length_horizontal + bs.skipUeGolomb(); // log2_max_mv_length_vertical + bs.skipUeGolomb(); // num_reorder_frames + bs.skipUeGolomb(); // max_dec_frame_buffering + } +#endif } if ((bs.getIndex() / 8)>0) @@ -414,86 +433,6 @@ bool cMarkAdStreamInfo::FindH264VideoInfos(MarkAdContext *maContext, uchar *pkt, } } } - - if ((nalu==NAL_SLICE) || (nalu==NAL_IDR_SLICE)) - { - uint8_t *nal_data=(uint8_t*) alloca(len); - if (!nal_data) return false; - int nal_len = nalUnescape(nal_data, pkt + 5, len - 5); - cBitStream bs(nal_data, nal_len); - - bs.skipUeGolomb(); // first_mb_in_slice - int slice_type=bs.getUeGolomb(); - bs.skipUeGolomb(); // pic_parameter_set_id - if (H264.separate_colour_plane_flag) - { - bs.skipBits(2); // colour_plane_id - } - int frame_num=bs.getBits(H264.log2_max_frame_num); // frame_num - if (H264.frame_num==-1) H264.frame_num=frame_num; - - /* - if (maContext->Video.Info.Interlaced) - { - bool field_pic_flag=bs.getBit(); - if (field_pic_flag) - { - bool bottom_field_flag=bs.getBit(); - } - } - */ - switch (slice_type) - { - case 0: - case 5: - slice_type=MA_P_TYPE; - break; - - case 1: - case 6: - slice_type=MA_B_TYPE; - break; - - case 2: - case 7: - slice_type=MA_I_TYPE; - break; - - case 3: - case 8: - slice_type=MA_SP_TYPE; - break; - - case 4: - case 9: - slice_type=MA_SI_TYPE; - break; - - default: - break; - } - - maContext->Video.Info.Pict_Type=slice_type; - - if (!maContext->Video.Info.Interlaced) - { - if (frame_num!=H264.frame_num) - { - if (H264.primary_pic_typeI) - { - maContext->Video.Info.Pict_Type=MA_I_TYPE; - H264.primary_pic_typeI=false; - } - H264.frame_num=frame_num; - return true; - } - else - { - return false; - } - } - return true; - } return false; } diff --git a/command/streaminfo.h b/command/streaminfo.h index 7b5ba71..4c4a957 100644 --- a/command/streaminfo.h +++ b/command/streaminfo.h @@ -23,18 +23,12 @@ private: NAL_PPS = 0x08, // Picture Parameter Set NAL_AUD = 0x09, // Access Unit Delimiter NAL_END_SEQ = 0x0A, // End of Sequence + NAL_FILLER = 0x0C, // Filler data NAL_SPS_EXT = 0x0D, // Sequence Parameter Set Extension NAL_AUX_SLICE = 0x19 // Auxilary Slice }; - struct H264 - { - bool primary_pic_typeI; - bool separate_colour_plane_flag; - int log2_max_frame_num; - int frame_num; - } H264; - + bool H264skiptoggle; int nalUnescape(uint8_t *dst, const uint8_t *src, int len); bool FindH264VideoInfos(MarkAdContext *maContext, uchar *pkt, int len); bool FindH262VideoInfos(MarkAdContext *maContext, uchar *pkt, int len); diff --git a/command/ts2pkt.cpp b/command/ts2pkt.cpp index 583cf1b..8667fd6 100644 --- a/command/ts2pkt.cpp +++ b/command/ts2pkt.cpp @@ -31,7 +31,7 @@ void cMarkAdTS2Pkt::Clear() Reset(); } -void cMarkAdTS2Pkt::Reset(int ErrIndex) +bool cMarkAdTS2Pkt::Reset(int ErrIndex) { sync=false; switch (ErrIndex) @@ -57,6 +57,7 @@ void cMarkAdTS2Pkt::Reset(int ErrIndex) } counter=-1; if (queue) queue->Clear(); + return false; } bool cMarkAdTS2Pkt::InjectVideoPES(uchar *PESData, int PESSize) @@ -96,25 +97,23 @@ bool cMarkAdTS2Pkt::InjectVideoPES(uchar *PESData, int PESSize) return true; } -void cMarkAdTS2Pkt::Process(MarkAdPid Pid, uchar *TSData, int TSSize, uchar **PktData, int *PktSize) +bool cMarkAdTS2Pkt::Process(MarkAdPid Pid, uchar *TSData, int TSSize, MarkAdPacket *Pkt) { - if ((!PktData) || (!PktSize) || (!queue)) return; - *PktData=NULL; - *PktSize=0; + if ((!Pkt) || (!queue)) return false; + + bool ret=true; if (TSData) { if (TSSize!=TS_SIZE) { - Reset(MA_ERR_TSSIZE); - return; // we need a full packet + return Reset(MA_ERR_TSSIZE); // we need a full packet } // check TS packet sync if (TSData[0]!=0x47) { - Reset(MA_ERR_NOSYNC); - return; + return Reset(MA_ERR_NOSYNC); // no sync } struct TSHDR *tshdr = (struct TSHDR *) TSData; @@ -122,38 +121,36 @@ void cMarkAdTS2Pkt::Process(MarkAdPid Pid, uchar *TSData, int TSSize, uchar **Pk int pid = (tshdr->PidH << 8) | tshdr->PidL; if (Pid.Num!=pid) { - return; // not for us - } - - if (tshdr->PayloadStart) sync=true; - if (!sync) - { - return; // not synced + return true; // not for us, but this is ok } if ((counter!=-1) && (((counter+1) & 0xF)!=tshdr->Counter)) { if (counter==(int) tshdr->Counter) { - // duplicate paket -> just ignore - return; + return true; // duplicate paket -> just ignore } // sequence error - Reset(MA_ERR_SEQ); - return; + ret=Reset(MA_ERR_SEQ); + if (!tshdr->PayloadStart) return ret; } counter=tshdr->Counter; + if (tshdr->PayloadStart) sync=true; + if (!sync) + { + return false; // not synced + } + if ((tshdr->AFC<=0) || (tshdr->AFC>3)) { - Reset(MA_ERR_AFC); - return; + 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; + return true; } int buflen=TS_SIZE+1; @@ -178,30 +175,29 @@ void cMarkAdTS2Pkt::Process(MarkAdPid Pid, uchar *TSData, int TSSize, uchar **Pk if (buflen>TS_SIZE) { // size to large - Reset(MA_ERR_TOBIG); - return; + return Reset(MA_ERR_TOBIG); } if (buflen<0) { // error in size - Reset(MA_ERR_NEG); - return; + return Reset(MA_ERR_NEG); } if (buflen==0) { // no data? - return; + return false; } queue->Put(buf,buflen); } + if (!ret) return ret; if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) { - *PktData=queue->GetPacket(PktSize,MA_PACKET_H264); + Pkt->Data=queue->GetPacket(&Pkt->Length,MA_PACKET_H264); } else { - *PktData=queue->GetPacket(PktSize,MA_PACKET_PKT); + Pkt->Data=queue->GetPacket(&Pkt->Length,MA_PACKET_PKT); } - return; + return ret; } diff --git a/command/ts2pkt.h b/command/ts2pkt.h index 6dbcd13..367a007 100644 --- a/command/ts2pkt.h +++ b/command/ts2pkt.h @@ -48,8 +48,28 @@ unsigned TSC: { unsigned Len: 8; -unsigned Flags: - 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 @@ -108,12 +128,12 @@ unsigned Length: #define MA_ERR_AFC 4 #define MA_ERR_TOBIG 5 #define MA_ERR_NEG 6 - void Reset(int ErrIndex=MA_ERR_STARTUP); + bool Reset(int ErrIndex=MA_ERR_STARTUP); public: cMarkAdTS2Pkt(const char *QueueName="TS2Pkt", int QueueSize=32768); ~cMarkAdTS2Pkt(); void Clear(); - void Process(MarkAdPid Pid,uchar *TSData, int TSSize, uchar **PktData, int *PktSize); + bool Process(MarkAdPid Pid,uchar *TSData, int TSSize, MarkAdPacket *Pkt); bool InjectVideoPES(uchar *PESData, int PESSize); }; diff --git a/command/video.cpp b/command/video.cpp index 3072542..06a9bf3 100644 --- a/command/video.cpp +++ b/command/video.cpp @@ -110,13 +110,13 @@ int cMarkAdLogo::Load(char *directory, char *file) -void cMarkAdLogo::Save(int lastiframe, uchar *picture) +void cMarkAdLogo::Save(int framenumber, uchar *picture) { if (!macontext) return; char *buf=NULL; - if (asprintf(&buf,"%s/%06d-%s-A%i_%i.pgm","/tmp/",lastiframe, + if (asprintf(&buf,"%s/%06d-%s-A%i_%i.pgm","/tmp/",framenumber, macontext->Info.ChannelID, area.aspectratio.Num,area.aspectratio.Den)!=-1) { @@ -139,7 +139,7 @@ void cMarkAdLogo::Save(int lastiframe, uchar *picture) } } -int cMarkAdLogo::Detect(int lastiframe, int *logoiframe) +int cMarkAdLogo::Detect(int framenumber, int *logoframenumber) { // Detection is made with Sobel-Operator @@ -300,18 +300,18 @@ int cMarkAdLogo::Detect(int lastiframe, int *logoiframe) if (area.counter>=LOGO_VMAXCOUNT) { area.status=ret=LOGO; - *logoiframe=area.lastiframe; + *logoframenumber=area.framenumber; area.counter=0; } else { - if (!area.counter) area.lastiframe=lastiframe; + if (!area.counter) area.framenumber=framenumber; area.counter++; } } else { - area.lastiframe=lastiframe; + area.framenumber=framenumber; area.counter=0; } } @@ -323,7 +323,7 @@ int cMarkAdLogo::Detect(int lastiframe, int *logoiframe) if (area.counter>=LOGO_IMAXCOUNT) { area.status=ret=NOLOGO; - *logoiframe=area.lastiframe; + *logoframenumber=area.framenumber; area.counter=0; } else @@ -343,21 +343,21 @@ int cMarkAdLogo::Detect(int lastiframe, int *logoiframe) } #if 0 - printf("%5i %3i %4i %4i %i %i %i\n",lastiframe,SUMA,area.rpixel,area.mpixel, + printf("%5i %3i %4i %4i %i %i %i\n",framenumber,SUMA,area.rpixel,area.mpixel, (area.rpixel>=(area.mpixel*LOGO_VMARK)),(area.rpixel<(area.mpixel*LOGO_IMARK)), area.counter ); - Save(lastiframe,area.sobel); // TODO: JUST FOR DEBUGGING! + Save(framenumber,area.sobel); // TODO: JUST FOR DEBUGGING! #endif } else { - Save(lastiframe,area.sobel); + Save(framenumber,area.sobel); } } return ret; } -int cMarkAdLogo::Process(int LastIFrame, int *LogoIFrame) +int cMarkAdLogo::Process(int FrameNumber, int *LogoFrameNumber) { if (!macontext) return ERROR; if (!macontext->Video.Data.Valid) return ERROR; @@ -413,7 +413,7 @@ int cMarkAdLogo::Process(int LastIFrame, int *LogoIFrame) if (!area.valid) return ERROR; - return Detect(LastIFrame,LogoIFrame); + return Detect(FrameNumber,LogoFrameNumber); } cMarkAdBlackBordersHoriz::cMarkAdBlackBordersHoriz(MarkAdContext *maContext) @@ -426,10 +426,10 @@ cMarkAdBlackBordersHoriz::cMarkAdBlackBordersHoriz(MarkAdContext *maContext) void cMarkAdBlackBordersHoriz::Clear() { borderstatus=UNINITIALIZED; - borderiframe=-1; + borderframenumber=-1; } -int cMarkAdBlackBordersHoriz::Process(int LastIFrame, int *BorderIFrame) +int cMarkAdBlackBordersHoriz::Process(int FrameNumber, int *BorderIFrame) { #define CHECKHEIGHT 20 #define BRIGHTNESS 20 @@ -482,9 +482,9 @@ int cMarkAdBlackBordersHoriz::Process(int LastIFrame, int *BorderIFrame) if ((fbottom) && (ftop)) { - if (borderiframe==-1) + if (borderframenumber==-1) { - borderiframe=LastIFrame; + borderframenumber=FrameNumber; } else { @@ -492,52 +492,227 @@ int cMarkAdBlackBordersHoriz::Process(int LastIFrame, int *BorderIFrame) switch (borderstatus) { case UNINITIALIZED: - if (LastIFrame>(borderiframe+macontext->Video.Info.FramesPerSecond*MINSECS)) + if (FrameNumber>(borderframenumber+macontext->Video.Info.FramesPerSecond*MINSECS)) { borderstatus=BORDER; } break; case NOBORDER: - if (LastIFrame>(borderiframe+macontext->Video.Info.FramesPerSecond*MINSECS)) + if (FrameNumber>(borderframenumber+macontext->Video.Info.FramesPerSecond*MINSECS)) { - *BorderIFrame=borderiframe; + *BorderIFrame=borderframenumber; borderstatus=BORDER; return 1; // detected start of black border } break; case BORDER: - borderiframe=LastIFrame; + borderframenumber=FrameNumber; break; } } } else { - if (borderiframe!=-1) + if (borderframenumber!=-1) { if (borderstatus==BORDER) { - *BorderIFrame=borderiframe; + *BorderIFrame=borderframenumber; borderstatus=NOBORDER; - borderiframe=-1; + borderframenumber=-1; return -1; // detected stop of black border } else { - borderiframe=-1; + borderframenumber=-1; } } else { - borderiframe=-1; + borderframenumber=-1; borderstatus=NOBORDER; } } return 0; } +cMarkAdOverlap::cMarkAdOverlap(MarkAdContext *maContext) +{ + macontext=maContext; + + histbuf[BEFORE]=NULL; + histbuf[AFTER]=NULL; + result.CommentBefore=NULL; + result.CommentAfter=NULL; + Clear(); +} + +cMarkAdOverlap::~cMarkAdOverlap() +{ + Clear(); +} + +void cMarkAdOverlap::Clear() +{ + histcnt[BEFORE]=0; + histcnt[AFTER]=0; + histframes[BEFORE]=0; + histframes[AFTER]=0; + if (histbuf[BEFORE]) + { + delete[] histbuf[BEFORE]; + histbuf[BEFORE]=NULL; + } + if (histbuf[AFTER]) + { + delete[] histbuf[AFTER]; + histbuf[AFTER]=NULL; + } + if (result.CommentBefore) free(result.CommentBefore); + if (result.CommentAfter) free(result.CommentAfter); + memset(&result,0,sizeof(result)); + similarCutOff=0; + similarMaxCnt=0; + + lastframenumber=-1; +} + +void cMarkAdOverlap::getHistogram(simpleHistogram &dest) +{ + memset(dest,0,sizeof(simpleHistogram)); + for (int Y=0; Y<macontext->Video.Info.Height;Y++) + { + for (int X=0; X<macontext->Video.Info.Width;X++) + { + uchar val=macontext->Video.Data.Plane[0][X+(Y*macontext->Video.Data.PlaneLinesize[0])]; + dest[val]++; + } + } +} + +bool cMarkAdOverlap::areSimilar(simpleHistogram &hist1, simpleHistogram &hist2) +{ + int similar=0; + for (int i=0; i<256; i++) + { + similar+=abs(hist1[i]-hist2[i]); + } + if (similar<similarCutOff) return true; + return false; +} + +MarkAdPos *cMarkAdOverlap::Detect() +{ + int start=0,simcnt=0; + int tmpA=0,tmpB=0; + if (result.FrameNumberBefore==-1) return NULL; + result.FrameNumberBefore=-1; + for (int B=0; B<histcnt[BEFORE]; B++) + { + for (int A=start; A<histcnt[AFTER]; A++) + { + bool simil=areSimilar(histbuf[BEFORE][B].histogram,histbuf[AFTER][A].histogram); + if (simil) + { + tmpA=A; + tmpB=B; + start=A+1; + simcnt++; + break; + } + else + { + //if (simcnt) printf("%i %i %i\n",simcnt,histbuf[BEFORE][B].framenumber,histbuf[AFTER][A].framenumber); + + if (simcnt>similarMaxCnt) + { + result.FrameNumberBefore=histbuf[BEFORE][tmpB].framenumber; + result.FrameNumberAfter=histbuf[AFTER][tmpA].framenumber; + } + else + { + start=0; + } + simcnt=0; + } + } + } + if (result.FrameNumberBefore==-1) + { + if (simcnt>similarMaxCnt) + { + result.FrameNumberBefore=histbuf[BEFORE][tmpB].framenumber; + result.FrameNumberAfter=histbuf[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; +} + +MarkAdPos *cMarkAdOverlap::Process(int FrameNumber, int Frames, bool BeforeAd) +{ + if ((lastframenumber>0) && (!similarMaxCnt)) + { + similarCutOff=50000; + similarMaxCnt=4; + } + + if (BeforeAd) + { + if ((histframes[BEFORE]) && (histcnt[BEFORE]>=histframes[BEFORE])) + { + if (result.FrameNumberBefore) + { + Clear(); + } + else + { + return NULL; + } + } + if (!histbuf[BEFORE]) + { + histframes[BEFORE]=Frames; + histbuf[BEFORE]=new histbuffer[Frames+1]; + } + getHistogram(histbuf[BEFORE][histcnt[BEFORE]].histogram); + histbuf[BEFORE][histcnt[BEFORE]].framenumber=FrameNumber; + histcnt[BEFORE]++; + } + else + { + if (!histbuf[AFTER]) + { + histframes[AFTER]=Frames; + histbuf[AFTER]=new histbuffer[Frames+1]; + } + + if (histcnt[AFTER]>=histframes[AFTER]-1) + { + if (result.FrameNumberBefore) return NULL; + return Detect(); + } + getHistogram(histbuf[AFTER][histcnt[AFTER]].histogram); + histbuf[AFTER][histcnt[AFTER]].framenumber=FrameNumber; + histcnt[AFTER]++; + } + lastframenumber=FrameNumber; + return NULL; +} cMarkAdVideo::cMarkAdVideo(MarkAdContext *maContext) { @@ -549,6 +724,7 @@ cMarkAdVideo::cMarkAdVideo(MarkAdContext *maContext) hborder=new cMarkAdBlackBordersHoriz(maContext); logo = new cMarkAdLogo(maContext); + overlap = NULL; Clear(); } @@ -557,6 +733,7 @@ cMarkAdVideo::~cMarkAdVideo() ResetMark(); if (hborder) delete hborder; if (logo) delete logo; + if (overlap) delete overlap; } void cMarkAdVideo::Clear() @@ -610,56 +787,68 @@ bool cMarkAdVideo::AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b) } -MarkAdMark *cMarkAdVideo::Process(int LastIFrame) +MarkAdPos *cMarkAdVideo::Process2ndPass(int FrameNumber, int Frames, bool BeforeAd) +{ + if (!FrameNumber) return NULL; + if (!overlap) overlap=new cMarkAdOverlap(macontext); + if (!overlap) return NULL; + + return overlap->Process(FrameNumber, Frames, BeforeAd); +} + +MarkAdMark *cMarkAdVideo::Process(int FrameNumber, int FrameNumberNext) { + if ((!FrameNumber) && (!FrameNumberNext)) return NULL; + ResetMark(); - if (!LastIFrame) return NULL; if (!macontext->Video.Options.IgnoreLogoDetection) { - int logoiframe; - int lret=logo->Process(LastIFrame,&logoiframe); + int logoframenumber; + int lret=logo->Process(FrameNumber,&logoframenumber); if ((lret>=-1) && (lret!=0)) { char *buf=NULL; if (lret>0) { - if (asprintf(&buf,"detected logo start (%i)",logoiframe)!=-1) + if (asprintf(&buf,"detected logo start (%i)",logoframenumber)!=-1) { - AddMark(MT_LOGOSTART,logoiframe,buf); + AddMark(MT_LOGOSTART,logoframenumber,buf); free(buf); } } else { - if (asprintf(&buf,"detected logo stop (%i)",logoiframe)!=-1) + if (asprintf(&buf,"detected logo stop (%i)",logoframenumber)!=-1) { - AddMark(MT_LOGOSTOP,logoiframe,buf); + AddMark(MT_LOGOSTOP,logoframenumber,buf); free(buf); } } } } - int borderiframe; - int hret=hborder->Process(LastIFrame,&borderiframe); + int borderframenumber; + int hret=hborder->Process(FrameNumber,&borderframenumber); - if ((hret>0) && (borderiframe)) + if ((hret>0) && (borderframenumber)) { char *buf=NULL; - if (asprintf(&buf,"detected start of horiz. borders (%i [%i])",borderiframe,LastIFrame)!=-1) + if (asprintf(&buf,"detected start of horiz. borders (%i [%i])", + borderframenumber,FrameNumber)!=-1) { - AddMark(MT_BORDERSTART,borderiframe,buf); + AddMark(MT_BORDERSTART,borderframenumber,buf); free(buf); } } - if ((hret<0) && (borderiframe)) + if ((hret<0) && (borderframenumber)) { char *buf=NULL; - if (asprintf(&buf,"detected stop of horiz. borders (%i [%i])",borderiframe,LastIFrame)!=-1) + if (asprintf(&buf,"detected stop of horiz. borders (%i [%i])", + borderframenumber,FrameNumber)!=-1) { - AddMark(MT_BORDERSTOP,borderiframe,buf); + AddMark(MT_BORDERSTOP,borderframenumber,buf); free(buf); } } @@ -668,34 +857,47 @@ MarkAdMark *cMarkAdVideo::Process(int LastIFrame) { if (AspectRatioChange(&macontext->Video.Info.AspectRatio,&aspectratio)) { - char *buf=NULL; - if (asprintf(&buf,"aspect ratio change from %i:%i to %i:%i (%i)", - aspectratio.Num,aspectratio.Den, - macontext->Video.Info.AspectRatio.Num, - macontext->Video.Info.AspectRatio.Den,LastIFrame)!=-1) + char *buf=(char *) calloc(1,256); + 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 ((macontext->Info.AspectRatio.Num) && (macontext->Info.AspectRatio.Den)) { - if ((macontext->Info.AspectRatio.Num) && (macontext->Info.AspectRatio.Den)) + if ((macontext->Video.Info.AspectRatio.Num==macontext->Info.AspectRatio.Num) && + (macontext->Video.Info.AspectRatio.Den==macontext->Info.AspectRatio.Den)) { - if ((macontext->Video.Info.AspectRatio.Num==macontext->Info.AspectRatio.Num) && - (macontext->Video.Info.AspectRatio.Den==macontext->Info.AspectRatio.Den)) - { - AddMark(MT_ASPECTSTART,LastIFrame,buf); - } - else - { - AddMark(MT_ASPECTSTOP,LastIFrame,buf); - } + char nbuf[20]; + snprintf(nbuf,sizeof(nbuf),"%i)",FrameNumberNext); + nbuf[19]=0; + strcat(buf,nbuf); + AddMark(MT_ASPECTSTART,FrameNumberNext,buf); } else { - AddMark(MT_ASPECTCHANGE,LastIFrame,buf); + char nbuf[20]; + snprintf(nbuf,sizeof(nbuf),"%i)",framelast); + nbuf[19]=0; + strcat(buf,nbuf); + AddMark(MT_ASPECTSTOP,framelast,buf); } - free(buf); } + else + { + char nbuf[20]; + snprintf(nbuf,sizeof(nbuf),"%i)",FrameNumber); + nbuf[19]=0; + strcat(buf,nbuf); + + AddMark(MT_ASPECTCHANGE,FrameNumber,buf); + } + free(buf); } aspectratio.Num=macontext->Video.Info.AspectRatio.Num; aspectratio.Den=macontext->Video.Info.AspectRatio.Den; } + framelast=FrameNumberNext; return &mark; } diff --git a/command/video.h b/command/video.h index 1a03d97..22bcec7 100644 --- a/command/video.h +++ b/command/video.h @@ -22,6 +22,39 @@ #define LOGO_VMARK 0.5 // percantage of pixels for visible #define LOGO_IMARK 0.15 // percentage of pixels for invisible +class cMarkAdOverlap +{ +private: +#define BEFORE 0 +#define AFTER 1 + MarkAdContext *macontext; + typedef int simpleHistogram[256]; + + typedef struct + { + int framenumber; + simpleHistogram histogram; + } histbuffer; + histbuffer *histbuf[2]; + int histcnt[2]; + int histframes[2]; + + int lastframenumber; + + MarkAdPos result; + + int similarCutOff; + int similarMaxCnt; + bool areSimilar(simpleHistogram &hist1, simpleHistogram &hist2); + void getHistogram(simpleHistogram &dest); + MarkAdPos *Detect(); + void Clear(); +public: + cMarkAdOverlap(MarkAdContext *maContext); + ~cMarkAdOverlap(); + MarkAdPos *Process(int FrameNumber, int Frames, bool BeforeAd); +}; + class cMarkAdLogo { private: @@ -57,7 +90,7 @@ private: int rpixel; // black pixel in result int mpixel; // black pixel in mask int status; // status = LOGO on, off, uninitialized - int lastiframe; // start/stop frame + int framenumber; // start/stop frame int counter; // how many logo on, offs detected? int corner; // which corner MarkAdAspectRatio aspectratio; // aspectratio @@ -69,12 +102,12 @@ private: int GY[3][3]; MarkAdContext *macontext; - int Detect(int lastiframe, int *logoiframe); // ret 1 = logo, 0 = unknown, -1 = no logo + int Detect(int framenumber, int *logoframenumber); // ret 1 = logo, 0 = unknown, -1 = no logo int Load(char *directory, char *file); - void Save(int lastiframe, uchar *picture); + void Save(int framenumber, uchar *picture); public: cMarkAdLogo(MarkAdContext *maContext); - int Process(int LastIFrame, int *LogoIFrame); + int Process(int FrameNumber, int *LogoFrameNumber); void Clear(); }; @@ -91,11 +124,11 @@ private: }; int borderstatus; - int borderiframe; + int borderframenumber; MarkAdContext *macontext; public: cMarkAdBlackBordersHoriz(MarkAdContext *maContext); - int Process(int LastIFrame,int *BorderIFrame); + int Process(int FrameNumber,int *BorderFrameNumber); void Clear(); }; @@ -108,16 +141,19 @@ private: MarkAdAspectRatio aspectratio; cMarkAdBlackBordersHoriz *hborder; cMarkAdLogo *logo; + cMarkAdOverlap *overlap; void ResetMark(); bool AddMark(int Type, int Position, const char *Comment); bool AspectRatioChange(MarkAdAspectRatio *a, MarkAdAspectRatio *b); - void SetTimerMarks(int LastIFrame); + + int framelast; public: cMarkAdVideo(MarkAdContext *maContext); ~cMarkAdVideo(); - MarkAdMark *Process(int LastIFrame); + MarkAdPos *Process2ndPass(int FrameNumber, int Frames, bool BeforeAd); + MarkAdMark *Process(int FrameNumber, int FrameNumberNext); void Clear(); }; |