From 74cdd9ffa1d0e5f74942051e7e22e07542929c03 Mon Sep 17 00:00:00 2001 From: Jochen Dolze Date: Tue, 30 Mar 2010 18:49:58 +0200 Subject: Changed directory structure, added Makefiles --- Makefile | 127 +--- audio.cpp | 184 ----- audio.h | 59 -- command/Makefile | 88 +++ command/audio.cpp | 184 +++++ command/audio.h | 59 ++ command/debug.h | 23 + command/decoder.cpp | 478 ++++++++++++ command/decoder.h | 72 ++ command/demux.cpp | 190 +++++ command/demux.h | 45 ++ command/global.h | 139 ++++ command/markad-standalone.cpp | 1632 +++++++++++++++++++++++++++++++++++++++++ command/markad-standalone.h | 214 ++++++ command/marks.cpp | 373 ++++++++++ command/marks.h | 119 +++ command/pes2es.cpp | 100 +++ command/pes2es.h | 78 ++ command/queue.cpp | 451 ++++++++++++ command/queue.h | 159 ++++ command/streaminfo.cpp | 793 ++++++++++++++++++++ command/streaminfo.h | 112 +++ command/ts2pkt.cpp | 194 +++++ command/ts2pkt.h | 127 ++++ command/vdr2pkt.cpp | 30 + command/vdr2pkt.h | 28 + command/video.cpp | 657 +++++++++++++++++ command/video.h | 133 ++++ debug.h | 23 - decoder.cpp | 478 ------------ decoder.h | 72 -- demux.cpp | 190 ----- demux.h | 45 -- global.h | 139 ---- markad-standalone.cpp | 1632 ----------------------------------------- markad-standalone.h | 214 ------ markad.cpp | 175 ----- markad.h | 59 -- marks.cpp | 373 ---------- marks.h | 119 --- pes2es.cpp | 100 --- pes2es.h | 78 -- plugin/Makefile | 105 +++ plugin/markad.cpp | 175 +++++ plugin/markad.h | 59 ++ plugin/po/de_DE.po | 19 + plugin/po/it_IT.po | 22 + plugin/status.cpp | 136 ++++ plugin/status.h | 30 + po/de_DE.po | 19 - po/it_IT.po | 22 - queue.cpp | 451 ------------ queue.h | 159 ---- status.cpp | 136 ---- status.h | 30 - streaminfo.cpp | 793 -------------------- streaminfo.h | 112 --- ts2pkt.cpp | 194 ----- ts2pkt.h | 127 ---- vdr2pkt.cpp | 30 - vdr2pkt.h | 28 - video.cpp | 657 ----------------- video.h | 133 ---- 63 files changed, 7044 insertions(+), 6938 deletions(-) delete mode 100644 audio.cpp delete mode 100644 audio.h create mode 100644 command/Makefile create mode 100644 command/audio.cpp create mode 100644 command/audio.h create mode 100644 command/debug.h create mode 100644 command/decoder.cpp create mode 100644 command/decoder.h create mode 100644 command/demux.cpp create mode 100644 command/demux.h create mode 100644 command/global.h create mode 100644 command/markad-standalone.cpp create mode 100644 command/markad-standalone.h create mode 100644 command/marks.cpp create mode 100644 command/marks.h create mode 100644 command/pes2es.cpp create mode 100644 command/pes2es.h create mode 100644 command/queue.cpp create mode 100644 command/queue.h create mode 100644 command/streaminfo.cpp create mode 100644 command/streaminfo.h create mode 100644 command/ts2pkt.cpp create mode 100644 command/ts2pkt.h create mode 100644 command/vdr2pkt.cpp create mode 100644 command/vdr2pkt.h create mode 100644 command/video.cpp create mode 100644 command/video.h delete mode 100644 debug.h delete mode 100644 decoder.cpp delete mode 100644 decoder.h delete mode 100644 demux.cpp delete mode 100644 demux.h delete mode 100644 global.h delete mode 100644 markad-standalone.cpp delete mode 100644 markad-standalone.h delete mode 100644 markad.cpp delete mode 100644 markad.h delete mode 100644 marks.cpp delete mode 100644 marks.h delete mode 100644 pes2es.cpp delete mode 100644 pes2es.h create mode 100644 plugin/Makefile create mode 100644 plugin/markad.cpp create mode 100644 plugin/markad.h create mode 100644 plugin/po/de_DE.po create mode 100644 plugin/po/it_IT.po create mode 100644 plugin/status.cpp create mode 100644 plugin/status.h delete mode 100644 po/de_DE.po delete mode 100644 po/it_IT.po delete mode 100644 queue.cpp delete mode 100644 queue.h delete mode 100644 status.cpp delete mode 100644 status.h delete mode 100644 streaminfo.cpp delete mode 100644 streaminfo.h delete mode 100644 ts2pkt.cpp delete mode 100644 ts2pkt.h delete mode 100644 vdr2pkt.cpp delete mode 100644 vdr2pkt.h delete mode 100644 video.cpp delete mode 100644 video.h diff --git a/Makefile b/Makefile index 8184867..f77a297 100644 --- a/Makefile +++ b/Makefile @@ -1,124 +1,37 @@ # -# Makefile for a Video Disk Recorder plugin +# Makefile for a Video Disk Recorder plugin + program # -# $Id$ -# The official name of this plugin. -# This name will be used in the '-P...' option of VDR to load the plugin. -# By default the main source file also carries this name. -# IMPORTANT: the presence of this macro is important for the Make.config -# file. So it must be defined, even if it is not used here! -# -PLUGIN = markad - -### The version number of this plugin (taken from the main source file): +DIRS = command plugin VERSION = $(shell grep 'static const char \*VERSION *=' version.h | awk '{ print $$6 }' | sed -e 's/[";]//g') -### The C++ compiler and options: - -CXX ?= g++ -CXXFLAGS ?= -fPIC -g -O2 -Wall -Wextra -Woverloaded-virtual -Wno-parentheses -PKG-CONFIG ?= pkg-config - -### The directory environment: - -VDRDIR = ../../.. -LIBDIR = ../../lib TMPDIR = /tmp - -### Allow user defined options to overwrite defaults: - --include $(VDRDIR)/Make.config - -### The version number of VDR's plugin API (taken from VDR's "config.h"): - -APIVERSION = $(shell sed -ne '/define APIVERSION/s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/config.h) - -### The name of the distribution archive: - -ARCHIVE = $(PLUGIN)-$(VERSION) +ARCHIVE = markad-$(VERSION) PACKAGE = vdr-$(ARCHIVE) -### Includes and Defines (add further entries here): - -PKG-LIBS += libavcodec -PKG-INCLUDES += libavcodec - -INCLUDES += -I$(VDRDIR)/include - -DEFINES += -D_GNU_SOURCE -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE - -INCLUDES += $(shell $(PKG-CONFIG) --cflags $(PKG-INCLUDES)) -LIBS-CMD += $(shell $(PKG-CONFIG) --libs $(PKG-LIBS)) - -### The object files (add further files here): - -OBJS-CMD = markad-standalone.o decoder.o marks.o streaminfo.o video.o audio.o demux.o queue.o vdr2pkt.o ts2pkt.o pes2es.o -OBJS = $(PLUGIN).o status.o - -### The main target: - -all: libvdr-$(PLUGIN).so $(PLUGIN) i18n - -### Implicit rules: - -%.o: %.cpp - $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< - -### Dependencies: - -MAKEDEP = $(CXX) -MM -MG -DEPFILE = .dependencies -$(DEPFILE): Makefile - @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@ - @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS-CMD:%.o=%.cpp) >> $@ - --include $(DEPFILE) - -### Internationalization (I18N): - -PODIR = po -LOCALEDIR = $(VDRDIR)/locale -I18Npo = $(wildcard $(PODIR)/*.po) -I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/vdr-$(PLUGIN).mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) -I18Npot = $(PODIR)/$(PLUGIN).pot - -%.mo: %.po - msgfmt -c -o $@ $< - -$(I18Npot): $(wildcard *.cpp *.h) - xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='' -o $@ $^ - -%.po: $(I18Npot) - msgmerge -U --no-wrap --no-location --backup=none -q $@ $< - @touch $@ - -$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo - @mkdir -p $(dir $@) - cp $< $@ - -.PHONY: i18n -i18n: $(I18Nmsgs) $(I18Npot) - -### Targets: - -libvdr-$(PLUGIN).so: $(OBJS) - $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) -o $@ - @cp --remove-destination $@ $(LIBDIR)/$@.$(APIVERSION) +all: + for i in $(DIRS); do make -C $$i; done -$(PLUGIN): $(OBJS-CMD) - $(CXX) $(CXXFLAGS) $(OBJS-CMD) $(LIBS-CMD) -o $@ +install: + for i in $(DIRS); do make -C $$i install; done -dist: clean +dist: @-rm -rf $(TMPDIR)/$(ARCHIVE) @mkdir $(TMPDIR)/$(ARCHIVE) - @cp -a *.cpp *.h $(TMPDIR)/$(ARCHIVE) - @cp -a logos po COPYING HISTORY README Makefile $(TMPDIR)/$(ARCHIVE) - @tar czf $(PACKAGE).tgz -C $(TMPDIR) --exclude debian --exclude CVS --exclude .svn --exclude markad.kdevelop --exclude markad.kdevelop.filelist --exclude markad.kdevelop.pcs --exclude markad.kdevses --exclude Doxyfile --exclude test $(ARCHIVE) + @mkdir $(TMPDIR)/$(ARCHIVE)/plugin + @mkdir $(TMPDIR)/$(ARCHIVE)/plugin/po + @mkdir $(TMPDIR)/$(ARCHIVE)/command + @mkdir $(TMPDIR)/$(ARCHIVE)/command/logos + @cp -a plugin/*.cpp plugin/*.h plugin/Makefile $(TMPDIR)/$(ARCHIVE)/plugin + @cp -a plugin/po/*.po $(TMPDIR)/$(ARCHIVE)/plugin/po + @cp -a command/*.cpp command/*.h command/Makefile $(TMPDIR)/$(ARCHIVE)/command + @cp -a command/logos/*.pgm $(TMPDIR)/$(ARCHIVE)/command/logos + @cp -a *.h COPYING HISTORY README INSTALL Makefile $(TMPDIR)/$(ARCHIVE) + @tar czf $(PACKAGE).tgz -C $(TMPDIR) $(ARCHIVE) @-rm -rf $(TMPDIR)/$(ARCHIVE) @echo Distribution package created as $(PACKAGE).tgz clean: - @-rm -f $(OBJS) $(OBJS-COMMON) $(OBJS-CMD) $(DEPFILE) $(PLUGIN) *.so *.so.* *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot + for i in $(DIRS); do make -C $$i clean; done + @-rm -f $(PACKAGE).tgz diff --git a/audio.cpp b/audio.cpp deleted file mode 100644 index bd35ddc..0000000 --- a/audio.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* - * audio.cpp: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - * $Id$ - */ - -#include "audio.h" - -cMarkAdAudio::cMarkAdAudio(MarkAdContext *maContext) -{ - macontext=maContext; - mark.Comment=NULL; - mark.Position=0; - mark.Type=0; - channels=0; -#if 0 - lastiframe_gain=-ANALYZEFRAMES; -#endif - lastiframe_silence=-1; -} - -cMarkAdAudio::~cMarkAdAudio() -{ - ResetMark(); -} - -void cMarkAdAudio::ResetMark() -{ - if (mark.Comment) free(mark.Comment); - mark.Comment=NULL; - mark.Position=0; - mark.Type=0; -} - -bool cMarkAdAudio::AddMark(int Type, int Position, const char *Comment) -{ - if (!Comment) return false; - if (mark.Comment) - { - int oldlen=strlen(mark.Comment); - mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment)); - if (!mark.Comment) - { - mark.Position=0; - return false; - } - strcat(mark.Comment," ["); - strcat(mark.Comment,Comment); - strcat(mark.Comment,"]"); - } - else - { - mark.Comment=strdup(Comment); - } - mark.Position=Position; - mark.Type=Type; - return true; -} - -bool cMarkAdAudio::SilenceDetection() -{ - if (!macontext->Audio.Data.Valid) return false; - if (lastiframe_silence==lastiframe) return false; // we already detected silence for this frame - - int samples=macontext->Audio.Data.SampleBufLen/ - sizeof(*macontext->Audio.Data.SampleBuf)/ - macontext->Audio.Info.Channels; - - short left,right; - int lowvalcount=0; - for (int i=0; iAudio.Data.SampleBuf[0+(i*2)]; - right=macontext->Audio.Data.SampleBuf[1+(i*2)]; - - if ((abs(left)+abs(right))MIN_LOWVALS) - { - lastiframe_silence=lastiframe; - return true; - } - } - else - { - lowvalcount=0; - } - } - return false; -} - -#if 0 -bool cMarkAdAudio::AnalyzeGain() -{ - if (!macontext->Audio.Data.Valid) return false; - - int samples=macontext->Audio.Data.SampleBufLen/ - sizeof(*macontext->Audio.Data.SampleBuf)/ - macontext->Audio.Info.Channels; - - double left[samples]; - double right[samples]; - - for (int i=0; iAudio.Data.SampleBuf[0+(i*2)]; - right[i]=macontext->Audio.Data.SampleBuf[1+(i*2)]; - } - - if ((lastiframe-lastiframe_gain)>ANALYZEFRAMES) - { - if (lastiframe_gain>0) - { - double dgain,gain = audiogain.GetGain(); - dgain=gain-lastgain; - printf("%05i %+.2f db %+.2f db\n",lastiframe_gain,gain,lastgain); - lastgain=gain; - } - audiogain.Init(macontext->Audio.Info.SampleRate); - lastiframe_gain=lastiframe; - } - if (audiogain.AnalyzeSamples(left,right,samples,2)!=GAIN_ANALYSIS_OK) - { - lastiframe_gain=-ANALYZEFRAMES; - } - - return true; -} -#endif - -bool cMarkAdAudio::ChannelChange(int a, int b) -{ - if ((a==0) || (b==0)) return false; - if (a!=b) return true; - return false; -} - -MarkAdMark *cMarkAdAudio::Process(int LastIFrame) -{ - ResetMark(); - if (!LastIFrame) return NULL; - lastiframe=LastIFrame; - -#if 0 - AnalyzeGain(); -#endif - if (macontext->Audio.Options.AudioSilenceDetection) - { - if (SilenceDetection()) - { - char *buf=NULL; - if (asprintf(&buf,"audio channel silence detecion (%i)",lastiframe)!=-1) - { - AddMark(MT_SILENCECHANGE,lastiframe,buf); - free(buf); - } - } - } - - 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) - { - if (macontext->Audio.Info.Channels>2) - { - AddMark(MT_CHANNELSTART,lastiframe,buf); - } - else - { - AddMark(MT_CHANNELSTOP,lastiframe,buf); - } - free(buf); - } - } - - channels=macontext->Audio.Info.Channels; - return &mark; -} - diff --git a/audio.h b/audio.h deleted file mode 100644 index 8995732..0000000 --- a/audio.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * audio.h: A program for the Video Disk Recorder - * - * See the README file for copyright information and how to reach the author. - * - */ - -#ifndef __audio_h_ -#define __audio_h_ - -#include // for htonl -#include -#include -#include - -#include "global.h" - -extern "C" -{ -#include "debug.h" -} - -#if 0 -#include "audio_gain_analysis.h" -#endif - -class cMarkAdAudio -{ -private: - int lastiframe; - 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; - -#if 0 -#define ANALYZEFRAMES 1 - int lastiframe_gain; - double lastgain; - cMarkAdAudioGainAnalysis audiogain; - bool AnalyzeGain(); -#endif - - int channels; - bool ChannelChange(int a, int b); -public: - cMarkAdAudio(MarkAdContext *maContext); - ~cMarkAdAudio(); - MarkAdMark *Process(int LastIFrame); -}; - - -#endif diff --git a/command/Makefile b/command/Makefile new file mode 100644 index 0000000..6091c55 --- /dev/null +++ b/command/Makefile @@ -0,0 +1,88 @@ +# +# Makefile for a Video Disk Recorder addon +# + +### The version number of this plugin (taken from the main source file): + +VERSION = $(shell grep 'static const char \*VERSION *=' ../version.h | awk '{ print $$6 }' | sed -e 's/[";]//g') + +### The C++ compiler and options: + +CXX ?= g++ +CXXFLAGS ?= -g -O2 -Wall -Wextra -Woverloaded-virtual -Wno-parentheses +PKG-CONFIG ?= pkg-config + +### Includes and Defines (add further entries here): + +PKG-LIBS += libavcodec +PKG-INCLUDES += libavcodec + +DEFINES += -D_GNU_SOURCE +DEFINES += -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE + +INCLUDES += $(shell $(PKG-CONFIG) --cflags $(PKG-INCLUDES)) +LIBS += $(shell $(PKG-CONFIG) --libs $(PKG-LIBS)) + +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 vdr2pkt.o ts2pkt.o pes2es.o + +### The main target: + +all: markad i18n + +### Implicit rules: + +%.o: %.cpp + $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $< + +### Dependencies: + +MAKEDEP = $(CXX) -MM -MG +DEPFILE = .dependencies +$(DEPFILE): Makefile + @$(MAKEDEP) $(DEFINES) $(INCLUDES) $(OBJS:%.o=%.cpp) > $@ + +-include $(DEPFILE) + +### Internationalization (I18N): + +PODIR = po +LOCALEDIR = $(DESTDIR)/usr/share/locale +I18Npo = $(wildcard $(PODIR)/*.po) +I18Nmsgs = $(addprefix $(LOCALEDIR)/, $(addsuffix /LC_MESSAGES/markad.mo, $(notdir $(foreach file, $(I18Npo), $(basename $(file)))))) +I18Npot = $(PODIR)/markad.pot + +%.mo: %.po + msgfmt -c -o $@ $< + +$(I18Npot): $(wildcard *.cpp *.h) + xgettext -C -cTRANSLATORS --no-wrap --no-location -k -ktr -ktrNOOP --msgid-bugs-address='' -o $@ $^ + +%.po: $(I18Npot) + msgmerge -U --no-wrap --no-location --backup=none -q $@ $< + @touch $@ + +$(I18Nmsgs): $(LOCALEDIR)/%/LC_MESSAGES/vdr-$(PLUGIN).mo: $(PODIR)/%.mo + @mkdir -p $(dir $@) + cp $< $@ + +.PHONY: i18n +i18n: $(I18Nmsgs) $(I18Npot) + +### Targets: + +markad: $(OBJS) + $(CXX) $(CXXFLAGS) $(OBJS) $(LIBS) -o $@ + +install: markad + @cp --remove-destination markad $(DESTDIR)/usr/bin/markad + @strip $(DESTDIR)/usr/bin/markad + @mkdir -p $(DESTDIR)/var/lib/markad + @cp logos/* $(DESTDIR)/var/lib/markad + @echo markad installed + +clean: + @-rm -f $(OBJS) $(DEPFILE) markad *.so *.so.* *.tgz core* *~ $(PODIR)/*.mo $(PODIR)/*.pot diff --git a/command/audio.cpp b/command/audio.cpp new file mode 100644 index 0000000..bd35ddc --- /dev/null +++ b/command/audio.cpp @@ -0,0 +1,184 @@ +/* + * audio.cpp: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + * $Id$ + */ + +#include "audio.h" + +cMarkAdAudio::cMarkAdAudio(MarkAdContext *maContext) +{ + macontext=maContext; + mark.Comment=NULL; + mark.Position=0; + mark.Type=0; + channels=0; +#if 0 + lastiframe_gain=-ANALYZEFRAMES; +#endif + lastiframe_silence=-1; +} + +cMarkAdAudio::~cMarkAdAudio() +{ + ResetMark(); +} + +void cMarkAdAudio::ResetMark() +{ + if (mark.Comment) free(mark.Comment); + mark.Comment=NULL; + mark.Position=0; + mark.Type=0; +} + +bool cMarkAdAudio::AddMark(int Type, int Position, const char *Comment) +{ + if (!Comment) return false; + if (mark.Comment) + { + int oldlen=strlen(mark.Comment); + mark.Comment=(char *) realloc(mark.Comment,oldlen+10+strlen(Comment)); + if (!mark.Comment) + { + mark.Position=0; + return false; + } + strcat(mark.Comment," ["); + strcat(mark.Comment,Comment); + strcat(mark.Comment,"]"); + } + else + { + mark.Comment=strdup(Comment); + } + mark.Position=Position; + mark.Type=Type; + return true; +} + +bool cMarkAdAudio::SilenceDetection() +{ + if (!macontext->Audio.Data.Valid) return false; + if (lastiframe_silence==lastiframe) return false; // we already detected silence for this frame + + int samples=macontext->Audio.Data.SampleBufLen/ + sizeof(*macontext->Audio.Data.SampleBuf)/ + macontext->Audio.Info.Channels; + + short left,right; + int lowvalcount=0; + for (int i=0; iAudio.Data.SampleBuf[0+(i*2)]; + right=macontext->Audio.Data.SampleBuf[1+(i*2)]; + + if ((abs(left)+abs(right))MIN_LOWVALS) + { + lastiframe_silence=lastiframe; + return true; + } + } + else + { + lowvalcount=0; + } + } + return false; +} + +#if 0 +bool cMarkAdAudio::AnalyzeGain() +{ + if (!macontext->Audio.Data.Valid) return false; + + int samples=macontext->Audio.Data.SampleBufLen/ + sizeof(*macontext->Audio.Data.SampleBuf)/ + macontext->Audio.Info.Channels; + + double left[samples]; + double right[samples]; + + for (int i=0; iAudio.Data.SampleBuf[0+(i*2)]; + right[i]=macontext->Audio.Data.SampleBuf[1+(i*2)]; + } + + if ((lastiframe-lastiframe_gain)>ANALYZEFRAMES) + { + if (lastiframe_gain>0) + { + double dgain,gain = audiogain.GetGain(); + dgain=gain-lastgain; + printf("%05i %+.2f db %+.2f db\n",lastiframe_gain,gain,lastgain); + lastgain=gain; + } + audiogain.Init(macontext->Audio.Info.SampleRate); + lastiframe_gain=lastiframe; + } + if (audiogain.AnalyzeSamples(left,right,samples,2)!=GAIN_ANALYSIS_OK) + { + lastiframe_gain=-ANALYZEFRAMES; + } + + return true; +} +#endif + +bool cMarkAdAudio::ChannelChange(int a, int b) +{ + if ((a==0) || (b==0)) return false; + if (a!=b) return true; + return false; +} + +MarkAdMark *cMarkAdAudio::Process(int LastIFrame) +{ + ResetMark(); + if (!LastIFrame) return NULL; + lastiframe=LastIFrame; + +#if 0 + AnalyzeGain(); +#endif + if (macontext->Audio.Options.AudioSilenceDetection) + { + if (SilenceDetection()) + { + char *buf=NULL; + if (asprintf(&buf,"audio channel silence detecion (%i)",lastiframe)!=-1) + { + AddMark(MT_SILENCECHANGE,lastiframe,buf); + free(buf); + } + } + } + + 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) + { + if (macontext->Audio.Info.Channels>2) + { + AddMark(MT_CHANNELSTART,lastiframe,buf); + } + else + { + AddMark(MT_CHANNELSTOP,lastiframe,buf); + } + free(buf); + } + } + + channels=macontext->Audio.Info.Channels; + return &mark; +} + diff --git a/command/audio.h b/command/audio.h new file mode 100644 index 0000000..8995732 --- /dev/null +++ b/command/audio.h @@ -0,0 +1,59 @@ +/* + * audio.h: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __audio_h_ +#define __audio_h_ + +#include // for htonl +#include +#include +#include + +#include "global.h" + +extern "C" +{ +#include "debug.h" +} + +#if 0 +#include "audio_gain_analysis.h" +#endif + +class cMarkAdAudio +{ +private: + int lastiframe; + 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; + +#if 0 +#define ANALYZEFRAMES 1 + int lastiframe_gain; + double lastgain; + cMarkAdAudioGainAnalysis audiogain; + bool AnalyzeGain(); +#endif + + int channels; + bool ChannelChange(int a, int b); +public: + cMarkAdAudio(MarkAdContext *maContext); + ~cMarkAdAudio(); + MarkAdMark *Process(int LastIFrame); +}; + + +#endif diff --git a/command/debug.h b/command/debug.h new file mode 100644 index 0000000..a12a972 --- /dev/null +++ b/command/debug.h @@ -0,0 +1,23 @@ +/* + * debug.h: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __debug_h_ +#define __debug_h_ + +#ifndef LOG_ERR +#define LOG_ERR 3 +#endif + +extern int SysLogLevel; +extern void syslog_with_tid(int priority, const char *format, ...) __attribute__ ((format (printf, 2, 3))); + +#define esyslog(a...) void( (SysLogLevel > 0) ? syslog_with_tid(LOG_ERR, a) : void() ) +#define isyslog(a...) void( (SysLogLevel > 1) ? syslog_with_tid(LOG_ERR, a) : void() ) +#define dsyslog(a...) void( (SysLogLevel > 2) ? syslog_with_tid(LOG_ERR, a) : void() ) +#define tsyslog(a...) void( (SysLogLevel > 3) ? syslog_with_tid(LOG_ERR, a) : void() ) + +#endif diff --git a/command/decoder.cpp b/command/decoder.cpp new file mode 100644 index 0000000..6b01404 --- /dev/null +++ b/command/decoder.cpp @@ -0,0 +1,478 @@ +/* + * decoder.cpp: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "decoder.h" + +cMarkAdDecoder::cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3) +{ + avcodec_init(); + avcodec_register_all(); + + last_qscale_table=NULL; + + cpu_set_t cpumask; + uint len = sizeof(cpumask); + int cpucount; + if (sched_getaffinity(0,len,&cpumask)<0) + { + cpucount=1; + } + else + { + cpucount=CPU_COUNT(&cpumask); + } + + int ver = avcodec_version(); + char libver[256]; + snprintf(libver,sizeof(libver),"%i.%i.%i",ver >> 16 & 0xFF,ver >> 8 & 0xFF,ver & 0xFF); + isyslog("using libavcodec.so.%s with %i threads",libver,cpucount); + + if (ver!=LIBAVCODEC_VERSION_INT) + { + esyslog("libavcodec header version %s",AV_STRINGIFY(LIBAVCODEC_VERSION)); + esyslog("header and library mismatch, decoding disabled"); + video_context=NULL; + ac3_context=NULL; + mp2_context=NULL; + audiobuf=NULL; + return; + } + + if (((ver >> 16)<52) && (useH264)) + { + esyslog("dont report bugs about H264, use libavcodec >= 52 instead!"); + } + + if (useMP2) + { + CodecID mp2_codecid=CODEC_ID_MP2; + AVCodec *mp2_codec= avcodec_find_decoder(mp2_codecid); + if (mp2_codec) + { + mp2_context = avcodec_alloc_context(); + if (mp2_context) + { + mp2_context->thread_count=cpucount; + mp2_context->codec_id = mp2_codecid; + mp2_context->codec_type = CODEC_TYPE_AUDIO; + if (avcodec_open(mp2_context, mp2_codec) < 0) + { + esyslog("could not open codec for MP2"); + av_free(mp2_context); + mp2_context=NULL; + } + } + else + { + esyslog("could not allocate mp2 context"); + } + } + else + { + esyslog("codec for MP2 not found"); + mp2_context=NULL; + } + } + else + { + mp2_context=NULL; + } + + if (hasAC3) + { + CodecID ac3_codecid=CODEC_ID_AC3; + AVCodec *ac3_codec= avcodec_find_decoder(ac3_codecid); + if (ac3_codec) + { + ac3_context = avcodec_alloc_context(); + if (ac3_context) + { + ac3_context->thread_count=cpucount; + ac3_context->codec_id = ac3_codecid; + ac3_context->codec_type = CODEC_TYPE_AUDIO; + if (avcodec_open(ac3_context, ac3_codec) < 0) + { + esyslog("could not open codec for AC3"); + av_free(ac3_context); + ac3_context=NULL; + } + } + else + { + esyslog("could not allocate ac3 context"); + } + } + else + { + esyslog("codec for AC3 not found"); + ac3_context=NULL; + } + } + else + { + ac3_context=NULL; + } + + AVCodec *video_codec=NULL; + CodecID video_codecid; + + if (useH264) + { + video_codecid=CODEC_ID_H264; + } + else + { + video_codecid=CODEC_ID_MPEG2VIDEO_XVMC; + } + + video_codec = avcodec_find_decoder(video_codecid); + if ((!video_codec) && (video_codecid==CODEC_ID_MPEG2VIDEO_XVMC)) + { + // fallback to MPEG2VIDEO + video_codecid=CODEC_ID_MPEG2VIDEO; + video_codec=avcodec_find_decoder(video_codecid); + } + + if (video_codec) + { + video_context = avcodec_alloc_context(); + if (video_context) + { + 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; + + 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); + if ((ret < 0) && (video_codecid==CODEC_ID_MPEG2VIDEO_XVMC)) + { + // fallback to MPEG2VIDEO + video_codecid=CODEC_ID_MPEG2VIDEO; + video_codec=avcodec_find_decoder(video_codecid); + if (video_codec) + { + video_context->codec_type=CODEC_TYPE_UNKNOWN; + video_context->codec_id=CODEC_ID_NONE; + video_context->codec_tag=0; + memset(video_context->codec_name,0,sizeof(video_context->codec_name)); + ret=avcodec_open(video_context, video_codec); + } + else + { + ret=-1; + } + } + if (ret < 0) + { + switch (video_codecid) + { + case CODEC_ID_H264: + esyslog("could not open codec for H264"); + break; + case CODEC_ID_MPEG2VIDEO_XVMC: + esyslog("could not open codec MPEG2 (XVMC)"); + break; + case CODEC_ID_MPEG2VIDEO: + esyslog("could not open codec MPEG2"); + break; + default: + esyslog("could not open video codec"); + break; + } + av_free(video_context); + video_context=NULL; + } + else + { + isyslog("using codec %s",video_codec->long_name); + +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(22<<8)+2) + if (video_context->hwaccel) + { + isyslog("using hwaccel %s",video_context->hwaccel->name); + } +#endif + + video_frame = avcodec_alloc_frame(); + if (!video_frame) + { + esyslog("could not allocate frame"); + avcodec_close(video_context); + av_free(video_context); + video_context=NULL; + } + } + } + else + { + esyslog("could not allocate video context"); + } + } + else + { + switch (video_codecid) + { + case CODEC_ID_H264: + esyslog("codec for H264 not found"); + break; + case CODEC_ID_MPEG2VIDEO_XVMC: + esyslog("codec for MPEG2 (XVMC) not found"); + break; + case CODEC_ID_MPEG2VIDEO: + esyslog("codec for MPEG2 not found"); + break; + default: + esyslog("video codec not found"); + break; + } + video_context=NULL; + } + + if ((ac3_context) || (mp2_context)) + { + audiobuf=(int16_t*) malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE); + } + else + { + audiobuf=NULL; + } + +} + +cMarkAdDecoder::~cMarkAdDecoder() +{ + if (video_context) + { + avcodec_close(video_context); + av_free(video_context); + av_free(video_frame); + } + + if (ac3_context) + { + avcodec_close(ac3_context); + av_free(ac3_context); + } + + if (mp2_context) + { + avcodec_close(mp2_context); + av_free(mp2_context); + } + if (audiobuf) free(audiobuf); +} + +bool cMarkAdDecoder::DecodeMP2(MarkAdContext *maContext, uchar *espkt, int eslen) +{ + if (!mp2_context) return false; + maContext->Audio.Data.Valid=false; + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=espkt; + avpkt.size=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, + avpkt.data,avpkt.size); +#else + int len=avcodec_decode_audio3(mp2_context,Taudiobuf,&audiobufsize,&avpkt); +#endif + if (len<0) + { + esyslog("error decoding mp2"); + break; + } + if (audiobufsize>0) + { + memcpy(audiobuf,Taudiobuf,audiobufsize); + SetAudioInfos(maContext,mp2_context); + ret=true; + avpkt.size-=len; + avpkt.data+=len; + } + } + 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; + maContext->Audio.Data.Valid=false; + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=espkt; + 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, + avpkt.data,avpkt.size); +#else + int len=avcodec_decode_audio3(ac3_context,Taudiobuf,&audiobufsize,&avpkt); +#endif + if (len<0) + { + esyslog("error decoding ac3"); + break; + } + if (audiobufsize>0) + { + memcpy(audiobuf,Taudiobuf,audiobufsize); + SetAudioInfos(maContext,ac3_context); + ret=true; + avpkt.size-=len; + avpkt.data+=len; + } + } + 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; + for (int i=0; i<4; i++) + { + if (Video_Frame->data[i]) + { + maContext->Video.Data.Plane[i]=Video_Frame->data[i]; + maContext->Video.Data.PlaneLinesize[i]=Video_Frame->linesize[i]; + maContext->Video.Data.Valid=true; + } + } + 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; + maContext->Video.Data.Valid=false; + + if ((video_context->codec_id==CODEC_ID_H264) && (!video_context->skip_frame)) + { + if (maContext->Video.Info.Pict_Type) + { + if (maContext->Video.Info.Interlaced) + { + video_context->skip_frame=AVDISCARD_BIDIR; // just P/I-frames + } + else + { + video_context->skip_frame=AVDISCARD_NONKEY; // just I-frames + } + } + } + + AVPacket avpkt; +#if LIBAVCODEC_VERSION_INT >= ((52<<16)+(25<<8)+0) + av_init_packet(&avpkt); +#else + memset(&avpkt,0,sizeof(avpkt)); + avpkt.pts = avpkt.dts = AV_NOPTS_VALUE; + avpkt.pos = -1; +#endif + avpkt.data=pkt; + avpkt.size=plen; + + // decode video + int video_frame_ready=0; + int len,ret=false; + + while (avpkt.size>0) + { +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(25<<8)+0) + len=avcodec_decode_video(video_context,video_frame,&video_frame_ready, + avpkt.data,avpkt.size); +#else + len=avcodec_decode_video2(video_context,video_frame,&video_frame_ready, + &avpkt); +#endif + if (len<0) + { + esyslog("error decoding video"); + break; + } + else + { + avpkt.size-=len; + avpkt.data+=len; + } + if (video_frame_ready) + { + if (last_qscale_table!=video_frame->qscale_table) + { + if (SetVideoInfos(maContext,video_context,video_frame)) ret=true; + last_qscale_table=video_frame->qscale_table; + } + } + } + return ret; +} diff --git a/command/decoder.h b/command/decoder.h new file mode 100644 index 0000000..46e9f74 --- /dev/null +++ b/command/decoder.h @@ -0,0 +1,72 @@ +/* + * decoder.h: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __decoder_h_ +#define __decoder_h_ + +#define __STDC_CONSTANT_MACROS + +#include +#include + +#ifndef DECLARE_ALIGNED +#define DECLARE_ALIGNED(n,t,v) t v __attribute__ ((aligned (n))) +#endif + +#ifndef CPU_COUNT +#define CPU_COUNT(i) 1 // very crude ;) +#endif + +#ifndef uchar +typedef unsigned char uchar; +#endif + +extern "C" +{ +#include + +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0) +#warning H264 parsing may be broken, better use libavcodec52 +#endif + +#if LIBAVCODEC_VERSION_INT < ((52<<16)+(23<<8)+0) +#include +#endif +#include "debug.h" +} + +#include "global.h" + +class cMarkAdDecoder +{ +private: + int16_t *audiobuf; + int audiobufsize; + + AVCodecContext *ac3_context; + AVCodecContext *mp2_context; + AVCodecContext *video_context; + AVFrame *video_frame; + + int8_t *last_qscale_table; + + 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); + cMarkAdDecoder(bool useH264, bool useMP2, bool hasAC3); + ~cMarkAdDecoder(); +}; + + + +#endif diff --git a/command/demux.cpp b/command/demux.cpp new file mode 100644 index 0000000..55113a8 --- /dev/null +++ b/command/demux.cpp @@ -0,0 +1,190 @@ +/* + * demux.cpp: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "demux.h" + +cMarkAdDemux::cMarkAdDemux() +{ + ts2pkt=NULL; + vdr2pkt=NULL; + pes2audioes=NULL; + pes2videoes=NULL; + pause=false; + pause_retval=0; + queue = new cMarkAdPaketQueue(NULL,376); +} + +cMarkAdDemux::~cMarkAdDemux() +{ + if (ts2pkt) delete ts2pkt; + if (vdr2pkt) delete vdr2pkt; + if (pes2audioes) delete pes2audioes; + if (pes2videoes) delete pes2videoes; + if (queue) delete queue; +} + +void cMarkAdDemux::ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen) +{ + if ((!Pkt) || (!PktLen)) return; + *Pkt=NULL; + *PktLen=0; + + uchar *pkt; + int pktlen; + + if (!vdr2pkt) + { + if ((Pid.Type==MARKAD_PIDTYPE_AUDIO_AC3) || (Pid.Type==MARKAD_PIDTYPE_AUDIO_MP2)) + { + vdr2pkt= new cMarkAdVDR2Pkt("VDR2PKT audio"); + } + else + { + vdr2pkt= new cMarkAdVDR2Pkt("VDR2PKT video"); + } + } + if (!vdr2pkt) return; + + vdr2pkt->Process(Pid,Data,Count,&pkt,&pktlen); + + 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); + } + + if ((Pid.Type==MARKAD_PIDTYPE_VIDEO_H262) || (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264)) + { + if (!pes2videoes) + { + if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) + { + pes2videoes=new cMarkAdPES2ES("PES2H264ES video",393216); + } + else + { + pes2videoes=new cMarkAdPES2ES("PES2ES video",65536); + } + } + if (!pes2videoes) return; + pes2videoes->Process(Pid,pkt,pktlen,Pkt,PktLen); + } + + return; +} + +void cMarkAdDemux::ProcessTS(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen) +{ + if ((!Pkt) || (!PktLen)) return; + *Pkt=NULL; + *PktLen=0; + + uchar *pkt; + int pktlen; + + if (!ts2pkt) + { + if (Pid.Type==MARKAD_PIDTYPE_VIDEO_H264) + { + ts2pkt=new cMarkAdTS2Pkt("TS2H264",819200); + } + else + { + ts2pkt=new cMarkAdTS2Pkt("TS2PKT",262144); + } + } + if (!ts2pkt) return; + + ts2pkt->Process(Pid,Data,Count,&pkt,&pktlen); + + 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); + } + + 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)) + { + ts2pkt->InjectVideoPES(pkt,pktlen); + pkt=NULL; + pktlen=0; + } + } + + if ((pkt) && (!*Pkt)) + { + *Pkt=pkt; + *PktLen=pktlen; + } + + return; +} + +int cMarkAdDemux::Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen) +{ + if ((!Data) && (!Count) && (!Pkt) || (!PktLen)) return -1; + + *Pkt=NULL; + *PktLen=0; + + uchar *in=NULL; + int inlen=0; + int retval=0; + + if (!pause) + { + int min_needed=TS_SIZE; + + int needed=min_needed-queue->Length(); + if (Count>needed) + { + queue->Put(Data,needed); + retval=needed; + } + else + { + queue->Put(Data,Count); + retval=Count; + } + if (queue->Length()Get(&inlen); + } + + if (Pid.Num>=0) + { + ProcessTS(Pid, in, inlen, Pkt, PktLen); + } + else + { + ProcessVDR(Pid, in, inlen, Pkt, PktLen); + } + + if (*Pkt) + { + if (!pause_retval) pause_retval=retval; + pause=true; + return 0; + } + + if (pause) + { + if (pause_retval) + { + retval=pause_retval; + pause_retval=0; + } + pause=false; + } + + return retval; +} diff --git a/command/demux.h b/command/demux.h new file mode 100644 index 0000000..3cb85cc --- /dev/null +++ b/command/demux.h @@ -0,0 +1,45 @@ +/* + * demux.h: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __demux_h_ +#define __demux_h_ + +#ifndef TS_SIZE +#define TS_SIZE 188 +#endif + +#ifndef VDR_SIZE +#define VDR_SIZE 2048 +#endif + +#include "global.h" +#include "queue.h" +#include "vdr2pkt.h" +#include "ts2pkt.h" +#include "pes2es.h" + +class cMarkAdDemux +{ +private: + cMarkAdVDR2Pkt *vdr2pkt; + cMarkAdTS2Pkt *ts2pkt; + cMarkAdPES2ES *pes2audioes; + cMarkAdPES2ES *pes2videoes; + cMarkAdPaketQueue *queue; + + bool pause; + int pause_retval; + + void ProcessTS(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen); + void ProcessVDR(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen); +public: + cMarkAdDemux(); + ~cMarkAdDemux(); + int Process(MarkAdPid Pid, uchar *Data, int Count, uchar **Pkt, int *PktLen); +}; + +#endif diff --git a/command/global.h b/command/global.h new file mode 100644 index 0000000..1034ded --- /dev/null +++ b/command/global.h @@ -0,0 +1,139 @@ +/* + * global.h: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#ifndef __global_h_ +#define __global_h_ + +#include + +#ifndef uchar +typedef unsigned char uchar; +#endif + +#define MA_I_TYPE 1 +#define MA_P_TYPE 2 +#define MA_B_TYPE 3 +#define MA_D_TYPE 4 +#define MA_SI_TYPE 5 +#define MA_SP_TYPE 6 +#define MA_BI_TYPE 7 + +#define MT_COMMON 0x10 + +#define MT_ASPECTCHANGE 0x20 + +#define MT_CHANNELCHANGE 0x30 +#define MT_CHANNELSTART 0x30 +#define MT_CHANNELSTOP 0x31 + +#define MT_LOGOCHANGE 0x40 +#define MT_LOGOSTART 0x40 +#define MT_LOGOSTOP 0x41 + +#define MT_BORDERCHANGE 0x50 +#define MT_BORDERSTART 0x50 +#define MT_BORDERSTOP 0x51 + +#define MT_SILENCECHANGE 0x60 + +#define MT_MOVED 0xE0 +#define MT_ALL 0xFF + +typedef struct MarkAdMark +{ + int Type; + int Position; + char *Comment; +} MarkAdMark; + +typedef struct MarkAdAspectRatio +{ + int Num; + int Den; +} MarkAdAspectRatio; + +#define MARKAD_PIDTYPE_VIDEO_H262 0x10 +#define MARKAD_PIDTYPE_VIDEO_H264 0x11 +#define MARKAD_PIDTYPE_AUDIO_AC3 0x20 +#define MARKAD_PIDTYPE_AUDIO_MP2 0x21 + +typedef struct MarkAdPid +{ + int Num; + int Type; +} MarkAdPid; + +typedef struct MarkAdContext +{ + char *LogoDir; // Logo Directory, default /var/lib/markad + + struct Options + { + int LogoExtraction; + int LogoWidth; + int LogoHeight; + bool ASD; + } Options; + + struct Info + { + int Length; // in Minutes + char *ChannelID; + MarkAdPid VPid; + MarkAdPid APid; + MarkAdPid DPid; + } Info; + + struct Video + { + struct Options + { + bool IgnoreAspectRatio; + bool IgnoreLogoDetection; + } Options; + + struct Info + { + int Width; // width of pic + int Height; // height of pic + int Pict_Type; // picture type (I,P,B,S,SI,SP,BI) + MarkAdAspectRatio AspectRatio; + double FramesPerSecond; + bool Interlaced; + } Info; + + struct Data + { + bool Valid; // flag, if true data is valid + uchar *Plane[4]; // picture planes (YUV420) + int PlaneLinesize[4]; // size int bytes of each picture plane line + } Data; + } Video; + + struct Audio + { + struct Options + { + bool AudioSilenceDetection; + } Options; + + struct Info + { + int Channels; // number of audio channels + int SampleRate; + } Info; + struct Data + { + bool Valid; + short *SampleBuf; + int SampleBufLen; + } Data; + } Audio; + +} MarkAdContext; + +#endif diff --git a/command/markad-standalone.cpp b/command/markad-standalone.cpp new file mode 100644 index 0000000..9d4cb82 --- /dev/null +++ b/command/markad-standalone.cpp @@ -0,0 +1,1632 @@ +/* + * markad-standalone.cpp: A program for the Video Disk Recorder + * + * See the README file for copyright information and how to reach the author. + * + */ + +#include "markad-standalone.h" + +bool SYSLOG=false; +cMarkAdStandalone *cmasta=NULL; +int SysLogLevel=2; + +void syslog_with_tid(int priority, const char *format, ...) +{ + va_list ap; + if (SYSLOG) + { + char fmt[255]; + snprintf(fmt, sizeof(fmt), "[%d] %s", getpid(), format); + va_start(ap, format); + vsyslog(priority, fmt, ap); + va_end(ap); + } + else + { + char fmt[255]; + snprintf(fmt, sizeof(fmt), "markad: [%d] %s", getpid(), format); + va_start(ap, format); + vprintf(fmt,ap); + va_end(ap); + printf("\n"); + fflush(stdout); + } +} + +void cMarkAdStandalone::AddStartMark() +{ + char *buf; + if (asprintf(&buf,"start of recording (0)")!=-1) + { + marks.Add(MT_COMMON,0,buf); + isyslog(buf); + free(buf); + } +} + +bool cMarkAdStandalone::CheckFirstMark() +{ + if (marksAligned) return true; + + // Check the second mark + clMark *second=marks.GetNext(0); + if (!second) return false; + + if ((second->type==MT_LOGOSTART) || (second->type==MT_BORDERSTART) || + (second->type==MT_CHANNELSTART)) + { + clMark *first=marks.Get(0); + if (first) marks.Del(first); + marksAligned=true; + } + + if ((second->type==MT_LOGOSTOP) || (second->type==MT_BORDERSTOP) || + (second->type==MT_CHANNELSTOP)) + { + marksAligned=true; + } + + // If we have an aspectchange, check the next aspectchange mark + // and the difference between + if ((second->type==MT_ASPECTCHANGE) && (macontext.Video.Info.FramesPerSecond>0)) + { + clMark *next=marks.GetNext(second->position,MT_ASPECTCHANGE); + if (next) + { + int MAXPOSDIFF=(int) (macontext.Video.Info.FramesPerSecond*60*13); + if ((next->position-second->position)>MAXPOSDIFF) + { + clMark *first=marks.Get(0); + if (first) marks.Del(first); + marksAligned=true; + } + } + } + return marksAligned; +} + +void cMarkAdStandalone::AddMark(MarkAdMark *Mark) +{ + if (!Mark) return; + if (!Mark->Type) return; + + if (Mark->Type==MT_LOGOSTART) + { + // check if last mark is an aspectratio change + clMark *prev=marks.GetLast(); + if ((prev) && (prev->type==MT_ASPECTCHANGE)) + { + if ((Mark->Position-prev->position)<20) + { + if (Mark->Comment) isyslog(Mark->Comment); + isyslog("aspectratio change in short distance, moving mark (%i->%i)",Mark->Position,prev->position); + marks.Add(MT_MOVED,prev->position,Mark->Comment); + return; + } + } + } + + if (((Mark->Type & 0xF0)==MT_LOGOCHANGE) && (macontext.Video.Info.FramesPerSecond>0)) + { + // check if the distance to the last mark is large enough! + clMark *prev=marks.GetLast(); + if ((prev) && ((prev->type & 0xF0)==MT_LOGOCHANGE)) + { + int MINMARKDIFF=(int) (macontext.Video.Info.FramesPerSecond*60*2); + if ((Mark->Position-prev->position)Comment) isyslog(Mark->Comment); + isyslog("logo distance too short, deleting (%i,%i)",prev->position,Mark->Position); + marks.Del(prev); + return; + } + } + } + + if (((Mark->Type & 0xF0)==MT_BORDERCHANGE) && (Mark->Position>25000) && + (!macontext.Video.Options.IgnoreLogoDetection)) + { + isyslog("border change detected. logo detection disabled"); + macontext.Video.Options.IgnoreLogoDetection=true; + marks.Del(MT_LOGOSTART); + marks.Del(MT_LOGOSTOP); + } + + if ((((Mark->Type & 0xF0)==MT_CHANNELCHANGE) || (Mark->Type==MT_ASPECTCHANGE)) && + (Mark->Position>25000) && (bDecodeVideo)) + { + bool TurnOff=true; + if (Mark->Type==MT_ASPECTCHANGE) + { + if (marks.Count(MT_ASPECTCHANGE)<3) + { + TurnOff=false; + } + } + + if (Mark->Type==MT_CHANNELCHANGE) + { + if ((marks.Count(MT_CHANNELSTART)+marks.Count(MT_CHANNELSTOP))<3) + { + TurnOff=false; + } + } + + if (TurnOff) + { + isyslog("%s change detected. logo/border detection disabled", + Mark->Type==MT_ASPECTCHANGE ? "aspectratio" : "audio channel"); + + bDecodeVideo=false; + macontext.Video.Data.Valid=false; + marks.Del(MT_LOGOSTART); + marks.Del(MT_LOGOSTOP); + marks.Del(MT_BORDERSTART); + marks.Del(MT_BORDERSTOP); + } + } + CheckFirstMark(); + clMark *old=marks.Get(Mark->Position); + if (old) + { + // Aspect- / Channelchange wins over Logo/Border + if (((old->type & 0xF0)!=MT_ASPECTCHANGE) && ((old->type & 0xF0)!=MT_CHANNELCHANGE)) + { + if (Mark->Comment) isyslog(Mark->Comment); + marks.Add(Mark->Type,Mark->Position,Mark->Comment); + } + } + else + { + if (Mark->Comment) isyslog(Mark->Comment); + marks.Add(Mark->Type,Mark->Position,Mark->Comment); + } +} + +void cMarkAdStandalone::RateMarks() +{ + if (macontext.Info.Length) + { + if ((marks.Count()>3) && (macontext.Info.Length>30)) + { + int logomarks=marks.Count(MT_LOGOSTART) + marks.Count(MT_LOGOSTOP); + int audiomarks=marks.Count(MT_CHANNELSTART) + marks.Count(MT_CHANNELSTOP); + + // If we have logomarks or audiomarks get rid of the aspect changes, + // cause if we have a recording with (>=3) aspect changes the + // logomarks were already deleted in AddMark + if ((logomarks) || (audiomarks)) + { + marks.Del(MT_ASPECTCHANGE); + } + } + } + + // Check the first mark again + CheckFirstMark(); + + // TODO: more checks +} + +void cMarkAdStandalone::SaveFrame(int frame) +{ + if (!macontext.Video.Info.Width) return; + if (!macontext.Video.Data.Valid) return; + + FILE *pFile; + char szFilename[256]; + + // Open file + sprintf(szFilename, "/tmp/frame%06d.pgm", frame); + pFile=fopen(szFilename, "wb"); + if (pFile==NULL) + return; + + // Write header + fprintf(pFile, "P5\n%d %d\n255\n", macontext.Video.Data.PlaneLinesize[0], + macontext.Video.Info.Height); + + // Write pixel data + fwrite(macontext.Video.Data.Plane[0],1, + macontext.Video.Data.PlaneLinesize[0]*macontext.Video.Info.Height,pFile); + // Close file + fclose(pFile); +} + +void cMarkAdStandalone::CheckIndex(const char *Directory) +{ + // Here we check if the index is more + // advanced than our framecounter. + // If not we wait. If we wait too much, + // we discard this check... + +#define WAITTIME 10 + + if (!indexFile) return; + if (sleepcnt>=2) return; // we already slept too much + + bool notenough=true; + do + { + struct stat statbuf; + if (stat(indexFile,&statbuf)==-1) return; + + int maxframes=statbuf.st_size/8; + if (maxframes<(framecnt+200)) + { + if ((difftime(time(NULL),statbuf.st_mtime))>=10) return; // "old" file + 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; + sleepcnt++; + if (sleepcnt>=2) + { + esyslog("no new data after %i seconds, skipping wait!", + sleepcnt*WAITTIME); + notenough=false; // something went wrong? + } + } + else + { + sleepcnt=0; + notenough=false; + } + } + while (notenough); +} + +bool cMarkAdStandalone::ProcessFile(const char *Directory, int Number) +{ + if (!Directory) return false; + if (!Number) return false; + + CheckIndex(Directory); + if (abort) return false; + + const int datalen=385024; + 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; + dsyslog("processing file %05i",Number); + + while ((dataread=read(f,data,datalen))>0) + { + if (abort) break; + MarkAdMark *mark=NULL; + + if ((video_demux) && (video) && (streaminfo)) + { + uchar *pkt; + int pktlen; + + uchar *tspkt = data; + int tslen = dataread; + + while (tslen>0) + { + int len=video_demux->Process(macontext.Info.VPid,tspkt,tslen,&pkt,&pktlen); + if (len<0) + { + break; + } + else + { + if (pkt) + { + if (streaminfo->FindVideoInfos(&macontext,pkt,pktlen)) + { + if (!framecnt) + { + isyslog("%s %i%c",(macontext.Video.Info.Height>576) ? "HDTV" : "SDTV", + macontext.Video.Info.Height, + macontext.Video.Info.Interlaced ? 'i' : 'p'); + AddStartMark(); + } + //printf("%05i( %c )\n",framecnt,frametypes[macontext.Video.Info.Pict_Type]); + framecnt++; + if (macontext.Video.Info.Pict_Type==MA_I_TYPE) + { + lastiframe=iframe; + iframe=framecnt-1; + } + } + + bool dRes=true; + if ((decoder) && (bDecodeVideo)) dRes=decoder->DecodeVideo(&macontext,pkt,pktlen); + if (dRes) + { + if ((framecnt-iframe)<=3) + { + mark=video->Process(lastiframe); + AddMark(mark); + //SaveFrame(lastiframe); // TODO: JUST FOR DEBUGGING! + } + } + } + tspkt+=len; + tslen-=len; + } + } + } + + if ((ac3_demux) && (streaminfo) && (audio)) + { + uchar *pkt; + int pktlen; + + uchar *tspkt = data; + int tslen = dataread; + + while (tslen>0) + { + int len=ac3_demux->Process(macontext.Info.DPid,tspkt,tslen,&pkt,&pktlen); + if (len<0) + { + break; + } + else + { + if (pkt) + { + if (streaminfo->FindAC3AudioInfos(&macontext,pkt,pktlen)) + { + 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 ((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); + if (len<0) + { + break; + } + else + { + if (pkt) + { + if (decoder->DecodeMP2(&macontext,pkt,pktlen)) + { + if ((!isTS) && (!noticeVDR_MP2)) + { + dsyslog("found MP2"); + noticeVDR_MP2=true; + } + mark=audio->Process(lastiframe); + AddMark(mark); + } + } + tspkt+=len; + tslen-=len; + } + } + } + + CheckIndex(Directory); + if (abort) + { + if (f!=-1) close(f); + return false; + } + } + close(f); + return true; +} + +void cMarkAdStandalone::Process(const char *Directory) +{ + if (abort) return; + + struct timeval tv1,tv2; + struct timezone tz; + + gettimeofday(&tv1,&tz); + + if (bBackupMarks) marks.Backup(Directory,isTS); + + for (int i=1; i<=MaxFiles; i++) + { + if (abort) break; + if (!ProcessFile(Directory,i)) + { + break; + } + } + if (abort) + { + isyslog("aborted"); + } + else + { + if (lastiframe) + { + MarkAdMark tempmark; + tempmark.Type=MT_COMMON; + tempmark.Position=lastiframe; + char *buf; + + if (asprintf(&buf,"stop of recording (%i)",lastiframe)!=-1) + { + tempmark.Comment=buf; + AddMark(&tempmark); + free(buf); + } + } + + 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--; + } + RateMarks(); + if (marks.Save(Directory,macontext.Video.Info.FramesPerSecond,isTS)) + { + bool bIndexError=false; + if (marks.CheckIndex(Directory,isTS,&bIndexError)) + { + if (bIndexError) + { + if ((macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264) && (!isTS)) + { + esyslog("index doesn't match marks, sorry you're lost"); + } + else + { + esyslog("index doesn't match marks%s", + isTS ? ", please recreate it" : ", please run genindex"); + } + } + } + } + + 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); + + } +} + +bool cMarkAdStandalone::LoadInfo(const char *Directory) +{ + char *buf; + if (isTS) + { + if (asprintf(&buf,"%s/info",Directory)==-1) return false; + } + else + { + if (asprintf(&buf,"%s/info.vdr",Directory)==-1) return false; + } + + FILE *f; + f=fopen(buf,"r"); + if (!f) + { + free(buf); + return false; + } + + char *line=NULL; + size_t length; + while (getline(&line,&length,f)!=-1) + { + if (line[0]=='C') + { + int ntok=0; + char *str=line,*tok; + while ((tok=strtok(str," "))) + { + if (ntok==1) macontext.Info.ChannelID=strdup(tok); + ntok++; + str=NULL; + } + if (macontext.Info.ChannelID) + { + for (int i=0; i<(int) strlen(macontext.Info.ChannelID); i++) + { + if (macontext.Info.ChannelID[i]=='.') macontext.Info.ChannelID[i]='_'; + } + } + } + if (line[0]=='E') + { + int result=sscanf(line,"%*c %*i %*i %i %*i %*x",&macontext.Info.Length); + if (result==1) + { + macontext.Info.Length/=60; + } + else + { + macontext.Info.Length=0; + } + if ((bIgnoreAudioInfo) && (bIgnoreVideoInfo)) break; + } + if (line[0]=='X') + { + int stream=0,type=0; + char descr[256]=""; + int result=sscanf(line,"%*c %i %i %250c",&stream,&type,(char *) &descr); + if ((result!=0) && (result!=EOF)) + { + if ((stream==1) && (!bIgnoreVideoInfo)) + { + if ((type!=1) && (type!=5)) + { + // we dont have 4:3, so ignore AspectRatio-Changes + macontext.Video.Options.IgnoreAspectRatio=true; + isyslog("broadcasts aspectratio is not 4:3, disabling aspect ratio"); + } + } + + if ((stream==2) && (!bIgnoreAudioInfo)) + { + if (type==5) + { + // if we have DolbyDigital 2.0 disable AC3 + if (strchr(descr,'2')) + { + macontext.Info.DPid.Num=0; + isyslog("broadcast with DolbyDigital2.0, disabling AC3 decoding"); + } + // if we have DolbyDigital 5.1 disable video decoding + if (strchr(descr,'5')) + { + bDecodeVideo=false; + isyslog("broadcast with DolbyDigital5.1, disabling video decoding"); + } + + } + } + } + } + } + if (line) free(line); + + fclose(f); + free(buf); + if (!macontext.Info.ChannelID) + { + return false; + } + else + { + return true; + } +} + +bool cMarkAdStandalone::CheckTS(const char *Directory) +{ + MaxFiles=0; + isTS=false; + if (!Directory) return false; + char *buf; + if (asprintf(&buf,"%s/00001.ts",Directory)==-1) return false; + struct stat statbuf; + if (stat(buf,&statbuf)==-1) + { + if (errno!=ENOENT) + { + free(buf); + return false; + } + free(buf); + if (asprintf(&buf,"%s/001.vdr",Directory)==-1) return false; + if (stat(buf,&statbuf)==-1) + { + free(buf); + return false; + } + free(buf); + // .VDR detected + isTS=false; + MaxFiles=999; + return true; + } + free(buf); + // .TS detected + isTS=true; + MaxFiles=65535; + return true; +} + +bool cMarkAdStandalone::CheckVDRHD(const char *Directory) +{ + char *buf; + if (asprintf(&buf,"%s/001.vdr",Directory)==-1) return false; + + int fd=open(buf,O_RDONLY); + free(buf); + if (fd==-1) return false; + + uchar pes_buf[32]; + if (read(fd,pes_buf,sizeof(pes_buf))!=sizeof(pes_buf)) + { + close(fd); + return false; + } + close(fd); + + if ((pes_buf[0]==0) && (pes_buf[1]==0) && (pes_buf[2]==1) && ((pes_buf[3] & 0xF0)==0xE0)) + { + int payloadstart=9+pes_buf[8]; + if (payloadstart>23) return false; + uchar *start=&pes_buf[payloadstart]; + if ((start[0]==0) && (start[1]==0) && (start[2]==1) && (start[5]==0) && (start[6]==0) + && (start[7]==0) && (start[8]==1)) + { + return true; + } + } + return false; +} + +bool cMarkAdStandalone::CheckPATPMT(const char *Directory) +{ + char *buf; + if (asprintf(&buf,"%s/00001.ts",Directory)==-1) return false; + + int fd=open(buf,O_RDONLY); + free(buf); + if (fd==-1) return false; + + uchar patpmt_buf[564]; + uchar *patpmt; + + if (read(fd,patpmt_buf,sizeof(patpmt_buf))!=sizeof(patpmt_buf)) + { + close(fd); + return false; + } + close(fd); + patpmt=patpmt_buf; + + if ((patpmt[0]==0x47) && ((patpmt[1] & 0x5F)==0x40) && (patpmt[2]==0x11) && + ((patpmt[3] & 0x10)==0x10)) patpmt+=188; // skip SDT + + // some checks + if ((patpmt[0]!=0x47) || (patpmt[188]!=0x47)) return false; // no TS-Sync + if (((patpmt[1] & 0x5F)!=0x40) && (patpmt[2]!=0)) return false; // no PAT + if ((patpmt[3] & 0x10)!=0x10) return false; // PAT not without AFC + if ((patpmt[191] & 0x10)!=0x10) return false; // PMT not without AFC + struct PAT *pat = (struct PAT *) &patpmt[5]; + + // more checks + if (pat->reserved1!=3) return false; // is always 11 + if (pat->reserved3!=7) return false; // is always 111 + + int pid=pat->pid_L+(pat->pid_H<<8); + int pmtpid=((patpmt[189] & 0x1f)<<8)+patpmt[190]; + if (pid!=pmtpid) return false; // pid in PAT differs from pid in PMT + + struct PMT *pmt = (struct PMT *) &patpmt[193]; + + // still more checks + if (pmt->reserved1!=3) return false; // is always 11 + if (pmt->reserved2!=3) return false; // is always 11 + if (pmt->reserved3!=7) return false; // is always 111 + if (pmt->reserved4!=15) return false; // is always 1111 + + if ((pmt->program_number_H!=pat->program_number_H) || + (pmt->program_number_L!=pat->program_number_L)) return false; + + int desc_len=(pmt->program_info_length_H<<8)+pmt->program_info_length_L; + if (desc_len>166) return false; // beyond patpmt buffer + + int section_end = 196+(pmt->section_length_H<<8)+pmt->section_length_L; + section_end-=4; // we don't care about the CRC32 + if (section_end>376) return false; //beyond patpmt buffer + + int i=205+desc_len; + + while (iES_info_length_H<<8)+si->ES_info_length_L; + if (esinfo_len) + { + es = (struct ES_DESCRIPTOR *) &patpmt[i+sizeof(struct STREAMINFO)]; + } + + // oh no -> more checks! + if (si->reserved1!=7) return false; + if (si->reserved2!=15) return false; + + int pid=(si->PID_H<<8)+si->PID_L; + + switch (si->stream_type) + { + case 0x1: + case 0x2: + macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H262; + // just use the first pid + if (!macontext.Info.VPid.Num) macontext.Info.VPid.Num=pid; + break; + + case 0x3: + case 0x4: + // just use the first pid + if (!macontext.Info.APid.Num) macontext.Info.APid.Num=pid; + break; + + case 0x6: + if (es) + { + if (es->Descriptor_Tag==0x6A) macontext.Info.DPid.Num=pid; + } + break; + + case 0x1b: + macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H264; + // just use the first pid + if (!macontext.Info.VPid.Num) macontext.Info.VPid.Num=pid; + break; + } + + i+=(sizeof(struct STREAMINFO)+esinfo_len); + } + + return true; +} + +bool cMarkAdStandalone::CreatePidfile(const char *Directory) +{ + char *buf; + if (asprintf(&buf,"%s/markad.pid",Directory)==-1) return false; + + // check for other running markad process + FILE *oldpid=fopen(buf,"r"); + if (oldpid) + { + // found old pidfile, check if it's still running + int pid; + if (fscanf(oldpid,"%i\n",&pid)==1) + { + char procname[256]=""; + snprintf(procname,sizeof(procname),"/proc/%i",pid); + struct stat statbuf; + if (stat(procname,&statbuf)!=-1) + { + // found another, running markad + isyslog("another instance is running on this recording"); + abort=duplicate=true; + } + } + fclose(oldpid); + } + if (duplicate) return false; + + FILE *pidfile=fopen(buf,"w+"); + + if (getuid()==0 || geteuid()!=0) + { + // 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) + { + struct stat statbuf; + if (!stat(spath,&statbuf)) + { + chown(buf,statbuf.st_uid, statbuf.st_gid); + } + free(spath); + } + } + + free(buf); + if (!pidfile) return false; + fprintf(pidfile,"%i\n",(int) getpid()); + fflush(pidfile); + fclose(pidfile); + return true; +} + +void cMarkAdStandalone::RemovePidfile(const char *Directory) +{ + char *buf; + if (asprintf(&buf,"%s/markad.pid",Directory)!=-1) + { + unlink(buf); + free(buf); + } +} + +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, bool IgnoreVideoInfo, bool IgnoreAudioInfo, + const char *LogoDir, const char *MarkFileName, bool ASD, + bool noPid) +{ + directory=Directory; + abort=false; + + noticeVDR_MP2=false; + noticeVDR_AC3=false; + + sleepcnt=0; + waittime=0; + duplicate=false; + marksAligned=false; + + memset(&macontext,0,sizeof(macontext)); + macontext.LogoDir=(char *) LogoDir; + macontext.Options.LogoExtraction=LogoExtraction; + macontext.Options.LogoWidth=LogoWidth; + macontext.Options.LogoHeight=LogoHeight; + macontext.Audio.Options.AudioSilenceDetection=ASD; + + bDecodeVideo=DecodeVideo; + bDecodeAudio=DecodeAudio; + bIgnoreAudioInfo=IgnoreAudioInfo; + bIgnoreVideoInfo=IgnoreVideoInfo; + + bBackupMarks=BackupMarks; + + macontext.Info.DPid.Type=MARKAD_PIDTYPE_AUDIO_AC3; + macontext.Info.APid.Type=MARKAD_PIDTYPE_AUDIO_MP2; + + isyslog("starting v%s",VERSION); + isyslog("on %s",Directory); + + if (!bDecodeAudio) + { + isyslog("audio decoding disabled by user"); + } + if (!bDecodeVideo) + { + isyslog("video decoding disabled by user"); + } + if (bIgnoreAudioInfo) + { + isyslog("audio info usage disabled by user"); + } + if (bIgnoreVideoInfo) + { + isyslog("video info usage disabled by user"); + } + + if (LogoExtraction!=-1) + { + // just to be sure extraction works + bDecodeVideo=true; + bIgnoreAudioInfo=true; + bIgnoreVideoInfo=true; + } + + if (!CheckTS(Directory)) + { + video_demux=NULL; + ac3_demux=NULL; + mp2_demux=NULL; + decoder=NULL; + video=NULL; + audio=NULL; + return; + } + + if (!noPid) + { + CreatePidfile(Directory); + if (abort) + { + video_demux=NULL; + ac3_demux=NULL; + mp2_demux=NULL; + decoder=NULL; + video=NULL; + audio=NULL; + return; + } + } + + if (isTS) + { + if (!CheckPATPMT(Directory)) + { + esyslog("no PAT/PMT found -> nothing to process"); + abort=true; + } + if (!macontext.Audio.Options.AudioSilenceDetection) + { + macontext.Info.APid.Num=0; + } + if (asprintf(&indexFile,"%s/index",Directory)==-1) indexFile=NULL; + } + else + { + if (macontext.Audio.Options.AudioSilenceDetection) + { + macontext.Info.APid.Num=-1; + } + macontext.Info.DPid.Num=-1; + macontext.Info.VPid.Num=-1; + + if (CheckVDRHD(Directory)) + { + macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H264; + } + else + { + macontext.Info.VPid.Type=MARKAD_PIDTYPE_VIDEO_H262; + } + if (asprintf(&indexFile,"%s/index.vdr",Directory)==-1) indexFile=NULL; + } + + if (!LoadInfo(Directory)) + { + if (bDecodeVideo) + { + esyslog("failed loading info - logo %s disabled", + (LogoExtraction!=-1) ? "extraction" : "detection"); + } + } + + if (MarkFileName[0]) marks.SetFileName(MarkFileName); + + if (macontext.Info.VPid.Num) + { + if (isTS) + { + dsyslog("using %s-video (0x%04x)", + macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262", + macontext.Info.VPid.Num); + } + else + { + dsyslog("using %s-video", + macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264 ? "H264": "H262"); + } + video_demux = new cMarkAdDemux(); + } + else + { + video_demux=NULL; + } + + if (macontext.Info.APid.Num) + { + if (macontext.Info.APid.Num!=-1) + dsyslog("using MP2 (0x%04x)",macontext.Info.APid.Num); + mp2_demux = new cMarkAdDemux(); + } + else + { + mp2_demux=NULL; + } + + if (macontext.Info.DPid.Num) + { + if (macontext.Info.DPid.Num!=-1) + dsyslog("using AC3 (0x%04x)",macontext.Info.DPid.Num); + ac3_demux = new cMarkAdDemux(); + } + else + { + ac3_demux=NULL; + } + + if (!abort) + { + decoder = new cMarkAdDecoder(macontext.Info.VPid.Type==MARKAD_PIDTYPE_VIDEO_H264, + macontext.Info.APid.Num!=0,macontext.Info.DPid.Num!=0); + video = new cMarkAdVideo(&macontext); + audio = new cMarkAdAudio(&macontext); + streaminfo = new cMarkAdStreamInfo; + if (macontext.Info.ChannelID) + dsyslog("channel %s",macontext.Info.ChannelID); + } + else + { + decoder=NULL; + video=NULL; + audio=NULL; + streaminfo=NULL; + } + + framecnt=0; + lastiframe=0; + iframe=0; +} + +cMarkAdStandalone::~cMarkAdStandalone() +{ + if (macontext.Info.ChannelID) free(macontext.Info.ChannelID); + if (indexFile) free(indexFile); + + if (video_demux) delete video_demux; + if (ac3_demux) delete ac3_demux; + if (mp2_demux) delete mp2_demux; + if (decoder) delete decoder; + if (video) delete video; + if (audio) delete audio; + if (streaminfo) delete streaminfo; + + if ((directory) && (!duplicate)) RemovePidfile(directory); +} + +bool isnumber(const char *s) +{ + while (*s) + { + if (!isdigit(*s)) + return false; + s++; + } + return true; +} + +int usage() +{ + // nothing done, give the user some help + printf("Usage: markad [options] cmd \n" + "options:\n" + "-b --background\n" + " markad runs as a background-process\n" + " this will be automatically set if called with \"after\"\n" + "-d --disable=