Initial import to berlios.de

--HG--
extra : convert_revision : svn%3Aeff31bef-be4a-0410-a8fe-e47997df2690/trunk%401
issue20
tibs 2008-04-14 04:09:29 +00:00
commit 70e9b431a7
96 zmienionych plików z 56453 dodań i 0 usunięć

373
Makefile 100644
Wyświetl plik

@ -0,0 +1,373 @@
# Makefile for the H.264 Elementary Stream software
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
#
### RUN WITH GNU MAKE
# Gnu make recommends always setting some standard variables
SHELL = /bin/sh
# And re-establishing the required suffix list
.SUFFIXES:
.SUFFIXES: .c .o
ifdef CROSS_COMPILE
CC = $(CROSS_COMPILE)gcc
else
CC = gcc
endif
# Use WARN=1 periodically to get too many warnings...
ifdef WARN
WARNING_FLAGS = -Wall -W -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wcast-qual -Wconversion -Wmissing-prototypes -Wmissing-declarations -Wunreachable-code -Winline
else
WARNING_FLAGS = -Wall
endif
# Use NOOPT=1 if using valgrind --tool=memcheck/addrecheck
ifdef NOOPT
OPTIMISE_FLAGS = -g
else
OPTIMISE_FLAGS = -O2 -g
endif
# Use PROFILE=1 to allow use of gprof (but this is *not* needed for valgrind)
ifdef PROFILE
PROFILE_FLAGS = -pg
else
PROFILE_FLAGS =
endif
# On Linux, large file support is not necessarily enabled. To make programs
# assume large file support, it is necessary to build them with _FILE_OFFSET_BITS=64.
# This replaces the "standard" short file operations with equivalent large file
# operations.
# On (Free)BSD, this is not necessary, but conversely it does not look like defining
# the flags will have any effect either.
LFS_FLAGS = -D_FILE_OFFSET_BITS=64
CFLAGS = $(WARNING_FLAGS) $(OPTIMISE_FLAGS) $(LFS_FLAGS) -I. $(PROFILE_FLAGS)
LDFLAGS = -g -lm $(PROFILE_FLAGS)
# Target directories
OBJDIR = obj
LIBDIR = lib
BINDIR = bin
# All of our non-program source files
SRCS = \
accessunit.c \
adts.c \
avs.c \
bitdata.c \
es.c \
fmtx.c \
h262.c \
audio.c \
l2audio.c \
misc.c \
nalunit.c \
ps.c \
pes.c \
pidint.c \
ts.c \
tswrite.c
# All of our non-program object modules
OBJS = \
accessunit.o \
avs.o \
adts.o \
bitdata.o \
es.o \
filter.o \
fmtx.o \
h262.o \
audio.o \
l2audio.o \
misc.o \
nalunit.o \
ps.o \
pes.o \
pidint.o \
reverse.o \
ts.o \
tswrite.o
# Our program object modules
PROG_OBJS = \
$(OBJDIR)/es2ts.o \
$(OBJDIR)/esdots.o \
$(OBJDIR)/esfilter.o \
$(OBJDIR)/esmerge.o \
$(OBJDIR)/esreport.o \
$(OBJDIR)/esreverse.o \
$(OBJDIR)/ps2ts.o \
$(OBJDIR)/psreport.o \
$(OBJDIR)/psdots.o \
$(OBJDIR)/stream_type.o \
$(OBJDIR)/ts2es.o \
$(OBJDIR)/tsinfo.o \
$(OBJDIR)/tsplay.o \
$(OBJDIR)/tsreport.o \
$(OBJDIR)/tsserve.o
#\
# $(OBJDIR)/test_ps.o
TS2PS_OBJS = $(OBJDIR)/ts2ps.o
TEST_PES_OBJS = $(OBJDIR)/test_pes.o
TEST_OBJS = \
$(OBJDIR)/test_nal_unit_list.o \
$(OBJDIR)/test_es_unit_list.o
# Our library
LIB = $(LIBDIR)/libtstools.a
LIBOPTS = -L$(LIBDIR) -ltstools
# All of our programs (except the testing ones)
PROGS = \
$(BINDIR)/esfilter \
$(BINDIR)/ts2es \
$(BINDIR)/es2ts \
$(BINDIR)/esdots \
$(BINDIR)/esmerge \
$(BINDIR)/esreport \
$(BINDIR)/esreverse \
$(BINDIR)/ps2ts \
$(BINDIR)/psreport \
$(BINDIR)/psdots \
$(BINDIR)/stream_type \
$(BINDIR)/tsinfo \
$(BINDIR)/tsreport \
$(BINDIR)/tsplay \
$(BINDIR)/tsserve
#\
# $(BINDIR)/test_ps
TS2PS_PROG = $(BINDIR)/ts2ps
# Is test_pes still useful?
TEST_PES_PROG = $(BINDIR)/test_pes
# And then the testing programs (which we only build if we are
# running the tests)
TEST_PROGS = test_nal_unit_list test_es_unit_list
# ------------------------------------------------------------
all: $(BINDIR) $(LIBDIR) $(OBJDIR) $(PROGS)
# ts2ps is not yet an offical program, so for the moment build
# it separately
.PHONY: ts2ps
ts2ps: $(TS2PS_PROG)
$(LIB): $(LIB)($(OBJS))
$(BINDIR)/esfilter: $(OBJDIR)/esfilter.o $(LIB)
$(CC) $< -o $(BINDIR)/esfilter $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ts2es: $(OBJDIR)/ts2es.o $(LIB)
$(CC) $< -o $(BINDIR)/ts2es $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/es2ts: $(OBJDIR)/es2ts.o $(LIB)
$(CC) $< -o $(BINDIR)/es2ts $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esdots: $(OBJDIR)/esdots.o $(LIB)
$(CC) $< -o $(BINDIR)/esdots $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esmerge: $(OBJDIR)/esmerge.o $(LIB)
$(CC) $< -o $(BINDIR)/esmerge $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esreport: $(OBJDIR)/esreport.o $(LIB)
$(CC) $< -o $(BINDIR)/esreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esreverse: $(OBJDIR)/esreverse.o $(LIB)
$(CC) $< -o $(BINDIR)/esreverse $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/stream_type: $(OBJDIR)/stream_type.o $(LIB)
$(CC) $< -o $(BINDIR)/stream_type $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/psreport: $(OBJDIR)/psreport.o $(LIB)
$(CC) $< -o $(BINDIR)/psreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/psdots: $(OBJDIR)/psdots.o $(LIB)
$(CC) $< -o $(BINDIR)/psdots $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ps2ts: $(OBJDIR)/ps2ts.o $(LIB)
$(CC) $< -o $(BINDIR)/ps2ts $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsinfo: $(OBJDIR)/tsinfo.o $(LIB)
$(CC) $< -o $(BINDIR)/tsinfo $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsreport: $(OBJDIR)/tsreport.o $(LIB)
$(CC) $< -o $(BINDIR)/tsreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsserve: $(OBJDIR)/tsserve.o $(LIB)
$(CC) $< -o $(BINDIR)/tsserve $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsplay: $(OBJDIR)/tsplay.o $(LIB)
$(CC) $< -o $(BINDIR)/tsplay $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_ps: $(OBJDIR)/test_ps.o $(LIB)
$(CC) $< -o $(BINDIR)/test_ps $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ts2ps: $(OBJDIR)/ts2ps.o $(LIB)
$(CC) $< -o $(BINDIR)/ts2ps $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_pes: $(OBJDIR)/test_pes.o $(LIB)
$(CC) $< -o $(BINDIR)/test_pes $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_nal_unit_list: $(OBJDIR)/test_nal_unit_list.o $(LIB)
$(CC) $< -o $(BINDIR)/test_nal_unit_list $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_es_unit_list: $(OBJDIR)/test_es_unit_list.o $(LIB)
$(CC) $< -o $(BINDIR)/test_es_unit_list $(LDFLAGS) $(LIBOPTS)
# Some header files depend upon others, so including one requires
# the others as well
ES_H = es_fns.h es_defns.h h222_defns.h
TS_H = ts_fns.h ts_defns.h h222_defns.h tswrite_fns.h tswrite_defns.h \
pidint_fns.h pidint_defns.h
ACCESSUNIT_H = accessunit_fns.h accessunit_defns.h $(NALUNIT_H)
NALUNIT_H = nalunit_fns.h nalunit_defns.h es_fns.h es_defns.h \
bitdata_fns.h bitdata_defns.h
PES_H = pes_fns.h pes_defns.h
PS_H = ps_fns.h ps_defns.h
AVS_H = avs_fns.h avs_defns.h
H262_H = h262_fns.h h262_defns.h
TSWRITE_H = tswrite_fns.h tswrite_defns.h
REVERSE_H = reverse_fns.h reverse_defns.h
FILTER_H = filter_fns.h filter_defns.h $(REVERSE_H)
AUDIO_H = adts_fns.h l2audio_fns.h audio_fns.h audio_defns.h adts_defns.h
# Everyone depends upon the basic configuration file
$(LIB)($(OBJS)) $(TEST_OBJS) $(PROG_OBJS): compat.h
# Which library modules depend on which header files is complex, so
# lets just be simple
$(LIB)($(OBJS)): $(ACCESSUNIT_H) $(NALUNIT_H) $(TS_H) $(ES_H) $(PES_H) \
misc_fns.h $(PS_H) $(H262_H) $(TSWRITE_H) $(AVS_H) \
$(REVERSE_H) $(FILTER_H) $(AUDIO_H)
$(OBJDIR)/es2ts.o: es2ts.c $(ES_H) $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esdots.o: esdots.c misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esfilter.o: esfilter.c $(TS_H) misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esreport.o: esreport.c misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esmerge.o: esmerge.c misc_fns.h $(ACCESSUNIT_H) $(AUDIO_H) $(TSWRITE_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esreverse.o: esreverse.c $(TS_H) $(REVERSE_H) misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/fmtx.o: fmtx.c fmtx.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/psreport.o: psreport.c $(ES_H) $(PS_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/psdots.o: psdots.c $(ES_H) $(PS_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ps2ts.o: ps2ts.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/stream_type.o: stream_type.c $(ES_H) $(TS_H) $(NALUNIT_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ts2es.o: ts2es.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ts2ps.o: ts2ps.c $(TS_H) $(PS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsinfo.o: tsinfo.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsreport.o: tsreport.c $(TS_H) fmtx.h misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsserve.o: tsserve.c $(TS_H) $(PS_H) $(ES_H) misc_fns.h $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsplay.o: tsplay.c $(TS_H) misc_fns.h $(PS_H) $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tswrite.o: tswrite.c misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_pes.o: test_pes.c $(TS_H) $(PS_H) $(ES_H) misc_fns.h $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_ps.o: test_ps.c $(PS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_nal_unit_list.o: test_nal_unit_list.c $(NALUNIT_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_es_unit_list.o: test_es_unit_list.c $(ES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
# ------------------------------------------------------------
# Directory creation
$(OBJDIR):
mkdir $(OBJDIR)
$(LIBDIR):
mkdir $(LIBDIR)
$(BINDIR):
mkdir $(BINDIR)
# ------------------------------------------------------------
.PHONY: clean
clean:
-rm -f $(OBJS)
-rm -f $(TEST_OBJS)
-rm -f $(TEST_PROGS)
-rm -f $(TS2PS_OBJS) $(TS2PS_PROG)
-rm -f $(TEST_PES_OBJS) $(TEST_PES_PROG)
-rm -f ES_test3.ts es_test3.ts
-rm -f ES_test2.264 es_test3.264
-rm -f es_test_a.ts es_test_a.264
-rm -f es_test_b.ts es_test_b.264
-rm -f *.core
.PHONY: distclean
distclean: clean
-rm -f $(PROGS)
-rm -f $(LIB)
-rm -f $(PROG_OBJS)
-rmdir $(OBJDIR)
-rmdir $(LIBDIR)
-rmdir $(BINDIR)
TESTDATAFILE = /data/video/CVBt_hp_trail.264
# Only build test_pes if explicitly asked to do so
.PHONY: test_pes
test_pes: $(BINDIR)/test_pes
.PHONY: test
test: test_lists
.PHONY: test_lists
test_lists: $(BINDIR)/test_nal_unit_list $(BINDIR)/test_es_unit_list
@echo +++ Testing NAL unit lists
$(BINDIR)//test_nal_unit_list
@echo +++ Test succeeded
@echo +++ Testing ES unit lists
$(BINDIR)/test_es_unit_list
@echo +++ Test succeeded

376
Makefile.osx 100644
Wyświetl plik

@ -0,0 +1,376 @@
# Makefile for the H.264 Elementary Stream software
# - temporarily hacked to work on Mac OS/X 10.5 (Leopard)
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
#
### RUN WITH GNU MAKE
# Gnu make recommends always setting some standard variables
SHELL = /bin/sh
# And re-establishing the required suffix list
.SUFFIXES:
.SUFFIXES: .c .o
ifdef CROSS_COMPILE
CC = $(CROSS_COMPILE)gcc
else
CC = gcc
endif
# Use WARN=1 periodically to get too many warnings...
ifdef WARN
WARNING_FLAGS = -Wall -W -Wfloat-equal -Wundef -Wshadow -Wpointer-arith -Wcast-qual -Wconversion -Wmissing-prototypes -Wmissing-declarations -Wunreachable-code -Winline
else
WARNING_FLAGS = -Wall
endif
# Use NOOPT=1 if using valgrind --tool=memcheck/addrecheck
ifdef NOOPT
OPTIMISE_FLAGS = -g
else
OPTIMISE_FLAGS = -O2 -g
endif
# Use PROFILE=1 to allow use of gprof (but this is *not* needed for valgrind)
ifdef PROFILE
PROFILE_FLAGS = -pg
else
PROFILE_FLAGS =
endif
# On Linux, large file support is not necessarily enabled. To make programs
# assume large file support, it is necessary to build them with _FILE_OFFSET_BITS=64.
# This replaces the "standard" short file operations with equivalent large file
# operations.
# On (Free)BSD, this is not necessary, but conversely it does not look like defining
# the flags will have any effect either.
LFS_FLAGS = -D_FILE_OFFSET_BITS=64
# Experimentally, for Mac OS/X
ARCH_FLAGS = -arch ppc -arch i386
CFLAGS = $(WARNING_FLAGS) $(OPTIMISE_FLAGS) $(LFS_FLAGS) -I. $(PROFILE_FLAGS) $(ARCH_FLAGS)
LDFLAGS = -g -lm $(PROFILE_FLAGS) $(ARCH_FLAGS)
# Target directories
OBJDIR = obj
LIBDIR = lib
BINDIR = bin
# All of our non-program source files
SRCS = \
accessunit.c \
adts.c \
avs.c \
bitdata.c \
es.c \
h262.c \
audio.c \
l2audio.c \
misc.c \
nalunit.c \
ps.c \
pes.c \
pidint.c \
ts.c \
tswrite.c
# All of our non-program object modules
OBJS = \
accessunit.o \
avs.o \
adts.o \
bitdata.o \
es.o \
filter.o \
h262.o \
audio.o \
l2audio.o \
misc.o \
nalunit.o \
ps.o \
pes.o \
pidint.o \
reverse.o \
ts.o \
tswrite.o
# Our program object modules
PROG_OBJS = \
$(OBJDIR)/es2ts.o \
$(OBJDIR)/esdots.o \
$(OBJDIR)/esfilter.o \
$(OBJDIR)/esmerge.o \
$(OBJDIR)/esreport.o \
$(OBJDIR)/esreverse.o \
$(OBJDIR)/ps2ts.o \
$(OBJDIR)/psreport.o \
$(OBJDIR)/psdots.o \
$(OBJDIR)/stream_type.o \
$(OBJDIR)/ts2es.o \
$(OBJDIR)/tsinfo.o \
$(OBJDIR)/tsplay.o \
$(OBJDIR)/tsreport.o \
$(OBJDIR)/tsserve.o
#\
# $(OBJDIR)/test_ps.o
TS2PS_OBJS = $(OBJDIR)/ts2ps.o
TEST_PES_OBJS = $(OBJDIR)/test_pes.o
TEST_OBJS = \
$(OBJDIR)/test_nal_unit_list.o \
$(OBJDIR)/test_es_unit_list.o
# Our library
LIB = $(LIBDIR)/libtstools.a
LIBOPTS = -L$(LIBDIR) -ltstools $(ARCH_FLAGS)
# All of our programs (except the testing ones)
PROGS = \
$(BINDIR)/esfilter \
$(BINDIR)/ts2es \
$(BINDIR)/es2ts \
$(BINDIR)/esdots \
$(BINDIR)/esmerge \
$(BINDIR)/esreport \
$(BINDIR)/esreverse \
$(BINDIR)/ps2ts \
$(BINDIR)/psreport \
$(BINDIR)/psdots \
$(BINDIR)/stream_type \
$(BINDIR)/tsinfo \
$(BINDIR)/tsreport \
$(BINDIR)/tsplay \
$(BINDIR)/tsserve
#\
# $(BINDIR)/test_ps
TS2PS_PROG = $(BINDIR)/ts2ps
# Is test_pes still useful?
TEST_PES_PROG = $(BINDIR)/test_pes
# And then the testing programs (which we only build if we are
# running the tests)
TEST_PROGS = test_nal_unit_list test_es_unit_list
# ------------------------------------------------------------
all: $(BINDIR) $(LIBDIR) $(OBJDIR) $(PROGS)
# ts2ps is not yet an offical program, so for the moment build
# it separately
.PHONY: ts2ps
ts2ps: $(TS2PS_PROG)
#$(LIB): $(LIB)($(OBJS))
# Try getting a library containing universal objects on Mac
$(LIB): $(OBJS)
libtool -static $(OBJS) -o $(LIB)
$(BINDIR)/esfilter: $(OBJDIR)/esfilter.o $(LIB)
$(CC) $< -o $(BINDIR)/esfilter $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ts2es: $(OBJDIR)/ts2es.o $(LIB)
$(CC) $< -o $(BINDIR)/ts2es $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/es2ts: $(OBJDIR)/es2ts.o $(LIB)
$(CC) $< -o $(BINDIR)/es2ts $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esdots: $(OBJDIR)/esdots.o $(LIB)
$(CC) $< -o $(BINDIR)/esdots $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esmerge: $(OBJDIR)/esmerge.o $(LIB)
$(CC) $< -o $(BINDIR)/esmerge $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esreport: $(OBJDIR)/esreport.o $(LIB)
$(CC) $< -o $(BINDIR)/esreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/esreverse: $(OBJDIR)/esreverse.o $(LIB)
$(CC) $< -o $(BINDIR)/esreverse $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/stream_type: $(OBJDIR)/stream_type.o $(LIB)
$(CC) $< -o $(BINDIR)/stream_type $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/psreport: $(OBJDIR)/psreport.o $(LIB)
$(CC) $< -o $(BINDIR)/psreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/psdots: $(OBJDIR)/psdots.o $(LIB)
$(CC) $< -o $(BINDIR)/psdots $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ps2ts: $(OBJDIR)/ps2ts.o $(LIB)
$(CC) $< -o $(BINDIR)/ps2ts $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsinfo: $(OBJDIR)/tsinfo.o $(LIB)
$(CC) $< -o $(BINDIR)/tsinfo $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsreport: $(OBJDIR)/tsreport.o $(LIB)
$(CC) $< -o $(BINDIR)/tsreport $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsserve: $(OBJDIR)/tsserve.o $(LIB)
$(CC) $< -o $(BINDIR)/tsserve $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/tsplay: $(OBJDIR)/tsplay.o $(LIB)
$(CC) $< -o $(BINDIR)/tsplay $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_ps: $(OBJDIR)/test_ps.o $(LIB)
$(CC) $< -o $(BINDIR)/test_ps $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/ts2ps: $(OBJDIR)/ts2ps.o $(LIB)
$(CC) $< -o $(BINDIR)/ts2ps $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_pes: $(OBJDIR)/test_pes.o $(LIB)
$(CC) $< -o $(BINDIR)/test_pes $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_nal_unit_list: $(OBJDIR)/test_nal_unit_list.o $(LIB)
$(CC) $< -o $(BINDIR)/test_nal_unit_list $(LDFLAGS) $(LIBOPTS)
$(BINDIR)/test_es_unit_list: $(OBJDIR)/test_es_unit_list.o $(LIB)
$(CC) $< -o $(BINDIR)/test_es_unit_list $(LDFLAGS) $(LIBOPTS)
# Some header files depend upon others, so including one requires
# the others as well
ES_H = es_fns.h es_defns.h h222_defns.h
TS_H = ts_fns.h ts_defns.h h222_defns.h tswrite_fns.h tswrite_defns.h \
pidint_fns.h pidint_defns.h
ACCESSUNIT_H = accessunit_fns.h accessunit_defns.h $(NALUNIT_H)
NALUNIT_H = nalunit_fns.h nalunit_defns.h es_fns.h es_defns.h \
bitdata_fns.h bitdata_defns.h
PES_H = pes_fns.h pes_defns.h
PS_H = ps_fns.h ps_defns.h
AVS_H = avs_fns.h avs_defns.h
H262_H = h262_fns.h h262_defns.h
TSWRITE_H = tswrite_fns.h tswrite_defns.h
REVERSE_H = reverse_fns.h reverse_defns.h
FILTER_H = filter_fns.h filter_defns.h $(REVERSE_H)
AUDIO_H = adts_fns.h l2audio_fns.h audio_fns.h audio_defns.h adts_defns.h
# Everyone depends upon the basic configuration file
$(LIB)($(OBJS)) $(TEST_OBJS) $(PROG_OBJS): compat.h
# Which library modules depend on which header files is complex, so
# lets just be simple
$(LIB)($(OBJS)): $(ACCESSUNIT_H) $(NALUNIT_H) $(TS_H) $(ES_H) $(PES_H) \
misc_fns.h $(PS_H) $(H262_H) $(TSWRITE_H) $(AVS_H) \
$(REVERSE_H) $(FILTER_H) $(AUDIO_H)
$(OBJDIR)/es2ts.o: es2ts.c $(ES_H) $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esdots.o: esdots.c misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esfilter.o: esfilter.c $(TS_H) misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esreport.o: esreport.c misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esmerge.o: esmerge.c misc_fns.h $(ACCESSUNIT_H) $(AUDIO_H) $(TSWRITE_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/esreverse.o: esreverse.c $(TS_H) $(REVERSE_H) misc_fns.h $(ACCESSUNIT_H) $(H262_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/psreport.o: psreport.c $(ES_H) $(PS_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/psdots.o: psdots.c $(ES_H) $(PS_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ps2ts.o: ps2ts.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/stream_type.o: stream_type.c $(ES_H) $(TS_H) $(NALUNIT_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ts2es.o: ts2es.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/ts2ps.o: ts2ps.c $(TS_H) $(PS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsinfo.o: tsinfo.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsreport.o: tsreport.c $(TS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsserve.o: tsserve.c $(TS_H) $(PS_H) $(ES_H) misc_fns.h $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tsplay.o: tsplay.c $(TS_H) misc_fns.h $(PS_H) $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/tswrite.o: tswrite.c misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_pes.o: test_pes.c $(TS_H) $(PS_H) $(ES_H) misc_fns.h $(PES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_ps.o: test_ps.c $(PS_H) misc_fns.h version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_nal_unit_list.o: test_nal_unit_list.c $(NALUNIT_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
$(OBJDIR)/test_es_unit_list.o: test_es_unit_list.c $(ES_H) version.h
$(CC) -c $< -o $@ $(CFLAGS)
# ------------------------------------------------------------
# Directory creation
$(OBJDIR):
mkdir $(OBJDIR)
$(LIBDIR):
mkdir $(LIBDIR)
$(BINDIR):
mkdir $(BINDIR)
# ------------------------------------------------------------
.PHONY: clean
clean:
-rm -f $(OBJS)
-rm -f $(TEST_OBJS)
-rm -f $(TEST_PROGS)
-rm -f $(TS2PS_OBJS) $(TS2PS_PROG)
-rm -f $(TEST_PES_OBJS) $(TEST_PES_PROG)
-rm -f ES_test3.ts es_test3.ts
-rm -f ES_test2.264 es_test3.264
-rm -f es_test_a.ts es_test_a.264
-rm -f es_test_b.ts es_test_b.264
-rm -f *.core
.PHONY: distclean
distclean: clean
-rm -f $(PROGS)
-rm -f $(LIB)
-rm -f $(PROG_OBJS)
-rmdir $(OBJDIR)
-rmdir $(LIBDIR)
-rmdir $(BINDIR)
TESTDATAFILE = /data/video/CVBt_hp_trail.264
# Only build test_pes if explicitly asked to do so
.PHONY: test_pes
test_pes: $(BINDIR)/test_pes
.PHONY: test
test: test_lists
.PHONY: test_lists
test_lists: $(BINDIR)/test_nal_unit_list $(BINDIR)/test_es_unit_list
@echo +++ Testing NAL unit lists
$(BINDIR)//test_nal_unit_list
@echo +++ Test succeeded
@echo +++ Testing ES unit lists
$(BINDIR)/test_es_unit_list
@echo +++ Test succeeded

219
Makefile.w32 100644
Wyświetl plik

@ -0,0 +1,219 @@
# Makefile for use on Windows
#
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
.SUFFIXES:
!if "$(DEBUG)"==""
DEBUG=0
!endif
DEBUG_OBJDIR=w32\debug
PLAIN_OBJDIR=w32\obj
!if $(DEBUG)
OBJDIR=$(DEBUG_OBJDIR)
COPT=/Od /Zi /MTd
LOPT=/DEBUG /INCREMENTAL:NO /NOLOGO
# /NODEFAULTLIB:CMT
!else
OBJDIR=$(PLAIN_OBJDIR)
COPT=/O2b2 /MT /DNDEBUG
LOPT=/INCREMENTAL:NO /NOLOGO
# /NODEFAULTLIB:CMT
!endif
EXEDIR=w32\bin
LIBDIR=$(OBJDIR)
LIBFILE=$(LIBDIR)\libtstools.lib
CC = cl /c /W3 /WX /GF /Fd$(OBJDIR)\ $(COPT)
#CC=cl /nologo /c /WX /GF /Fd$(OBJDIR)\ $(COPT)
.SUFFIXES: .c .cpp .obj .exe {$(OBJDIR)}.obj
# ------------------------------------------------------------
# Rules
.c{$(OBJDIR)}.obj::
$(CC) /Fo$(OBJDIR)\ $<
{$(OBJDIR)}.obj{$(EXEDIR)}.exe:
link /out:$@ $(LOPT) $<
# ------------------------------------------------------------
# Things to build
PROGS = \
$(EXEDIR)\esfilter.exe \
$(EXEDIR)\esdots.exe \
$(EXEDIR)\esmerge.exe \
$(EXEDIR)\esreport.exe \
$(EXEDIR)\ts2es.exe \
$(EXEDIR)\es2ts.exe \
$(EXEDIR)\esreverse.exe \
$(EXEDIR)\ps2ts.exe \
$(EXEDIR)\psreport.exe \
$(EXEDIR)\stream_type.exe \
$(EXEDIR)\tsinfo.exe \
$(EXEDIR)\tsreport.exe \
$(EXEDIR)\tsplay.exe \
$(EXEDIR)\tsserve.exe
# Object files for the library
LIB_OBJS = \
$(OBJDIR)\accessunit.obj \
$(OBJDIR)\adts.obj \
$(OBJDIR)\avs.obj \
$(OBJDIR)\audio.obj \
$(OBJDIR)\bitdata.obj \
$(OBJDIR)\es.obj \
$(OBJDIR)\filter.obj \
$(OBJDIR)\fmtx.obj \
$(OBJDIR)\h262.obj \
$(OBJDIR)\l2audio.obj \
$(OBJDIR)\misc.obj \
$(OBJDIR)\nalunit.obj \
$(OBJDIR)\ps.obj \
$(OBJDIR)\pes.obj \
$(OBJDIR)\pidint.obj \
$(OBJDIR)\reverse.obj \
$(OBJDIR)\ts.obj \
$(OBJDIR)\tswrite.obj
# Object files for the programs
PROG_OBJS = \
$(OBJDIR)/es2ts.obj \
$(OBJDIR)/esdots.obj \
$(OBJDIR)/esfilter.obj \
$(OBJDIR)/esmerge.obj \
$(OBJDIR)/esreport.obj \
$(OBJDIR)/esreverse.obj \
$(OBJDIR)/ps2ts.obj \
$(OBJDIR)/psreport.obj \
$(OBJDIR)/stream_type.obj \
$(OBJDIR)/ts2es.obj \
$(OBJDIR)/tsinfo.obj \
$(OBJDIR)/tsplay.obj \
$(OBJDIR)/tsreport.obj \
$(OBJDIR)/tsserve.obj
# ------------------------------------------------------------
# Targets
all: $(OBJDIR) $(EXEDIR) $(LIBDIR) $(PROGS)
$(OBJDIR)\fmtx.obj: fmtx.h
$(OBJDIR)\tsreport.obj: fmtx.h
$(LIBFILE): $(LIBDIR) $(LIB_OBJS)
lib /nologo /out:$@ $(LIB_OBJS)
$(EXEDIR)\esfilter.exe: $(OBJDIR)\esfilter.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\esmerge.exe: $(OBJDIR)\esmerge.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\ts2es.exe: $(OBJDIR)\ts2es.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\es2ts.exe: $(OBJDIR)\es2ts.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\esdots.exe: $(OBJDIR)\esdots.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\esreport.exe: $(OBJDIR)\esreport.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\esreverse.exe: $(OBJDIR)\esreverse.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\stream_type.exe: $(OBJDIR)\stream_type.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\psreport.exe: $(OBJDIR)\psreport.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\ps2ts.exe: $(OBJDIR)\ps2ts.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\tsinfo.exe: $(OBJDIR)\tsinfo.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\tsreport.exe: $(OBJDIR)\tsreport.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\tsplay.exe: $(OBJDIR)\tsplay.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
$(EXEDIR)\tsserve.exe: $(OBJDIR)\tsserve.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
# For the moment, ts2ps is not an official program, so must
# be built separately
ts2ps: $(EXEDIR)\ts2ps.exe
$(EXEDIR)\ts2ps.exe: $(OBJDIR)\ts2ps.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
# Only build test_pes if explicitly asked to do so
test_pes: $(EXEDIR)\test_pes.exe
$(EXEDIR)\test_pes.exe: $(OBJDIR)\test_pes.obj $(LIBFILE)
link /out:$@ $(LOPT) $** wsock32.lib
# ------------------------------------------------------------
# Directories
$(OBJDIR):
$(COMSPEC) /x /c md $(OBJDIR)
!if "$(LIBDIR)" != "$(OBJDIR)"
$(LIBDIR):
$(COMSPEC) /x /c md $(LIBDIR)
!endif
!if "$(EXEDIR)" != "$(OBJDIR)"
$(EXEDIR):
$(COMSPEC) /x /c md $(EXEDIR)
!endif
# ------------------------------------------------------------
# Tidying up
clean:
-del /q $(OBJDIR)\*.obj
-del /q $(OBJDIR)\*.lib
distclean: clean
-del /q $(DEBUG_OBJDIR)\*.obj
-del /q $(DEBUG_OBJDIR)\*.lib
-del /q $(PLAIN_OBJDIR)\*.obj
-del /q $(PLAIN_OBJDIR)\*.lib
-del /q $(EXEDIR)\*.exe
-del /q $(EXEDIR)\*.pdb

1432
accessunit.c 100644

Plik diff jest za duży Load Diff

131
accessunit_defns.h 100644
Wyświetl plik

@ -0,0 +1,131 @@
/*
* Datastructures for working with access units in H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _accessunit_defns
#define _accessunit_defns
#include "nalunit_defns.h"
#include "es_defns.h"
// Since reverse_data refers to h262 and acces_unit datastructures, and
// *they* refer to reverse_data, we need to break the circular referencing
// at some point
typedef struct access_unit_context *access_unit_context_p;
#include "reverse_defns.h"
// ------------------------------------------------------------
// A single access unit
struct access_unit
{
u_int32 index; // The (notional) index of this unit in the stream
// (i.e., from the context's access_unit_index)
int started_primary_picture; // True if we have, indeed, done so
nal_unit_p primary_start; // First NAL unit of our primary picture
// (a pointer into `nal_units`)
// A "list" of the NAL units that form us
// (the only reason we don't use an ES unit list is that I want to be
// able to report on the content of an access unit in terms of the NAL
// units that form it - perhaps more important in development than in
// prodcution, but still)
nal_unit_list_p nal_units;
// Did we ignore any "broken" NAL units when we were being built?
// (if so, there's a case to say we might be broken too)
int ignored_broken_NAL_units;
// Information derived from the slices in this access unit
// (all slices in an access unit must have the same values for these,
// so these could actually just be derived from the `primary_start`,
// but it's slightly easier to have them more available, and saves
// the need to check if `primary_start` is defined before using them).
u_int32 frame_num;
byte field_pic_flag; // frame or field?
byte bottom_field_flag; // for a field (only), bottom or top?
// (After merging two field access units into a single frame,
// `field_pic_flag` will be set to 0, to "pretend" that we have a
// "proper" frame access unit)
};
typedef struct access_unit *access_unit_p;
#define SIZEOF_ACCESS_UNIT sizeof(struct access_unit)
// ------------------------------------------------------------
// Context for looping over the access units in an elementary stream
struct access_unit_context
{
// ---------------------------------------------------------------
// Public information - things it makes sense for users to inspect
// ---------------------------------------------------------------
nal_unit_context_p nac; // short and nasty "mnemonic"
// If we read an EndOfStream or EndOfSequence NAL unit, then we
// want to remember as much. We don't really want to put it into
// the preceding access unit (for a start, that would mess up
// reversing the stream), but we also don't want to forget it,
// and in the case of EndOfStream, we don't want to carry on
// reading the ES after finding it. The simplest thing is just
// to remember them.
nal_unit_p end_of_sequence;
nal_unit_p end_of_stream;
// We count all of the access units as we read them (this is useful
// when we are building up reverse_data arrays). If functions
// move around in the data stream, we assume that they will
// (re)set this to a sensible value.
// The index of the first access unit read is 1, and this value is
// incremented by each call of `get_next_access_unit`
u_int32 access_unit_index; // The index of the last access unit read
// If we are collecting reversing information, then we keep a reference
// to the reverse data here
reverse_data_p reverse_data;
// -------------------------------------------------------------
// Private information - used internally by the software, not to
// be relied upon by outsiders
// -------------------------------------------------------------
// If we ended the previous access unit because of finding a NAL
// unit that provokes a *new* access unit, then we remember it
// (after all, we'll want to put it into the new access unit)
nal_unit_p pending_nal;
// We need to remember the VCL NAL unit that started the previous
// primary picture, so that we can compare a later VCL NAL unit
// to it, to see if we've got a new primary picture starting
// (actually, we'll only remember a "stub" of information for it)
nal_unit_p earlier_primary_start;
// Some items go "in front of" the next VCL NAL unit, so we need
// a list of such
nal_unit_list_p pending_list;
// If we read an end of stream NAL unit, then next time we try
// to read an access unit, we want to know that there is no point.
// Similarly, if we read EOF on the input stream.
byte no_more_data;
};
#define SIZEOF_ACCESS_UNIT_CONTEXT sizeof(struct access_unit_context)
#endif // _accessunit_defns

298
accessunit_fns.h 100644
Wyświetl plik

@ -0,0 +1,298 @@
/*
* Functions for working with access units in H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _accessunit_fns
#define _accessunit_fns
#include "accessunit_defns.h"
/*
* Build a new access unit context datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_access_unit_context(ES_p es,
access_unit_context_p *context);
/*
* Free a new access unit context datastructure.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does not free any `reverse_data` datastructure.
*
* Does nothing if `context` is already NULL.
*/
extern void free_access_unit_context(access_unit_context_p *context);
/*
* Reset an acccess unit context, so it "forgets" its current information
* about what it is reading, etc.
*/
extern void reset_access_unit_context(access_unit_context_p context);
/*
* Rewind a file being read as access units.
*
* This is a wrapper for `rewind_nal_unit_context` that also knows to
* unset things appropriate to the access unit context.
*
* If a reverse context is attached to this access unit, it also will
* be "rewound" appropriately.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_access_unit_context(access_unit_context_p context);
/*
* Tidy up and free an access unit datastructure after we've finished with it.
*
* Clears the datastructure, frees it, and returns `acc_unit` as NULL.
*
* Does nothing if `acc_unit` is already NULL.
*/
extern void free_access_unit(access_unit_p *acc_unit);
/*
* Report on this access unit, on the given output stream.
*/
extern void report_access_unit(FILE *stream,
access_unit_p access_unit);
/*
* Retrieve the bounds of this access unit in the file it was read from.
*
* - `access_unit` is the access unit we're interested in
* - `start` is its start position (i.e., the location at which to start
* reading to retrieve all of the data for the access unit, including
* the 00 00 01 prefix at the start of the first NAL unit therein)
* - `length` is the total length of the NAL units within this access unit
*
* Returns 0 if all goes well, 1 if the access unit has no content.
*/
extern int get_access_unit_bounds(access_unit_p access_unit,
ES_offset *start,
u_int32 *length);
/*
* Are all slices in this access unit I slices?
*/
extern int all_slices_I(access_unit_p access_unit);
/*
* Are all slices in this access unit P slices?
*/
extern int all_slices_P(access_unit_p access_unit);
/*
* Are all slices in this access unit I or P slices?
*/
extern int all_slices_I_or_P(access_unit_p access_unit);
/*
* Are all slices in this access unit B slices?
*/
extern int all_slices_B(access_unit_p access_unit);
/*
* Write out an access unit as ES.
*
* Also writes out any end of sequence or end of stream NAL unit found in the
* `context` (since they are assumed to have immediately followed this access
* unit).
*
* - `access_unit` is the access unit to write out
* - `context` may contain additional things to write (see above), but may
* legitimately be NULL if there is no context.
* - `output` is the ES file to write to
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_access_unit_as_ES(access_unit_p access_unit,
access_unit_context_p context,
FILE *output);
/*
* Write out an access unit as TS.
*
* Also writes out any end of sequence or end of stream NAL unit found in the
* `context` (since they are assumed to have immediately followed this access
* unit).
*
* - `access_unit` is the access unit to write out
* - `context` may contain additional things to write (see above), but may
* legitimately be NULL if there is no context.
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_access_unit_as_TS(access_unit_p access_unit,
access_unit_context_p context,
TS_writer_p tswriter,
u_int32 video_pid);
/*
* Write out an access unit as TS, with PTS timing in the first PES packet
* (and PCR timing in the first TS of the frame).
*
* Also writes out any end of sequence or end of stream NAL unit found in the
* `context` (since they are assumed to have immediately followed this access
* unit).
*
* - `access_unit` is the access unit to write out
* - `context` may contain additional things to write (see above), but may
* legitimately be NULL if there is no context.
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `pts` is the PTS time (which is also used as the PCR base).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_access_unit_as_TS_with_pts_dts(access_unit_p access_unit,
access_unit_context_p context,
TS_writer_p tswriter,
u_int32 video_pid,
int got_pts,
u_int64 pts,
int got_dts,
u_int64 dts);
/*
* Write out an access unit as TS, with PCR timing in the first TS of the
* frame.
*
* Also writes out any end of sequence or end of stream NAL unit found in the
* `context` (since they are assumed to have immediately followed this access
* unit).
*
* - `access_unit` is the access unit to write out
* - `context` may contain additional things to write (see above), but may
* legitimately be NULL if there is no context.
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `pcr_base` and `pcr_extn` encode the PCR value.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_access_unit_as_TS_with_PCR(access_unit_p access_unit,
access_unit_context_p context,
TS_writer_p tswriter,
u_int32 video_pid,
u_int64 pcr_base,
u_int32 pcr_extn);
/*
* Retrieve the next access unit from the given elementary stream.
*
* - `context` is the context information needed to allow us to find
* successive access units.
* - `quiet` is true if we should try to be silent about it
* - `show_details` is true if we should output more info than normal
* - `ret_access_unit` is the next access unit.
*
* If the access unit was ended because an end of sequence or end of
* stream NAL unit was encountered, then said end of sequence/stream
* NAL unit will be remembered in the `context`.
*
* Note that it is possible to get back an *empty* access unit in
* certain situations - the most obvious of which is if we get two
* ``end of sequence`` NAL units with nothing betwen them.
*
* Because of this possibility, some care should be taken to allow for
* access units that do not contain a primary picture (no VCL NAL unit),
* and contain zero NAL units. Also, if one is trying for an accurate
* count of access units, such instances should probably be ignored.
*
* Returns 0 if it succeeds, EOF if there is no more data to read, or 1 if
* some error occurs.
*
* EOF can be returned because the end of file has been reached, or because an
* end of stream NAL unit has been encountered. The two may be distinguished
* by looking at `context->end_of_stream`, which will be NULL if it was a true
* EOF.
*
* Note that `ret_access_unit` will be NULL if EOF is returned.
*/
extern int get_next_access_unit(access_unit_context_p context,
int quiet,
int show_details,
access_unit_p *ret_access_unit);
/*
* Retrieve the next H.264 frame from the given elementary stream.
*
* The next access unit is retrieved from the input stream (using
* get_next_access_unit).
*
* If that access unit represents a frame, it is returned.
*
* If it represents a field, then the *following* access unit is retrieved,
* and if that is the second field of its frame, it is merged into the first,
* and the resultant frame is returned.
*
* If a field with frame number A is followed by a field with frame number B,
* it is assumed that synchronisation has been lost. In this case, the first
* field (frame A) will be discarded, and an attempt made to read the second
* field of frame B.
*
* Similarly, if a frame is found instead of the second field, the first
* field will be discarded and the frame returned.
*
* Note that if the context is associated with a reverse context,
* then appropriate frames will automatically be remembered therein.
*
* - `context` is the context information needed to allow us to find
* successive access units.
* - `quiet` is true if we should try to be silent about it
* - `show_details` is true if we should output more info than normal
* - `frame` is an access unit datastructure representing the next
* frame.
*
* If the access unit was ended because an end of sequence or end of
* stream NAL unit was encountered, then said end of sequence/stream
* NAL unit will be remembered in the `context`.
*
* Returns 0 if it succeeds, EOF if there is no more data to read, or 1 if
* some error occurs.
*
* EOF can be returned because the end of file has been reached, or because an
* end of stream NAL unit has been encountered. The two may be distinguished
* by looking at `context->end_of_stream`, which will be NULL if it was a true
* EOF.
*
* Note that `ret_access_unit` will be NULL if EOF is returned.
*/
extern int get_next_h264_frame(access_unit_context_p context,
int quiet,
int show_details,
access_unit_p *frame);
/*
* If this access unit was read from PES, did any of its PES packets contain
* a PTS?
*
* Returns TRUE if so, FALSE if not.
*/
extern int access_unit_has_PTS(access_unit_p access_unit);
#endif // _accessunit_fns

156
adts.c 100644
Wyświetl plik

@ -0,0 +1,156 @@
/*
* Support for ISO/IEC 14496-3:2001(E) AAC ADTS audio streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "compat.h"
#include "misc_fns.h"
#include "adts_fns.h"
#define DEBUG 0
/*
* Read the next ADTS frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the ADTS file to read from
* - `frame` is the ADTS frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_adts_frame(int file,
audio_frame_p *frame)
{
#define JUST_ENOUGH 6 // just enough to hold the bits of the headers we want
int err, ii;
int id, layer;
byte header[JUST_ENOUGH];
byte *data = NULL;
int frame_length;
offset_t posn = tell_file(file);
#if DEBUG
printf("Offset: " OFFSET_T_FORMAT "\n",posn);
#endif
err = read_bytes(file,JUST_ENOUGH,header);
if (err == EOF)
return EOF;
else if (err)
{
fprintf(stderr,"### Error reading header bytes of ADTS frame\n");
fprintf(stderr," (in frame starting at " OFFSET_T_FORMAT ")\n",posn);
free(data);
return 1;
}
#if DEBUG
printf("ADTS frame\n");
print_data(stdout,"Start",header,JUST_ENOUGH,JUST_ENOUGH);
#endif
if (header[0] != 0xFF || (header[1] & 0xF0) != 0xF0)
{
fprintf(stderr,"### ADTS frame does not start with '1111 1111 1111'"
" syncword - lost synchronisation?\n"
" Found 0x%X%X%X instead of 0xFFF\n",
(unsigned)(header[0] & 0xF0) >> 4,
(header[0] & 0x0F),
(unsigned)(header[1] & 0xF0) >> 4);
fprintf(stderr," (in frame starting at " OFFSET_T_FORMAT ")\n",posn);
return 1;
}
id = (header[1] & 0x08) >> 3;
#if DEBUG
printf(" ID %d (%s)\n",id,(id==1?"MPEG-2 AAC":"MPEG-4"));
#endif
layer = (header[1] & 0x06) >> 1;
if (layer != 0)
printf(" layer is %d, not 0 (in frame at " OFFSET_T_FORMAT ")\n",
layer,posn);
if (id == 1)
{
// We assume the Emphasis field is not present
// (experience appears to show that it is not used for MPEG-2 AVC)
frame_length = ((header[3] & 0x03) << 11) | (header[4] << 3) |
((unsigned)(header[5] & 0xE0) >> 5);
}
else
{
// We assume the Emphasis field *is* present
frame_length = (header[4] << 5) | ((unsigned)(header[5] & 0xF8) >> 3);
}
#if DEBUG
printf(" length %d\n",frame_length);
#endif
data = malloc(frame_length);
if (data == NULL)
{
fprintf(stderr,"### Unable to extend data buffer for ADTS frame\n");
free(data);
return 1;
}
for (ii=0; ii<JUST_ENOUGH; ii++)
data[ii] = header[ii];
err = read_bytes(file,frame_length - JUST_ENOUGH,&(data[JUST_ENOUGH]));
if (err)
{
if (err == EOF)
fprintf(stderr,"### Unexpected EOF reading rest of ADTS frame\n");
else
fprintf(stderr,"### Error reading rest of ADTS frame\n");
free(data);
return 1;
}
#if DEBUG
print_data(stdout,"Again",data,frame_length,20);
#endif
err = build_audio_frame(frame);
if (err)
{
free(data);
return 1;
}
(*frame)->data = data;
(*frame)->data_len = frame_length;
return 0;
}

35
adts_defns.h 100644
Wyświetl plik

@ -0,0 +1,35 @@
/*
* Support for ISO/IEC 14496-3:2001(E) AAC ADTS audio streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _adts_defns
#define _adts_defns
#include "audio_defns.h"
// AAC ADTS provides audio in frames of constant time
#endif // _adts_defns

50
adts_fns.h 100644
Wyświetl plik

@ -0,0 +1,50 @@
/*
* Support for ISO/IEC 14496-3:2001(E) AAC ADTS audio streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _adts_fns
#define _adts_fns
#include "adts_defns.h"
#include "audio_fns.h"
/*
* Read the next ADTS frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the ADTS file to read from
* - `frame` is the ADTS frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_adts_frame(int file,
audio_frame_p *frame);
#endif // _adts_fns

110
audio.c 100644
Wyświetl plik

@ -0,0 +1,110 @@
/*
* Generic audio functionality
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "compat.h"
#include "audio_fns.h"
#include "adts_fns.h"
#include "l2audio_fns.h"
/*
* Build a new generic audio frame datastructure
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_audio_frame(audio_frame_p *frame)
{
audio_frame_p new = malloc(SIZEOF_AUDIO_FRAME);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate audio frame datastructure\n");
return 1;
}
new->data = NULL;
new->data_len = 0;
*frame = new;
return 0;
}
/*
* Tidy up and free an audio frame datastructure when we've finished with it
*
* Empties the datastructure, frees it, and sets `frame` to NULL.
*
* If `frame` is already NULL, does nothing.
*/
extern void free_audio_frame(audio_frame_p *frame)
{
if (*frame == NULL)
return;
if ((*frame)->data != NULL)
{
free((*frame)->data);
(*frame)->data = NULL;
}
(*frame)->data_len = 0;
free(*frame);
*frame = NULL;
}
/*
* Read the next audio frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if (for MPEG2) the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the audio file to read from
* - `audio_type` indicates what type of audio - e.g., AUDIO_ADTS
* - `frame` is the audio frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_audio_frame(int file,
int audio_type,
audio_frame_p *frame)
{
switch (audio_type)
{
case AUDIO_ADTS:
return read_next_adts_frame(file,frame);
case AUDIO_L2:
return read_next_l2audio_frame(file,frame);
default:
fprintf(stderr,"### Unrecognised audio type %d - cannot get next audio frame\n",
audio_type);
return 1;
}
}

55
audio_defns.h 100644
Wyświetl plik

@ -0,0 +1,55 @@
/*
* Support for generic audio streams
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _audio_defns
#define _audio_defns
#include "h222_defns.h"
// A simple wrapper for a frame of audio data
struct audio_frame
{
byte *data; // The frame data, including the syncword at the start
u_int32 data_len;
};
typedef struct audio_frame *audio_frame_p;
#define SIZEOF_AUDIO_FRAME sizeof(struct audio_frame)
// The types of audio we know about
// These are convenience names, defined in terms of the H222 values
#define AUDIO_UNKNOWN 0 // which is a reserved value
#define AUDIO_ADTS ADTS_AUDIO_STREAM_TYPE
#define AUDIO_L2 MPEG2_AUDIO_STREAM_TYPE
#define AUDIO_STR(x) ((x)==AUDIO_UNKNOWN?"unknown": \
(x)==AUDIO_ADTS ?"ADTS": \
(x)==AUDIO_L2 ?"MPEG2": \
"???")
#endif // _audio_defns

66
audio_fns.h 100644
Wyświetl plik

@ -0,0 +1,66 @@
/*
* Generic support for audio streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _audio_fns
#define _audio_fns
#include "audio_defns.h"
/*
* Build a new generic audio frame datastructure
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_audio_frame(audio_frame_p *frame);
/*
* Tidy up and free an audio frame datastructure when we've finished with it
*
* Empties the datastructure, frees it, and sets `frame` to NULL.
*
* If `frame` is already NULL, does nothing.
*/
extern void free_audio_frame(audio_frame_p *frame);
/*
* Read the next audio frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if (for MPEG2) the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the audio file to read from
* - `audio_type` indicates what type of audio - e.g., AUDIO_ADTS
* - `frame` is the audio frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_audio_frame(int file,
int audio_type,
audio_frame_p *frame);
#endif // _audio_fns

874
avs.c 100644
Wyświetl plik

@ -0,0 +1,874 @@
/*
* Datastructures and prototypes for reading AVS elementary streams.
*
* XXX Ignores the issue of the equivalent of AFD data. This *will* cause
* XXX problems if rewinding or filtering is to be done. However, what
* XXX needs to be done to fix this can probably be based on the code in
* XXX h262.c.
* XXX And, also, reversing is not yet supported for AVS, anyway.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "compat.h"
#include "avs_fns.h"
#include "es_fns.h"
#include "ts_fns.h"
#include "reverse_fns.h"
#include "misc_fns.h"
#define DEBUG 0
#define DEBUG_GET_NEXT_PICTURE 1
/*
* Return a string representing the start code
*/
extern const char *avs_start_code_str(byte start_code)
{
if (start_code < 0xB0) return "Slice";
switch (start_code)
{
// AVS start codes that we are interested in
case 0xB0: return "Video sequence start";
case 0xB1: return "Video sequence end";
case 0xB2: return "User data";
case 0xB3: return "I frame";
case 0xB4: return "Reserved";
case 0xB5: return "Extension start";
case 0xB6: return "P/B frame";
case 0xB7: return "Video edit";
default: return "Reserved";
}
}
/*
* Print out information derived from the start code, to the given stream.
*/
extern void print_avs_start_code_str(FILE *stream,
byte start_code)
{
if (stream != NULL)
{
if (start_code < 0xB0)
fprintf(stream,"Slice %02x\n",start_code & 0xAF);
else
fprintf(stream,"%s",avs_start_code_str(start_code));
}
}
// ------------------------------------------------------------
// AVS item *data* stuff
// ------------------------------------------------------------
/*
* Build a new AVS frame reading context.
*
* This acts as a "jacket" around the ES context, and is used when reading
* AVS frames with get_next_avs_frame(). It "remembers" the last
* item read, which is the first item that was not part of the frame.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_avs_context(ES_p es,
avs_context_p *context)
{
avs_context_p new = malloc(SIZEOF_AVS_CONTEXT);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate AVS context datastructure\n");
return 1;
}
new->es = es;
new->frame_index = 0;
new->last_item = NULL;
new->reverse_data = NULL;
new->count_since_seq_hdr = 0;
*context = new;
return 0;
}
/*
* Free an AVS frame reading context.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does not free any `reverse_data` datastructure.
*
* Does nothing if `context` is already NULL.
*/
extern void free_avs_context(avs_context_p *context)
{
avs_context_p cc = *context;
if (cc == NULL)
return;
if (cc->last_item != NULL)
free_ES_unit(&cc->last_item);
cc->reverse_data = NULL;
free(*context);
*context = NULL;
return;
}
/*
* Rewind a file being read as AVS frames
*
* This is a wrapper for `seek_ES` that also knows to unset things appropriate
* to the AVS frame reading context.
*
* If a reverse context is attached to this context, it also will
* be "rewound" appropriately.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_avs_context(avs_context_p context)
{
ES_offset start_of_file = {0,0};
// First, forget where we are
if (context->last_item)
{
free_ES_unit(&context->last_item);
context->last_item = NULL;
}
context->frame_index = 0; // no frames read from this file yet
// Next, take care of rewinding
if (context->reverse_data)
{
context->reverse_data->last_posn_added = -1; // next entry to be 0
context->count_since_seq_hdr = 0; // what else can we do?
}
// And then, do the relocation itself
return seek_ES(context->es,start_of_file);
}
// ------------------------------------------------------------
// AVS "frames"
// ------------------------------------------------------------
/*
* Add (the information from) an AVS ES unit to the given frame.
*
* Note that since this takes a copy of the ES unit data,
* it is safe to free the original ES unit.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int append_to_avs_frame(avs_frame_p frame,
ES_unit_p unit)
{
return append_to_ES_unit_list(frame->list,unit);
}
/*
* Determine the picture coding type of an AVS ES unit
*
* P/B frames are distinguished by their picture coding types. For I frames,
* we make one up...
*
* Returns an appropriate value (0 if none suitable)
*/
extern int avs_picture_coding_type(ES_unit_p unit)
{
if (unit->start_code == 0xB3)
return AVS_I_PICTURE_CODING; // strictly our invention
else if (unit->start_code == 0xB6)
{
byte picture_coding_type = (unit->data[6] & 0xC0) >> 6;
if (picture_coding_type == AVS_P_PICTURE_CODING ||
picture_coding_type == AVS_B_PICTURE_CODING)
return picture_coding_type;
else
{
fprintf(stderr,"AVS Picture coding type %d (in %02x)\n",
picture_coding_type,unit->data[3]);
return 0;
}
}
else
{
fprintf(stderr,"AVS 'frame' with start code %02x does not have picture coding type\n",
unit->data[0]);
return 0;
}
}
/*
* Build a new AVS "frame", starting with the given item (which is
* copied, so may be freed after this call).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int build_avs_frame(avs_context_p context,
avs_frame_p *frame,
ES_unit_p unit)
{
int err;
byte *data = unit->data;
avs_frame_p new = malloc(SIZEOF_AVS_FRAME);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate AVS frame datastructure\n");
return 1;
}
err = build_ES_unit_list(&(new->list));
if (err)
{
fprintf(stderr,"### Unable to allocate internal list for AVS frame\n");
free(new);
return 1;
}
// Deduce what we can from the first unit of the "frame"
new->start_code = unit->start_code;
if (is_avs_frame_item(unit))
{
new->picture_coding_type = avs_picture_coding_type(unit);
new->is_frame = TRUE;
new->is_sequence_header = FALSE;
if (new->picture_coding_type != AVS_I_PICTURE_CODING)
new->picture_distance = (data[6] << 2) | ((data[7] & 0xC0) >> 6);
else
// I frames *do* have a picture_distance field, but I'm not interested
// and it takes more work to find it...
new->picture_distance = 0;
}
else if (is_avs_seq_header_item(unit))
{
new->is_frame = FALSE;
new->is_sequence_header = TRUE;
new->picture_coding_type = 0xFF; // Meaningless value, just in case
new->aspect_ratio = (data[10] & 0x3C) >> 2;
new->frame_rate_code = ((data[10] & 0x03) << 2) | ((data[11] & 0xC0) >> 4);
#if DEBUG
printf("aspect_ratio=%d, frame_rate_code=%d (%.2f)\n",new->aspect_ratio,
new->frame_rate_code,avs_frame_rate(new->frame_rate_code));
#endif
}
else if (is_avs_seq_end_item(unit))
{
new->is_frame = FALSE;
new->is_sequence_header = FALSE;
new->picture_coding_type = 0xFF; // Meaningless value, just in case
}
else
{
fprintf(stderr,
"!!! Building AVS frame that starts with a %s (%02x)\n",
avs_start_code_str(unit->start_code),unit->start_code);
new->is_frame = FALSE;
new->is_sequence_header = FALSE;
new->picture_coding_type = 0xFF; // Meaningless value, just in case
}
err = append_to_avs_frame(new,unit);
if (err)
{
fprintf(stderr,"### Error appending first ES unit to AVS %s\n",
avs_start_code_str(unit->start_code));
free_avs_frame(&new);
return 1;
}
*frame = new;
return 0;
}
/*
* Free an AVS "frame".
*
* Clears the datastructure, frees it, and returns `frame` as NULL.
*
* Does nothing if `frame` is already NULL.
*/
extern void free_avs_frame(avs_frame_p *frame)
{
avs_frame_p pic = *frame;
if (pic == NULL)
return;
if (pic->list != NULL)
free_ES_unit_list(&pic->list);
free(*frame);
*frame = NULL;
return;
}
#if 0
/*
* Remember a frame for future reversing, if it's an I frame or a
* sequence header
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int maybe_remember_this_frame(avs_context_p avs,
int verbose,
avs_frame_p this_frame)
{
int err;
ES_offset start_posn = {0,0};
u_int32 num_bytes = 0;
if (this_frame->is_frame)
{
if (this_frame->picture_coding_type == AVS_I_PICTURE_CODING)
{
// It's an I frame - we want to remember it in our reverse list
(avs->count_since_seq_hdr) ++;
err = get_ES_unit_list_bounds(this_frame->list,&start_posn,&num_bytes);
if (err)
{
fprintf(stderr,
"### Error working out position/size of AVS frame\n");
return 1;
}
err = remember_reverse_avs_data(avs->reverse_data,avs->frame_index,
start_posn,num_bytes,
avs->count_since_seq_hdr,0);
if (err)
{
fprintf(stderr,
"### Error remembering reversing data for AVS item\n");
return 1;
}
if (verbose)
printf("REMEMBER I frame %5d at " OFFSET_T_FORMAT_08
"/%04d for %5d\n",avs->frame_index,
start_posn.infile,start_posn.inpacket,num_bytes);
}
}
else if (this_frame->is_sequence_header)
{
// It's a sequence header - remember it for the next frame
avs->count_since_seq_hdr = 0;
err = get_ES_unit_list_bounds(this_frame->list,&start_posn,&num_bytes);
if (err)
{
fprintf(stderr,"### Error working out position/size of AVS"
" sequence header for reversing data\n");
return 1;
}
err = remember_reverse_avs_data(avs->reverse_data,0,
start_posn,num_bytes,0);
if (err)
{
fprintf(stderr,"### Error remembering reversing data for AVS item\n");
return 1;
}
if (verbose)
printf("REMEMBER Sequence header at " OFFSET_T_FORMAT_08
"/%04d for %5d\n",
start_posn.infile,start_posn.inpacket,num_bytes);
}
return 0;
}
#endif
#if DEBUG_GET_NEXT_PICTURE
/*
* Print a representation of an item for debugging
*/
static void _show_item(ES_unit_p unit)
{
printf("__ ");
if (unit == NULL)
{
printf("<no ES unit>\n");
return;
}
if (is_avs_frame_item(unit))
printf("%s frame",AVS_PICTURE_CODING_STR(avs_picture_coding_type(unit)));
else
printf("%s",avs_start_code_str(unit->start_code));
printf(" at " OFFSET_T_FORMAT "/%d for %d\n",
unit->start_posn.infile,unit->start_posn.inpacket,unit->data_len);
}
#endif
/*
* Retrieve the the next AVS "frame".
*
* The AVS "frame" returned can be one of:
*
* 1. A frame, including its data.
* 2. A sequence header, including its sequence extension, if any.
* 3. A sequence end.
*
* - `context` is the AVS frame reading context.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `frame` is the AVS "frame", containing a frame, a sequence header or a
* sequence end
*
* Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some
* error occurs.
*/
static int get_next_avs_single_frame(avs_context_p context,
int verbose,
avs_frame_p *frame)
{
int err = 0;
int in_sequence_header = FALSE;
int in_sequence_end = FALSE;
int in_frame = FALSE;
int last_was_slice = FALSE;
ES_unit_p item = context->last_item;
#if DEBUG_GET_NEXT_PICTURE
int num_slices = 0;
int had_slice = FALSE;
int last_slice_start_code = 0;
if (verbose && context->last_item) printf("__ reuse last item\n");
#endif
context->last_item = NULL;
// Find the first item of our next "frame"
for (;;)
{
if (item == NULL)
{
err = find_and_build_next_ES_unit(context->es,&item);
if (err) return err;
}
if (is_avs_frame_item(item))
{
in_frame = TRUE;
break;
}
else if (is_avs_seq_header_item(item))
{
in_sequence_header = TRUE;
break;
}
else if (is_avs_seq_end_item(item))
{
in_sequence_end = TRUE;
break;
}
#if DEBUG_GET_NEXT_PICTURE
else if (verbose)
_show_item(item);
#endif
free_ES_unit(&item);
}
#if DEBUG_GET_NEXT_PICTURE
if (verbose)
{
printf("__ --------------------------------- <start frame>\n");
_show_item(item);
}
#endif
err = build_avs_frame(context,frame,item);
if (err) return 1;
free_ES_unit(&item);
if (in_sequence_end)
{
// A sequence end is a single item, so we're done
#if DEBUG_GET_NEXT_PICTURE
if (verbose)
printf("__ --------------------------------- <end frame>\n");
#endif
return 0;
}
// Now find all the rest of the frame/sequence header
for (;;)
{
err = find_and_build_next_ES_unit(context->es,&item);
if (err)
{
if (err != EOF)
free_avs_frame(frame);
return err;
}
if (in_frame)
{
// Have we just finished a frame?
// We know we have if the last item was a slice, but this one isn't
if (last_was_slice && !is_avs_slice_item(item))
break;
last_was_slice = is_avs_slice_item(item);
}
else if (in_sequence_header)
{
// Have we just finished a sequence header and its friends?
// We know we have if we've hit something that isn't an
// extension start or user data start code (perhaps we could
// get away with just keeping the (in MPEG-2) sequence_extension,
// but it's safer (and simpler) to keep the lot
if (!is_avs_extension_start_item(item) &&
!is_avs_user_data_item(item))
break;
}
#if DEBUG_GET_NEXT_PICTURE
if (verbose)
{
if (!had_slice)
_show_item(item);
if (is_avs_slice_item(item))
{
num_slices ++;
last_slice_start_code = item->start_code;
if (!had_slice)
had_slice = TRUE;
}
}
#endif
// Don't forget to remember the actual item
err = append_to_avs_frame(*frame,item);
if (err)
{
fprintf(stderr,"### Error adding item to AVS sequence header\n");
free_avs_frame(frame);
return 1;
}
free_ES_unit(&item);
}
if (in_frame)
context->frame_index ++;
context->last_item = item;
#if DEBUG_GET_NEXT_PICTURE
if (verbose)
{
if (in_frame)
{
if (num_slices > 1)
{
ES_unit_p unit = &(*frame)->list->array[(*frame)->list->length-1];
printf("__ ...\n");
printf("__ slice %2x",last_slice_start_code);
printf(" at " OFFSET_T_FORMAT "/%d for %d\n",
unit->start_posn.infile,unit->start_posn.inpacket,
unit->data_len);
}
printf("__ (%2d slices)\n",num_slices);
}
printf("__ --------------------------------- <end frame>\n");
if (in_frame || in_sequence_header)
_show_item(item);
}
#endif
return 0;
}
/*
* Retrieve the the next AVS "frame".
*
* The AVS "frame" returned can be one of:
*
* 1. A frame, including its slices.
* 2. A sequence header, including its sequence extension, if any.
* 3. A sequence end.
*
* Specifically, the next AVS "frame" is retrieved from the input stream.
*
* If that "frame" represents a sequence header or a frame, it is returned.
*
* Note that if the context is associated with a reverse context,
* then appropriate frames/sequence headers will automatically be
* remembered therein.
*
* - `context` is the AVS frame reading context.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `frame` is the AVS "frame", containing a frame, a sequence header or a
* sequence end
*
* Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some
* error occurs.
*/
extern int get_next_avs_frame(avs_context_p context,
int verbose,
int quiet,
avs_frame_p *frame)
{
int err;
err = get_next_avs_single_frame(context,verbose,frame);
if (err) return err;
#if 0
if (context->reverse_data)
{
err = maybe_remember_this_frame(context,verbose,*frame);
if (err)
{
free_avs_frame(frame);
return 1;
}
}
#endif
return 0;
}
/*
* Write out an AVS frame as TS
*
* - `tswriter` is TS the output stream
* - `frame` is the frame to write out
* - `pid` is the PID to use for the TS packets
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS(TS_writer_p tswriter,
avs_frame_p frame,
u_int32 pid)
{
int ii;
ES_unit_list_p list;
if (frame == NULL || frame->list == NULL)
return 0;
list = frame->list;
for (ii = 0; ii < list->length; ii++)
{
int err;
ES_unit_p unit = &(list->array[ii]);
err = write_ES_as_TS_PES_packet(tswriter,unit->data,unit->data_len,pid,
DEFAULT_VIDEO_STREAM_ID);
if (err)
{
fprintf(stderr,"### Error writing out frame list to TS\n");
return err;
}
}
return 0;
}
/*
* Write out an AVS frame as TS, with PTS timing in the first PES packet
* (and PCR timing in the first TS of the frame).
*
* - `frame` is the frame to write out
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `got_pts` is TRUE if we have a PTS value, in which case
* - `pts` is said PTS value
* - `got_dts` is TRUE if we also have DTS, in which case
* - `dts` is said DTS value.
*
* If we are given a DTS (which must, by definition, always go up) we will also
* use it as the value for PCR.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS_with_pts_dts(avs_frame_p frame,
TS_writer_p tswriter,
u_int32 video_pid,
int got_pts,
u_int64 pts,
int got_dts,
u_int64 dts)
{
int ii;
ES_unit_list_p list;
if (frame == NULL || frame->list == NULL)
return 0;
list = frame->list;
for (ii = 0; ii < list->length; ii++)
{
int err;
ES_unit_p unit = &(list->array[ii]);
// Only write the first PES packet out with PTS
if (ii == 0)
err = write_ES_as_TS_PES_packet_with_pts_dts(tswriter,
unit->data,
unit->data_len,
video_pid,
DEFAULT_VIDEO_STREAM_ID,
got_pts,pts,
got_dts,dts);
else
err = write_ES_as_TS_PES_packet(tswriter,
unit->data,unit->data_len,
video_pid,DEFAULT_VIDEO_STREAM_ID);
if (err)
{
fprintf(stderr,"### Error writing out frame list to TS\n");
return err;
}
}
return 0;
}
/*
* Write out an AVS frame as TS, with PCR timing in the first TS of the
* frame.
*
* - `frame` is the frame to write out
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `pcr_base` and `pcr_extn` encode the PCR value.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS_with_PCR(avs_frame_p frame,
TS_writer_p tswriter,
u_int32 video_pid,
u_int64 pcr_base,
u_int32 pcr_extn)
{
int ii;
ES_unit_list_p list;
if (frame == NULL || frame->list == NULL)
return 0;
list = frame->list;
for (ii = 0; ii < list->length; ii++)
{
int err;
ES_unit_p unit = &(list->array[ii]);
// Only write the first PES packet out with PCR
if (ii == 0)
err = write_ES_as_TS_PES_packet_with_pcr(tswriter,
unit->data,
unit->data_len,
video_pid,
DEFAULT_VIDEO_STREAM_ID,
pcr_base,pcr_extn);
else
err = write_ES_as_TS_PES_packet(tswriter,
unit->data,unit->data_len,
video_pid,DEFAULT_VIDEO_STREAM_ID);
if (err)
{
fprintf(stderr,"### Error writing out frame list to TS\n");
return err;
}
}
return 0;
}
/*
* Write out a frame (as stored in an ES unit list) as ES
*
* - `output` is the ES output file
* - `frame` is the frame to write out
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_ES(FILE *output,
avs_frame_p frame)
{
int ii;
ES_unit_list_p list;
if (frame == NULL || frame->list == NULL)
return 0;
list = frame->list;
for (ii = 0; ii < list->length; ii++)
{
int err;
ES_unit_p unit = &(list->array[ii]);
err = write_ES_unit(output,unit);
if (err)
{
fprintf(stderr,"### Error writing out frame list to ES\n");
return err;
}
}
return 0;
}
/*
* Report on an AVS frame's contents.
*
* - `stream` is where to write the information
* - `frame` is the frame to report on
* - if `report_data`, then the component ES units will be printed out as well
*/
extern void report_avs_frame(FILE *stream,
avs_frame_p frame,
int report_data)
{
if (frame->is_frame)
{
printf("%s #%02d",
AVS_PICTURE_CODING_STR(frame->picture_coding_type),
frame->picture_distance);
printf("\n");
}
else if (frame->is_sequence_header)
{
printf("Sequence header: ");
printf(" frame rate %d (%.2f), aspect ratio %d (%s)",
frame->frame_rate_code,
avs_frame_rate(frame->frame_rate_code),
frame->aspect_ratio,
(frame->aspect_ratio==1?"SAR: 1.0":
frame->aspect_ratio==2?"4/3":
frame->aspect_ratio==3?"16/9":
frame->aspect_ratio==4?"2.21/1":"???"));
printf("\n");
}
else
{
printf("Sequence end\n");
}
if (report_data)
report_ES_unit_list(stream,"ES units",frame->list);
}

132
avs_defns.h 100644
Wyświetl plik

@ -0,0 +1,132 @@
/*
* Datastructures for reading AVS elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _avs_defns
#define _avs_defns
#include <stdio.h>
#include "compat.h"
#include "es_defns.h"
#include "ts_defns.h"
// Since reverse_data refers to avs and acces_unit datastructures, and
// *they* refer to reverse_data, we need to break the circular referencing
// at some point
typedef struct avs_context *avs_context_p;
#include "reverse_defns.h"
// ------------------------------------------------------------
// An AVS frame. This might be a picture or a sequence header (and
// its associated ES units).
struct _avs_frame
{
// The main thing we need is a list of the units that make up this picture
ES_unit_list_p list;
// An AVS "picture" might be a "proper" picture, a sequence header,
// or (just) a sequence end item. It's useful to be able to identify
// the two more common cases easily
int is_frame;
int is_sequence_header;
// It's also useful to remember what the first ES unit is
int start_code;
// Data defined for a frame.
byte picture_coding_type; // I, P or B (0, 1, 2)
byte picture_distance; // presentation order of P/B frames
// Data defined for a sequence header
byte aspect_ratio; // 1=SAR/1.0 2=4/3, 3=16/9, 4=2.21/1 (?)
byte frame_rate_code; // see Table 7-6
};
typedef struct _avs_frame *avs_frame_p;
#define SIZEOF_AVS_FRAME sizeof(struct _avs_frame)
// ------------------------------------------------------------
#define is_avs_slice_item(unit) ((unit)->start_code<0xB0)
#define is_avs_frame_item(unit) ((unit)->start_code==0xB3 || \
(unit)->start_code==0xB6)
#define is_avs_user_data_item(unit) ((unit)->start_code==0xB2)
#define is_avs_seq_header_item(unit) ((unit)->start_code==0xB0)
#define is_avs_seq_end_item(unit) ((unit)->start_code==0xB1)
#define is_avs_extension_start_item(unit) ((unit)->start_code==0xB5)
#define is_avs_video_edit_item(unit) ((unit)->start_code==0xB7)
#define avs_frame_rate(code) ((code)==1?24000.0/1001: /* 23.967... */\
(code)==2?24: \
(code)==3?25: \
(code)==4?30000.0/1001: /* 29.97... */ \
(code)==5?30: \
(code)==6?50: \
(code)==7?60000.0/1001: /* 59.94... */ \
(code)==8?60: \
25) /* Hmm-really an error */
#define AVS_I_PICTURE_CODING 0 // our invention, but reasonable in context
#define AVS_P_PICTURE_CODING 1
#define AVS_B_PICTURE_CODING 2
// Note that "I" is made up by us (there is no picture coding on I frames)
#define AVS_PICTURE_CODING_STR(s) \
((s)==AVS_I_PICTURE_CODING?"I": \
(s)==AVS_P_PICTURE_CODING?"P": \
(s)==AVS_B_PICTURE_CODING?"B": \
"Reserved")
// ------------------------------------------------------------
// Context for looping over the AVS items and pictures in an elementary
// stream
struct avs_context
{
ES_p es;
// We count all of the frames as we read them (this is useful
// when we are building up reverse_data arrays). If functions
// move around in the data stream, we assume that they will
// (re)set this to a sensible value.
// The index of the first frame read is 1, and this value is
// incremented by each call of `get_next_avs_frame` (note that
// for this purpose, sequence headers are *not* considered frames)
u_int32 frame_index; // The index of the last frame read
// We detect the end of an AVS frame (or sequence header) by
// reading the first item that cannot be part of it. We then need
// to remember that item for *next* time we try to read a frame.
ES_unit_p last_item;
// If we are collecting reversing information, then we keep a reference
// to the reverse data here
reverse_data_p reverse_data;
// In the same context, we need to remember how long it is since the
// last sequence header
byte count_since_seq_hdr;
};
#define SIZEOF_AVS_CONTEXT sizeof(struct avs_context)
#endif // _avs_defns

196
avs_fns.h 100644
Wyświetl plik

@ -0,0 +1,196 @@
/*
* Prototypes for reading AVS elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _avs_fns
#define _avs_fns
#include "avs_defns.h"
/*
* Return a string representing the start code
*/
extern const char *avs_start_code_str(byte start_code);
/*
* Print out information derived from the start code, to the given stream.
*/
extern void print_avs_start_code_str(FILE *stream,
byte start_code);
/*
* Determine the picture coding type of an AVS ES unit
*
* P/B frames are distinguished by their picture coding types. For I frames,
* we make one up...
*
* Returns an appropriate value (0 if none suitable)
*/
extern int avs_picture_coding_type(ES_unit_p unit);
/*
* Build a new AVS frame reading context.
*
* This acts as a "jacket" around the ES context, and is used when reading
* AVS frames with get_next_avs_frame(). It "remembers" the last
* item read, which is the first item that was not part of the frame.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_avs_context(ES_p es,
avs_context_p *context);
/*
* Free an AVS frame reading context.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does not free any `reverse_data` datastructure.
*
* Does nothing if `context` is already NULL.
*/
extern void free_avs_context(avs_context_p *context);
/*
* Rewind a file being read as AVS frames
*
* This is a wrapper for `seek_ES` that also knows to unset things appropriate
* to the AVS frame reading context.
*
* If a reverse context is attached to this context, it also will
* be "rewound" appropriately.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_avs_context(avs_context_p context);
/*
* Free an AVS "frame".
*
* Clears the datastructure, frees it, and returns `frame` as NULL.
*
* Does nothing if `frame` is already NULL.
*/
extern void free_avs_frame(avs_frame_p *frame);
/*
* Retrieve the the next AVS "frame".
*
* The AVS "frame" returned can be one of:
*
* 1. A frame, including its slices.
* 2. A sequence header, including its sequence extension, if any.
* 3. A sequence end.
*
* Specifically, the next AVS "frame" is retrieved from the input stream.
*
* If that "frame" represents a sequence header or a frame, it is returned.
*
* Note that if the context is associated with a reverse context,
* then appropriate frames/sequence headers will automatically be
* remembered therein.
*
* - `context` is the AVS frame reading context.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `frame` is the AVS "frame", containing a frame, a sequence header or a
* sequence end
*
* Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some
* error occurs.
*/
extern int get_next_avs_frame(avs_context_p context,
int verbose,
int quiet,
avs_frame_p *frame);
/*
* Write out an AVS frame as TS
*
* - `tswriter` is TS the output stream
* - `frame` is the frame to write out
* - `pid` is the PID to use for the TS packets
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS(TS_writer_p tswriter,
avs_frame_p frame,
u_int32 pid);
/*
* Write out an AVS frame as TS, with PTS timing in the first PES packet
* (and PCR timing in the first TS of the frame).
*
* - `frame` is the frame to write out
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `got_pts` is TRUE if we have a PTS value, in which case
* - `pts` is said PTS value
* - `got_dts` is TRUE if we also have DTS, in which case
* - `dts` is said DTS value.
*
* If we are given a DTS (which must, by definition, always go up) we will also
* use it as the value for PCR.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS_with_pts_dts(avs_frame_p frame,
TS_writer_p tswriter,
u_int32 video_pid,
int got_pts,
u_int64 pts,
int got_dts,
u_int64 dts);
/*
* Write out an AVS frame as TS, with PCR timing in the first TS of the
* frame.
*
* - `frame` is the frame to write out
* - `tswriter` is the TS context to write with
* - `video_pid` is the PID to use to write the data
* - `pcr_base` and `pcr_extn` encode the PCR value.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_TS_with_PCR(avs_frame_p frame,
TS_writer_p tswriter,
u_int32 video_pid,
u_int64 pcr_base,
u_int32 pcr_extn);
/*
* Write out a frame (as stored in an ES unit list) as ES
*
* - `output` is the ES output file
* - `frame` is the frame to write out
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_avs_frame_as_ES(FILE *output,
avs_frame_p frame);
/*
* Report on an AVS frame's contents.
*
* - `stream` is where to write the information
* - `frame` is the frame to report on
* - if `report_data`, then the component ES units will be printed out as well
*/
extern void report_avs_frame(FILE *stream,
avs_frame_p frame,
int report_data);
#endif // _avs_fns

247
bitdata.c 100644
Wyświetl plik

@ -0,0 +1,247 @@
/*
* Functions to handle byte data as bit data, and particularly to read
* Exp-Golomb encoded data.
*
* See H.264 clause 10.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "compat.h"
#include "bitdata_fns.h"
static int MASK[] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
/*
* Build a new bitdata datastructure.
*
* - `data` is the byte array we're extracting bits from.
* - `data_len` is its length (in bytes).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_bitdata(bitdata_p *bitdata,
byte data[],
int data_len)
{
bitdata_p new = malloc(SIZEOF_BITDATA);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate bitdata datastructure\n");
return 1;
}
new->data = data;
new->data_len = data_len;
new->cur_byte = 0;
new->cur_bit = -1;
*bitdata = new;
return 0;
}
/*
* Tidy up and free a bitdata datastructure after we've finished with it.
*
* Clears the bitdata datastructure, frees it, and sets `bitdata` to NULL.
*
* Does nothing if `bitdata` is already NULL.
*/
extern void free_bitdata(bitdata_p *bitdata)
{
if (*bitdata == NULL)
return;
(*bitdata)->data = NULL;
(*bitdata)->cur_byte = 0;
(*bitdata)->cur_bit = -1;
free(*bitdata);
*bitdata = NULL;
}
/*
* Return the next bit from the data.
*
* Returns 0 or 1 if it reads the bit correctly, -1 if there are no more
* bits to be read.
*/
static inline int next_bit(bitdata_p bitdata)
{
bitdata->cur_bit += 1;
if (bitdata->cur_bit == 8)
{
bitdata->cur_bit = 0;
bitdata->cur_byte += 1;
if (bitdata->cur_byte > (bitdata->data_len - 1))
{
fprintf(stderr,"### No more bits to read from input stream\n");
return -1;
}
}
return (bitdata->data[bitdata->cur_byte] & MASK[bitdata->cur_bit])
>> (7 - bitdata->cur_bit);
}
/*
* Return the next bit from the data.
*
* Returns 0 if it reads the bit correctly, 1 if there are no more
* bits to be read.
*/
extern int read_bit(bitdata_p bitdata,
byte *bit)
{
int next = next_bit(bitdata);
if (next < 0)
return 1;
else
{
*bit = next;
return 0;
}
}
/*
* Reads `count` bits from the data.
*
* Note it is asserted that `count` must be in the range 0..32.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_bits(bitdata_p bitdata,
int count,
u_int32 *bits)
{
int index = 0;
u_int32 result = 0;
assert((count >=0 && count <= 32));
for (index=0; index<count; index++)
{
int bit = next_bit(bitdata);
if (bit < 0)
return 1;
else
result = (result << 1) | bit;
}
*bits = result;
return 0;
}
/*
* Reads `count` bits from the data, into a byte.
*
* Note it is asserted that `count` must be in the range 0..8.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_bits_into_byte(bitdata_p bitdata,
int count,
byte *bits)
{
int index = 0;
byte result = 0;
assert((count >=0 && count <= 8));
for (index=0; index<count; index++)
{
int bit = next_bit(bitdata);
if (bit < 0)
return 1;
else
result = (result << 1) | bit;
}
*bits = result;
return 0;
}
/*
* Read zero bits, counting them. Stop at the first non-zero bit.
*
* Returns the number of zero bits. Note that the non-zero bit is not
* "unread" in any way, so reading another bit will retrieve the first bit
* thereafter.
*/
extern int count_zero_bits(bitdata_p bitdata)
{
int count = 0;
while (next_bit(bitdata) == 0)
count ++;
return count;
}
/*
* Read and decode an Exp-Golomb code.
*
* Reference H.264 10.1 for an explanation.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_exp_golomb(bitdata_p bitdata,
u_int32 *result)
{
u_int32 next = 0;
int leading_zero_bits = count_zero_bits(bitdata);
int err = read_bits(bitdata,leading_zero_bits,&next);
if (err)
{
fprintf(stderr,
"### Unable to read ExpGolomb value - not enough bits (%d)\n",
leading_zero_bits);
return err;
}
*result = (int) (pow(2,leading_zero_bits) - 1 + next);
return 0;
}
/*
* Read and decode a signed Exp-Golomb code.
*
* Reference H.264 10.1 sqq for an explanation.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_signed_exp_golomb(bitdata_p bitdata,
int32 *result)
{
u_int32 val = 0;
int err = read_exp_golomb(bitdata,&val);
if (err)
{
fprintf(stderr,"### Unable to read signed ExpGolomb value\n");
return err;
}
*result = (int) (pow(-1,(val+1)) * ceil(val / 2.0));
return 0;
}

47
bitdata_defns.h 100644
Wyświetl plik

@ -0,0 +1,47 @@
/*
* Infrastructure to handle byte data as bit data, and particularly to read
* Exp-Golomb encoded data.
*
* See H.264 clause 10.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _bitdata_defns
#define _bitdata_defns
#include "compat.h"
struct bitdata
{
byte *data; // The data we're reading from
int data_len; // It's length
int cur_byte; // Which byte our current bit is in
int cur_bit; // Which bit within that byte
};
typedef struct bitdata *bitdata_p;
#define SIZEOF_BITDATA sizeof(struct bitdata)
#endif // _bitdata_defns

116
bitdata_fns.h 100644
Wyświetl plik

@ -0,0 +1,116 @@
/*
* Infrastructure to handle byte data as bit data, and particularly to read
* Exp-Golomb encoded data.
*
* See H.264 clause 10.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _bitdata_fns
#define _bitdata_fns
#include "bitdata_defns.h"
/*
* Build a new bitdata datastructure.
*
* - `data` is the byte array we're extracting bits from.
* - `data_len` is its length (in bytes).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_bitdata(bitdata_p *bitdata,
byte data[],
int data_len);
/*
* Tidy up and free a bitdata datastructure after we've finished with it.
*
* Clears the bitdata datastructure, frees it, and sets `bitdata` to NULL.
*/
extern void free_bitdata(bitdata_p *bitdata);
/*
* Return the next bit from the data.
*
* Returns 0 if it reads the bit correctly, 1 if there are no more
* bits to be read.
*/
extern int read_bit(bitdata_p bitdata,
byte *bit);
/*
* Reads `count` bits from the data.
*
* Note it is asserted that `count` must be in the range 0..32.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_bits(bitdata_p bitdata,
int count,
u_int32 *bits);
/*
* Reads `count` bits from the data, into a byte.
*
* Note it is asserted that `count` must be in the range 0..8.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_bits_into_byte(bitdata_p bitdata,
int count,
byte *bits);
/*
* Read zero bits, counting them. Stop at the first non-zero bit.
*
* Returns the number of zero bits. Note that the non-zero bit is not
* "unread" in any way, so reading another bit will retrieve the first bit
* thereafter.
*/
extern int count_zero_bits(bitdata_p bitdata);
/*
* Read and decode an Exp-Golomb code.
*
* Reference H.264 10.1 for an explanation.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_exp_golomb(bitdata_p bitdata,
u_int32 *result);
/*
* Read and decode a signed Exp-Golomb code.
*
* Reference H.264 10.1 sqq for an explanation.
*
* Returns 0 if all went well, 1 if there were not enough bits in the data.
*/
extern int read_signed_exp_golomb(bitdata_p bitdata,
int32 *result);
#endif // _bitdata_fns

154
compat.h 100644
Wyświetl plik

@ -0,0 +1,154 @@
/*
* Standard names for various quantities.
*
* These are:
*
* 1. for historical reasons
* 2. to handle differences between BSD or Linux and Windows
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _compat
#define _compat
#ifdef _WIN32
// Kill deprecation warnings
#pragma warning( 4: 4996 )
// I can't find a sensible location that defines "guaranteed" quantities
// for these, so we'll have to be old-fashioned
typedef unsigned char byte;
typedef unsigned short u_int16;
typedef unsigned long u_int32;
typedef unsigned __int64 u_int64;
typedef short int16;
typedef long int32;
typedef __int64 int64;
// On BSD, lseek takes a 64-bit off_t value
// On Linux, if the system supports long files, it does the same
// On Windows, one has the choice of _lseek or _lseeki64
#define lseek _lseeki64
// MS Visual C 2003 for .Net defines off_t in sys/types.h as "long"
// I want to use the same name for my file offsets on Windows and Unix,
// but I also want to use a 64 bit quantity. So:
typedef __int64 offset_t;
// On Windows, printf supports %lld but only uses 32 bits of the input value,
// which leads to confusing results. Correct representation of 64 bit integers,
// requires the use of %I64d, which is suitable for printing out offset_t
#define OFFSET_T_FORMAT "%I64d"
#define OFFSET_T_FORMAT_8 "%8I64d"
#define OFFSET_T_FORMAT_08 "%08I64d"
// Whilst we're at it, define the format for a 64 bit integer as such
#define LLD_FORMAT "%I64d"
#define LLU_FORMAT "%I64u"
#define LLD_FORMAT_STUMP "I64d"
#define LLU_FORMAT_STUMP "I64u"
// The MSDN documentation for Visual Studio seems to indicate that
// the low-level "names" for stdin, etc., are not STDIN_FILENO, etc.,
// but are instead stdin, etc.
// This seems to naturally be confusing with the C terms stdin, etc.
// It *may* be that they actually are not distinct. However, the *numbers*
// follow the normal definitions.
#ifndef STDIN_FILENO
#define STDIN_FILENO 0
#endif
// On Windows, "inline" is a C++ only keyword. In C, it is:
#define inline __inline
// Miscellaneous other Windows-related issues...
#define snprintf _snprintf
#else // _WIN32
#include <sys/types.h> // Posix standard primitive system data types
// C99 also defines equivalent types in <stdint.h>, but the unsigned types
// are spelt uint8_t, etc., instead of u_int8_t. Given the need to support
// older compilers, go with the Posix standard.
typedef u_int8_t byte;
typedef u_int16_t u_int16;
typedef u_int32_t u_int32;
typedef u_int64_t u_int64;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
// lseek on BSD/Linux uses an off_t quantity to specify the required
// position. Where 64 bit file positions are supported, this is a 64 bit
// value. Unfortunately, Windows has off_t defined as being a long.
// For compatibility, therefore, we define a type that can be used on
// both Windows and Unix
typedef off_t offset_t;
#if defined(__linux__) && !defined(__USE_FILE_OFFSET64)
// If Linux does not have 64 bit support built in, then our offsets will
// be just 32 bit integers
#define OFFSET_T_FORMAT "%ld"
#define OFFSET_T_FORMAT_08 "%08ld" // deprecated, because it looks like hex/octal
#define OFFSET_T_FORMAT_8 "%8ld"
#else
// On Unices, printf supports %lld for 64 bit integers, and this is suitable
// for printing out offset_t when it is 64 bit
#define OFFSET_T_FORMAT "%lld"
#define OFFSET_T_FORMAT_08 "%08lld" // deprecated, because it looks like hex/octal
#define OFFSET_T_FORMAT_8 "%8lld"
#endif
// Whilst we're at it, define the format for a 64 bit integer as such
#define LLD_FORMAT "%lld"
#define LLU_FORMAT "%llu"
#define LLD_FORMAT_STUMP "lld"
#define LLU_FORMAT_STUMP "llu"
// Useful macros, but not side-effect free
#define max(i,j) ((i)>(j)?(i):(j))
#define min(i,j) ((i)<(j)?(i):(j))
#endif // WIN32
// Other useful things
typedef void * void_p;
#define TRUE 1
#define FALSE 0
// The following defaults are common, and it's difficult
// to decide which other header file they might belong in
#define DEFAULT_VIDEO_PID 0x68
#define DEFAULT_AUDIO_PID 0x67
#define DEFAULT_PMT_PID 0x66
#endif /* _compat */

462
docs/ac3.html 100644
Wyświetl plik

@ -0,0 +1,462 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
<title>AC3</title>
<style type="text/css">
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="ac3">
<h1 class="title">AC3</h1>
<p>Specification taken from a_52a.pdf</p>
<p>I <em>think</em> that looking at the start of <cite>syncinfo</cite> should give me all the
information I need to (a) determine if this is AC3 and (b) determine if it is
&quot;main&quot; audio.</p>
<pre class="literal-block">
syncframe: -- synchronization frame
syncinfo: -- synchronization info
16: syncword -- 0x0B77
16: crc1
2: fscod -- [1]
6: frmsizecod
bsi: -- bit stream info
5: bsid -- 8 in *this* version of the standard [2]
3: bsmod -- [3]
3: acmod -- which main service channels are in use [4]
2: cmixlev -- center mix level (if 3 front channels)
2: surmixlev -- surround mix level (if surround sound channel)
2: dsurmod -- dolby surround mode (if in 2/0 mode) 2=surround
1: lfeon
5: dialnorm
1: compre
&lt;etc&gt;
for n in range(6):
audblk: -- coded audio block
(256 new audio samples per channel)
auxdata:
errorcheck: -- CRC for whole syncframe
1: crcsv
16: crc2
[1] fscod: sampling rate in kHz:
00 = 48
01 = 44.1
10 = 32
11 = reserved
[2] bsid: values less than 8 are for subsets of the standard. If the software
can decode data with bsid=8, it can also decode data with bsid&lt;8.
[3] bit stream mode:
(the &quot;full&quot; column gives the full service flag for use in DVB's
AC-3_descriptor:AC-3_type [5])
bsmod acmod type of service full?
000 any main audio service: complete main (CM) 1
001 any main audio service: music &amp; effects (ME) 0
010 any associated service: visually impaired (VI) X
011 any associated service: hearing impaired (HI) X
100 any associated service: dialogue (D) 0
101 any associated service: commentary (C) X
110 any associated service: emergency (E) 1
111 001 associated service: voice over (VO) 0
111 010-111 main audio service: karaoke 1
[4] audio coding mode:
bit meaning
0 center channel in use
1 &lt;too complex to summarise here&gt;
2 surround sound channels in use
audio full
coding bandwidth
acmod mode chans order
0 1+1 2 Ch1,Ch2 (&quot;dual mono&quot;)
1 1/0 1 C
2 2/0 2 L,R
3 3/0 3 L,C,R
4 2/1 3 L,R,S
5 3/1 4 L,C,R,S
6 2/2 4 L,R,SL,SR
7 3/2 5 L,C,R,SL,SR
</pre>
<div class="section">
<h1><a id="ac3-in-ts" name="ac3-in-ts">AC3 in TS</a></h1>
<p>AC sync frame contains 1536 audio samples. Its duration is:</p>
<pre class="literal-block">
48kHz -&gt; 32ms
44.1kHz -&gt; approx 34.83ms
32 kHz -&gt; 48ms
</pre>
<p>For ATSC:</p>
<pre class="literal-block">
stream_type 0x81
stream_id 0xBD (private_stream_1) in PES header
registration_descriptor: (in PMT)
8: descriptor_tag -- 0x05
8: descriptor_length -- 0x04
32: format_identifier -- 0x41432D33 (&quot;AC-3&quot;)
audio_stream_descriptor: (in PSI)
8: descriptor_tag -- 0x81
8: descriptor_length -- &lt;number of bytes after this field&gt;
3: sample_rate_code
5: bsid -- same as bsid above [2]
6: bit_rate_code
2: surround_mode
3: bsmod -- same as bsmod above [3]
4: num_channels
1: full_svc
---------------- further optional fields, depending on the above
ISO_639_language_code descriptor allows a stream to be tagged with
the 24-bit ISO 639 language code.
</pre>
<p>For DVB:</p>
<pre class="literal-block">
stream_type 0x06 (private_data)
stream_id 0xBD (private_stream_1) in PES header
AC-3_descriptor: (in PSI and PMT)
8: descriptor_tag -- 0x6A
8: descriptor_length -- &lt;number of bytes after this field&gt;
1: AC-3_type_flag
1: bsid_flag
1: mainid_flag
1: asvc_flag
4: &lt;reserved bits, set to 0&gt;
---------------- further fields present if their flag is set
8: AC-3_type -- [5]
8: bsid -- same as bsid above [2]
8: mainid -- 0-7 main audio service id
8: asvc -- associate service with main service
---------------- further info to the number of bytes indicated
n*8: additional info
[5] I *think* this is interpreted as follows:
Bits Meaning
0-2 0: mono
1: 1+1
2: 2 channel (stereo)
3: 2 channel Dolby surround encoded (stereo)
4: Multichannel audio (&gt;2 channels)
Other values reserved
3-5 same as [3], bit stream mode
6 0: Use channel in combination with another
1: Full service channel, use alone
7 Must be 0
</pre>
<!-- ***** BEGIN LICENSE BLOCK ***** -->
<div class="section">
<h2><a id="license" name="license">License</a></h2>
<p>Version: MPL 1.1</p>
<p>The contents of this file are subject to the Mozilla Public License Version
1.1 (the &quot;License&quot;); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
<a class="reference" href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></p>
<p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.</p>
<p>The Original Code is the MPEG TS, PS and ES tools.</p>
<p>The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright © 2008
the Initial Developer. All Rights Reserved.</p>
<p>Contributor(s):</p>
<blockquote>
Amino Communications Ltd, Swavesey, Cambridge UK</blockquote>
<!-- ***** END LICENSE BLOCK ***** -->
</div>
</div>
</div>
</body>
</html>

180
docs/ac3.txt 100644
Wyświetl plik

@ -0,0 +1,180 @@
===
AC3
===
Specification taken from a_52a.pdf
I *think* that looking at the start of `syncinfo` should give me all the
information I need to (a) determine if this is AC3 and (b) determine if it is
"main" audio.
::
syncframe: -- synchronization frame
syncinfo: -- synchronization info
16: syncword -- 0x0B77
16: crc1
2: fscod -- [1]
6: frmsizecod
bsi: -- bit stream info
5: bsid -- 8 in *this* version of the standard [2]
3: bsmod -- [3]
3: acmod -- which main service channels are in use [4]
2: cmixlev -- center mix level (if 3 front channels)
2: surmixlev -- surround mix level (if surround sound channel)
2: dsurmod -- dolby surround mode (if in 2/0 mode) 2=surround
1: lfeon
5: dialnorm
1: compre
<etc>
for n in range(6):
audblk: -- coded audio block
(256 new audio samples per channel)
auxdata:
errorcheck: -- CRC for whole syncframe
1: crcsv
16: crc2
[1] fscod: sampling rate in kHz:
00 = 48
01 = 44.1
10 = 32
11 = reserved
[2] bsid: values less than 8 are for subsets of the standard. If the software
can decode data with bsid=8, it can also decode data with bsid<8.
[3] bit stream mode:
(the "full" column gives the full service flag for use in DVB's
AC-3_descriptor:AC-3_type [5])
bsmod acmod type of service full?
000 any main audio service: complete main (CM) 1
001 any main audio service: music & effects (ME) 0
010 any associated service: visually impaired (VI) X
011 any associated service: hearing impaired (HI) X
100 any associated service: dialogue (D) 0
101 any associated service: commentary (C) X
110 any associated service: emergency (E) 1
111 001 associated service: voice over (VO) 0
111 010-111 main audio service: karaoke 1
[4] audio coding mode:
bit meaning
0 center channel in use
1 <too complex to summarise here>
2 surround sound channels in use
audio full
coding bandwidth
acmod mode chans order
0 1+1 2 Ch1,Ch2 ("dual mono")
1 1/0 1 C
2 2/0 2 L,R
3 3/0 3 L,C,R
4 2/1 3 L,R,S
5 3/1 4 L,C,R,S
6 2/2 4 L,R,SL,SR
7 3/2 5 L,C,R,SL,SR
AC3 in TS
=========
AC sync frame contains 1536 audio samples. Its duration is::
48kHz -> 32ms
44.1kHz -> approx 34.83ms
32 kHz -> 48ms
For ATSC::
stream_type 0x81
stream_id 0xBD (private_stream_1) in PES header
registration_descriptor: (in PMT)
8: descriptor_tag -- 0x05
8: descriptor_length -- 0x04
32: format_identifier -- 0x41432D33 ("AC-3")
audio_stream_descriptor: (in PSI)
8: descriptor_tag -- 0x81
8: descriptor_length -- <number of bytes after this field>
3: sample_rate_code
5: bsid -- same as bsid above [2]
6: bit_rate_code
2: surround_mode
3: bsmod -- same as bsmod above [3]
4: num_channels
1: full_svc
---------------- further optional fields, depending on the above
ISO_639_language_code descriptor allows a stream to be tagged with
the 24-bit ISO 639 language code.
For DVB::
stream_type 0x06 (private_data)
stream_id 0xBD (private_stream_1) in PES header
AC-3_descriptor: (in PSI and PMT)
8: descriptor_tag -- 0x6A
8: descriptor_length -- <number of bytes after this field>
1: AC-3_type_flag
1: bsid_flag
1: mainid_flag
1: asvc_flag
4: <reserved bits, set to 0>
---------------- further fields present if their flag is set
8: AC-3_type -- [5]
8: bsid -- same as bsid above [2]
8: mainid -- 0-7 main audio service id
8: asvc -- associate service with main service
---------------- further info to the number of bytes indicated
n*8: additional info
[5] I *think* this is interpreted as follows:
Bits Meaning
0-2 0: mono
1: 1+1
2: 2 channel (stereo)
3: 2 channel Dolby surround encoded (stereo)
4: Multichannel audio (>2 channels)
Other values reserved
3-5 same as [3], bit stream mode
6 0: Use channel in combination with another
1: Full service channel, use alone
7 Must be 0
.. ***** BEGIN LICENSE BLOCK *****
License
-------
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

358
docs/adts.html 100644
Wyświetl plik

@ -0,0 +1,358 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
<title>ADTS</title>
<style type="text/css">
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="adts">
<h1 class="title">ADTS</h1>
<pre class="literal-block">
frame:
byte alignment
fixed header
variable header
error check
for (ii = 0; ii &lt; no raw blocks in frame + 1; ii++)
raw data block
fixed header: (the same for all frames)
syncword 12 ('1111 1111 1111') 12
id 1 (1=MPEG-2 AAC, 0=MPEG-4)
layer 2 ('00')
protection absent 1 (see below)
profile objecttype 2
sampling freq index 4
private bit 1 (not used)
channel config 3
original/copy 1
home 1 16
emphasis 2 (maybe only if id=1?)
variable header:
copyright id bit 1
copyright id start 1
aac frame length 13 (length of whole frame, inc. headers)
adts buffer fullness 11
no raw data blocks in frame 2 (0=&gt;1 raw data block)
error check:
if protection absent == 0:
crc check 16
</pre>
<!-- ***** BEGIN LICENSE BLOCK ***** -->
<div class="section">
<h1><a id="license" name="license">License</a></h1>
<p>Version: MPL 1.1</p>
<p>The contents of this file are subject to the Mozilla Public License Version
1.1 (the &quot;License&quot;); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
<a class="reference" href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></p>
<p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.</p>
<p>The Original Code is the MPEG TS, PS and ES tools.</p>
<p>The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright © 2008
the Initial Developer. All Rights Reserved.</p>
<p>Contributor(s):</p>
<blockquote>
Amino Communications Ltd, Swavesey, Cambridge UK</blockquote>
<!-- ***** END LICENSE BLOCK ***** -->
</div>
</div>
</body>
</html>

74
docs/adts.txt 100644
Wyświetl plik

@ -0,0 +1,74 @@
ADTS
====
::
frame:
byte alignment
fixed header
variable header
error check
for (ii = 0; ii < no raw blocks in frame + 1; ii++)
raw data block
fixed header: (the same for all frames)
syncword 12 ('1111 1111 1111') 12
id 1 (1=MPEG-2 AAC, 0=MPEG-4)
layer 2 ('00')
protection absent 1 (see below)
profile objecttype 2
sampling freq index 4
private bit 1 (not used)
channel config 3
original/copy 1
home 1 16
emphasis 2 (maybe only if id=1?)
variable header:
copyright id bit 1
copyright id start 1
aac frame length 13 (length of whole frame, inc. headers)
adts buffer fullness 11
no raw data blocks in frame 2 (0=>1 raw data block)
error check:
if protection absent == 0:
crc check 16
.. ***** BEGIN LICENSE BLOCK *****
License
-------
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

47
docs/build_html.py 100755
Wyświetl plik

@ -0,0 +1,47 @@
#! /usr/bin/env python
"""Build HTML from the reStructuredText files in this directory.
This is a script just so I don't have to remember the particular incantation
required. It's not in the Makefile because I'm not yet sure it belongs there...
Requires Python and docutils.
For the moment, assumes that docutils' tools are installed in the
user's home directory...
"""
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
import os
TOOLSDIR = os.path.expanduser("~/docutils/tools")
BUILDHTML = os.path.join(TOOLSDIR,"buildhtml.py")
def main():
os.system("%s --stylesheet-path=default.css --embed-stylesheet"%BUILDHTML)
if __name__ == "__main__":
main()

232
docs/default.css 100644
Wyświetl plik

@ -0,0 +1,232 @@
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:date: $Date: 2004/08/02 13:51:25 $
:version: $Revision: 1.1 $
:copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
*/
.first {
margin-top: 0 }
.last {
margin-bottom: 0 }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dd {
margin-bottom: 0.5em }
/* Uncomment (& remove this text!) to get bold-faced definition list terms
dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.attention, div.caution, div.danger, div.error, div.hint,
div.important, div.note, div.tip, div.warning, div.admonition {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
div.hint p.admonition-title, div.important p.admonition-title,
div.note p.admonition-title, div.tip p.admonition-title,
div.admonition p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em }
div.footer, div.header {
font-size: smaller }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 0em 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr {
width: 75% }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.line-block {
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.option-argument {
font-style: italic }
span.pre {
white-space: pre }
span.problematic {
color: red }
table {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.citation {
border-left: solid thin gray ;
padding-left: 0.5ex }
table.docinfo {
margin: 2em 4em }
table.footnote {
border-left: solid thin black ;
padding-left: 0.5ex }
td, th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
th.docinfo-name, th.field-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap }
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
font-size: 100% }
tt {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }

Wyświetl plik

@ -0,0 +1,159 @@
# Pseudo-Python rendition of the code for ``get_next_access_unit()``.
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
def get_next_access_unit(context):
"""Retrieve the next access unit from the file described by `context`.
"""
access_unit = build_access_unit()
if context.pending_nal: # i.e., we already had a NAL to start this unit
access_unit.append(context.pending_nal,TRUE,context.pending_list)
context.pending_nal = NULL
context.pending_list.reset(FALSE)
while 1:
try:
nal = context.find_next_NAL_unit()
except EOF:
context.no_more_data = TRUE; # prevent future reads on this stream
break
except BrokenNALUnit:
WARNING("!!! Ignoring broken NAL unit\n")
access_unit.ignored_broken_NAL_units += 1
continue
if nal.is_slice():
if not access_unit.started_primary_picture:
# We're in a new access unit, but we haven't had a slice
# yet, so we can be lazy and assume that this must be the
# first slice
nal.start_reason = "First slice of new access unit"
access_unit.append(nal,TRUE,context.pending_list)
context.pending_list.reset(FALSE)
context.remember_earlier_primary_start(nal)
elif nal.is_first_VCL_NAL(context.earlier_primary_start):
# Regardless of what we determine next, we need to remember
# that the NAL started (what may later be the previous) access
# unit
context.remember_earlier_primary_start(nal)
if access_unit.started_primary_picture:
# We were already in an access unit with a primary
# picture, so this NAL unit must start a new access unit.
# Remember it for next time, and return the access unit so
# far.
context.pending_nal = nal
break; # Ready to return the access unit
else:
# This access unit was waiting for its primary picture
access_unit.append(nal,TRUE,context.pending_list)
context.pending_list.reset(FALSE)
elif not access_unit.started_primary_picture:
# But this is not a NAL unit that may start a new
# access unit. So what should we do? Ignore it?
if not quiet:
WARNING("!!! Ignoring VCL NAL that cannot start a new"
" primary picture: "
nal.report(stderr)
elif nal_is_redundant(nal):
# printf(" ignoring redundant NAL unit\n")
pass
else:
# We're part of the same access unit, but not special
access_unit.append(nal,FALSE,context.pending_list)
context.pending_list.reset(FALSE)
elif nal.nal_unit_type == NAL_ACCESS_UNIT_DELIM:
# An access unit delimiter always starts a new access unit
if access_unit.started_primary_picture:
context.pending_list.append(nal)
break # Ready to return the "previous" access unit
else:
# The current access unit doesn't yet have any VCL NALs
if context.pending_list.length > 0:
WARNING("!!! Ignoring items after last VCL NAL and"
" before Access Unit Delimiter\n")
context.pending_list.report(stderr," ",NULL,)
context.pending_list.reset(TRUE)
if access_unit.nal_units.length > 0:
WARNING("!!! Ignoring incomplete access unit\n")
access_unit.nal_units.report(stderr," ",NULL,)
access_unit.nal_units.reset(TRUE)
access_unit.append(nal,FALSE,NULL)
elif nal.nal_unit_type == NAL_SEI:
# SEI units always precede the primary coded picture
# - so they also implicitly end any access unit that has already
# started its primary picture
if access_unit.started_primary_picture:
context.pending_list.append(nal)
break # Ready to return the "previous" access unit
else:
context.pending_list.append(nal)
elif nal.nal_unit_type in [NAL_SEQ_PARAM_SET, NAL_PIC_PARAM_SET,
13, 14, 15, 16, 17, 18]:
# These start a new access unit *if* they come after the last VCL
# NAL of an access unit. But we can only *tell* that they are
# after the last VCL NAL of an access unit when we start the next
# access unit - so we need to hold them in hand until we know that
# we need them. (i.e., they'll get added to an access unit just
# before the next "more determined" NAL unit we add to an access
# unit)
context.pending_list.append(nal)
elif nal.nal_unit_type == NAL_END_OF_SEQ:
if context.pending_list.length > 0:
WARNING("!!! Ignoring items after last VCL NAL and"
" before End of Sequence\n")
context.pending_list.report(stderr," ",NULL,)
context.pending_list.reset(TRUE)
# And remember this as the End of Sequence marker
context.end_of_sequence = nal
break
elif nal.nal_unit_type == NAL_END_OF_STREAM:
# And remember this as the End of Stream marker
context.end_of_stream = nal
# Which means there's no point in reading more from this stream
# (setting no_more_data like this means that *next* time this
# function is called, it will return EOF)
context.no_more_data = TRUE
# And we're done
break
else:
# It's not a slice, or an access unit delimiter, or an
# end of sequence or stream, or a sequence or picture
# parameter set, or various other odds and ends, so it
# looks like we can ignore it.
pass
# Check for an immediate "end of file with no data"
# - i.e., we read EOF or end of stream, and there was nothing
# between the last access unit and such reading
if context.no_more_data and access_unit.nal_units.length == 0:
raise EOF
# Otherwise, finish off and return the access unit we have in hand
access_unit.end(context,show_details)
# Remember to count it
context.access_unit_index += 1
return access_unit

Wyświetl plik

@ -0,0 +1,169 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>get_next_access_unit.py.html</title>
<meta name="Generator" content="Vim/7.1">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body bgcolor="#e5e5e5" text="#000000"><font face="monospace">
<font color="#0000ff"># Pseudo-Python rendition of the code for ``get_next_access_unit()``.</font><br>
<br>
<font color="#0000ff"># ***** BEGIN LICENSE BLOCK *****</font><br>
<font color="#0000ff"># Version: MPL 1.1</font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># The contents of this file are subject to the Mozilla Public License Version</font><br>
<font color="#0000ff"># 1.1 (the &quot;License&quot;); you may not use this file except in compliance with</font><br>
<font color="#0000ff"># the License. You may obtain a copy of the License at</font><br>
<font color="#0000ff"># <a href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># Software distributed under the License is distributed on an &quot;AS IS&quot; basis,</font><br>
<font color="#0000ff"># WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License</font><br>
<font color="#0000ff"># for the specific language governing rights and limitations under the</font><br>
<font color="#0000ff"># License.</font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># The Original Code is the MPEG TS, PS and ES tools.</font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># The Initial Developer of the Original Code is Amino Communications Ltd.</font><br>
<font color="#0000ff"># Portions created by the Initial Developer are Copyright (C) 2008</font><br>
<font color="#0000ff"># the Initial Developer. All Rights Reserved.</font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># Contributor(s):</font><br>
<font color="#0000ff">#&nbsp;&nbsp; Amino Communications Ltd, Swavesey, Cambridge UK</font><br>
<font color="#0000ff"># </font><br>
<font color="#0000ff"># ***** END LICENSE BLOCK *****</font><br>
<br>
<font color="#a52a2a"><b>def</b></font>&nbsp;<font color="#008b8b">get_next_access_unit</font>(context):<br>
&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;&quot;&quot;Retrieve the next access unit from the file described by `context`.</font></span><br>
<span style="background-color: #f2f2f2"><font color="#ff00ff">&nbsp;&nbsp;&nbsp;&nbsp;&quot;&quot;&quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;access_unit = build_access_unit()<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;context.pending_nal: <font color="#0000ff"># i.e., we already had a NAL to start this unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.append(context.pending_nal,TRUE,context.pending_list)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_nal = NULL<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(FALSE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>while</b></font>&nbsp;<span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>try</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nal = context.find_next_NAL_unit()<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>except</b></font>&nbsp;EOF:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.no_more_data = TRUE; <font color="#0000ff"># prevent future reads on this stream</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>except</b></font>&nbsp;BrokenNALUnit:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;!!! Ignoring broken NAL unit</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\n</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;</font></span>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.ignored_broken_NAL_units += <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>continue</b></font><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;nal.is_slice():<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;<font color="#a52a2a"><b>not</b></font>&nbsp;access_unit.started_primary_picture:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># We're in a new access unit, but we haven't had a slice</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># yet, so we can be lazy and assume that this must be the</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># first slice</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nal.start_reason = <span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;First slice of new access unit&quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.append(nal,TRUE,context.pending_list)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(FALSE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.remember_earlier_primary_start(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.is_first_VCL_NAL(context.earlier_primary_start):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Regardless of what we determine next, we need to remember</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># that the NAL started (what may later be the previous) access</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.remember_earlier_primary_start(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;access_unit.started_primary_picture:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># We were already in an access unit with a primary</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># picture, so this NAL unit must start a new access unit.</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Remember it for next time, and return the access unit so</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># far.</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_nal = nal<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font>;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Ready to return the access unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>else</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># This access unit was waiting for its primary picture</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.append(nal,TRUE,context.pending_list)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(FALSE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;<font color="#a52a2a"><b>not</b></font>&nbsp;access_unit.started_primary_picture:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># But this is not a NAL unit that may start a new</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># access unit. So what should we do? Ignore it?</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;<font color="#a52a2a"><b>not</b></font>&nbsp;quiet:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;!!! Ignoring VCL NAL that cannot start a new&quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot; primary picture: &quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nal.report(stderr)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal_is_redundant(nal):<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># printf(&quot;&nbsp;&nbsp;&nbsp;&nbsp; ignoring redundant NAL unit\n&quot;)</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>pass</b></font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>else</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># We're part of the same access unit, but not special</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.append(nal,FALSE,context.pending_list)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(FALSE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.nal_unit_type == NAL_ACCESS_UNIT_DELIM:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># An access unit delimiter always starts a new access unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;access_unit.started_primary_picture:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.append(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font>&nbsp;<font color="#0000ff"># Ready to return the &quot;previous&quot; access unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>else</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># The current access unit doesn't yet have any VCL NALs</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;context.pending_list.length &gt; <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;!!! Ignoring items after last VCL NAL and&quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot; before Access Unit Delimiter</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\n</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;</font></span>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;&nbsp;&nbsp;&nbsp;&nbsp;&quot;</font></span>,NULL,)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(TRUE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;access_unit.nal_units.length &gt; <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;!!! Ignoring incomplete access unit</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\n</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;</font></span>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.nal_units.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;&nbsp;&nbsp;&nbsp;&nbsp;&quot;</font></span>,NULL,)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.nal_units.reset(TRUE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;access_unit.append(nal,FALSE,NULL)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.nal_unit_type == NAL_SEI:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># SEI units always precede the primary coded picture</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># - so they also implicitly end any access unit that has already</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># started its primary picture</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;access_unit.started_primary_picture:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.append(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font>&nbsp;<font color="#0000ff"># Ready to return the &quot;previous&quot; access unit</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>else</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.append(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.nal_unit_type <font color="#a52a2a"><b>in</b></font>&nbsp;[NAL_SEQ_PARAM_SET, NAL_PIC_PARAM_SET,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style="background-color: #f2f2f2"><font color="#ff00ff">13</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">14</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">15</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">16</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">17</font></span>, <span style="background-color: #f2f2f2"><font color="#ff00ff">18</font></span>]:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># These start a new access unit *if* they come after the last VCL</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># NAL of an access unit. But we can only *tell* that they are</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># after the last VCL NAL of an access unit when we start the next</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># access unit - so we need to hold them in hand until we know that</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># we need them.&nbsp;&nbsp;(i.e., they'll get added to an access unit just</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># before the next &quot;more determined&quot; NAL unit we add to an access</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># unit)</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.append(nal)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.nal_unit_type == NAL_END_OF_SEQ:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;context.pending_list.length &gt; <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;!!! Ignoring items after last VCL NAL and&quot;</font></span><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot; before End of Sequence</font></span><span style="background-color: #f2f2f2"><font color="#6a5acd">\n</font></span><span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;</font></span>)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">&quot;&nbsp;&nbsp;&nbsp;&nbsp;&quot;</font></span>,NULL,)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.pending_list.reset(TRUE)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># And remember this as the End of Sequence marker</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.end_of_sequence = nal<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>elif</b></font>&nbsp;nal.nal_unit_type == NAL_END_OF_STREAM:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># And remember this as the End of Stream marker</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.end_of_stream = nal<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Which means there's no point in reading more from this stream</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># (setting no_more_data like this means that *next* time this</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># function is called, it will return EOF)</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;context.no_more_data = TRUE<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># And we're done</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>break</b></font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>else</b></font>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># It's not a slice, or an access unit delimiter, or an</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># end of sequence or stream, or a sequence or picture</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># parameter set, or various other odds and ends, so it</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># looks like we can ignore it.</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>pass</b></font><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Check for an immediate &quot;end of file with no data&quot;</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># - i.e., we read EOF or end of stream, and there was nothing</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># between the last access unit and such reading</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>if</b></font>&nbsp;context.no_more_data <font color="#a52a2a"><b>and</b></font>&nbsp;access_unit.nal_units.length == <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>raise</b></font>&nbsp;EOF<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Otherwise, finish off and return the access unit we have in hand</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;access_unit.end(context,show_details)<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#0000ff"># Remember to count it</font><br>
&nbsp;&nbsp;&nbsp;&nbsp;context.access_unit_index += <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span><br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;<font color="#a52a2a"><b>return</b></font>&nbsp;access_unit<br>
</font></body>
</html>

341
docs/index.html 100644
Wyświetl plik

@ -0,0 +1,341 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
<title>TS tools documentation</title>
<style type="text/css">
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="ts-tools-documentation">
<h1 class="title">TS tools documentation</h1>
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr class="field"><th class="docinfo-name"><a class="reference" href="tools.html">Tools</a>:</th><td class="field-body">Short descriptions of how to use the various tools provided.</td>
</tr>
<tr class="field"><th class="docinfo-name"><a class="reference" href="library.html">Library</a>:</th><td class="field-body">An overview of the functionality of the library, and how it works.</td>
</tr>
<tr class="field"><th class="docinfo-name" colspan="2"><a class="reference" href="get_next_access_unit.py">get_next_access_unit.py</a>:</th></tr>
<tr><td>&nbsp;</td><td class="field-body">The code for <tt class="docutils literal"><span class="pre">get_next_access_unit</span></tt> from
<tt class="docutils literal"><span class="pre">accessunit.c</span></tt> expressed as pseudo-Python. This is the function that
gathers H.264 NAL units into access units.</td>
</tr>
<tr class="field"><th class="docinfo-name" colspan="2"><a class="reference" href="get_next_access_unit.py.html">get_next_access_unit.py.html</a>:</th></tr>
<tr><td>&nbsp;</td><td class="field-body">The code for <tt class="docutils literal"><span class="pre">get_next_access_unit</span></tt> from
<tt class="docutils literal"><span class="pre">accessunit.c</span></tt> expressed as pseudo-Python and colourised as HTML.</td>
</tr>
<tr class="field"><th class="docinfo-name"><a class="reference" href="todo.html">To do</a>:</th><td class="field-body">Things still outstanding.</td>
</tr>
<tr class="field"><th class="docinfo-name"><a class="reference" href="ac3.html">ac3</a>:</th><td class="field-body">Some notes on AC-3</td>
</tr>
</tbody>
</table>
<!-- ***** BEGIN LICENSE BLOCK ***** -->
<div class="section">
<h1><a id="license" name="license">License</a></h1>
<p>Version: MPL 1.1</p>
<p>The contents of this file are subject to the Mozilla Public License Version
1.1 (the &quot;License&quot;); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
<a class="reference" href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></p>
<p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.</p>
<p>The Original Code is the MPEG TS, PS and ES tools.</p>
<p>The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright © 2008
the Initial Developer. All Rights Reserved.</p>
<p>Contributor(s):</p>
<blockquote>
Amino Communications Ltd, Swavesey, Cambridge UK</blockquote>
<!-- ***** END LICENSE BLOCK ***** -->
</div>
</div>
</body>
</html>

56
docs/index.txt 100644
Wyświetl plik

@ -0,0 +1,56 @@
======================
TS tools documentation
======================
:Tools_: Short descriptions of how to use the various tools provided.
:Library_: An overview of the functionality of the library, and how it works.
:get_next_access_unit.py_: The code for ``get_next_access_unit`` from
``accessunit.c`` expressed as pseudo-Python. This is the function that
gathers H.264 NAL units into access units.
:get_next_access_unit.py.html_: The code for ``get_next_access_unit`` from
``accessunit.c`` expressed as pseudo-Python and colourised as HTML.
:`To do`_: Things still outstanding.
:`ac3`_: Some notes on AC-3
.. _Tools: tools.html
.. _Library: library.html
.. _`To do`: todo.html
.. _`ac3`: ac3.html
.. _get_next_access_unit.py: get_next_access_unit.py
.. _get_next_access_unit.py.html: get_next_access_unit.py.html
.. ***** BEGIN LICENSE BLOCK *****
License
-------
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

833
docs/library.html 100644
Wyświetl plik

@ -0,0 +1,833 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
<title>TS tools library</title>
<style type="text/css">
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="ts-tools-library">
<h1 class="title">TS tools library</h1>
<div class="contents topic">
<p class="topic-title first"><a id="contents" name="contents">Contents</a></p>
<ul class="simple">
<li><a class="reference" href="#relevant-international-standards" id="id2" name="id2">Relevant International Standards</a></li>
<li><a class="reference" href="#overview-of-modules" id="id3" name="id3">Overview of modules</a></li>
<li><a class="reference" href="#reading-data" id="id4" name="id4">Reading data</a><ul>
<li><a class="reference" href="#access-units-h-264-mpeg-4-avc" id="id5" name="id5">Access units, H.264, MPEG-4/AVC</a></li>
<li><a class="reference" href="#h-262-pictures-mpeg-2-and-mpeg-1" id="id6" name="id6">H.262 pictures, MPEG-2 and MPEG-1</a></li>
<li><a class="reference" href="#below-the-picture-level" id="id7" name="id7">Below the picture level</a></li>
<li><a class="reference" href="#elementary-stream-data" id="id8" name="id8">Elementary Stream data</a></li>
<li><a class="reference" href="#pes-reading-ts-and-ps-data" id="id9" name="id9">PES reading - TS and PS data</a><ul>
<li><a class="reference" href="#server-mode" id="id10" name="id10">Server mode</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference" href="#read-ahead-buffers" id="id11" name="id11">Read-ahead buffers</a></li>
<li><a class="reference" href="#rewinding" id="id12" name="id12">Rewinding</a></li>
<li><a class="reference" href="#reversing" id="id13" name="id13">Reversing</a></li>
<li><a class="reference" href="#filtering" id="id14" name="id14">Filtering</a></li>
<li><a class="reference" href="#license" id="id15" name="id15">License</a></li>
</ul>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id2" id="relevant-international-standards" name="relevant-international-standards">Relevant International Standards</a></h1>
<ul>
<li><p class="first">ISO/IEC 13818-1 (H.222.0) <em>Information technology - Generic coding of moving
pictures and associated audio information: Systems</em></p>
<p>This describes:</p>
<ul class="simple">
<li>TS (Transport Stream)</li>
<li>PS (Program Stream)</li>
<li>ES (Elementary Stream) and</li>
<li>PES (Packetised Elementary Stream)</li>
</ul>
<p>which form the transport layers for the following standards.</p>
</li>
<li><p class="first">ISO/IEC 13818-2 (H.262) <em>Information technology - Generic coding of moving
pictures and associated audio information: Video</em></p>
<blockquote>
<p>This defines MPEG-2.</p>
</blockquote>
</li>
<li><p class="first">ISO/IEC 14496-10 (H.264)</p>
<blockquote>
<p>This defines MPEG-4/AVC.</p>
</blockquote>
</li>
</ul>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id3" id="overview-of-modules" name="overview-of-modules">Overview of modules</a></h1>
<p>Standalone header files:</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">compat.h:</th><td class="field-body">Defines useful types for portability between Unices and Windows (for
instance, the basic integer types and <tt class="docutils literal"><span class="pre">offset_t</span></tt>, which is a 64 or
32 bit file offset as appropriate).</td>
</tr>
<tr class="field"><th class="field-name">h222_defns.h:</th><td class="field-body">Defines various values useful when using H.222.</td>
</tr>
</tbody>
</table>
<p>Source files:</p>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">accessunit.c:</th><td class="field-body">Handling H.264 access units, including reading them in as NAL units.</td>
</tr>
<tr class="field"><th class="field-name">adts.c:</th><td class="field-body">Some minimal support for ISO/IEC 14496-3:2001(E) AAC ADTS audio streams -
basically what is needed by the <cite>esmerge</cite> tool.</td>
</tr>
<tr class="field"><th class="field-name">bitdata.c:</th><td class="field-body">Handling bit level data, including reading Exp-Golomb encoded values. Used
by the NAL unit reading functions in nalunit.c.</td>
</tr>
<tr class="field"><th class="field-name">es.c:</th><td class="field-body">Reading and writing at an Elementary Stream level.</td>
</tr>
<tr class="field"><th class="field-name">filter.c:</th><td class="field-body">Fast forward algorithms.</td>
</tr>
<tr class="field"><th class="field-name">h262.c:</th><td class="field-body">Handling H.262 pictures.</td>
</tr>
<tr class="field"><th class="field-name">misc.c:</th><td class="field-body">As it says, various things that provide miscellaneous support.</td>
</tr>
<tr class="field"><th class="field-name">nalunit.c:</th><td class="field-body">Handling H.264 NAL units, mainly as a base for access units.</td>
</tr>
<tr class="field"><th class="field-name">pes.c:</th><td class="field-body">Reading PS or TS data, and extracting PES therefrom. Used as a level
under es.c to allow reading of ES data from PS and TS files.</td>
</tr>
<tr class="field"><th class="field-name">pidint.c:</th><td class="field-body">Handling &quot;dictionaries&quot; of PIDs versus integers (for instance, PID and
program stream).</td>
</tr>
<tr class="field"><th class="field-name">ps.c:</th><td class="field-body">Provides the <tt class="docutils literal"><span class="pre">ps_to_ts</span></tt> function, which forms the basis of the ps2ts tool.</td>
</tr>
<tr class="field"><th class="field-name">reverse.c:</th><td class="field-body">Reversing algorithms and support.</td>
</tr>
<tr class="field"><th class="field-name">ts.c:</th><td class="field-body">Reading and writing Transport Stream.</td>
</tr>
<tr class="field"><th class="field-name">tswrite.c:</th><td class="field-body">Support for writing Transport Stream packets, either to a file, over TCP/IP
or (via a circular buffer) over UDP. This thus provides support for all
tools that have a <tt class="docutils literal"><span class="pre">-host</span></tt> switch, and also the bulk of the functionality
of tsplay. Also provides the code that allows tsserve to read command
characters from a socket.</td>
</tr>
</tbody>
</table>
<p>Each source file <em>xxx</em> also has associated with it a header file defining
datastructures, constants and macros, called <em>xxx</em>_defns.h, and a header file
detailing <tt class="docutils literal"><span class="pre">extern</span></tt> functions therefrom, called <em>xxx</em>_fns.h. The latter will
always include the former.</p>
<p>The documentation for each <tt class="docutils literal"><span class="pre">extern</span></tt> function is reproduced in the header
file, directly copied from the source. This is done for the convenience of the
user, but if any discrepancy occurs, the version of the functionc header
comment in the source file should be taken as correct.</p>
<p>Not all <tt class="docutils literal"><span class="pre">extern</span></tt> functions are intended for use by end-users. Some are
really only used within the library itself. Unfortunately, these functions are
not flagged as such at the moment.</p>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id4" id="reading-data" name="reading-data">Reading data</a></h1>
<p>In general, the various MPEG entities are not read directly from a file, but
through a context datastructure.</p>
<p>For instance, reading an access unit may stop before a particular NAL unit,
which thus forms the start of the next access unit, and NAL units themselves
need to be interpreted in the context of sequence and picture parameter sets.</p>
<p>These are arranged roughly as follows:</p>
<pre class="literal-block">
+-------------------+ (r) +---------------+
| H.264 access unit | | H.262 context |
| context | +---------------+
+-------------------+ :
: :
: :
+------------------+ (*) :
| NAL unit context | :
+------------------+ :
: :
: :
+---------------------------+
| ES context |
+---------------------------+
: :
: :
+------------------+ :
| PES reader | :
+------------------+ :
: : :
+-----------+ +-----------+ :
| TS reader | | PS reader | :
+-----------+ +-----------+ :
: : :
: : :
+---------------------------+
| File |
+---------------------------+
</pre>
<table class="docutils field-list" frame="void" rules="none">
<col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th class="field-name">(r):</th><td class="field-body">Both H.264 and H.262 contexts can be associated with a &quot;reversing&quot;
context, to accumulate data for outputting the stream in (fast) reverse.</td>
</tr>
<tr class="field"><th class="field-name">(*):</th><td class="field-body">A NAL unit context is created implicitly when building an access unit
context &quot;over&quot; an ES context.</td>
</tr>
</tbody>
</table>
<div class="section">
<h2><a class="toc-backref" href="#id5" id="access-units-h-264-mpeg-4-avc" name="access-units-h-264-mpeg-4-avc">Access units, H.264, MPEG-4/AVC</a></h2>
<p>An access unit context is explicitly built on top of an ES context:</p>
<pre class="literal-block">
err = build_access_unit_context(es,&amp;acontext);
free_access_unit_context(&amp;acontext);
</pre>
<p>Freeing the access unit context does not free the ES context.</p>
<p>As well as maintaining the information to allow reading access units, the
context also remembers any trailing (end of sequence or end of stream) NAL
units. This is mostly transparent to the user, but is explained in the
appropriate function header comments.</p>
<p>An individual access unit can be retrieved:</p>
<pre class="literal-block">
err = get_next_access_unit(acontext,quiet,show_details,&amp;access_unit);
</pre>
<p>but it is more normal to retrieve a frame:</p>
<pre class="literal-block">
err = get_next_h264_frame(acontext,quiet,show_details,&amp;frame);
</pre>
<p>If the frame was composed of two access units (i.e., two fields), then the NAL
units for the second will have been appended to the first, which is returned,
and its field/frame indicator will have been set to &quot;frame&quot;.</p>
<p>Regardless, the same function is used to free the resultant datastructure:</p>
<pre class="literal-block">
free_access_unit(&amp;frame);
</pre>
<p>Access units may be written to ES or TS:</p>
<pre class="literal-block">
err = write_access_unit_as_ES(access_unit,context,filedesc);
err = write_access_unit_as_TS(access_unit,context,tswriter,video_pid);
</pre>
<p>Note that the latter assumes that the video stream id is 0xE0. Variants are
alsp provided to output PTS and/or PCR values for the first PES packet written
out.</p>
<p>A report on the content of an access unit can be obtained with:</p>
<pre class="literal-block">
report_access_unit(filedesc,access_unit);
</pre>
<p>Various utility functions are provided to investigate the properties of a
particular access unit:</p>
<pre class="literal-block">
all_I = all_slices_I(access_unit);
all_P = all_slices_P(access_unit);
all_IP = all_slices_I_or_P(access_unit);
all_B = all_slices_B(access_unit);
</pre>
<p>Lastly, an access unit context can be rewound with:</p>
<pre class="literal-block">
err = rewind_access_unit_context(acontext);
</pre>
</div>
<div class="section">
<h2><a class="toc-backref" href="#id6" id="h-262-pictures-mpeg-2-and-mpeg-1" name="h-262-pictures-mpeg-2-and-mpeg-1">H.262 pictures, MPEG-2 and MPEG-1</a></h2>
<p>For most purposes, MPEG-1 data is supported as a subset of MPEG-2.</p>
<p>An H.262 context is explicitly built on top of an ES context:</p>
<pre class="literal-block">
err = build_h262_context(es,&amp;context);
free_h262_context(&amp;context);
</pre>
<p>Freeing the H.262 context does not free the ES context.</p>
<p>An individual H.262 picture can be retrieved:</p>
<pre class="literal-block">
err = get_next_h262_single_picture(context,verbose,&amp;picture);
</pre>
<p>but it is more normal to retrieve a frame:</p>
<pre class="literal-block">
err = get_next_h262_frame(context,verbose,quiet,&amp;frame);
</pre>
<p>If the frame was composed of two field pictures, then the H.262 items
for the second will have been appended to the first, which is returned,
and its field/frame indicator will have been set to &quot;frame&quot;.</p>
<p>Regardless, the same function is used to free the resultant datastructure:</p>
<pre class="literal-block">
free_h262_picture(&amp;frame);
</pre>
<p>Pictures may be written to ES or TS:</p>
<pre class="literal-block">
err = write_h262_picture_as_ES(filedesc,picture);
err = write_h262_picture_as_TS(tswriter,picture,video_pid);
</pre>
<p>Note that the latter assumes that the video stream id is 0xE0.</p>
<p>A report on the content of a picture can be obtained with:</p>
<pre class="literal-block">
report_h262_picture(filedesc,picture,report_data);
</pre>
<p>Lastly, an H.262 context can be rewound with:</p>
<pre class="literal-block">
err = rewind_h262_context(context);
</pre>
</div>
<div class="section">
<h2><a class="toc-backref" href="#id7" id="below-the-picture-level" name="below-the-picture-level">Below the picture level</a></h2>
<p>H.264 access units are composed from NAL units, read with an underlying NAL
unit context (which is created automatically within an access unit context).
The NAL unit context is then retrievable as <tt class="docutils literal"><span class="pre">acontext-&gt;nac</span></tt>.</p>
<p>A NAL unit context may also be created (and then freed) directly:</p>
<pre class="literal-block">
err = build_nal_unit_context(es,&amp;context);
free_nal_unit_context(context);
</pre>
<p>The NAL unit context remembers the picture and sequence parameter sets for the
H.264 data stream.</p>
<p>From whatever source, the NAL unit context can be used to read NAL units
directly (although doing this with the <tt class="docutils literal"><span class="pre">nac</span></tt> from an access unit context
will disrupt access unit reading):</p>
<pre class="literal-block">
err = find_next_NAL_unit(context,verbose,&amp;nal);
free_nal_unit(&amp;nal);
</pre>
<p>Functions also exist to report on an individual NAL unit, and to write it out
as ES or TS data.</p>
<p>H.262 pictures are composed of individual units as well, although there does
not appear to be a standard name for these. The H.262 context manages their
reading directly, and they may also be read individually (although doing so
will disrupt H.262 picture reading):</p>
<pre class="literal-block">
err = find_next_h262_item(es,&amp;item);
</pre>
<p>Again, functions are provided to report on such an item, or write it out as ES
or TS.</p>
<p>Each NAL unit or MPEG-2 item contains a single ES unit (which is why the
contexts used to read them and their higher level data constructs require an
ES context).</p>
</div>
<div class="section">
<h2><a class="toc-backref" href="#id8" id="elementary-stream-data" name="elementary-stream-data">Elementary Stream data</a></h2>
<p>Various ways are provided to open an Elementary Stream. The simplest opens a
file containing &quot;bare&quot; ES data:</p>
<pre class="literal-block">
err = open_elementary_stream(filename,&amp;es);
</pre>
<p>If a PES reader is available (for reading TS or PS data), then an elementary
stream can be constructed atop that:</p>
<pre class="literal-block">
err = build_elementary_stream_PES(pes_reader,&amp;es);
</pre>
<p>Once the elementary stream is available, however, its underlying form does not
matter, and it can normally be closed with:</p>
<pre class="literal-block">
close_elementary_stream(&amp;es);
</pre>
<p>(this will not &quot;close&quot; a PES reader if one is involved).</p>
<p>Functions are then provided to read in individual ES units, although in
practice the higher level (H.264 access unit and H.262 picture) functions will
be used to read data.</p>
</div>
<div class="section">
<h2><a class="toc-backref" href="#id9" id="pes-reading-ts-and-ps-data" name="pes-reading-ts-and-ps-data">PES reading - TS and PS data</a></h2>
<p>PES data may be encapsulated as either PS or TS. The normal way to open a PES
reader is with:</p>
<pre class="literal-block">
err = open_PES_reader(filename,give_info,give_warnings,&amp;reader);
</pre>
<p>which will inspect the start of the file to work out if it is PS or
TS. Alternatively, if it is known which the file is, then one can directly
call:</p>
<pre class="literal-block">
err = open_PES_reader_for_PS(filename,give_info,give_warnings,&amp;reader);
err = open_PES_reader_for_TS(filename,program_number,
give_info,give_warnings,&amp;reader);
</pre>
<p>(the latter must also be used if one wants a different program number than the
&quot;first found&quot; in TS data). The function:</p>
<pre class="literal-block">
err = determine_if_TS_file(filedesc,&amp;is_TS);
</pre>
<p>may also be used to figure out if an already opened file is TS, and that
may then be wrapped in a reader:</p>
<pre class="literal-block">
err = build_PES_reader(filedesc,is_TS,give_info,give_warnings,
program_number,&amp;reader);
</pre>
<p>If a PS or TS reader context is already built, then they may be wrapped within
a PES reader:</p>
<pre class="literal-block">
err = build_TS_PES_reader(tsreader,give_info,give_warnings,program_number,
&amp;reader);
err = build_PS_PES_reader(psreader,give_info,give_warnings,&amp;reader);
</pre>
<p>When finished with, the PES reader may be freed or closed (the latter also
closes the PS/TS reader and underlying file):</p>
<pre class="literal-block">
err = free_PES_reader(&amp;reader);
err = close_PES_reader(&amp;reader);
</pre>
<p>It is possible to request that only video be read from the reader:</p>
<pre class="literal-block">
set_PES_reader_video_only(reader);
</pre>
<p>or that audio be taken from Private Stream 1 (normally used for Dolby), as
opposed to the &quot;normal&quot; audio streams:</p>
<pre class="literal-block">
set_PES_reader_audio_private1(reader);
</pre>
<p>For PS data, which does not have PAT/PMT packets to describe the program being
read, it is possible to set various key pieces of information:</p>
<pre class="literal-block">
set_PES_reader_program_data(reader,program_number,pmt_pid,
video_pid,audio_pid,pcr_pid);
</pre>
<p>In situations where the software has &quot;guessed&quot; wrongly whether the data is
H.262 or H.264, or where data is being read from standard input and it did not
have an opportunity to decide, it is possible to insist:</p>
<pre class="literal-block">
set_PES_reader_h264(reader);
</pre>
<p>(the default is H.262).</p>
<p>PES packets may be read individually, but this is normally mediated by one of
the higher levels.</p>
<div class="section">
<h3><a class="toc-backref" href="#id10" id="server-mode" name="server-mode">Server mode</a></h3>
<p>It is possible to associate a Transport Stream writer with the PES input
stream. This is then used to &quot;mirror&quot; each PES packet, so that the input
stream is automatically written out as TS (specifically, each time a new PES
packet is read in, the previous packet is written out).</p>
<p>Where to write the data is specified with:</p>
<pre class="literal-block">
set_server_output(reader,tswriter,program_freq);
</pre>
<p>This also starts the mirroring. <tt class="docutils literal"><span class="pre">program_freq</span></tt> is how often (in PES packets)
the PAT/PMT program information should be written out.</p>
<p>Mirroring may be switched on and off using:</p>
<pre class="literal-block">
start_server_output(reader);
stop_server_output(reader);
</pre>
<p><tt class="docutils literal"><span class="pre">tsserve</span></tt> is the main program that takes advantage of this capability -
using it whilst moving linearly forwards in the data is simple enough, but if
one needs to fast forwards or move backwards, things rapidly become more
complex.</p>
</div>
</div>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id11" id="read-ahead-buffers" name="read-ahead-buffers">Read-ahead buffers</a></h1>
<p>Since the bottom-most file access is done via file descriptors, there is no
system-provided buffering.</p>
<p>Currently, read-ahead buffers are provided by:</p>
<ul class="simple">
<li>The TS reader</li>
<li>The &quot;bare&quot; ES reader (i.e., reading bytes directly from a file)</li>
</ul>
<p>In both of these contexts, <tt class="docutils literal"><span class="pre">ftell</span></tt> cannot usefully be used to determine
where in the file the application is/will be reading - instead, the TS reader
context and ES context maintain their own notions of current position, which
should be used instead.</p>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id12" id="rewinding" name="rewinding">Rewinding</a></h1>
<p>As a rule, when rewinding a data stream, use the rewind function for
the &quot;highest level&quot; context available.</p>
<p>Thus if reading access units, use <tt class="docutils literal"><span class="pre">rewind_access_unit_context</span></tt>, rather than
(for instance) <tt class="docutils literal"><span class="pre">seek_ES</span></tt>.</p>
<p>General seeking within files above the ES level has not been implemented, as
none of the existing tools require it.</p>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id13" id="reversing" name="reversing">Reversing</a></h1>
<p>For issues when reversing H.262 data, see the documentation for <tt class="docutils literal"><span class="pre">esreverse</span></tt>
in the <a class="reference" href="tools.html">Tools</a> document.</p>
<p>Reversing of H.264 currently uses non-IDR frames more than it should. This is
primarily because the Harry Potter clip only has a single IDR, and thus it has
been difficult to be sure what to do. Unfortunately, in H.264, B and P frames
can refer back before the last I frame, so just outputting a couple of
reference frames does not guarantee a coherent picture when the next
non-reference frame is encountered. The solution is to enfore output of IDR
frames at such transitions, and this will be investigated later on.</p>
<p>Reversing in the library is handed in a relatively &quot;black box&quot; manner. A
reverse data context must be built:</p>
<pre class="literal-block">
err = build_reverse_data(&amp;reverse_data,is_h264);
</pre>
<p>and then added to the appropriate H.262 picture or H.264 access unit context:</p>
<pre class="literal-block">
err = add_h262_reverse_context(context,reverse_data);
err = add_access_unit_reverse_context(acontext,reverse_data);
</pre>
<p>(this could obviously use some streamlining). After this, normal reading of
frames in the forwards direction remembers appropriate reversing information.</p>
<p>Alternatively, the reversing data for a whole file can be accumulated with one
call (it just processes through the file):</p>
<pre class="literal-block">
err = collect_reverse_h262(context,max,verbose,quiet);
err = collect_reverse_access_units(acontext,max,verbose,quiet,
seq_param_data,pic_param_data);
</pre>
<p>Data may be output in reverse using the appropriate call - these are the same
for H.262 and H.264 data:</p>
<pre class="literal-block">
err = output_in_reverse_as_ES(es,filedesc,freqency,verbose,quiet,
start_with,max,reverse_data);
err = output_in_reverse_as_TS(es,tswriter,verbose,quiet,offset,
start_with,max,reverse_data);
</pre>
<p><tt class="docutils literal"><span class="pre">start_with</span></tt> indicates which frame to start reversing from - <tt class="docutils literal"><span class="pre">-1</span></tt> means
the &quot;current&quot; picture. <tt class="docutils literal"><span class="pre">frequency</span></tt> indicates the speed of reversing required
- thus a value of <tt class="docutils literal"><span class="pre">8</span></tt> means reversing at (about) 8 times.</p>
<p>The reversing datastructures can be freed when no longer needed:</p>
<pre class="literal-block">
free_reverse_data(reverse_data);
</pre>
<p>but this does not detach them from the H.262 or H.264 context, so should only
be used when tidying up those datastructures.</p>
</div>
<div class="section">
<h1><a class="toc-backref" href="#id14" id="filtering" name="filtering">Filtering</a></h1>
<p>For issues when filtering data, see the documentation for <tt class="docutils literal"><span class="pre">esfilter</span></tt>
in the <a class="reference" href="tools.html">Tools</a> document.</p>
<p>An appropriate filter context is built:</p>
<pre class="literal-block">
err = build_h262_filter_context_strip(&amp;fcontext,context,all_IP);
err = build_h262_filter_context(&amp;fcontext,context,frequency);
err = build_h264_filter_context_strip(&amp;fcontext,acontext,all_ref);
err = build_h264_filter_context(&amp;fcontext,acontext,frequency);
</pre>
<p>and later freed:</p>
<pre class="literal-block">
free_h262_filter_context(fcontext);
free_h264_filter_context(fcontext);
</pre>
<p>For the stripping contexts, the <tt class="docutils literal"><span class="pre">all_IP</span></tt> flag means keep all I <em>and</em> P
frames (rather than just I), and the <tt class="docutils literal"><span class="pre">all_ref</span></tt> flag means keep all reference
pictures.</p>
<p>For the filtering contexts, the <tt class="docutils literal"><span class="pre">frequency</span></tt> is the speedup that is required
- for instance, a value of <tt class="docutils literal"><span class="pre">8</span></tt> means that 8x fast forward is desired.</p>
<p>These may then be used to retrieve the next appropriate frame from the input
stream:</p>
<pre class="literal-block">
err = get_next_stripped_h262_frame(fcontext,verbose,quiet,
&amp;seq_hdr,&amp;frame,&amp;frames_seen);
err = get_next_filtered_h262_frame(fcontext,verbose,quiet,
&amp;seq_hdr,&amp;frame,&amp;frames_seen);
err = get_next_stripped_h264_frame(fcontext,verbose,quiet,
&amp;frame,&amp;frames_seen);
err = get_next_filtered_h264_frame(fcontext,verbose,quiet,
&amp;frame,&amp;frames_seen);
</pre>
<p>In all cases, the caller must free <tt class="docutils literal"><span class="pre">frame</span></tt> when they have finished with
it. However, for H.262 data, <tt class="docutils literal"><span class="pre">seq_hdr</span></tt> must not be freed.</p>
<p>When filtering, <tt class="docutils literal"><span class="pre">frame</span></tt> is returned as NULL to indicate that the previous
frame should be repeated, to produce (an approximation to) the desired
frequency.</p>
<!-- ***** BEGIN LICENSE BLOCK ***** -->
</div>
<div class="section">
<h1><a class="toc-backref" href="#id15" id="license" name="license">License</a></h1>
<p>Version: MPL 1.1</p>
<p>The contents of this file are subject to the Mozilla Public License Version
1.1 (the &quot;License&quot;); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
<a class="reference" href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></p>
<p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.</p>
<p>The Original Code is the MPEG TS, PS and ES tools.</p>
<p>The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright © 2008
the Initial Developer. All Rights Reserved.</p>
<p>Contributor(s):</p>
<blockquote>
Amino Communications Ltd, Swavesey, Cambridge UK</blockquote>
<!-- ***** END LICENSE BLOCK ***** -->
</div>
</div>
</body>
</html>

564
docs/library.txt 100644
Wyświetl plik

@ -0,0 +1,564 @@
================
TS tools library
================
.. contents::
Relevant International Standards
================================
- ISO/IEC 13818-1 (H.222.0) *Information technology - Generic coding of moving
pictures and associated audio information: Systems*
This describes:
- TS (Transport Stream)
- PS (Program Stream)
- ES (Elementary Stream) and
- PES (Packetised Elementary Stream)
which form the transport layers for the following standards.
- ISO/IEC 13818-2 (H.262) *Information technology - Generic coding of moving
pictures and associated audio information: Video*
This defines MPEG-2.
- ISO/IEC 14496-10 (H.264)
This defines MPEG-4/AVC.
Overview of modules
===================
Standalone header files:
:compat.h:
Defines useful types for portability between Unices and Windows (for
instance, the basic integer types and ``offset_t``, which is a 64 or
32 bit file offset as appropriate).
:h222_defns.h:
Defines various values useful when using H.222.
Source files:
:accessunit.c:
Handling H.264 access units, including reading them in as NAL units.
:adts.c:
Some minimal support for ISO/IEC 14496-3:2001(E) AAC ADTS audio streams -
basically what is needed by the `esmerge` tool.
:bitdata.c:
Handling bit level data, including reading Exp-Golomb encoded values. Used
by the NAL unit reading functions in nalunit.c.
:es.c:
Reading and writing at an Elementary Stream level.
:filter.c:
Fast forward algorithms.
:h262.c:
Handling H.262 pictures.
:misc.c:
As it says, various things that provide miscellaneous support.
:nalunit.c:
Handling H.264 NAL units, mainly as a base for access units.
:pes.c:
Reading PS or TS data, and extracting PES therefrom. Used as a level
under es.c to allow reading of ES data from PS and TS files.
:pidint.c:
Handling "dictionaries" of PIDs versus integers (for instance, PID and
program stream).
:ps.c:
Provides the ``ps_to_ts`` function, which forms the basis of the ps2ts tool.
:reverse.c:
Reversing algorithms and support.
:ts.c:
Reading and writing Transport Stream.
:tswrite.c:
Support for writing Transport Stream packets, either to a file, over TCP/IP
or (via a circular buffer) over UDP. This thus provides support for all
tools that have a ``-host`` switch, and also the bulk of the functionality
of tsplay. Also provides the code that allows tsserve to read command
characters from a socket.
Each source file *xxx* also has associated with it a header file defining
datastructures, constants and macros, called *xxx*\ _defns.h, and a header file
detailing ``extern`` functions therefrom, called *xxx*\ _fns.h. The latter will
always include the former.
The documentation for each ``extern`` function is reproduced in the header
file, directly copied from the source. This is done for the convenience of the
user, but if any discrepancy occurs, the version of the functionc header
comment in the source file should be taken as correct.
Not all ``extern`` functions are intended for use by end-users. Some are
really only used within the library itself. Unfortunately, these functions are
not flagged as such at the moment.
Reading data
============
In general, the various MPEG entities are not read directly from a file, but
through a context datastructure.
For instance, reading an access unit may stop before a particular NAL unit,
which thus forms the start of the next access unit, and NAL units themselves
need to be interpreted in the context of sequence and picture parameter sets.
These are arranged roughly as follows::
+-------------------+ (r) +---------------+
| H.264 access unit | | H.262 context |
| context | +---------------+
+-------------------+ :
: :
: :
+------------------+ (*) :
| NAL unit context | :
+------------------+ :
: :
: :
+---------------------------+
| ES context |
+---------------------------+
: :
: :
+------------------+ :
| PES reader | :
+------------------+ :
: : :
+-----------+ +-----------+ :
| TS reader | | PS reader | :
+-----------+ +-----------+ :
: : :
: : :
+---------------------------+
| File |
+---------------------------+
:(r): Both H.264 and H.262 contexts can be associated with a "reversing"
context, to accumulate data for outputting the stream in (fast) reverse.
:(*): A NAL unit context is created implicitly when building an access unit
context "over" an ES context.
Access units, H.264, MPEG-4/AVC
-------------------------------
An access unit context is explicitly built on top of an ES context::
err = build_access_unit_context(es,&acontext);
free_access_unit_context(&acontext);
Freeing the access unit context does not free the ES context.
As well as maintaining the information to allow reading access units, the
context also remembers any trailing (end of sequence or end of stream) NAL
units. This is mostly transparent to the user, but is explained in the
appropriate function header comments.
An individual access unit can be retrieved::
err = get_next_access_unit(acontext,quiet,show_details,&access_unit);
but it is more normal to retrieve a frame::
err = get_next_h264_frame(acontext,quiet,show_details,&frame);
If the frame was composed of two access units (i.e., two fields), then the NAL
units for the second will have been appended to the first, which is returned,
and its field/frame indicator will have been set to "frame".
Regardless, the same function is used to free the resultant datastructure::
free_access_unit(&frame);
Access units may be written to ES or TS::
err = write_access_unit_as_ES(access_unit,context,filedesc);
err = write_access_unit_as_TS(access_unit,context,tswriter,video_pid);
Note that the latter assumes that the video stream id is 0xE0. Variants are
alsp provided to output PTS and/or PCR values for the first PES packet written
out.
A report on the content of an access unit can be obtained with::
report_access_unit(filedesc,access_unit);
Various utility functions are provided to investigate the properties of a
particular access unit::
all_I = all_slices_I(access_unit);
all_P = all_slices_P(access_unit);
all_IP = all_slices_I_or_P(access_unit);
all_B = all_slices_B(access_unit);
Lastly, an access unit context can be rewound with::
err = rewind_access_unit_context(acontext);
H.262 pictures, MPEG-2 and MPEG-1
---------------------------------
For most purposes, MPEG-1 data is supported as a subset of MPEG-2.
An H.262 context is explicitly built on top of an ES context::
err = build_h262_context(es,&context);
free_h262_context(&context);
Freeing the H.262 context does not free the ES context.
An individual H.262 picture can be retrieved::
err = get_next_h262_single_picture(context,verbose,&picture);
but it is more normal to retrieve a frame::
err = get_next_h262_frame(context,verbose,quiet,&frame);
If the frame was composed of two field pictures, then the H.262 items
for the second will have been appended to the first, which is returned,
and its field/frame indicator will have been set to "frame".
Regardless, the same function is used to free the resultant datastructure::
free_h262_picture(&frame);
Pictures may be written to ES or TS::
err = write_h262_picture_as_ES(filedesc,picture);
err = write_h262_picture_as_TS(tswriter,picture,video_pid);
Note that the latter assumes that the video stream id is 0xE0.
A report on the content of a picture can be obtained with::
report_h262_picture(filedesc,picture,report_data);
Lastly, an H.262 context can be rewound with::
err = rewind_h262_context(context);
Below the picture level
-----------------------
H.264 access units are composed from NAL units, read with an underlying NAL
unit context (which is created automatically within an access unit context).
The NAL unit context is then retrievable as ``acontext->nac``.
A NAL unit context may also be created (and then freed) directly::
err = build_nal_unit_context(es,&context);
free_nal_unit_context(context);
The NAL unit context remembers the picture and sequence parameter sets for the
H.264 data stream.
From whatever source, the NAL unit context can be used to read NAL units
directly (although doing this with the ``nac`` from an access unit context
will disrupt access unit reading)::
err = find_next_NAL_unit(context,verbose,&nal);
free_nal_unit(&nal);
Functions also exist to report on an individual NAL unit, and to write it out
as ES or TS data.
H.262 pictures are composed of individual units as well, although there does
not appear to be a standard name for these. The H.262 context manages their
reading directly, and they may also be read individually (although doing so
will disrupt H.262 picture reading)::
err = find_next_h262_item(es,&item);
Again, functions are provided to report on such an item, or write it out as ES
or TS.
Each NAL unit or MPEG-2 item contains a single ES unit (which is why the
contexts used to read them and their higher level data constructs require an
ES context).
Elementary Stream data
----------------------
Various ways are provided to open an Elementary Stream. The simplest opens a
file containing "bare" ES data::
err = open_elementary_stream(filename,&es);
If a PES reader is available (for reading TS or PS data), then an elementary
stream can be constructed atop that::
err = build_elementary_stream_PES(pes_reader,&es);
Once the elementary stream is available, however, its underlying form does not
matter, and it can normally be closed with::
close_elementary_stream(&es);
(this will not "close" a PES reader if one is involved).
Functions are then provided to read in individual ES units, although in
practice the higher level (H.264 access unit and H.262 picture) functions will
be used to read data.
PES reading - TS and PS data
----------------------------
PES data may be encapsulated as either PS or TS. The normal way to open a PES
reader is with::
err = open_PES_reader(filename,give_info,give_warnings,&reader);
which will inspect the start of the file to work out if it is PS or
TS. Alternatively, if it is known which the file is, then one can directly
call::
err = open_PES_reader_for_PS(filename,give_info,give_warnings,&reader);
err = open_PES_reader_for_TS(filename,program_number,
give_info,give_warnings,&reader);
(the latter must also be used if one wants a different program number than the
"first found" in TS data). The function::
err = determine_if_TS_file(filedesc,&is_TS);
may also be used to figure out if an already opened file is TS, and that
may then be wrapped in a reader::
err = build_PES_reader(filedesc,is_TS,give_info,give_warnings,
program_number,&reader);
If a PS or TS reader context is already built, then they may be wrapped within
a PES reader::
err = build_TS_PES_reader(tsreader,give_info,give_warnings,program_number,
&reader);
err = build_PS_PES_reader(psreader,give_info,give_warnings,&reader);
When finished with, the PES reader may be freed or closed (the latter also
closes the PS/TS reader and underlying file)::
err = free_PES_reader(&reader);
err = close_PES_reader(&reader);
It is possible to request that only video be read from the reader::
set_PES_reader_video_only(reader);
or that audio be taken from Private Stream 1 (normally used for Dolby), as
opposed to the "normal" audio streams::
set_PES_reader_audio_private1(reader);
For PS data, which does not have PAT/PMT packets to describe the program being
read, it is possible to set various key pieces of information::
set_PES_reader_program_data(reader,program_number,pmt_pid,
video_pid,audio_pid,pcr_pid);
In situations where the software has "guessed" wrongly whether the data is
H.262 or H.264, or where data is being read from standard input and it did not
have an opportunity to decide, it is possible to insist::
set_PES_reader_h264(reader);
(the default is H.262).
PES packets may be read individually, but this is normally mediated by one of
the higher levels.
Server mode
...........
It is possible to associate a Transport Stream writer with the PES input
stream. This is then used to "mirror" each PES packet, so that the input
stream is automatically written out as TS (specifically, each time a new PES
packet is read in, the previous packet is written out).
Where to write the data is specified with::
set_server_output(reader,tswriter,program_freq);
This also starts the mirroring. ``program_freq`` is how often (in PES packets)
the PAT/PMT program information should be written out.
Mirroring may be switched on and off using::
start_server_output(reader);
stop_server_output(reader);
``tsserve`` is the main program that takes advantage of this capability -
using it whilst moving linearly forwards in the data is simple enough, but if
one needs to fast forwards or move backwards, things rapidly become more
complex.
Read-ahead buffers
==================
Since the bottom-most file access is done via file descriptors, there is no
system-provided buffering.
Currently, read-ahead buffers are provided by:
* The TS reader
* The "bare" ES reader (i.e., reading bytes directly from a file)
In both of these contexts, ``ftell`` cannot usefully be used to determine
where in the file the application is/will be reading - instead, the TS reader
context and ES context maintain their own notions of current position, which
should be used instead.
Rewinding
=========
As a rule, when rewinding a data stream, use the rewind function for
the "highest level" context available.
Thus if reading access units, use ``rewind_access_unit_context``, rather than
(for instance) ``seek_ES``.
General seeking within files above the ES level has not been implemented, as
none of the existing tools require it.
Reversing
=========
For issues when reversing H.262 data, see the documentation for ``esreverse``
in the Tools_ document.
.. _Tools: tools.html
Reversing of H.264 currently uses non-IDR frames more than it should. This is
primarily because the Harry Potter clip only has a single IDR, and thus it has
been difficult to be sure what to do. Unfortunately, in H.264, B and P frames
can refer back before the last I frame, so just outputting a couple of
reference frames does not guarantee a coherent picture when the next
non-reference frame is encountered. The solution is to enfore output of IDR
frames at such transitions, and this will be investigated later on.
Reversing in the library is handed in a relatively "black box" manner. A
reverse data context must be built::
err = build_reverse_data(&reverse_data,is_h264);
and then added to the appropriate H.262 picture or H.264 access unit context::
err = add_h262_reverse_context(context,reverse_data);
err = add_access_unit_reverse_context(acontext,reverse_data);
(this could obviously use some streamlining). After this, normal reading of
frames in the forwards direction remembers appropriate reversing information.
Alternatively, the reversing data for a whole file can be accumulated with one
call (it just processes through the file)::
err = collect_reverse_h262(context,max,verbose,quiet);
err = collect_reverse_access_units(acontext,max,verbose,quiet,
seq_param_data,pic_param_data);
Data may be output in reverse using the appropriate call - these are the same
for H.262 and H.264 data::
err = output_in_reverse_as_ES(es,filedesc,freqency,verbose,quiet,
start_with,max,reverse_data);
err = output_in_reverse_as_TS(es,tswriter,verbose,quiet,offset,
start_with,max,reverse_data);
``start_with`` indicates which frame to start reversing from - ``-1`` means
the "current" picture. ``frequency`` indicates the speed of reversing required
- thus a value of ``8`` means reversing at (about) 8 times.
The reversing datastructures can be freed when no longer needed::
free_reverse_data(reverse_data);
but this does not detach them from the H.262 or H.264 context, so should only
be used when tidying up those datastructures.
Filtering
=========
For issues when filtering data, see the documentation for ``esfilter``
in the Tools_ document.
.. _Tools: tools.html
An appropriate filter context is built::
err = build_h262_filter_context_strip(&fcontext,context,all_IP);
err = build_h262_filter_context(&fcontext,context,frequency);
err = build_h264_filter_context_strip(&fcontext,acontext,all_ref);
err = build_h264_filter_context(&fcontext,acontext,frequency);
and later freed::
free_h262_filter_context(fcontext);
free_h264_filter_context(fcontext);
For the stripping contexts, the ``all_IP`` flag means keep all I *and* P
frames (rather than just I), and the ``all_ref`` flag means keep all reference
pictures.
For the filtering contexts, the ``frequency`` is the speedup that is required
- for instance, a value of ``8`` means that 8x fast forward is desired.
These may then be used to retrieve the next appropriate frame from the input
stream::
err = get_next_stripped_h262_frame(fcontext,verbose,quiet,
&seq_hdr,&frame,&frames_seen);
err = get_next_filtered_h262_frame(fcontext,verbose,quiet,
&seq_hdr,&frame,&frames_seen);
err = get_next_stripped_h264_frame(fcontext,verbose,quiet,
&frame,&frames_seen);
err = get_next_filtered_h264_frame(fcontext,verbose,quiet,
&frame,&frames_seen);
In all cases, the caller must free ``frame`` when they have finished with
it. However, for H.262 data, ``seq_hdr`` must not be freed.
When filtering, ``frame`` is returned as NULL to indicate that the previous
frame should be repeated, to produce (an approximation to) the desired
frequency.
.. ***** BEGIN LICENSE BLOCK *****
License
=======
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

540
docs/todo.html 100644
Wyświetl plik

@ -0,0 +1,540 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.4.1: http://docutils.sourceforge.net/" />
<title>To do</title>
<style type="text/css">
/*
:Author: David Goodger
:Contact: goodger@users.sourceforge.net
:Date: $Date: 2005-12-18 01:56:14 +0100 (Sun, 18 Dec 2005) $
:Revision: $Revision: 4224 $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin-left: 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left {
clear: left }
img.align-right {
clear: right }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font-family: serif ;
font-size: 100% }
pre.literal-block, pre.doctest-block {
margin-left: 2em ;
margin-right: 2em ;
background-color: #eeeeee }
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
tt.docutils {
background-color: #eeeeee }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="to-do">
<h1 class="title">To do</h1>
<!-- notes: Check that all outstanding items in this file are still outstanding. -->
<p>Bugs:</p>
<ul>
<li><p class="first">esreverse (in particular, but really all the filtering programs) should not
abort if an error occurs reading through the file - in esreverse's case,
this causes no output to be produced, which is irritating.</p>
</li>
<li><p class="first">When reading PS data, if the next packet does not start with 00 00 01,
then a search will be made for the next pack header (specifically, this
is done by <tt class="docutils literal"><span class="pre">read_PS_packet_start</span></tt>).</p>
<p>This means that a file with &quot;broken&quot; data in its middle can be coped
with by the PS reader - it will skip when it fails to find the next
00 00 01 in the right place.</p>
<p>It does not mean that other layers above the PS reader will cope with
broken data neatly, in particular if said &quot;breakage&quot; happened inside the
last PS packet read before the skip. Ideally, these other levels would
also have a strategy for moving on to the next believed-good point
(perhaps even the next PS pack header).</p>
<blockquote>
<p>NB: TS data is expected not to contain errors in this sense.</p>
</blockquote>
</li>
</ul>
<p>Outstanding items:</p>
<ul>
<li><p class="first">Sort out filter for H.264 so that it works sensibly.
Generally check that H.264 works correctly with tsserve - there are
believed to be known transition issues still outstanding.</p>
<p>In more detail:</p>
<blockquote>
<blockquote>
<ol class="arabic">
<li><p class="first">When stopping filtering, go back in the reverse list to the previous
IDR and output that before continuing. This is needed to stop things
referring to non-existent reference frames.</p>
<p>Note that this might be a good thing to do cosmetically when stripping
as well, but since tsserve outputs all reference pictures when
stripping, it is not <em>necessary</em>.</p>
</li>
<li><p class="first">When reversing, an IDR must also be output, so that any P/B frames
thereafter are known not to be referring &quot;past&quot; it. This can be done
either by insisting that reversing stop on an IDR, or else by
reading forwards and outputting I and IDR frames until an IDR is found.</p>
</li>
</ol>
</blockquote>
<p>This adds a requirement to be able to identify IDR frames in the reverse
list.</p>
</blockquote>
</li>
<li><p class="first">Worry about AFD values and sequence headers in H.262.</p>
<blockquote>
<p><em>Believed DONE, except for the &quot;read and remember the GOP&quot; bits, which
may or may not matter.</em></p>
</blockquote>
<p>Note that if sequence headers are not output when F or FF is used (the
default, currently), then using F or FF as soon as tsserve is listening
will not display any pictures, until N is selected (at least on norton),
because nothing is displayed before a sequence header.</p>
<blockquote>
<p>The work needed to make reversing work &quot;properly&quot; for H.262.</p>
<p>For H.264 it is sufficient to output just the block of data for a picture
when reversing. However, for H.262 it is clearly not that simple, if only
because of the AFD problem. What is probably wanted for H.262 is something
more like:</p>
<blockquote>
<ul>
<li><p class="first">Remember the start of a frame</p>
</li>
<li><p class="first">Remember the start of &quot;relevant&quot; sequence headers</p>
</li>
<li><p class="first">Remember (as now) which sequence header that &quot;corresponds&quot; to a frame</p>
</li>
<li><p class="first">Remember the AFD (it's only 8 bytes, and not all of that necessarily
is needed) for each frame</p>
</li>
<li><p class="first">Maybe also remember the start of GOP headers, and their correspondences</p>
<p>(NB: remember to cope with data that doesn't have AFDs?)</p>
</li>
</ul>
</blockquote>
<p>To <em>output</em> a frame then means:</p>
<blockquote>
<ol class="loweralpha simple">
<li>[Locate its GOP and output that]</li>
<li>Locate its sequence header, and output that</li>
<li>Move to the start of the frame, and read and output its picture header</li>
<li>Output the AFD</li>
<li>Read and output the rest of the frame</li>
</ol>
</blockquote>
<p>Simple optimisations along the lines of &quot;if the GOP/sequence header have
not changed then don't bother to re-output them&quot; and &quot;if no sequence
header has been output then don't output an AFD&quot; can be added when the
approach seems to be working.</p>
<p>Refreshing my memory - the sequence of H.262 data is:</p>
<pre class="literal-block">
for 1..n:
Seq header
Seq extension
for 0..n:
Extension &amp; user data
for 1..n:
optional:
GOP header
for 0..n: User data
Picture header
Picture coding extension
for 0..n:
Extension &amp; user data # including AFD
Picture data...
Seq end
</pre>
<p>MPEG-1 is slightly simpler, and goes something like:</p>
<pre class="literal-block">
for 1..n:
Seq header
for 0..n:
Extension &amp; user data
for 1..n:
optional:
GOP header
for 0..n: User data
Picture header
for 0..n:
Extension &amp; user data # including AFD
Picture data...
Seq end
</pre>
</blockquote>
</li>
<li><p class="first">PS reading may still be slow.</p>
</li>
<li><p class="first">Continue to use valgrind and its cohorts to check for leaks and
inefficiencies.</p>
</li>
</ul>
<hr class="docutils" />
<p>Other outstanding items, in no particular order. Some of these may never
actually be worth the time to do.</p>
<ul>
<li><p class="first">Overhaul the older code to bring it up to the style of tsserve.</p>
</li>
<li><p class="first">Locate and check all <tt class="docutils literal"><span class="pre">&#64;&#64;&#64;</span></tt> comments in the code.</p>
</li>
<li><p class="first">Consider moving the verbose and quiet flags into the various context
entities, allowing closer control on exactly <em>what</em> is quiet/verbose.
This should allow replacement of debug conditions in individual files
with equivalent flags in the appropriate context, and closer control
of what output is available for debugging.</p>
</li>
<li><p class="first">Consider leaving a gap between PAT and PMT, since some clients read the
program stream and program metadata in parallel, so may take a while to
&quot;notice&quot; that they should be using a new PMT.</p>
<blockquote>
<p>This actually sounds like a good idea.</p>
</blockquote>
</li>
<li><p class="first">Maybe remove the (tail) recursion from write_some_TS_PES_packet (except that
it doesn't appear to impact efficiency at all, so it can wait).</p>
</li>
<li><p class="first">When outputting a picture (access unit), consider outputting the whole
thing as one (TS) PES packet, rather than outputting each item/NAL unit
as a PES packet.</p>
</li>
<li><p class="first">Regularise/make sensible the units used for -max in all utilities
(some are still working on NAL units/H.262 items when they should
be using pictures?).</p>
</li>
<li><p class="first">Make verbose and quiet be in the same order in all functions using them(!)</p>
</li>
<li><p class="first">Write more documentation.</p>
</li>
<li><p class="first">Note which functions are (only used as) LIBRARY (functions), and which
&quot;truly&quot; EXTERN.</p>
</li>
<li><p class="first">Check all help texts.</p>
<p>In particular, check what tsplay does for its timing info, and ensure its
help text is correct. Also, check what ps2ts does re timing, vs what it says
in its help text.</p>
</li>
<li><p class="first">PS from DVD has an extra field (navigation info?). A user has said that
DVDAuthor won't write PS to DVD without it containing said field. Consider a
small utility to read PS and write PS with said field (as a dummy) added in.</p>
</li>
<li><p class="first">Report __LINE__ and __FILE__ in internal errors that &quot;should not happen&quot;???</p>
</li>
<li><p class="first">Regularise meaning and behaviour of -verbose, -quiet and -x (should that
last be called thus, or something more like -debug?). Regularise if -verbose
implies not -quiet, and vice versa, in particular.</p>
</li>
<li><p class="first">Make all the messages output that currently say &quot;picture&quot; or &quot;access unit&quot;
that <em>should</em> say &quot;frame&quot; actually say &quot;frame&quot;.</p>
</li>
<li><p class="first">In pes.c, should it allow such flexibility in choice of which PIDs to
aggregate (or just keep) PES data for?</p>
<p>If the user had to select video/audio PIDs up-front, and no other PES
packets were kept, then it could get away with reusing the PES packet
data for each (just resetting the innards, and reallocing the data buffer
if it was not big enough).</p>
<p>Would this save enough time to be worthwhile?</p>
</li>
<li><p class="first">Similarly, the current codebase is profligate in its use of malloc/free,
because of the way it handles all the &quot;context&quot; entities. It would be
possible to use more local variables for many of these (although still
at the cost of remembering to call setup/teardown functions on them).
Given this, some variables could (probably) be reused instead of
building new instances and then freeing them.</p>
<p>Again, would this save enough time to be worthwhile?</p>
</li>
<li><p class="first">Missing programs?</p>
<blockquote>
<ul class="simple">
<li>ts2ps -- a preliminary implementation exists (build it explicitly with
<tt class="docutils literal"><span class="pre">make</span> <span class="pre">ts2ps</span></tt>), but it generates incorrect data.</li>
<li>es2ps -- is this ever needed?</li>
<li>ps2es -- <tt class="docutils literal"><span class="pre">ts2es</span> <span class="pre">-pes</span></tt> should actually do this</li>
<li>tsdots -- would be useful sometimes</li>
</ul>
</blockquote>
</li>
</ul>
<!-- ***** BEGIN LICENSE BLOCK ***** -->
<div class="section">
<h1><a id="license" name="license">License</a></h1>
<p>Version: MPL 1.1</p>
<p>The contents of this file are subject to the Mozilla Public License Version
1.1 (the &quot;License&quot;); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
<a class="reference" href="http://www.mozilla.org/MPL/">http://www.mozilla.org/MPL/</a></p>
<p>Software distributed under the License is distributed on an &quot;AS IS&quot; basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.</p>
<p>The Original Code is the MPEG TS, PS and ES tools.</p>
<p>The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright © 2008
the Initial Developer. All Rights Reserved.</p>
<p>Contributor(s):</p>
<blockquote>
Amino Communications Ltd, Swavesey, Cambridge UK</blockquote>
<!-- ***** END LICENSE BLOCK ***** -->
</div>
</div>
</body>
</html>

247
docs/todo.txt 100644
Wyświetl plik

@ -0,0 +1,247 @@
=====
To do
=====
.. notes: Check that all outstanding items in this file are still outstanding.
Bugs:
* esreverse (in particular, but really all the filtering programs) should not
abort if an error occurs reading through the file - in esreverse's case,
this causes no output to be produced, which is irritating.
* When reading PS data, if the next packet does not start with 00 00 01,
then a search will be made for the next pack header (specifically, this
is done by ``read_PS_packet_start``).
This means that a file with "broken" data in its middle can be coped
with by the PS reader - it will skip when it fails to find the next
00 00 01 in the right place.
It does not mean that other layers above the PS reader will cope with
broken data neatly, in particular if said "breakage" happened inside the
last PS packet read before the skip. Ideally, these other levels would
also have a strategy for moving on to the next believed-good point
(perhaps even the next PS pack header).
NB: TS data is expected not to contain errors in this sense.
Outstanding items:
* Sort out filter for H.264 so that it works sensibly.
Generally check that H.264 works correctly with tsserve - there are
believed to be known transition issues still outstanding.
In more detail:
1. When stopping filtering, go back in the reverse list to the previous
IDR and output that before continuing. This is needed to stop things
referring to non-existent reference frames.
Note that this might be a good thing to do cosmetically when stripping
as well, but since tsserve outputs all reference pictures when
stripping, it is not *necessary*.
2. When reversing, an IDR must also be output, so that any P/B frames
thereafter are known not to be referring "past" it. This can be done
either by insisting that reversing stop on an IDR, or else by
reading forwards and outputting I and IDR frames until an IDR is found.
This adds a requirement to be able to identify IDR frames in the reverse
list.
* Worry about AFD values and sequence headers in H.262.
*Believed DONE, except for the "read and remember the GOP" bits, which
may or may not matter.*
Note that if sequence headers are not output when F or FF is used (the
default, currently), then using F or FF as soon as tsserve is listening
will not display any pictures, until N is selected (at least on norton),
because nothing is displayed before a sequence header.
The work needed to make reversing work "properly" for H.262.
For H.264 it is sufficient to output just the block of data for a picture
when reversing. However, for H.262 it is clearly not that simple, if only
because of the AFD problem. What is probably wanted for H.262 is something
more like:
* Remember the start of a frame
* Remember the start of "relevant" sequence headers
* Remember (as now) which sequence header that "corresponds" to a frame
* Remember the AFD (it's only 8 bytes, and not all of that necessarily
is needed) for each frame
* Maybe also remember the start of GOP headers, and their correspondences
(NB: remember to cope with data that doesn't have AFDs?)
To *output* a frame then means:
a. [Locate its GOP and output that]
b. Locate its sequence header, and output that
c. Move to the start of the frame, and read and output its picture header
d. Output the AFD
e. Read and output the rest of the frame
Simple optimisations along the lines of "if the GOP/sequence header have
not changed then don't bother to re-output them" and "if no sequence
header has been output then don't output an AFD" can be added when the
approach seems to be working.
Refreshing my memory - the sequence of H.262 data is::
for 1..n:
Seq header
Seq extension
for 0..n:
Extension & user data
for 1..n:
optional:
GOP header
for 0..n: User data
Picture header
Picture coding extension
for 0..n:
Extension & user data # including AFD
Picture data...
Seq end
MPEG-1 is slightly simpler, and goes something like::
for 1..n:
Seq header
for 0..n:
Extension & user data
for 1..n:
optional:
GOP header
for 0..n: User data
Picture header
for 0..n:
Extension & user data # including AFD
Picture data...
Seq end
* PS reading may still be slow.
* Continue to use valgrind and its cohorts to check for leaks and
inefficiencies.
----------------------------------------
Other outstanding items, in no particular order. Some of these may never
actually be worth the time to do.
* Overhaul the older code to bring it up to the style of tsserve.
* Locate and check all ``@@@`` comments in the code.
* Consider moving the verbose and quiet flags into the various context
entities, allowing closer control on exactly *what* is quiet/verbose.
This should allow replacement of debug conditions in individual files
with equivalent flags in the appropriate context, and closer control
of what output is available for debugging.
* Consider leaving a gap between PAT and PMT, since some clients read the
program stream and program metadata in parallel, so may take a while to
"notice" that they should be using a new PMT.
This actually sounds like a good idea.
* Maybe remove the (tail) recursion from write_some_TS_PES_packet (except that
it doesn't appear to impact efficiency at all, so it can wait).
* When outputting a picture (access unit), consider outputting the whole
thing as one (TS) PES packet, rather than outputting each item/NAL unit
as a PES packet.
* Regularise/make sensible the units used for -max in all utilities
(some are still working on NAL units/H.262 items when they should
be using pictures?).
* Make verbose and quiet be in the same order in all functions using them(!)
* Write more documentation.
* Note which functions are (only used as) LIBRARY (functions), and which
"truly" EXTERN.
* Check all help texts.
In particular, check what tsplay does for its timing info, and ensure its
help text is correct. Also, check what ps2ts does re timing, vs what it says
in its help text.
* PS from DVD has an extra field (navigation info?). A user has said that
DVDAuthor won't write PS to DVD without it containing said field. Consider a
small utility to read PS and write PS with said field (as a dummy) added in.
* Report __LINE__ and __FILE__ in internal errors that "should not happen"???
* Regularise meaning and behaviour of -verbose, -quiet and -x (should that
last be called thus, or something more like -debug?). Regularise if -verbose
implies not -quiet, and vice versa, in particular.
* Make all the messages output that currently say "picture" or "access unit"
that *should* say "frame" actually say "frame".
* In pes.c, should it allow such flexibility in choice of which PIDs to
aggregate (or just keep) PES data for?
If the user had to select video/audio PIDs up-front, and no other PES
packets were kept, then it could get away with reusing the PES packet
data for each (just resetting the innards, and reallocing the data buffer
if it was not big enough).
Would this save enough time to be worthwhile?
* Similarly, the current codebase is profligate in its use of malloc/free,
because of the way it handles all the "context" entities. It would be
possible to use more local variables for many of these (although still
at the cost of remembering to call setup/teardown functions on them).
Given this, some variables could (probably) be reused instead of
building new instances and then freeing them.
Again, would this save enough time to be worthwhile?
* Missing programs?
- ts2ps -- a preliminary implementation exists (build it explicitly with
``make ts2ps``), but it generates incorrect data.
- es2ps -- is this ever needed?
- ps2es -- ``ts2es -pes`` should actually do this
- tsdots -- would be useful sometimes
.. ***** BEGIN LICENSE BLOCK *****
License
-------
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

1252
docs/tools.html 100644

Plik diff jest za duży Load Diff

949
docs/tools.txt 100644
Wyświetl plik

@ -0,0 +1,949 @@
========
TS Tools
========
.. contents::
Overview
========
The following tools are provided:
:es2ts_: Read ES (video), output TS
:esdots_: Print one character per ES unit
:esfilter_: "Fast forward" ES video data to a file (outputs ES or TS)
:esmerge_: Merge H.264 video and AAC ADTS audio ES to TS (very specific)
:esreport_: Report on the contents of an ES file
:esreverse_: "Reverse" ES video data to a file (outputs ES or TS)
:ps2es: Use ts2es_ (``ts2es -pes``) to obtain the effect of this.
:ps2ts_: Read PS data, output TS
:psdots_: Print one character per PS packet
:psreport_: Report on the contents of a PS file
:stream_type_: Make a (barely) educated guess what a file contains
:ts2es_: Extract an ES stream from a TS file
:tsinfo_: Report program info for a TS file (summarise PAT/PMT info)
:tsplay_: Play (and possibly loop) a PS/TS file over UDP (using timing
info) or TCP
:tsreport_: Report on the contents of a TS file
:tsserve_: Serve PS/TS files to clients (multicast) over TCP
There are also some test programs, which are not otherwise discussed:
:test_es_unit_list: Test the working of ES unit lists
:test_nal_unit_list: Test the working of NAL unit lists (built on the above)
:test_pes: Test the working of PES reading
It is the intention that all the tools should work on Linux, Windows, Mac OS/X
and BSD, although not all variants will always be tested at all times.
Common syntax and assumptions
=============================
In all of the tools, the switches and filenames (when used) may be mixed at
will - specifically, switches are allowed after filenames. This makes it more
convenient to run a particular program more than once by repeating the
previous command but appending new switches.
All switches are indicated by a single introductory ``-``. No provision is
made for coping with filenames that start with ``-``.
All tools have the following command line options in common:
:``-h``, ``-help``, ``--help``:
Provide help text about the program. There may also
be more detailed help texts for some programs, available
via different commands, which this help text will explain.
Note that all of the tools will provide this help text
if they are run with no arguments.
:``-q``, ``-quiet``:
Output no text except error messages.
:``-v``, ``-verbose``:
Output extra text. What this is depends on the type of
application; it might be extra information (for a report
tool), or debugging information (for a processing tool).
Note that verbose and quiet are [#]_ mutually incompatible. That is, there are
only three states:
1. "normal" (not verbose, not quiet)
2. verbose (and not quiet)
3. quiet (and not verbose)
.. [#] or should be - some programs might not yet enforce this.
Several tools share the following switches:
:``-tsout``: Output TS instead of ES (common amongst ES processing
tools).
:``-host <name>``, |hostandport|:
Specify a host to output to, and optionally a port
number. The default port number is 88.
.. We can't put a colon in a field lists :field name:, so use an indirection...
.. |hostandport| replace:: ``-host <name>:<port>``
:``-output <name>``, ``-o <name>``:
Specify output to the named file.
:``-pid <pid>``: Specify a PID. This is read as decimal by default, but
a hexadecimal value can be specifed as (for instance)
``-pid 0x68``. The specific switch ``-pid`` is not common,
but variants are.
:``-stdin``, ``-stdout``:
Take input from the standard input, write output to the
standard output. If -stdout is used, -quiet is always
enforced. If input is from standard input, the tool will
not be able to "guess" the input type -- tools will
generally default to H.262 (MPEG-2) in this case.
:``-h262``: Indicates that the input (video) data is H.262 data
(actually, either MPEG-1 or MPEG-2). Stops the tool trying
to determine this for itself.
:``-h264``, ``-avc``:
Indicates that the input (video) data is H.264 (i.e.,
MPEG-4/AVC). Stops the tools trying to determine this for
itself.
:``-dvd``, ``-notdvd``, ``-nodvd``:
Indicates that the PS data being read conforms (or doesn't)
to DVD conventions. This matters for audio data in
private_stream_1, which is packaged as "substreams" in DVD
data. The programs ps2ts and psreport assume DVD data
(since program stream is normally obtained from DVDs).
:``-dolby dvb``, ``-dolby atsc``:
Dolby (AC-3) audio data may be written out using either of
two TS stream types, 0x06 for DVB data, and 0x81 for ATSC
data. When reading TS data, the stream type read is used
for output, but when reading PS data and outputting TS
data, a decision must be made. The default is to use the
DVB convention. This switch is provided for ps2ts, tsplay
and tsserve.
:``-max <n>``: Some of the tools can stop after reading/processing <n>
of the appropriate data items. This can (for instance)
be used to truncate data files, or to inspect only part
of a data stream.
:``-pes``: Some of the ES reading tools will instead read from TS or
PS data if given the ``-pes`` switch. This saves piping
data through ts2es or (for PS) ps2ts and ts2es.
For all of the tools, the documentation provided by ``-help`` should be used
to find current command line definitions - these are not necessarily repeated
below.
Error and warning messages
==========================
Conventionally, error and warning messages are all output to ``stderr``.
Error messages are prefixed by ``###``, and warning messages by ``!!!``.
Errors may be expected to cause a tool to exit with "failure" status.
The final (outermost) message in a sequence of error messages for a particular
tool will indicate the tool name (this can be useful when piping several tools
together).
Common considerations
=====================
Support for MPEG-1
------------------
MPEG-1 is essentially a subset of MPEG-2. The tools provided do not explicitly
support MPEG-1, but should be lax enough in their requirements for MPEG-2 to
allow MPEG-1 data as well (for instance, not *requiring* the presence of
Sequence Extensions). In general, if this document talks about MPEG-2 (or
H.262), MPEG-1 may be assumed as well.
Determining the file type
-------------------------
Several of the tools attempt to determine what type of data is contained in
the input file. The mechanism used opts for simplicity over rigour, and can
thus conceivably make a mistake. In this case, the user should use the
appropriate switch (e.g., ``-h262`` or ``-h264``) to override it.
H.264 profiles
--------------
The underlying H.264 (MPEG-4/AVC) library checks the flags in the first
sequence parameter set to determine what H.264 profile the data claims to be
following. This library supports the "main" profile (profile indicator 77),
or other profiles if they declare that they only contain data from the "main"
profile.
If the first sequence parameter set indicates that the bistream contains a
different protocol, and does not indicate that it is conforming to the "main"
profile, then the library will output a warning message - for instance::
Warning: This bitstream declares itself as extended profile (88).
This software does not support extended profile,
and may give incorrect results or fail.
It will continue regardless, however, as experience shows that this
information is not always presented correctly.
(Note that although this is a warning message it is not prefixed
by ``!!!``. It is, however, still output to standard error.)
Frames and fields
-----------------
If the input data is being treated in terms of frames, the tools will all
aggregate individual fields into "frames" (by appending the NAL units or ES
units of the second field to the first).
In MPEG-2 processing tools, sequence headers are (broadly) treated as pictures
(and thus as frames), and are thus included in the frame count.
In MPEG-4/AVC data, the way that access units are aggregated can (when an end
of stream unit is found) lead to an apparent "extra" access unit at the end of
the data.
Broken Program Stream files
---------------------------
`Determining the file type`_ above explained how the tools are tolerant of
PS files that do not start with a pack header.
When reading PS data, if the start of a packet is being read (i.e., bytes 0x00
0x00 0x01 0x*XX*, where *XX* is the packet's stream type) and some other
sequence of bytes is found, then the software will scan forwards for the next
pack header. If no next pack header is found, then it will exit with an error.
This should cope with files that have "dropped" data, where some bytes have
been lost. It is unlikely to help directly with files that have corrupted
data, which will cause errors at higher levels (for instance, in the H.262
"picture" building routines).
es2ts
=====
Converts an elementary video stream to transport stream.
For instance::
$ es2ts hp-trail.264 hp-trail.ts
$ es2ts hp-trail.264 -host norton
or, taking the newly created TS file from above, extracting its video stream
using ts2es_ and outputting it anew with video in PID 0x99::
$ ts2es -video -stdout hp-trail.ts | es2ts -stdin -pid 0x99 hp-trail99.ts
The input stream may be MPEG-1 (as a subset of MPEG-2), MPEG-2 or
MPEG-4/AVC. By default the tool looks at the start of the input file to
determine whether it is MPEG-2 or MPEG-4/AVC.
Input may be a file or standard input.
Output may be a file, standard output or a host via TCP.
The maximum number of ES units to process may be specified with the ``-max``
switch.
No decoding of the contents of ES units is made, so the only reason that the
tool needs to know whether the data is MPEG-2 or MPEG-4/AVC is so that it can
write appropriate TS.
esdots
======
This is a diagnostic tool used to gain a "view" of the contents of a data
file.
For instance::
$ esdots -v hp-trail.264
Reading from hp-trail.264
Input appears to be MPEG-4/AVC (H.264)
Warning: This bitstream declares itself as extended profile (88).
This software does not support extended profile,
and may give incorrect results or fail.
Ipppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppppp
pppppppppppppppppppppppppppppippppppppppppppppppppppppppppppppppppppppp
... lines omitted ...
pppppppppppppppippppppppppppppppppppppppppppppppppppppppppppppp
Found 2550 NAL units in 2548 access units
$ esdots /data/CharliesAngels.es -max 1000
Reading from /data/CharliesAngels.es
Input appears to be MPEG-2 (H.262)
[E>iEUUbEUbEUpEUbEUbEUpEUbEUbEUpEUbEUbEU[E>iEUUbEUbEUpEUbEUbEUpEUbEUbEU
pEUbEUbEU[E>iEUUbEU
Found 1000 MPEG2 items
If the ``-v`` switch is used, then an explanation of the meaning of the
characters output will be prepended (it is slightly different depending on
whether the input is MPEG-2 or MPEG-4/AVC).
esfilter
========
Reads an input video stream and outputs it to another file, possibly filtering
the data.
For instance::
$ esfilter -copy some-file.es shorter-copy.es -max 2000
just copies the first 2,000 ES units from one file to the other, ::
$ esfilter -strip some-file.es stripped-file.es
outputs just the I (and for H.264, IDR) frames, whilst::
$ esfilter -filter some-file.es filtered-file.es -max 2000
attempts to "fast forward" the input file, outputting the result, but stopping
after 2,000 frames. The default "speed-up" is 8x, which may be altered with
the ``-freq`` switch.
Input is from an elementary stream, either an explicit file or standard input.
Output can be to elementary stream (the default), or to transport
stream (with the ``-tsout`` switch). For either, standard output can be chosen
instead of an explicit file. If TS output is specified, writing to a host
over TCP/IP can also be requested.
Note that for ``-strip`` and ``-filter``, the ``-max`` switch acts on the
number of frames, but since ``-copy`` works at the ES item/NAL unit level, its
``-max`` works on these lower level entities.
Fast forward algorithms
-----------------------
The simpler "strip" algorithm, acts by simply discarding all frames that are
not I (or, for MPEG-4/AVC, IDR) frames. This is quick, and produces a result
guaranteed to display without artefacts (since I and IDR frames do not refer
outside themselves), but will produce a variable speed-up depending on the
distribution of the different sorts of frame within the data.
If the ``-allref`` switch is supplied as well, then the "strip" algorithm is
modified to keep all reference frames in MPEG-4/AVC, and all I and P frames
in MPEG-2. This will produce a lesser speed-up, which should still display
safely.
The more complex "filter" algorithm attempts to emulate a specific speed-up,
defaulting to 8x (and alterable with the ``-freq`` switch). It may repeat
frames to produce the requested speed.
In either case, the output frames are not altered in any way, which means that
the resultant data stream is unlikely to be technically valid. For instance,
in the MPEG-4/AVC case, no attempt is made to amend frame numbers. Also,
metadata may not be correct, since (in MPEG-4/AVC) only the first sequence and
picture parameter sets found are output.
esmerge
=======
This utility was written specifically to merge an MPEG-4/AVC video stream with
an AAC ADTS audio stream.
For instance::
$ esmerge video.264 audio.aac result.ts
It is also possible to specify the audio rate (which defaults to 44.1KHz).
Timing information is output (based on 25 video frames/second and the given
audio rate, assuming 1024 samples per frame) as follows:
1. In the PES PTS and TS PCR for the first NAL unit of every I or IDR video
frame. The PCR is generated by using the PTS as its base and 0 as its
extension.
2. In the TS PCR for the first NAL unit of every other video frame.
3. In the PES PTS and TS PCR for the first NAL unit of every audio frame.
Whilst very specific at the moment, this tool could obviously be expanded to
be more versatile if future needs require.
Since original writing, some support for AVS (video) and MPEG layer 2
(audio) has been added.
esreport
========
Reads an input elementary stream (or, with ``-pes``, PS or TS) and reports on
it.
For instance::
$ esreport -max 5 CharliesAngels.es
Reading from CharliesAngels.es
Input appears to be MPEG-2 (H.262)
00000000/0000: MPEG2 item b3 (SEQUENCE HEADER) size 140
00000140/0000: MPEG2 item b5 (Extension start) size 10
00000150/0000: MPEG2 item b8 (Group start) size 8
00000158/0000: MPEG2 item 00 (Picture) 1 (I) size 8
00000166/0000: MPEG2 item b5 (Extension start) size 9
Found 5 MPEG-2 items
or::
$ esreport -max 5 hp-trail.264
Reading from hp-trail.264
Input appears to be MPEG-4/AVC (H.264)
00000001/0000: NAL unit 3/7 (seq param set) 11: 67 58 00 15 96 53 01 68 24 88...
Warning: This bitstream declares itself as extended profile (88).
This software does not support extended profile,
and may give incorrect results or fail.
00000015/0000: NAL unit 3/8 (pic param set) 5: 68 ce 38 80 00
00000023/0000: NAL unit 3/5 (IDR) 3191: 65 88 80 40 02 13 14 00 04 2f...
00003217/0000: NAL unit 2/1 (non-IDR) 5453: 41 9a 02 05 84 01 c5 d4 7d 88...
00008673/0000: NAL unit 2/1 (non-IDR) 7558: 41 9a 04 09 41 00 71 1a f8 ff...
Stopping because 5 NAL units have been read
Found 5 NAL units
nal_ref_idc:
2 of 2
3 of 3
nal_unit_type:
2 of 1 (non-IDR)
1 of 5 (IDR)
1 of 7 (seq param set)
1 of 8 (pic param set)
slice_type:
2 of 5 (P &c)
1 of 7 (I &c)
or::
$ esreport -frames -max 5 CharliesAngels.es
Reading from CharliesAngels.es
Input appears to be MPEG-2 (H.262)
Sequence header: frames and fields
I Frame #2
B Frame #0
B Frame #1
P Frame #5
Found 5 MPEG-2 pictures
The number after the ``#`` is the frames temporal reference, and the "picture"
count includes the sequence header.
If ``-q`` is specified, only the final counts are output.
esreverse
=========
Reads an input video stream and outputs it to another file, in "fast reverse".
For instance::
$ esreverse hp-trail.264 hp-reverse.264
Output may optionally be to Transport Stream, instead of ES::
$ esreverse hp-trail.264 -tsout hp-reverse.ts
TS or PS data may be read (instead of ES) using the ``-pes`` switch::
$ esreverse -pes CVBt_hp_trail.ts -tsout CVBt_reversed.ts
Note that this will only read (and reverse) the video stream.
The "frequency" of frames to try to keep when reversing the data (thus the
"speedup" in reversing) may be specified with the ``-freq`` switch. It
defaults to 8.
Reverse algorithms
------------------
The input data is scanned forwards. For MPEG-2, the location and index of I
frames and sequence headers is remembered. For MPEG-4/AVC, the location and
index of I and IDR frames is remembered.
Reversing is then done by starting at the end of the array of remembered data,
and moving backwards, attempting to reproduce the requsted frequency. This may
entail repeating particular frames.
In MPEG-2 data, each I frame "remembers" the sequence header that precedes
it, and also the AFD that is applicable to itself. When outputting the reverse
data, a section header is output if appropriate (i.e., if it not the same as
for the "previous" I frame), but AFDs are output for each frame.
In MPEG-4/AVC data, sequence parameter set and picture parameter set NAL units
must be output at the start of the output. ``esreverse`` writes out the data
for the last sequence and picture parameters sets found with each id
(typically, this means sequence parameter set 0 and picture parameter set 0)
at the start of the reversed output.
ps2ts
=====
Reads an input Program Stream and outputs equivalent Transport Stream.
For instance::
$ ps2ts CharliesAngels.mpg CharliesAngels.ts
One video stream is supported. If multiple audio streams are found in the PS,
then the first will be output (unless ``-noaudio`` is used to suppress it).
The video, audio and PMT PIDs may be specified.
The video data will be output to TS with stream type 0x02 for MPEG-2 video,
and 0x1b for MPEG-4/AVC video.
An attempt is made to work out an appropriate stream type for the audio,
depending upon what it is. Note that AC3 on DVD is stored in substreams,
which must be "unpacked" when outputting the data as TS.
PS program stream map and program stream directory are ignored, mainly because
I have not yet seen data with these present.
When writing the video data, the SCR base and extension from the PS pack
header are used as the PCR base and extension.
.. Note:: Reading PS data may be slower than reading ES or TS data.
psdots
======
This is a diagnostic tool used to gain a "view" of the contents of a data
file.
For instance::
$ psdots -v CharliesAngels.mpg -max 100
Reading from CharliesAngels.mpg
Stopping after 100 PS packets
[Hv[v[v[v[a[a[v[v[v[v[v[v[a[v[v[v[v[v[v[v[v[v[v[a[a[v[v[v[v[v[v[v[v[v[v
[v[v[a[a[v[v[v[v[v[v[v[v[v[v[v[a[v[v[v[v[v[v[v[v[v[v[v[v[a[a[v[v[v[v[v[
v[v[v[v[v[v[v[v[v[v[v[a[a[v[v[v[v[v[v[v[v[v[v[v[v[a[v[v[v[v
Stopping after 100 packs
If the ``-v`` switch is used, then an explanation of the meaning of the
characters output will be prepended.
psreport
========
Reports on the content of a Program Stream.
For instance::
$ psreport CharliesAngels.mpg
Reading from CharliesAngels.mpg
Packets (total): 984111
Packs: 492055
Video packets (stream 0): 426945 min size 3748, max size 7178, mean size 7042.7
Audio packets (stream 0): 65110 min size 602, max size 5888, mean size 3556.2
Program stream maps: 0
Program stream directories: 0
Information about the packets can be obtained with the ``-v`` switch::
$ psreport CharliesAngels.mpg -v -max 3
Reading from CharliesAngels.mpg
Stopping after 3 PS packets
00000000: Pack header: SCR 0 (0/0) mux rate 18020
00000014: System header 1
Read 1 system header
00000032: PS Packet 3 stream E0 (Video stream 0x0)
Packet (7168 bytes): 00 00 01 e0 1b fa 8f c0 0a 21 00 07 6d c5 11 00 07 19 65 00...
00007200: Pack header: SCR 214800 (716/0) mux rate 18020
Read 0 system headers
00007214: PS Packet 5 stream E0 (Video stream 0x0)
Packet (7168 bytes): 00 00 01 e0 1b fa 8b 00 01 ff eb 12 6b 21 0a f5 86 04 a4 eb...
a00014382: Pack header: SCR 429600 (1432/0) mux rate 18020
Read 0 system headers
00014396: PS Packet 7 stream E0 (Video stream 0x0)
Packet (7168 bytes): 00 00 01 e0 1b fa 8b 00 01 ff 25 7f b2 10 8d 75 bb 00 6b c9...
Stopping after 3 packs
Packets (total): 8
Packs: 3
Video packets (stream 0): 3 min size 7168, max size 7168, mean size 7168.0
Program stream maps: 0
Program stream directories: 0
stream_type
===========
Looks at the start of a file to attempt to determine if it is:
* Transport Stream
* Program Stream
* Elementary Stream containing MPEG-2
* Elementary Stream containing MPEG-4/AVC
* PES
The mechanisms used to decide are not very sophisticated, but appear to work
in practice.
For instance::
$ stream_type CharliesAngels.mpg
Reading from CharliesAngels.mpg
It appears to be Program Stream
or, with "explanations" enabled::
$ stream_type CharliesAngels.mpg -v
Reading from CharliesAngels.mpg
Trying to read pack header
File starts 00 00 01 BA - could be PS, reading pack header body
OK, trying to read start of next packet
Start of second packet found at right place - looks like PS
It appears to be Program Stream
``stream_type`` returns an exit value which may be used in shell scripts to
take action according to its decision.
ts2es
=====
Extract a single Elementary Stream from Transport Stream.
For instance::
$ ts2es -video CharliesAngels.ts CharliesAngels.es
The ES to be extracted may be the video stream (taken to be the first video
stream found), the first audio stream found, or the stream with a specific
PID.
The ``-pes`` switch may be used to read data via the PES reading mechanisms,
which allows ``ts2es`` to read PS data as well::
$ ts2es -pes CharliesAngels.mpg CharliesAngels.es
tsinfo
======
Present information on the program streams within a TS file.
For instance::
$ tsinfo CVBt_hp_trail.ts
Reading from CVBt_hp_trail.ts
Scanning 1000 TS packets
Packet 9 is PAT
Program list:
Program 1 -> PID 0066 (102)
Packet 10 is PMT with PID 0066 (102)
Program streams:
PID 0068 (104) -> Stream type 27 (H.264/14496-10 video (MPEG-4/AVC))
PID 0067 (103) -> Stream type 15 (13818-7 Audio with ADTS transport syntax)
PCR PID 0068 (104)
Found 1 PAT packet and 1 PMT packet in 1000 TS packets
The tool looks through the first 1000 TS packets for PAT and PMT entries, and
reports on their content. If multiple PAT/PMT entries are found, with
differing content, then this will be reported::
$ tsinfo CharliesAngels.ts
Reading from CharliesAngels.ts
Scanning 1000 TS packets
Packet 9 is PAT
Program list:
Program 1 -> PID 0066 (102)
Packet 10 is PMT with PID 0066 (102)
Program streams:
PID 0068 (104) -> Stream type 2 (H.262/13818-2 video (MPEG-2) or 11172-2 constrained video)
PCR PID 0068 (104)
Packet 168 is PMT with PID 0066 (102) - content changed
Program streams:
PID 0068 (104) -> Stream type 2 (H.262/13818-2 video (MPEG-2) or 11172-2 constrained video)
PID 0067 (103) -> Stream type 4 (13818-3 audio (MPEG-2))
PCR PID 0068 (104)
Found 2 PAT packets and 2 PMT packets in 1000 TS packets
tsplay
======
Plays TS data over UDP or TCP/IP.
Multicasting is supported over UDP::
$ tsplay hp-trail.ts 255.1.1.1
Playing can be looped (i.e., to repeat indefinitely)::
$ tsplay hp-trail.ts 255.1.1.1 -loop
TCP/IP may also be used::
tsplay hp-trail.ts -tcp norton
When playing over UDP, the tool manages a circular buffer of TS packets, which
are output to the target host at appropriate times, based on the TS packet
PCR.
Best response will be obtained with a fast machine and locally stored data.
Note that if output is over UDP, and output is to a multicast IP address,
then the network interface to use for the multicast broadcast can be chosen
with the ``-mcastif`` switch. For example::
tsplay hp-trail.ts 235.1.1.1:1234 -mcastif 192.168.172.12
This option may not be supported on some versions of Windows.
There are many tuning options - see ``-help tuning`` for details. The most
useful are probably:
* ``-maxnowait`` -- This specifies how many packets (each of 7 TS packets) may
be sent to the client without a gap. The default is 3, which is relatively
conservative. If the output is choppy, and tsplay reports that it is having
to restart its timing::
!!! Packet 701 (item 101): Outputting 0.5s late - restarting time sequence
Maybe consider running with -maxnowait greater than 3
then increasing the ``-maxnowait`` value may help (a value of 10 is
reasonable if the client process is happy with this).
* ``-hd`` -- This is provided for playing HD video, and is a "short hand"
instead of specifying a variety of other switches (including ``-maxnowait``)
with suitable values.
Circular buffer algorithm
-------------------------
This is only used for output over UDP - it is not applicable to TCP/IP.
Transport Stream packets are read in batches of 7 (chosen to give a sensible
number of bytes for sending over the network - seven TS packets is 1316 bytes,
which will fit within an ethernet packet of size 1500). These "batches" are
written to a circular buffer. Each "batch" has a timestamp, derived from the
PCRs in the Transport Stream.
A child process is forked, which reads the "batch" entries from the circular
buffer, and attempts to output over UDP at an appropriate time. If it believes
that it is sending too fast, it will wait. If it believes that it is sending
too slow, it will output multiple "batches" with no intervening gap, up to a
maximum number (as mentioned above).
A slow machine will find that it is always trying to catch up, and will not
"feed" the UDP client adequately.
Almost all aspects of the algorithm can be changed. Details are available via
the ``-tuning`` switch.
tsreport
========
Reports on the TS packets in a file.
For instance::
$ tsreport CharliesAngels.ts
Reading from CharliesAngels.ts
!!! 156 bytes ignored at end of file - not enough to make a TS packet
Read 382583 TS packets
More information can be obtained with the ``-v`` switch::
$ tsreport CharliesAngels.ts -v -max 20
Reading from CharliesAngels.ts
Stopping after 20 TS packets
00000000: TS Packet 1 PID 1fff PADDING - ignored
00000188: TS Packet 2 PID 1fff PADDING - ignored
00000376: TS Packet 3 PID 1fff PADDING - ignored
00000564: TS Packet 4 PID 1fff PADDING - ignored
00000752: TS Packet 5 PID 1fff PADDING - ignored
00000940: TS Packet 6 PID 1fff PADDING - ignored
00001128: TS Packet 7 PID 1fff PADDING - ignored
00001316: TS Packet 8 PID 1fff PADDING - ignored
00001504: TS Packet 9 PID 0000 [pusi] PAT
section length: 00d (13)
transport stream id: 0001
version number 00, current next 1, section number 0, last section number 0
Program 001 ( 1) -> PID 0066 (102)
00001692: TS Packet 10 PID 0066 [pusi] PMT
section length: 012 (18)
program number: 0001
version number 00, current next 1, section number 0, last section number 0
PCR PID: 0068
program info length: 0
Stream 02 (H.262/13818-2 video (MPEG-2) or 11172- -> PID 0068 Info (0 bytes)
00001880: TS Packet 11 PID 0068 [pusi]
Adaptation field len 7 [flags 10]: PCR
.. PCR 0
PES header
Start code: 00 00 01
Stream ID: e0 (224)
PES packet length: 1bfa (7162)
Flags: 8f c0 PES-priority data-aligned copyright original/copy : PTS DTS
PES header len 10
!!! Guard bits at start of PTS data are 2, not 3
PTS 112354
DTS 101554
00002068: TS Packet 12 PID 0068
00002256: TS Packet 13 PID 0068
00002444: TS Packet 14 PID 0068
00002632: TS Packet 15 PID 0068
00002820: TS Packet 16 PID 0068
00003008: TS Packet 17 PID 0068
00003196: TS Packet 18 PID 0068
00003384: TS Packet 19 PID 0068
00003572: TS Packet 20 PID 0068
Stopping after 20 packets
Read 20 TS packets
The ``-timing`` switch may be used to obtain PCR timing information::
$ tsreport CharliesAngels.ts -timing -max 500
Reading from CharliesAngels.ts
Stopping after 500 TS packets
.. PCR 0
.. PCR 214800 Mean byterate 921620 byterate 921620
.. PCR 429600 Mean byterate 921620 byterate 921620
.. PCR 644400 Mean byterate 921620 byterate 921620
.. PCR 5298300 Mean byterate 182986 byterate 80711
.. PCR 5513100 Mean byterate 211764 byterate 921620
.. PCR 5727900 Mean byterate 238384 byterate 921620
.. PCR 5942700 Mean byterate 263080 byterate 921620
.. PCR 6157500 Mean byterate 286053 byterate 921620
.. PCR 6372300 Mean byterate 307477 byterate 921620
.. PCR 10430700 Mean byterate 222394 byterate 88802
Stopping after 500 packets
Read 500 TS packets
tsserve
=======
Acts as a server, playing PS or TS files to clients.
For instance::
$ tsserve CharliesAngels.mpg
will listen for a client on the default port 88, and "serve" the file to it
when a connection is made.
Up to 10 files may be specified on the command line - for instance::
$ tsserve -0 CharliesAngels.mpg -1 hp-trail.ts -2 news24.ts
specifies files 0, 1 and 2. The first example, where no file number is
explicitly given, is equivalent to ``-0 CharliesAngels.mpg``.
If port 88 is not suitable, an alternative may be chosen::
$ tsserve -port 8889 -0 CharliesAngels.mpg -1 hp-trail.ts -2 news24.ts
The file being served starts in "p"ause mode. On reaching either end of the
file (the end by playing forwards at normal or accelerated speed, or the start
by rewinding), it returns to "p"ause mode.
A client may send single character commands to the server:
* ``p`` - pause (the initial state)
* ``n`` - normal play
* ``f`` - fast forward (using "strip" as described in
`Fast forward algorithms`_). The speed of this is dependent on the
distribution of reference frames in the data.
* ``F`` - fast fast forward (using "filter" as described in
`Fast forward algorithms`_). The speed of this is nominally 8x.
* ``r`` - reverse (as described in `Reverse algorithms`_). The nominal
speed for this is 8x.
* ``R`` - reverse at double speed, i.e., 16x.
* ``>`` - skip forwards 10 seconds.
* ``]`` - skip forwards 3 minutes.
* ``<`` - skip backwards 10 seconds.
* ``[`` - skip backwards 3 minutes.
* ``0`` .. ``9`` - select the file with that number, and start playing
it again from the start. If there is no file with that number, then
ignore the command.
* ``q`` - quit this client.
(typically by use of a handset).
For each client, tsserver spawns a new server (on Unix with ``fork``, on
Windows using a thread) which serves the nominated files to that client.
No particular limit is specified for the number of clients allowed.
When a client sends the ``q`` command, or if an error occurs, then the
particular process for that client will be terminated.
Notes
-----
Each file is output as a different TS program, file 0 as program 1, file 1 as
program 2, etc.
Different PIDs will be used for the data in each program. For program *i*,
* the video PID will be 0x68 + *i*,
* the audio PID will be 0x68 + *i* + 10, and
* the PMT PID will be 0x68 + *i* + 20.
The PCR PID will be assumed to be the video PID.
When reversing or fast forwarding MPEG-2 data, sequence headers will not, by
default be output (see `Reverse algorithms`_ for some background on this). The
``-seqhdr`` switch may be used to override this.
Alternate modes
---------------
If only a specific host is to be used as a "client", then that host may be
named explicitly::
$ tsserve -cmd -host norton -0 CharliesAngels.mpg -1 hp-trail.ts
The ``-cmd`` indicates that the host may send single character commands (as
above).
It is also possible (on Unix, but not on Windows) to give commands to the
program directly, instead of via the client socket.
For instance::
$ tsserve -cmdstin -host norton -0 CharliesAngels.mpg -1 hp-trail.ts
The program will then read commands from standard input. Note, however, that
such commands will not be "seen" until a newline is typed (at which point any
commands typed will occur in the order given).
Some canned test modes are also supplied, which perform a reproducable
sequence of actions. These are described in the ``-details`` help text.
.. ***** BEGIN LICENSE BLOCK *****
License
=======
Version: MPL 1.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL/
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the
License.
The Original Code is the MPEG TS, PS and ES tools.
The Initial Developer of the Original Code is Amino Communications Ltd.
Portions created by the Initial Developer are Copyright |copy| 2008
the Initial Developer. All Rights Reserved.
.. |copy| unicode:: 0xA9 .. copyright sign
Contributor(s):
Amino Communications Ltd, Swavesey, Cambridge UK
.. ***** END LICENSE BLOCK *****

1630
es.c 100644

Plik diff jest za duży Load Diff

431
es2ts.c 100644
Wyświetl plik

@ -0,0 +1,431 @@
/*
* Convert an Elementary Stream to Transport Stream.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "es_fns.h"
#include "ts_fns.h"
#include "tswrite_fns.h"
#include "misc_fns.h"
#include "version.h"
/*
* Write (copy) the current ES data unit to the output stream, wrapped up in a
* PES within TS.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int write_ES_unit_as_TS(TS_writer_p output,
ES_unit_p unit,
u_int32 video_pid)
{
int err = write_ES_as_TS_PES_packet(output,unit->data,unit->data_len,
video_pid,DEFAULT_VIDEO_STREAM_ID);
if (err)
{
fprintf(stderr,"### Error writing ES data unit\n");
return err;
}
else
return 0;
}
static int transfer_data(ES_p es,
TS_writer_p output,
u_int32 pmt_pid,
u_int32 video_pid,
byte stream_type,
int max,
int verbose,
int quiet)
{
int err = 0;
int count = 0;
// Write out a PAT and PMT first, or our stream won't make sense
if (!quiet)
printf("Using transport stream id 1, PMT PID %#x, program 1 ="
" PID %#x, stream type %#x\n",pmt_pid,video_pid,
stream_type);
err = write_TS_program_data(output,1,1,pmt_pid,video_pid,stream_type);
if (err)
{
fprintf(stderr,"### Error writing out TS program data\n");
return 1;
}
for (;;)
{
ES_unit_p unit;
err = find_and_build_next_ES_unit(es,&unit);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error copying ES data units\n");
return err;
}
count++;
if (verbose)
report_ES_unit(stderr,unit);
err = write_ES_unit_as_TS(output,unit,video_pid);
if (err)
{
free_ES_unit(&unit);
fprintf(stderr,"### Error copying ES data units\n");
return err;
}
free_ES_unit(&unit);
if (max > 0 && count >= max)
break;
}
if (!quiet)
printf("Transferred %d ES data unit%s\n",count,(count==1?"":"s"));
return 0;
}
static void print_usage()
{
printf(
"Usage: es2ts [switches] [<infile>] [<outfile>]\n"
"\n"
);
REPORT_VERSION("es2ts");
printf(
"\n"
" Convert an elementary video stream to H.222 transport stream.\n"
" Supports input streams conforming to MPEG-2 (H.262), MPEG-4/AVC\n"
" (H.264) and AVS. Also supports MPEG-1 input streams, insofar as MPEG-2\n"
" is backwards compatible with MPEG-1.\n"
"\n"
" Note that this program works by reading and packaging the elementary\n"
" stream packages directly - it does not parse them as H.262 or H.264\n"
" data.\n"
"\n"
"Files:\n"
" <infile> is a file containing the Elementary Stream data\n"
" (but see -stdin below)\n"
" <outfile> is an H.222 Transport Stream file\n"
" (but see -stdout and -host below)\n"
"\n"
"Switches:\n"
" -pid <pid> <pid> is the video PID to use for the data.\n"
" Use '-pid 0x<pid>' to specify a hex value.\n"
" Defaults to 0x68.\n"
" -pmt <pid> <pid> is the PMT PID to use.\n"
" Use '-pmt 0x<pid>' to specify a hex value.\n"
" Defaults to 0x66\n"
" -verbose, -v Output summary information about each ES packet\n"
" as it is read\n"
" -quiet, -q Only output error messages\n"
" -stdin Take input from <stdin>, instead of a named file\n"
" -stdout Write output to <stdout>, instead of a named file\n"
" Forces -quiet.\n"
" -host <host>, -host <host>:<port>\n"
" Writes output (over TCP/IP) to the named <host>,\n"
" instead of to a named file. If <port> is not\n"
" specified, it defaults to 88.\n"
" -max <n>, -m <n> Maximum number of ES data units to read\n"
"\n"
"Stream type:\n"
" When the TS data is being output, it is flagged to indicate whether\n"
" it conforms to H.262, H.264 or AVS. It is important to get this right,\n"
" as it will affect interpretation of the TS data.\n"
"\n"
" If input is from a file, then the program will look at the start of\n"
" the file to determine if the stream is H.264, H.262 or AVS. This\n"
" process may occasionally come to the wrong conclusion, in which case\n"
" the user can override the choice using the following switches.\n"
"\n"
" If input is from standard input (via -stdin), then it is not possible\n"
" for the program to make its own decision on the input stream type.\n"
" Instead, it defaults to H.262, and relies on the user indicating if\n"
" this is wrong.\n"
"\n"
" -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
" -h262 Force the program to treat the input as MPEG-2.\n"
" -avs Force the program to treat the input as AVS.\n"
);
}
int main(int argc, char **argv)
{
int use_stdin = FALSE;
int use_stdout = FALSE;
int use_tcpip = FALSE;
int port = 88; // Useful default port number
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
TS_writer_p output = NULL;
ES_p es;
int verbose = FALSE;
int quiet = FALSE;
int max = 0;
u_int32 video_pid = 0x68;
u_int32 pmt_pid = 0x66;
int err = 0;
int err2;
int ii = 1;
int video_type = VIDEO_H262; // hopefully a sensible default
int force_stream_type = FALSE;
byte stream_type = 0; // silly value to keep compiler quiet
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) ||
!strcmp("-h",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-avc",argv[ii]) || !strcmp("-h264",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_H264;
}
else if (!strcmp("-h262",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_H262;
}
else if (!strcmp("-avs",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_AVS;
}
else if (!strcmp("-stdin",argv[ii]))
{
had_input_name = TRUE; // more or less
use_stdin = TRUE;
}
else if (!strcmp("-stdout",argv[ii]))
{
had_output_name = TRUE; // more or less
use_stdout = TRUE;
}
else if (!strcmp("-host",argv[ii]))
{
CHECKARG("es2ts",ii);
err = host_value("es2ts",argv[ii],argv[ii+1],&output_name,&port);
if (err) return 1;
had_output_name = TRUE; // more or less
use_tcpip = TRUE;
ii++;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("es2ts",ii);
err = int_value("es2ts",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-pid",argv[ii]))
{
CHECKARG("es2ts",ii);
err = unsigned_value("es2ts",argv[ii],argv[ii+1],0,&video_pid);
if (err) return 1;
ii++;
}
else if (!strcmp("-pmt",argv[ii]))
{
CHECKARG("es2ts",ii);
err = unsigned_value("es2ts",argv[ii],argv[ii+1],0,&pmt_pid);
if (err) return 1;
ii++;
}
else
{
fprintf(stderr,"### es2ts: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && had_output_name)
{
fprintf(stderr,"### es2ts: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name)
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### es2ts: No input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### es2ts: No output file specified\n");
return 1;
}
// Try to stop extraneous data ending up in our output stream
if (use_stdout)
{
verbose = FALSE;
quiet = TRUE;
}
if (use_stdin)
err = open_elementary_stream(NULL,&es);
else
err = open_elementary_stream(input_name,&es);
if (err)
{
fprintf(stderr,"### es2ts: "
"Problem starting elementary stream - abandoning reading\n");
return 1;
}
if (!quiet)
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
// Decide if the input stream is H.262 or H.264
if (force_stream_type || use_stdin)
{
if (!quiet) printf("Reading input as ");
}
else
{
int video_type;
err = decide_ES_file_video_type(es->input,FALSE,verbose,&video_type);
if (err)
{
fprintf(stderr,"### es2ts: Error deciding on stream type\n");
close_elementary_stream(&es);
return 1;
}
if (!quiet) printf("Input appears to be ");
}
switch (video_type)
{
case VIDEO_H262:
stream_type = MPEG2_VIDEO_STREAM_TYPE;
if (!quiet) printf("MPEG-2 (H.262)\n");
break;
case VIDEO_H264:
stream_type = AVC_VIDEO_STREAM_TYPE;
if (!quiet) printf("MPEG-4/AVC (H.264)\n");
break;
case VIDEO_AVS:
stream_type = AVS_VIDEO_STREAM_TYPE;
if (!quiet) printf("AVS\n");
break;
case VIDEO_UNKNOWN:
if (!quiet) printf("Unknown\n");
fprintf(stderr,"### es2ts: Input video type is not recognised\n");
close_elementary_stream(&es);
return 1;
}
if (use_stdout)
err = tswrite_open(TS_W_STDOUT,NULL,NULL,0,quiet,&output);
else if (use_tcpip)
err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&output);
else
err = tswrite_open(TS_W_FILE,output_name,NULL,0,quiet,&output);
if (err)
{
close_elementary_stream(&es);
fprintf(stderr,"### es2ts: Unable to open %s\n",output_name);
return 1;
}
if (max && !quiet)
printf("Stopping after %d ES data units\n",max);
err = transfer_data(es,output,pmt_pid,video_pid,stream_type,
max,verbose,quiet);
if (err)
fprintf(stderr,"### es2ts: Error transferring data\n");
close_elementary_stream(&es); // Closes the input file for us
err2 = tswrite_close(output,quiet);
if (err2)
{
fprintf(stderr,"### es2ts: Error closing output %s: %s\n",output_name,
strerror(errno));
return 1;
}
if (err)
return 1;
else
return 0;
}

144
es_defns.h 100644
Wyświetl plik

@ -0,0 +1,144 @@
/*
* Datastructures for handling H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _es_defns
#define _es_defns
#include <stdio.h>
#include "compat.h"
#include "pes_defns.h"
// ------------------------------------------------------------
// A "file" offset in an ES stream, suitable for seeking to
// For an ES based on a "bare" file, the `infile` value is all that is needed
// For an ES based on a PES, the file offset of the PES packet and the byte
// offset within that packet's ES data are needed.
struct _ES_offset
{
offset_t infile; // as used by lseek
int32 inpacket;
};
typedef struct _ES_offset ES_offset;
typedef struct _ES_offset *ES_offset_p;
#define SIZEOF_ES_OFFSET sizeof(struct _ES_offset)
// The number of bytes to "read ahead" when reading directly from an
// elementary stream
#define ES_READ_AHEAD_SIZE 1000
// ------------------------------------------------------------
// A datastructure to represent our input elementary stream (ES)
// (*output* elementary streams shouldn't need any particular housekeeping)
struct elementary_stream
{
int reading_ES; // TRUE if we're reading ES data direct, FALSE if PES
// If we're reading from an elementary data stream directly, then
// we use the input directly
int input;
// And maintain a buffer of "read ahead" bytes
byte read_ahead[ES_READ_AHEAD_SIZE];
offset_t read_ahead_posn; // location of this data in the file
int32 read_ahead_len; // actual number of bytes in the buffer
// And the next byte to be read is specified by its offset in said
// data stream. For "bare" ES data, the `infile` value is used to
// remember the next bytes actual position in the file, and for PES
// based ES data, the `inpacket` value is used to remember the next
// bytes offset in the current PES packet. In both cases, the "unused"
// quantity in the ES_offset is undefined.
// (this is, in fact, more used by tsserve than by anything else)
ES_offset posn_of_next_byte;
// If we're reading from PES packets (from either a PS or TS file),
// then we need to remember our PES reader
PES_reader_p reader;
byte *data; // Where we're reading our bytes from
byte *data_end; // How to tell we've read them all
byte *data_ptr; // And which byte we're interested in
offset_t last_packet_posn; // Where the last PES packet was in the file
int32 last_packet_es_data_len; // And its number of ES bytes
// Regardless, our triple byte memory is the same
byte cur_byte; // The current (last read) byte
byte prev1_byte; // The previous byte
byte prev2_byte; // The byte before *that*
};
typedef struct elementary_stream *ES_p;
#define SIZEOF_ES sizeof(struct elementary_stream)
// ------------------------------------------------------------
// And a representation of a single unit from the elementary stream
// (whether an MPEG-2 (H.262) item, or an MPEG-4/AVC (H.264) NAL unit)
// - basically, the thing that starts with a 00 00 01 prefix, and continues
// to end of file or before the next 00 00 01 prefix (so note that it
// contains any "trailing" 00 bytes).
//
// The normal way to acquire such a datastructure is via `build_ES_unit()`,
// or `find_and_build_next_ES_unit()`. If instead you want to use the
// address of a `struct ES_unit`, it is imperative that it be set up
// correctly with `setup_ES_unit()` before it is passed to any of the
// functions that use it, otherwise the contents will not be valid (and,
// particularly, the "data" pointer will reference random memory).
struct ES_unit
{
ES_offset start_posn; // The start of the current data unit
byte *data; // Its data, including the leading 00 00 01
u_int32 data_len; // Its length
u_int32 data_size; // The total buffer size
byte start_code; // The byte after the 00 00 01 prefix
// Something of a hack - if we were reading PES, did any of the PES packets
// we read to make this ES unit contain a PTS?
byte PES_had_PTS;
};
typedef struct ES_unit *ES_unit_p;
#define SIZEOF_ES_UNIT sizeof(struct ES_unit)
// Start and increment sizes for the es_unit/data array.
#define ES_UNIT_DATA_START_SIZE 1000 // was 500
#define ES_UNIT_DATA_INCREMENT 500 // was 100
// ------------------------------------------------------------
// An expandable list of ES units
struct ES_unit_list
{
struct ES_unit *array; // The current array of ES units
int length; // How many there are
int size; // How big the array is
};
typedef struct ES_unit_list *ES_unit_list_p;
#define SIZEOF_ES_UNIT_LIST sizeof(struct ES_unit_list)
#define ES_UNIT_LIST_START_SIZE 20
#define ES_UNIT_LIST_INCREMENT 20
#endif // _es_defns

415
es_fns.h 100644
Wyświetl plik

@ -0,0 +1,415 @@
/*
* Prototypes for handling H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _es_fns
#define _es_fns
#include "es_defns.h"
// ============================================================
// Elementary stream functions - basic
// ============================================================
/*
* Open an ES file and build an elementary stream datastructure to read
* it with.
*
* - `filename` is the ES files name
*
* Opens the file for read, builds the datastructure, and reads the first 3
* bytes of the input file (this is done to prime the triple-byte search
* mechanism).
*
* Use `close_elementary_stream` to close the stream and the file.
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int open_elementary_stream(char *filename,
ES_p *es);
/*
* Build an elementary stream datastructure attached to an input file.
* This is intended for reading ES data files.
*
* - `input` is the file stream to read from.
*
* Builds the datastructure, and reads the first 3 bytes of the input
* file (this is done to prime the triple-byte search mechanism).
*
* Use `free_elementary_stream` to release the ES context without closing
* the associated file.
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int build_elementary_stream_file(int input,
ES_p *es);
/*
* Build an elementary stream datastructure for use with a PES reader.
* Reads the first (or next) three bytes of the ES.
*
* This reads data from the PES video data, ignoring any audio data.
*
* - `reader` is the PES reader we want to use to read our TS or PS data.
*
* The caller must explicitly close the PES reader as well as closing the
* elementary stream (closing the ES does not affect the PES reader).
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int build_elementary_stream_PES(PES_reader_p reader,
ES_p *es);
/*
* Tidy up the elementary stream datastructure after we've finished with it.
*
* Specifically:
*
* - free the datastructure
* - set `es` to NULL
*
* No return status is given, since there's not much one can do if anything
* *did* go wrong, and if something went wrong and the program is continuing,
* it's bound to show up pretty soon.
*/
extern void free_elementary_stream(ES_p *es);
/*
* Tidy up the elementary stream datastructure after we've finished with it.
*
* Specifically:
*
* - close the input file (if its stream is set, and if it's not STDIN)
* - call `free_elementary_stream()`
*
* No return status is given, since there's not much one can do if anything
* *did* go wrong, and if something went wrong and the program is continuing,
* it's bound to show up pretty soon.
*/
extern void close_elementary_stream(ES_p *es);
/*
* Ask an ES context if changed input is available.
*
* This is a convenience wrapper to save querying the ES context to see
* if it is (a) reading from PES, (b) automatically writing the PES packets
* out via a TS writer, and (c) if said TS writer has a changed command.
*
* Calls `tswrite_command_changed()` on the TS writer associated with this ES.
*
* Returns TRUE if there is a changed command.
*/
extern int es_command_changed(ES_p es);
// ============================================================
// Elementary stream functions - item/unit reading
// ============================================================
/*
* Prepare the contents of a (new) ES unit datastructure.
*
* Allocates a new data array, and unsets the counts.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int setup_ES_unit(ES_unit_p unit);
/*
* Tidy up an ES unit datastructure after we've finished with it.
*
* (Frees the internal data array, and unsets the counts)
*/
extern void clear_ES_unit(ES_unit_p unit);
/*
* Build a new ES unit datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_ES_unit(ES_unit_p *unit);
/*
* Tidy up and free an ES unit datastructure after we've finished with it.
*
* Empties the ES unit datastructure, frees it, and sets `unit` to NULL.
*
* If `unit` is already NULL, does nothing.
*/
extern void free_ES_unit(ES_unit_p *unit);
/*
* Print out some information this ES unit, on the given stream
*/
extern void report_ES_unit(FILE *stream,
ES_unit_p unit);
/*
* Retrieve ES data from the end of a PES packet. It is assumed (i.e, things
* will go wrong if it is not true) that at least one ES unit has been read
* from the PES data stream via the ES reader.
*
* - `es` is our ES reader. It must be reading ES from PES packets.
* - `data` is the ES data remaining (to be read) in the current PES packet.
* It is up to the caller to free this data.
* - `data_len` is the length of said data. If this is 0, then `data`
* will be NULL.
*
* Returns 0 if all goes well, 1 if an error occurs.
*/
extern int get_end_of_underlying_PES_packet(ES_p es,
byte **data,
int *data_len);
/*
* Find and read in the next ES unit.
*
* In general, unless there are compelling reasons, use
* `find_and_build_next_ES_unit()` instead.
*
* - `es` is the elementary stream we're reading from.
* - `unit` is the datastructure into which to read the ES unit
* - any previous content will be lost.
*
* Returns 0 if it succeeds, EOF if the end-of-file is read (i.e., there
* is no next ES unit), otherwise 1 if some error occurs.
*/
extern int find_next_ES_unit(ES_p es,
ES_unit_p unit);
/*
* Find and read the next ES unit into a new datastructure.
*
* - `es` is the elementary stream we're reading from.
* - `count` is an integer to use as an id for this ES unit - typically
* its index in the input stream
* - `unit` is the datastructure containing the ES unit found, or NULL
* if there was none.
*
* Returns 0 if it succeeds, EOF if the end-of-file is read (i.e., there
* is no next ES unit), otherwise 1 if some error occurs.
*/
extern int find_and_build_next_ES_unit(ES_p es,
ES_unit_p *unit);
/*
* Write (copy) the current ES unit to the output stream.
*
* Note that it writes out all of the data for this ES unit,
* including its 00 00 01 start code prefix.
*
* - `output` is the output stream (file descriptor) to write to
* - `unit` is the ES unit to write
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int write_ES_unit(FILE *output,
ES_unit_p unit);
// ------------------------------------------------------------
// Arbitrary reading from ES data
// ------------------------------------------------------------
/*
* "Seek" to the given position in the ES data, which is assumed to
* be an offset ready to read a 00 00 01 sequence.
*
* If the ES reader is using PES to read its data, then both fields
* of `where` are significant, but if the underlying file *is* just a file,
* only `where.infile` is used.
*
* Returns 0 if all went well, 1 is something went wrong
*/
extern int seek_ES(ES_p es,
ES_offset where);
/*
* Read in some ES data from disk.
*
* Suitable for use when reading in a set of ES units whose bounds
* (start offset and total number of bytes) have been remembered.
*
* "Seeks" to the given position in the ES data, which is assumed to
* be an offset ready to read a 00 00 01 sequence, and reads data thereafter.
*
* After this function, the triple byte context is set to FF FF FF, and the
* position of said bytes are undefined, but the next position to read a byte
* from *is* defined.
*
* The intent is to allow the caller to have a data array (`data`) that
* always contains the last data read, and is of the required size, and
* need only be freed when no more data is needed.
*
* - `es` is where to read our data from
* - `start_posn` is the file offset to start reading at
* - `num_bytes` is how many bytes we want to read
* - `data_len` may be NULL or a pointer to a value.
* If it is NULL, then the data array will be reallocated to size
* `num_bytes` regardless. If it is non-NULL, it should be passed *in*
* as the size that `data` *was*, and will be returned as the size
* that `data` is when the function returns.
* - `data` is the data array to read into. If this is NULL, or if `num_bytes`
* is NULL, or if `num_bytes` is greater than `data_len`, then it will be
* reallocated to size `num_bytes`.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int read_ES_data(ES_p es,
ES_offset start_posn,
u_int32 num_bytes,
u_int32 *data_len,
byte **data);
// ============================================================
// Lists of ES units
// ============================================================
/*
* Build a new list-of-ES-units datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_ES_unit_list(ES_unit_list_p *list);
/*
* Add a copy of an ES unit to the end of the ES unit list
*
* Note that since this takes a copy of the ES unit's data, it is safe
* to free the original ES unit.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int append_to_ES_unit_list(ES_unit_list_p list,
ES_unit_p unit);
/*
* Reset (empty) an ES unit list.
*/
extern void reset_ES_unit_list(ES_unit_list_p list);
/*
* Tidy up and free an ES unit list datastructure after we've finished with it.
*
* Clears the datastructure, frees it and returns `list` as NULL.
*
* Does nothing if `list` is already NULL.
*/
extern void free_ES_unit_list(ES_unit_list_p *list);
/*
* Report on an ES unit list's contents.
*
* - `stream` is where to write the information
* - `name` is the name of the list (used in the header)
* - `list` is the list to report on
*/
extern void report_ES_unit_list(FILE *stream,
char *name,
ES_unit_list_p list);
/*
* Retrieve the bounds of this ES unit list in the file it was read from.
*
* - `list` is the ES unit list we're interested in
* - `start` is its start position (i.e., the location at which to start
* reading to retrieve all of the data for the list)
* - `length` is the total length of the ES units within this list
*
* Returns 0 if all goes well, 1 if the ES unit list has no content.
*/
extern int get_ES_unit_list_bounds(ES_unit_list_p list,
ES_offset *start,
u_int32 *length);
/*
* Compare two ES unit lists. The comparison does not include the start
* position of the unit data, but just the actual data - i.e., two unit lists
* read from different locations in the input stream may be considered the
* same if their data content is identical.
*
* - `list1` and `list2` are the two ES unit lists to compare.
*
* Returns TRUE if the lists contain identical content, FALSE otherwise.
*/
extern int same_ES_unit_list(ES_unit_list_p list1,
ES_unit_list_p list2);
/*
* Compare two ES offsets
*
* Returns -1 if offset1 < offset2, 0 if they are the same, and 1 if
* offset1 > offset2.
*/
extern int compare_ES_offsets(ES_offset offset1,
ES_offset offset2);
// ============================================================
// Simple file type guessing
// ============================================================
/*
* Look at the start of an elementary stream to try to determine its
* video type.
*
* "Eats" the ES units that it looks at, and doesn't rewind the stream
* afterwards.
*
* - `es` is the ES file
* - if `print_dots` is true, print a dot for each ES unit that is inspected
* - if `show_reasoning` is true, then output messages explaining how the
* decision is being made
* - `video_type` is the final decision -- one of VIDEO_H264, VIDEO_H262,
* VIDEO_AVS, or VIDEO_UNKNOWN.
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int decide_ES_video_type(ES_p es,
int print_dots,
int show_reasoning,
int *video_type);
/*
* Look at the start of an elementary stream to try to determine it's
* video type.
*
* Note that it is easier to prove something is H.262 (or AVS) than to prove
* that it is H.264, and that the result of this routine is a best-guess, not a
* guarantee.
*
* Rewinds back to the original position in the file after it has finished.
*
* - `input` is the file to look at
* - if `print_dots` is true, print a dot for each ES unit that is inspected
* - if `show_reasoning` is true, then output messages explaining how the
* decision is being made
* - `video_type` is the final decision -- one of VIDEO_H264, VIDEO_H262,
* VIDEO_AVS, or VIDEO_UNKNOWN.
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int decide_ES_file_video_type(int input,
int print_dots,
int show_reasoning,
int *video_type);
#endif // _es_fns

737
esdots.c 100644
Wyświetl plik

@ -0,0 +1,737 @@
/*
* Report on the contents of an H.264 (MPEG-4/AVC) or H.262 (MPEG-2)
* elementary stream, as a sequence of single characters, representing
* appropriate entities.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "es_fns.h"
#include "pes_fns.h"
#include "accessunit_fns.h"
#include "h262_fns.h"
#include "avs_fns.h"
#include "misc_fns.h"
#include "version.h"
/*
* Print out a single character representative of our item.
*/
static void h262_item_dot(h262_item_p item)
{
char *str = NULL;
static int frames = 0;
if (item->unit.start_code == 0x00)
{
if (frames % (25*60) == 0)
printf("\n%d minute%s\n",frames/(25*60),(frames/(25*60)==1?"":"s"));
frames ++;
}
switch (item->unit.start_code)
{
case 0x00:
str = (item->picture_coding_type==1?"i":
item->picture_coding_type==2?"p":
item->picture_coding_type==3?"b":
item->picture_coding_type==4?"d":"x");
break;
case 0xB0: str = "R"; break; // Reserved
case 0xB1: str = "R"; break; // Reserved
case 0xB2: str = "U"; break; // User data
case 0xB3: str = "["; break; // SEQUENCE HEADER
case 0xB4: str = "X"; break; // Sequence error
case 0xB5: str = "E"; break; // Extension start
case 0xB6: str = "R"; break; // Reserved
case 0xB7: str = "]"; break; // SEQUENCE END
case 0xB8: str = ">"; break; // Group start
default:
if (str == NULL)
{
if (item->unit.start_code >= 0x01 && item->unit.start_code <= 0xAF)
return; //str = "."; // Don't report slice data explicitly
else
str = "?";
}
break;
}
printf(str);
fflush(stdout);
}
/*
* Simply report on the content of an MPEG2 file as single characters
*
* - `es` is the input elementary stream
* - if `max` is non-zero, then reporting will stop after `max` MPEG items
* - if `verbose` is true, then extra information will be output
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int report_h262_file_as_dots(ES_p es,
int max,
int verbose)
{
int err;
int count = 0;
if (verbose)
printf("\n"
"Each character represents a single H.262 item\n"
"Pictures are represented according to their picture coding\n"
"type, and the slices within a picture are not shown.\n"
" i means an I picture\n"
" p means a P picture\n"
" b means a B picture\n"
" d means a D picture (these should not occur in MPEG-2)\n"
" x means some other picture (such should not occur)\n"
"Other items are represented as follows:\n"
" [ means a Sequence header\n"
" > means a Group Start header\n"
" E means an Extension start header\n"
" U means a User data header\n"
" X means a Sequence Error\n"
" ] means a Sequence End\n"
" R means a Reserved item\n"
" ? means something else. This may indicate that the stream\n"
" is not an ES representing H.262 (it might, for instance\n"
" be PES)\n"
"\n");
for (;;)
{
h262_item_p item;
err = find_next_h262_item(es,&item);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error copying NAL units\n");
return err;
}
count++;
h262_item_dot(item);
free_h262_item(&item);
if (max > 0 && count >= max)
break;
}
printf("\nFound %d MPEG2 item%s\n",count,(count==1?"":"s"));
return 0;
}
/*
* Simply report on the content of an AVS file as single characters
*
* - `es` is the input elementary stream
* - if `max` is non-zero, then reporting will stop after `max` MPEG items
* - if `verbose` is true, then extra information will be output
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int report_avs_file_as_dots(ES_p es,
int max,
int verbose)
{
int err = 0;
int count = 0;
int frames = 0;
double frame_rate = 25.0; // as a guess
avs_context_p context;
if (verbose)
printf("\n"
"Each character represents a single AVS item\n"
"Frames are represented according to their picture coding\n"
"type, and the slices within a frame are not shown.\n"
" i means an I frame\n"
" p means a P frame\n"
" b means a B frame\n"
" _ means a (stray) slice, normally only at the start of a stream\n"
" ! means something else (this should not be possible)\n"
"Other items are represented as follows:\n"
" [ means a Sequence header\n"
" E means an Extension start header\n"
" U means a User data header\n"
" ] means a Sequence End\n"
" V means a Video edit item\n"
" ? means something else. This may indicate that the stream\n"
" is not an ES representing AVS (it might, for instance\n"
" be PES)\n"
"\n");
err = build_avs_context(es,&context);
if (err) return err;
for (;;)
{
avs_frame_p avs_frame;
err = get_next_avs_frame(context,TRUE,FALSE,&avs_frame);
if (err == EOF)
break;
else if (err)
{
free_avs_context(&context);
return 1;
}
if (avs_frame->is_frame)
{
frames ++;
if (avs_frame->picture_coding_type == AVS_I_PICTURE_CODING)
printf("i");
else if (avs_frame->picture_coding_type == AVS_P_PICTURE_CODING)
printf("p");
else if (avs_frame->picture_coding_type == AVS_B_PICTURE_CODING)
printf("b");
else
printf("!");
// Give a *rough* guide as to timing -- assume a constant frame rate
if (frames % (int)(frame_rate*60) == 0)
printf("\n%d minute%s\n",frames/(25*60),(frames/(25*60)==1?"":"s"));
}
else if (avs_frame->start_code < 0xB0)
printf("_"); // slice -- shouldn't happen
else
{
switch (avs_frame->start_code)
{
case 0xB0: // sequence header
frame_rate = avs_frame_rate(avs_frame->frame_rate_code);
printf("[");
break;
case 0xB1: printf("]"); break;
case 0xB2: printf("U"); break;
case 0xB5: printf("E"); break;
case 0xB7: printf("V"); break;
default: /*printf("?");*/ printf("<%x>",avs_frame->start_code); break;
}
}
fflush(stdout);
count ++;
free_avs_frame(&avs_frame);
if (max > 0 && frames >= max)
{
printf("\nStopping because %d frames have been read\n",frames);
break;
}
}
printf("\nFound %d frame%s in %d AVS item%s\n",
frames,(frames==1?"":"s"),count,(count==1?"":"s"));
free_avs_context(&context);
return 0;
}
/*
* Report on data by access unit, as single characters
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int dots_by_access_unit(ES_p es,
int max,
int verbose,
int hash_eos)
{
int err = 0;
int access_unit_count = 0;
access_unit_context_p context;
if (verbose)
printf("\n"
"Each character represents a single access unit\n"
" . means a non-reference frame\n"
" _ means an access unit without a primary picture\n"
" (presumably the last 'access unit' of the bitstream)\n"
"\n"
"For reference frames, an uppercase letter means an IDR frame\n"
"and a lowercase letter means a non-IDR frame:\n"
" I means a frame with only I slices\n"
" P means a frame with only P slices\n"
" B means a frame with only B slices\n"
" X means any other type of frame\n"
"\n"
"If -hasheos was specified:\n"
" # means an EOS (end-of-stream) NAL unit\n"
"\n");
err = build_access_unit_context(es,&context);
if (err) return err;
for (;;)
{
access_unit_p access_unit;
err = get_next_h264_frame(context,TRUE,FALSE,&access_unit);
if (err == EOF)
break;
else if (err)
{
free_access_unit_context(&context);
return 1;
}
if (access_unit->primary_start == NULL)
printf("_");
else if (access_unit->primary_start->nal_ref_idc == 0)
{
if (all_slices_I(access_unit))
printf("i");
else if (all_slices_P(access_unit))
printf("p");
else if (all_slices_B(access_unit))
printf("b");
else
printf("x");
}
else if (access_unit->primary_start->nal_unit_type == NAL_IDR)
{
if (all_slices_I(access_unit))
printf("R");
else
printf("?");
}
else if (access_unit->primary_start->nal_unit_type == NAL_NON_IDR)
{
if (all_slices_I(access_unit))
printf("I");
else if (all_slices_P(access_unit))
printf("P");
else if (all_slices_B(access_unit))
printf("B");
else
printf("X");
}
else
printf("?");
fflush(stdout);
access_unit_count ++;
free_access_unit(&access_unit);
// Did the logical stream end after the last access unit?
if (context->end_of_stream)
{
if (hash_eos)
{
printf("#");
// This should be enough to allow us to keep on after the EOS
context->end_of_stream = FALSE;
context->no_more_data = FALSE;
}
else
{
printf("\nStopping because found end-of-stream NAL unit\n");
break;
}
}
if (max > 0 && context->nac->count >= max)
{
printf("\nStopping because %d NAL units have been read\n",
context->nac->count);
break;
}
}
printf("\nFound %d NAL unit%s in %d access unit%s\n",
context->nac->count,(context->nac->count==1?"":"s"),
access_unit_count,(access_unit_count==1?"":"s"));
free_access_unit_context(&context);
return 0;
}
/*
* Simply report on the content of an ES file as single characters for each ES
* unit
*
* - `es` is the input elementary stream
* - `what_data` should be one of VIDEO_H262, VIDEO_H264 or VIDEO_AVS.
* - if `max` is non-zero, then reporting will stop after `max` MPEG items
* - if `verbose` is true, then extra information will be output
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
static int report_file_as_ES_dots(ES_p es,
int what_data,
int max,
int verbose)
{
int err = 0;
int count = 0;
struct ES_unit unit;
(void) setup_ES_unit(&unit);
if (verbose)
{
printf("\n"
"Each character represents a single ES unit\n");
switch (what_data)
{
case VIDEO_H262:
printf("Pictures are represented according to their picture coding\n"
"type, and the slices within a picture are not shown.\n"
" i means an I picture\n"
" p means a P picture\n"
" b means a B picture\n"
" d means a D picture (these should not occur in MPEG-2)\n"
" ! means some other picture (such should not occur)\n"
"Other items are represented as follows:\n"
" [ means a Sequence header\n"
" > means a Group Start header\n"
" E means an Extension start header\n"
" U means a User data header\n"
" X means a Sequence Error\n"
" ] means a Sequence End\n"
" R means a Reserved item\n");
break;
case VIDEO_H264:
printf("### esdots: -es is not yet supported for H.264\n");
return 1;
//break;
case VIDEO_AVS:
printf("Frames are represented according to their picture coding\n"
"type, and the slices within a frame are not shown.\n"
" i means an I frame\n"
" p means a P frame\n"
" b means a B frame\n"
" _ means a slice\n"
" ! means something else (this should not be possible)\n"
"Other items are represented as follows:\n"
" [ means a Sequence header\n"
" E means an Extension start header\n"
" U means a User data header\n"
" ] means a Sequence End\n"
" V means a Video edit item\n");
default:
printf("### esdots: Unexpected type of data\n");
return 1;
}
printf(" ? means something else. This may indicate that the stream\n"
" is not an ES representing AVS (it might, for instance\n"
" be PES)\n"
"\n");
}
for (;;)
{
err = find_next_ES_unit(es,&unit);
if (err == EOF)
break;
else if (err)
return 1;
switch (what_data)
{
case VIDEO_H262:
switch (unit.start_code)
{
int picture_coding_type;
case 0x00:
picture_coding_type = (unit.data[5] & 0x38) >> 3;
switch (picture_coding_type)
{
case 1: printf("i"); break;
case 2: printf("p"); break;
case 3: printf("b"); break;
case 4: printf("d"); break;
default: printf("!"); break;
}
break;
case 0xB0: printf("R"); break; // Reserved
case 0xB1: printf("R"); break; // Reserved
case 0xB2: printf("U"); break; // User data
case 0xB3: printf("["); break; // SEQUENCE HEADER
case 0xB4: printf("X"); break; // Sequence error
case 0xB5: printf("E"); break; // Extension start
case 0xB6: printf("R"); break; // Reserved
case 0xB7: printf("]"); break; // SEQUENCE END
case 0xB8: printf(">"); break; // Group start
default:
if (unit.start_code >= 0x01 && unit.start_code <= 0xAF)
printf("_");
else
printf("?");
break;
}
break;
case VIDEO_H264:
break;
case VIDEO_AVS:
switch (unit.start_code)
{
case 0xB3:
printf("i"); break;
case 0xB6:
switch (avs_picture_coding_type(&unit))
{
case AVS_P_PICTURE_CODING: printf("p"); break;
case AVS_B_PICTURE_CODING: printf("b"); break;
default: printf("!"); break;
}
break;
case 0xB0: printf("["); break;
case 0xB1: printf("]"); break;
case 0xB2: printf("U"); break;
case 0xB5: printf("E"); break;
case 0xB7: printf("V"); break;
default:
if (unit.start_code < 0xB0)
printf("_");
else
printf("?");
break;
}
default: /* shouldn't happen */ break;
}
fflush(stdout);
count ++;
if (max > 0 && count >= max)
{
printf("\nStopping because %d ES units have been read\n",count);
break;
}
}
clear_ES_unit(&unit);
printf("\nFound %d ES units%s\n",count,(count==1?"":"s"));
return 0;
}
static void print_usage()
{
printf(
"Usage: esdots [switches] [<infile>]\n"
"\n"
);
REPORT_VERSION("esdots");
printf(
"\n"
" Present the content of an H.264 (MPEG-4/AVC), H.262 (MPEG-2) or AVS\n"
" elementary stream as a sequence of characters, representing access\n"
" units/MPEG-2 items/AVS items.\n"
"\n"
" (Note that for H.264 it is access units and not frames that are\n"
" represented, and for H.262 it is items and not pictures.)\n"
"\n"
"Files:\n"
" <infile> is the Elementary Stream file (but see -stdin below)\n"
"\n"
"Switches:\n"
" -verbose, -v Preface the output with an explanation of the\n"
" characters being used.\n"
" -stdin Take input from <stdin>, instead of a named file\n"
" -max <n>, -m <n> Maximum number of entities to read\n"
" -pes, -ts The input file is TS or PS, to be read via the\n"
" PES->ES reading mechanisms\n"
" -hasheos Print a # on finding an EOS (end-of-stream) NAL unit\n"
" rather than stopping (only applies to H.264)\n"
" -es Report ES units, rather than any 'higher' unit\n"
" (not necessarily suppported for all file types)\n"
"\n"
"Stream type:\n"
" If input is from a file, then the program will look at the start of\n"
" the file to determine if the stream is H.264 or H.262 data. This\n"
" process may occasionally come to the wrong conclusion, in which case\n"
" the user can override the choice using the following switches.\n"
"\n"
" For AVS data, the program will never guess correctly, so the user must\n"
" specify the file type, using -avs.\n"
"\n"
" If input is from standard input (via -stdin), then it is not possible\n"
" for the program to make its own decision on the input stream type.\n"
" Instead, it defaults to H.262, and relies on the user indicating if\n"
" this is wrong.\n"
"\n"
" -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
" -h262 Force the program to treat the input as MPEG-2.\n"
" -avs Force the program to treat the input as AVS.\n"
);
}
int main(int argc, char **argv)
{
char *input_name = NULL;
int had_input_name = FALSE;
int use_stdin = FALSE;
int err = 0;
ES_p es = NULL;
int max = 0;
int verbose = FALSE;
int ii = 1;
int use_pes = FALSE;
int hash_eos = FALSE;
int want_data = VIDEO_H262;
int is_data = want_data;
int force_stream_type = FALSE;
int want_ES = FALSE;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) ||
!strcmp("-h",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-stdin",argv[ii]))
{
had_input_name = TRUE; // more or less
use_stdin = TRUE;
}
else if (!strcmp("-avc",argv[ii]) || !strcmp("-h264",argv[ii]))
{
force_stream_type = TRUE;
want_data = VIDEO_H264;
}
else if (!strcmp("-h262",argv[ii]))
{
force_stream_type = TRUE;
want_data = VIDEO_H262;
}
else if (!strcmp("-avs",argv[ii]))
{
force_stream_type = TRUE;
want_data = VIDEO_AVS;
}
else if (!strcmp("-es",argv[ii]))
{
want_ES = TRUE;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("es2dots",ii);
err = int_value("esfilter",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-hasheos",argv[ii]))
hash_eos = TRUE;
else if (!strcmp("-pes",argv[ii]) || !strcmp("-ts",argv[ii]))
use_pes = TRUE;
else
{
fprintf(stderr,"### esdots: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name)
{
fprintf(stderr,"### esdots: Unexpected '%s'\n",argv[ii]);
return 1;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### esdots: No input file specified\n");
return 1;
}
err = open_input_as_ES((use_stdin?NULL:input_name),use_pes,FALSE,
force_stream_type,want_data,&is_data,&es);
if (err)
{
fprintf(stderr,"### esdots: Error opening input file\n");
return 1;
}
if (want_ES)
err = report_file_as_ES_dots(es,is_data,max,verbose);
else if (is_data == VIDEO_H262)
err = report_h262_file_as_dots(es,max,verbose);
else if (is_data == VIDEO_H264)
err = dots_by_access_unit(es,max,verbose,hash_eos);
else if (is_data == VIDEO_AVS)
err = report_avs_file_as_dots(es,max,verbose);
else
{
fprintf(stderr,"### esdots: Unexpected type of video data\n");
}
if (err)
{
fprintf(stderr,"### esdots: Error producing 'dots'\n");
(void) close_input_as_ES(input_name,&es);
return 1;
}
err = close_input_as_ES(input_name,&es);
if (err)
{
fprintf(stderr,"### esdots: Error closing input file\n");
return 1;
}
return 0;
}

1145
esfilter.c 100644

Plik diff jest za duży Load Diff

851
esmerge.c 100644
Wyświetl plik

@ -0,0 +1,851 @@
/*
* Merge a video ES and an audio ES to produce TS.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "compat.h"
#include "es_fns.h"
#include "accessunit_fns.h"
#include "avs_fns.h"
#include "audio_fns.h"
#include "ts_fns.h"
#include "tswrite_fns.h"
#include "misc_fns.h"
#include "version.h"
// Default audio rates, in Hertz
#define CD_RATE 44100
#define DAT_RATE 48000
// Video frame rate (frames per second)
#define DEFAULT_VIDEO_FRAME_RATE 25
// Number of audio samples per frame
// For ADTS this will either be 1024 or 960. It's believed that it will
// actually, in practice, be 1024, and in fact the difference may not be
// significant enough to worry about for the moment.
#define ADTS_SAMPLES_PER_FRAME 1024
// For MPEG-1 audio layer 2, this is 1152
#define L2_SAMPLES_PER_FRAME 1152
// ------------------------------------------------------------
#define TEST_PTS_DTS 0
#if TEST_PTS_DTS
#include "pes_fns.h"
static int check(u_int64 value)
{
int err;
byte data[5];
u_int64 result;
encode_pts_dts(data,2,value);
err = decode_pts_dts(data,2,&result);
if (err) return 1;
if (value == result)
printf("Value " LLU_FORMAT " OK\n",value);
else
{
printf("Input " LLU_FORMAT ", output " LLU_FORMAT "\n",value,result);
return 1;
}
return 0;
}
static int test_pts()
{
if (check(0)) return 1;
if (check(1)) return 1;
if (check(2)) return 1;
if (check(3)) return 1;
if (check(4)) return 1;
if (check(5)) return 1;
if (check(6)) return 1;
if (check(7)) return 1;
if (check(8)) return 1;
if (check(100)) return 1;
if (check(10000)) return 1;
if (check(1000000)) return 1;
if (check(100000000)) return 1;
if (check(10000000000LL)) return 1;
if (check(1000000000000LL)) return 1;
return 0;
}
#endif // TEST_PTS_DTS
static int is_avs_I_frame(avs_frame_p frame)
{
return (frame->is_frame && frame->start_code == 0xB3);
}
static int is_I_or_IDR_frame(access_unit_p frame)
{
return (frame->primary_start != NULL &&
frame->primary_start->nal_ref_idc != 0 &&
(frame->primary_start->nal_unit_type == NAL_IDR ||
all_slices_I(frame)));
}
/*
* Merge the given elemetary streams to the given output.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
static int merge_with_avs(avs_context_p video_context,
int audio_file,
TS_writer_p output,
int audio_type,
int audio_samples_per_frame,
int audio_sample_rate,
double video_frame_rate,
int quiet,
int verbose,
int debugging)
{
int ii;
int err;
u_int32 prog_pids[2];
byte prog_type[2];
int video_frame_count = 0;
int audio_frame_count = 0;
u_int32 video_pts_increment = (u_int32)(90000.0 / video_frame_rate);
u_int32 audio_pts_increment = (90000 * audio_samples_per_frame) / audio_sample_rate;
u_int64 video_pts = 0;
u_int64 audio_pts = 0;
// The "actual" times are just for information, so we aren't too worried
// about accuracy - thus floating point should be OK.
double audio_time = 0.0;
double video_time = 0.0;
int got_video = TRUE;
int got_audio = TRUE;
if (verbose)
printf("Video PTS increment %u\n"
"Audio PTS increment %u\n",video_pts_increment,audio_pts_increment);
// Start off our output with some null packets - this is in case the
// reader needs some time to work out its byte alignment before it starts
// looking for 0x47 bytes
for (ii=0; ii<8; ii++)
{
err = write_TS_null_packet(output);
if (err) return 1;
}
// Then write some program data
// @@@ later on we might want to repeat this every so often
prog_pids[0] = DEFAULT_VIDEO_PID;
prog_pids[1] = DEFAULT_AUDIO_PID;
prog_type[0] = AVS_VIDEO_STREAM_TYPE;
switch (audio_type)
{
case AUDIO_ADTS:
prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
break;
case AUDIO_L2:
prog_type[1] = MPEG2_AUDIO_STREAM_TYPE;
break;
default: // what else can we do?
prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
break;
}
err = write_TS_program_data2(output,
1, // transport stream id
1, // program number
DEFAULT_PMT_PID,
DEFAULT_VIDEO_PID, // PCR pid
2,prog_pids,prog_type);
if (err)
{
fprintf(stderr,"### Error writing out TS program data\n");
return 1;
}
while (got_video || got_audio)
{
avs_frame_p avs_frame;
audio_frame_p aframe;
// Start with a video frame
if (got_video)
{
err = get_next_avs_frame(video_context,quiet,debugging,&avs_frame);
if (err == EOF)
{
if (verbose)
printf("EOF: no more video data\n");
got_video = FALSE;
}
else if (err)
return 1;
if (!avs_frame->is_frame)
{
// It's not actually a *picture*
// If we can, update the video frame rate to what we're told
if (avs_frame->is_sequence_header)
video_frame_rate = avs_frame_rate(avs_frame->frame_rate_code);
// And output the data right away
err = write_avs_frame_as_TS(output,avs_frame,DEFAULT_VIDEO_PID);
if (err)
{
free_avs_frame(&avs_frame);
fprintf(stderr,"### Error writing AVS frame (sequence header/end)\n");
return 1;
}
continue; // look for a "proper" frame
}
}
if (got_video)
{
video_time = video_frame_count / video_frame_rate;
video_pts += video_pts_increment;
video_frame_count ++;
if (verbose)
printf("\n%s video frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
(is_avs_I_frame(avs_frame)?"**":"++"),
video_frame_count,video_time,video_pts);
// PCR counts frames as seen in the stream, so is easy
// The presentation and decoding time for B frames (if we ever get any)
// could reasonably be the same as the PCR.
// The presentation and decoding time for I and IDR frames is unlikely to
// be the same as the PCR (since frames come out later...), but it may
// work to pretend the PTS is the PCR plus a delay time (for decoding)...
// We could output the timing information every video frame,
// but might as well only do it on index frames.
// (Actually, we *could* work out the proper PTS for I frames, but it's
// easier just to add a delay to allow for progress through the decoder)
if (is_avs_I_frame(avs_frame))
err = write_avs_frame_as_TS_with_pts_dts(avs_frame,
output,DEFAULT_VIDEO_PID,
TRUE,video_pts + 30000,
TRUE,video_pts);
else
err = write_avs_frame_as_TS_with_PCR(avs_frame,
output,DEFAULT_VIDEO_PID,
video_pts,0);
if (err)
{
free_avs_frame(&avs_frame);
fprintf(stderr,"### Error writing AVS frame\n");
return 1;
}
free_avs_frame(&avs_frame);
}
if (!got_audio)
continue;
// Then output enough audio frames to make up to a similar time
while (audio_pts < video_pts || !got_video)
{
err = read_next_audio_frame(audio_file,audio_type,&aframe);
if (err == EOF)
{
if (verbose)
printf("EOF: no more audio data\n");
got_audio = FALSE;
break;
}
else if (err)
return 1;
audio_time = audio_frame_count *
audio_samples_per_frame / (double)audio_sample_rate;
audio_pts += audio_pts_increment;
audio_frame_count ++;
if (verbose)
printf("** audio frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
audio_frame_count,audio_time,audio_pts);
err = write_ES_as_TS_PES_packet_with_pts_dts(output,aframe->data,
aframe->data_len,
DEFAULT_AUDIO_PID,
DEFAULT_AUDIO_STREAM_ID,
TRUE,audio_pts,
TRUE,audio_pts);
if (err)
{
free_audio_frame(&aframe);
fprintf(stderr,"### Error writing audio frame\n");
return 1;
}
free_audio_frame(&aframe);
}
}
if (!quiet)
{
u_int32 video_elapsed = (u_int32)((double)(100*video_frame_count)/video_frame_rate);
u_int32 audio_elapsed = 100*audio_frame_count*
audio_samples_per_frame/audio_sample_rate;
printf("Read %d video frame%s, %.2fs elapsed (%dm %.2fs)\n",
video_frame_count,(video_frame_count==1?"":"s"),
video_elapsed/100.0,video_elapsed/6000,(video_elapsed%6000)/100.0);
printf("Read %d audio frame%s, %.2fs elapsed (%dm %.2fs)\n",
audio_frame_count,(audio_frame_count==1?"":"s"),
audio_elapsed/100.0,audio_elapsed/6000,(audio_elapsed%6000)/100.0);
}
return 0;
}
/*
* Merge the given elemetary streams to the given output.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
static int merge_with_h264(access_unit_context_p video_context,
int audio_file,
TS_writer_p output,
int audio_type,
int audio_samples_per_frame,
int audio_sample_rate,
int video_frame_rate,
int quiet,
int verbose,
int debugging)
{
int ii;
int err;
u_int32 prog_pids[2];
byte prog_type[2];
int video_frame_count = 0;
int audio_frame_count = 0;
u_int32 video_pts_increment = 90000 / video_frame_rate;
u_int32 audio_pts_increment = (90000 * audio_samples_per_frame) / audio_sample_rate;
u_int64 video_pts = 0;
u_int64 audio_pts = 0;
// The "actual" times are just for information, so we aren't too worried
// about accuracy - thus floating point should be OK.
double audio_time = 0.0;
double video_time = 0.0;
int got_video = TRUE;
int got_audio = TRUE;
if (verbose)
printf("Video PTS increment %u\n"
"Audio PTS increment %u\n",video_pts_increment,audio_pts_increment);
// Start off our output with some null packets - this is in case the
// reader needs some time to work out its byte alignment before it starts
// looking for 0x47 bytes
for (ii=0; ii<8; ii++)
{
err = write_TS_null_packet(output);
if (err) return 1;
}
// Then write some program data
// @@@ later on we might want to repeat this every so often
prog_pids[0] = DEFAULT_VIDEO_PID;
prog_pids[1] = DEFAULT_AUDIO_PID;
prog_type[0] = AVC_VIDEO_STREAM_TYPE;
switch (audio_type)
{
case AUDIO_ADTS:
prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
break;
case AUDIO_L2:
prog_type[1] = MPEG2_AUDIO_STREAM_TYPE;
break;
default: // what else can we do?
prog_type[1] = ADTS_AUDIO_STREAM_TYPE;
break;
}
err = write_TS_program_data2(output,
1, // transport stream id
1, // program number
DEFAULT_PMT_PID,
DEFAULT_VIDEO_PID, // PCR pid
2,prog_pids,prog_type);
if (err)
{
fprintf(stderr,"### Error writing out TS program data\n");
return 1;
}
while (got_video || got_audio)
{
access_unit_p access_unit;
audio_frame_p aframe;
// Start with a video frame
if (got_video)
{
err = get_next_h264_frame(video_context,quiet,debugging,&access_unit);
if (err == EOF)
{
if (verbose)
printf("EOF: no more video data\n");
got_video = FALSE;
}
else if (err)
return 1;
}
if (got_video)
{
video_time = video_frame_count / (double) video_frame_rate;
video_pts += video_pts_increment;
video_frame_count ++;
if (verbose)
printf("\n%s video frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
(is_I_or_IDR_frame(access_unit)?"**":"++"),
video_frame_count,video_time,video_pts);
// PCR counts frames as seen in the stream, so is easy
// The presentation and decoding time for B frames (if we ever get any)
// could reasonably be the same as the PCR.
// The presentation and decoding time for I and IDR frames is unlikely to
// be the same as the PCR (since frames come out later...), but it may
// work to pretend the PTS is the PCR plus a delay time (for decoding)...
// We could output the timing information every video frame,
// but might as well only do it on index frames.
if (is_I_or_IDR_frame(access_unit))
err = write_access_unit_as_TS_with_pts_dts(access_unit,video_context,
output,DEFAULT_VIDEO_PID,
TRUE,video_pts+45000,
TRUE,video_pts);
else
err = write_access_unit_as_TS_with_PCR(access_unit,video_context,
output,DEFAULT_VIDEO_PID,
video_pts,0);
if (err)
{
free_access_unit(&access_unit);
fprintf(stderr,"### Error writing access unit (frame)\n");
return 1;
}
free_access_unit(&access_unit);
// Did the logical video stream end after the last access unit?
if (video_context->end_of_stream)
{
if (verbose)
printf("Found End-of-stream NAL unit\n");
got_video = FALSE;
}
}
if (!got_audio)
continue;
// Then output enough audio frames to make up to a similar time
while (audio_pts < video_pts || !got_video)
{
err = read_next_audio_frame(audio_file,audio_type,&aframe);
if (err == EOF)
{
if (verbose)
printf("EOF: no more audio data\n");
got_audio = FALSE;
break;
}
else if (err)
return 1;
audio_time = audio_frame_count *
audio_samples_per_frame / (double)audio_sample_rate;
audio_pts += audio_pts_increment;
audio_frame_count ++;
if (verbose)
printf("** audio frame %5d (@ %.2fs, " LLU_FORMAT ")\n",
audio_frame_count,audio_time,audio_pts);
err = write_ES_as_TS_PES_packet_with_pts_dts(output,aframe->data,
aframe->data_len,
DEFAULT_AUDIO_PID,
DEFAULT_AUDIO_STREAM_ID,
TRUE,audio_pts,
TRUE,audio_pts);
if (err)
{
free_audio_frame(&aframe);
fprintf(stderr,"### Error writing audio frame\n");
return 1;
}
free_audio_frame(&aframe);
}
}
if (!quiet)
{
u_int32 video_elapsed = 100*video_frame_count/video_frame_rate;
u_int32 audio_elapsed = 100*audio_frame_count*
audio_samples_per_frame/audio_sample_rate;
printf("Read %d video frame%s, %.2fs elapsed (%dm %.2fs)\n",
video_frame_count,(video_frame_count==1?"":"s"),
video_elapsed/100.0,video_elapsed/6000,(video_elapsed%6000)/100.0);
printf("Read %d audio frame%s, %.2fs elapsed (%dm %.2fs)\n",
audio_frame_count,(audio_frame_count==1?"":"s"),
audio_elapsed/100.0,audio_elapsed/6000,(audio_elapsed%6000)/100.0);
}
return 0;
}
static void print_usage()
{
printf(
"Usage:\n"
" esmerge <video-file> <audio-file> <output-file>\n"
"\n"
);
REPORT_VERSION("esmerge");
printf(
"\n"
" Merge the contents of two Elementary Stream (ES) files, one containing\n"
" video data, and the other audio, to produce an output file containing\n"
" Transport Stream (TS).\n"
"\n"
"Files:\n"
" <video-file> is the ES file containing video.\n"
" <audio-file> is the ES file containing audio.\n"
" <output-file> is the resultant TS file.\n"
"\n"
"Switches:\n"
" -quiet, -q Only output error messages.\n"
" -verbose, -v Output information about each audio/video frame.\n"
" -x Output diagnostic information.\n"
"\n"
" -h264 The video stream is H.264 (the default)\n"
" -avs The video stream is AVS\n"
"\n"
" -vidrate <hz> Video frame rate in Hz - defaults to 25Hz.\n"
"\n"
" -rate <hz> Audio sample rate in Hertz - defaults to 44100, i.e., 44.1KHz.\n"
" -cd Equivalent to -rate 44100 (CD rate), the default.\n"
" -dat Equivalent to -rate 48000 (DAT rate).\n"
"\n"
" -adts The audio stream is ADTS (the default)\n"
" -l2 The audio stream is MPEG layer 2 audio\n"
"\n"
"Limitations\n"
"===========\n"
"For the moment, the video input must be H.264 or AVS, and the audio input\n"
"ADTS or MPEG layer 2. Also, the audio is assumed to have a constant\n"
"number of samples per frame.\n"
);
}
int main(int argc, char **argv)
{
int had_video_name = FALSE;
int had_audio_name = FALSE;
int had_output_name = FALSE;
char *video_name = NULL;
char *audio_name = NULL;
char *output_name = NULL;
int err = 0;
ES_p video_es = NULL;
access_unit_context_p h264_video_context = NULL;
avs_context_p avs_video_context = NULL;
int audio_file = -1;
TS_writer_p output = NULL;
int quiet = FALSE;
int verbose = FALSE;
int debugging = FALSE;
int audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
int audio_sample_rate = CD_RATE;
int video_frame_rate = DEFAULT_VIDEO_FRAME_RATE;
int audio_type = AUDIO_ADTS;
int video_type = VIDEO_H264;
int ii = 1;
#if TEST_PTS_DTS
test_pts();
return 0;
#endif
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) ||
!strcmp("-h",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
quiet = TRUE;
}
else if (!strcmp("-x",argv[ii]))
{
debugging = TRUE;
quiet = FALSE;
}
else if (!strcmp("-rate",argv[ii]))
{
CHECKARG("esmerge",ii);
err = int_value("esmerge",argv[ii],argv[ii+1],TRUE,10,&audio_sample_rate);
if (err) return 1;
ii++;
}
else if (!strcmp("-cd",argv[ii]))
{
audio_sample_rate = CD_RATE;
}
else if (!strcmp("-dat",argv[ii]))
{
audio_sample_rate = DAT_RATE;
}
else if (!strcmp("-vidrate",argv[ii]))
{
CHECKARG("esmerge",ii);
err = int_value("esmerge",argv[ii],argv[ii+1],TRUE,10,&video_frame_rate);
if (err) return 1;
ii++;
}
else if (!strcmp("-adts",argv[ii]))
{
audio_type = AUDIO_ADTS;
}
else if (!strcmp("-l2",argv[ii]))
{
audio_type = AUDIO_L2;
}
else if (!strcmp("-h264",argv[ii]))
{
video_type = VIDEO_H264;
}
else if (!strcmp("-avs",argv[ii]))
{
video_type = VIDEO_AVS;
}
else
{
fprintf(stderr,"### esmerge: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (!had_video_name)
{
video_name = argv[ii];
had_video_name = TRUE;
}
else if (!had_audio_name)
{
audio_name = argv[ii];
had_audio_name = TRUE;
}
else if (!had_output_name)
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
fprintf(stderr,"### esmerge: Unexpected '%s'\n",argv[ii]);
return 1;
}
}
ii++;
}
if (!had_video_name)
{
fprintf(stderr,"### esmerge: No video input file specified\n");
return 1;
}
if (!had_audio_name)
{
fprintf(stderr,"### esmerge: No audio input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### esmerge: No output file specified\n");
return 1;
}
err = open_elementary_stream(video_name,&video_es);
if (err)
{
fprintf(stderr,"### esmerge: "
"Problem starting to read video as ES - abandoning reading\n");
return 1;
}
if (video_type == VIDEO_H264)
{
err = build_access_unit_context(video_es,&h264_video_context);
if (err)
{
fprintf(stderr,"### esmerge: "
"Problem starting to read video as H.264 - abandoning reading\n");
close_elementary_stream(&video_es);
return 1;
}
}
else if (video_type == VIDEO_AVS)
{
err = build_avs_context(video_es,&avs_video_context);
if (err)
{
fprintf(stderr,"### esmerge: "
"Problem starting to read video as H.264 - abandoning reading\n");
close_elementary_stream(&video_es);
return 1;
}
}
else
{
fprintf(stderr,"### esmerge: Unknown video type\n");
return 1;
}
audio_file = open_binary_file(audio_name,FALSE);
if (audio_file == -1)
{
fprintf(stderr,"### esmerge: "
"Problem opening audio file - abandoning reading\n");
close_elementary_stream(&video_es);
free_access_unit_context(&h264_video_context);
free_avs_context(&avs_video_context);
return 1;
}
err = tswrite_open(TS_W_FILE,output_name,NULL,0,quiet,&output);
if (err)
{
fprintf(stderr,"### esmerge: "
"Problem opening output file %s - abandoning reading\n",
output_name);
close_elementary_stream(&video_es);
close_file(audio_file);
free_access_unit_context(&h264_video_context);
free_avs_context(&avs_video_context);
return 1;
}
switch (audio_type)
{
case AUDIO_ADTS:
audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
break;
case AUDIO_L2:
audio_samples_per_frame = L2_SAMPLES_PER_FRAME;
break;
default: // hmm - or we could give up...
audio_samples_per_frame = ADTS_SAMPLES_PER_FRAME;
break;
}
if (!quiet)
{
printf("Reading video from %s\n",video_name);
printf("Reading audio from %s (as %s)\n",audio_name,AUDIO_STR(audio_type));
printf("Writing output to %s\n",output_name);
printf("Audio sample rate: %dHz (%.2fKHz)\n",audio_sample_rate,
audio_sample_rate/1000.0);
printf("Audio samples per frame: %d\n",audio_samples_per_frame);
printf("Video frame rate: %dHz\n",video_frame_rate);
}
if (video_type == VIDEO_H264)
err = merge_with_h264(h264_video_context,audio_file,output,
audio_type,
audio_samples_per_frame,audio_sample_rate,
video_frame_rate,
quiet,verbose,debugging);
else if (video_type == VIDEO_AVS)
err = merge_with_avs(avs_video_context,audio_file,output,
audio_type,
audio_samples_per_frame,audio_sample_rate,
video_frame_rate,
quiet,verbose,debugging);
else
{
fprintf(stderr,"### esmerge: Unknown video type\n");
return 1;
}
if (err)
{
fprintf(stderr,"### esmerge: Error merging video and audio streams\n");
close_elementary_stream(&video_es);
close_file(audio_file);
free_access_unit_context(&h264_video_context);
free_avs_context(&avs_video_context);
(void) tswrite_close(output,quiet);
return 1;
}
close_elementary_stream(&video_es);
close_file(audio_file);
free_access_unit_context(&h264_video_context);
free_avs_context(&avs_video_context);
err = tswrite_close(output,quiet);
if (err)
{
fprintf(stderr,"### esmerge: Error closing output %s\n",output_name);
return 1;
}
return 0;
}

1238
esreport.c 100644

Plik diff jest za duży Load Diff

762
esreverse.c 100644
Wyświetl plik

@ -0,0 +1,762 @@
/*
* Output a reversed representation of an H.264 (MPEG-4/AVC) or H.262 (MPEG-2)
* elementary stream.
*
* Note that the input stream must be seekable, which means that an option
* to read from standard input is not provided.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "es_fns.h"
#include "nalunit_fns.h"
#include "accessunit_fns.h"
#include "h262_fns.h"
#include "ts_fns.h"
#include "tswrite_fns.h"
#include "pes_fns.h"
#include "reverse_fns.h"
#include "misc_fns.h"
#include "version.h"
#define DEBUG 0
#define SHOW_REVERSE_DATA 1
#if SHOW_REVERSE_DATA
static int show_reverse_data = FALSE;
#endif
/*
* Write out packet data as ES or TS. This is defined in reverse.c, but
* otherwise unadvertised.
*/
extern int write_packet_data(WRITER output,
int as_TS,
byte data[],
int data_len,
u_int32 pid,
byte stream_id);
/*
* Find the I slices in our input stream, and output them in reverse order.
*
* - `es` is the input elementary stream
* - `output` is the stream to write to
* - if `max` is non-zero, then reporting will stop after `max` MPEG items
* - if `frequency` is non-zero, then attempt to produce the effect of
* keeping every <frequency>th picture (similar to reversing at a
* multiplication factor of `frequency`) If 0, just retain all I pictures.
* - if `as_TS` is true, then output as TS packets, not ES
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int reverse_h262(ES_p es,
WRITER output,
int max,
int frequency,
int as_TS,
int verbose,
int quiet)
{
int err = 0;
reverse_data_p reverse_data = NULL;
h262_context_p hcontext = NULL;
err = build_h262_context(es,&hcontext);
if (err) return 1;
err = build_reverse_data(&reverse_data,FALSE);
if (err)
{
free_h262_context(&hcontext);
return 1;
}
if (!quiet)
printf("\nScanning forwards\n");
add_h262_reverse_context(hcontext,reverse_data);
err = collect_reverse_h262(hcontext,max,verbose,quiet);
if (err && err != EOF)
{
if (reverse_data->length > 0)
{
fprintf(stderr,"!!! Collected %d pictures and sequence headers,"
" continuing to reverse\n",reverse_data->length);
}
else
{
free_reverse_data(&reverse_data);
free_h262_context(&hcontext);
return 1;
}
}
#if SHOW_REVERSE_DATA
if (show_reverse_data)
{
int ii;
for (ii=0; ii<reverse_data->length; ii++)
if (reverse_data->seq_offset[ii])
printf("%3d: %4d at " OFFSET_T_FORMAT "/%d for %d\n",
ii,reverse_data->index[ii],
reverse_data->start_file[ii],
reverse_data->start_pkt[ii],
reverse_data->data_len[ii]);
else
printf("%3d: seqh at " OFFSET_T_FORMAT "/%d for %d\n",
ii,
reverse_data->start_file[ii],
reverse_data->start_pkt[ii],
reverse_data->data_len[ii]);
}
if (!es->reading_ES)
write_program_data(es->reader,output.ts_output);
#endif
if (!es->reading_ES)
{
// Just in case (it can't hurt)
stop_server_output(es->reader);
// But this is important
set_PES_reader_video_only(es->reader,TRUE);
}
if (!quiet)
printf("\nOutputting in reverse order\n");
if (as_TS)
err = output_in_reverse_as_TS(es,output.ts_output,frequency,verbose,quiet,
-1,0,reverse_data);
else
err = output_in_reverse_as_ES(es,output.es_output,frequency,verbose,quiet,
-1,0,reverse_data);
if (!err && !quiet)
{
u_int32 final_index = reverse_data->index[reverse_data->first_written];
printf("\n");
printf("Summary\n");
printf("=======\n");
printf(" Considered Used Written\n");
printf("Pictures %10d %10d (%4.1f%%) %10d (%4.1f%%)\n",
final_index,reverse_data->pictures_kept,
100*(((double)reverse_data->pictures_kept)/final_index),
reverse_data->pictures_written,
100*(((double)reverse_data->pictures_written)/final_index));
if (frequency != 0)
printf("Target (pictures) . %10d (%4.1f%%) at requested"
" frequency %d\n",final_index/frequency,100.0/frequency,
frequency);
}
free_reverse_data(&reverse_data);
free_h262_context(&hcontext);
return err;
}
/*
* Output any sequence and picture parameter sets
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int output_parameter_sets(WRITER output,
access_unit_context_p context,
int as_TS,
int quiet)
{
nal_unit_context_p nac = context->nac;
param_dict_p seq_param_dict = nac->seq_param_dict;
param_dict_p pic_param_dict = nac->pic_param_dict;
int ii;
int err;
for (ii = 0; ii < seq_param_dict->length; ii++)
{
ES_offset posn = seq_param_dict->posns[ii];
u_int32 length = seq_param_dict->data_lens[ii];
byte *data = NULL;
if (!quiet)
printf("Writing out sequence parameter set %d\n",
seq_param_dict->ids[ii]);
err = read_ES_data(nac->es,posn,length,NULL,&data);
if (err)
{
fprintf(stderr,"### Error reading (sequence parameter set %d) data"
" from " OFFSET_T_FORMAT "/%d for %d\n",
seq_param_dict->ids[ii],posn.infile,posn.inpacket,length);
return 1;
}
err = write_packet_data(output,as_TS,data,length,DEFAULT_VIDEO_PID,
DEFAULT_VIDEO_STREAM_ID);
free(data);
if (err)
{
fprintf(stderr,"### Error writing out (sequence parameter set %d)"
"data\n",seq_param_dict->ids[ii]);
return 1;
}
}
for (ii = 0; ii < pic_param_dict->length; ii++)
{
ES_offset posn = pic_param_dict->posns[ii];
u_int32 length = pic_param_dict->data_lens[ii];
byte *data = NULL;
if (!quiet)
printf("Writing out picture parameter set %d\n",
pic_param_dict->ids[ii]);
err = read_ES_data(nac->es,posn,length,NULL,&data);
if (err)
{
fprintf(stderr,"### Error reading (picture parameter set %d) data"
" from " OFFSET_T_FORMAT "/%d for %d\n",
pic_param_dict->ids[ii],posn.infile,posn.inpacket,length);
return 1;
}
err = write_packet_data(output,as_TS,data,length,DEFAULT_VIDEO_PID,
DEFAULT_VIDEO_STREAM_ID);
free(data);
if (err)
{
fprintf(stderr,"### Error writing out (picture parameter set %d)"
"data\n",pic_param_dict->ids[ii]);
return 1;
}
}
return 0;
}
/*
* Find IDR and I access units, and output them in reverse order.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int reverse_access_units(ES_p es,
WRITER output,
int max,
int frequency,
int as_TS,
int verbose,
int quiet)
{
int err = 0;
reverse_data_p reverse_data = NULL;
access_unit_context_p acontext = NULL;
err = build_access_unit_context(es,&acontext);
if (err) return 1;
err = build_reverse_data(&reverse_data,TRUE);
if (err)
{
free_access_unit_context(&acontext);
return 1;
}
if (!quiet)
printf("\nScanning forwards\n");
add_access_unit_reverse_context(acontext,reverse_data);
err = collect_reverse_access_units(acontext,max,verbose,quiet);
if (err && err != EOF)
{
if (reverse_data->length > 0)
{
fprintf(stderr,"!!! Collected %d access units,"
" continuing to reverse\n",reverse_data->length);
}
else
{
free_reverse_data(&reverse_data);
free_access_unit_context(&acontext);
return 1;
}
}
#if SHOW_REVERSE_DATA
if (show_reverse_data)
{
int ii;
for (ii=0; ii<reverse_data->length; ii++)
printf("%3d: %4d at " OFFSET_T_FORMAT "/%d for %d\n",
ii,reverse_data->index[ii],
reverse_data->start_file[ii],
reverse_data->start_pkt[ii],
reverse_data->data_len[ii]);
}
//if (!es->reading_ES)
// write_program_data(es->reader,output.ts_output);
#endif
if (!es->reading_ES)
{
// Just in case (it can't hurt)
stop_server_output(es->reader);
// But this is important
set_PES_reader_video_only(es->reader,TRUE);
}
// Before outputting any reverse data, it's a good idea to write out the
// picture parameter set(s) and sequence parameter set(s)
if (!quiet)
printf("\nPreparing to output reverse data\n");
err = output_parameter_sets(output,acontext,as_TS,quiet);
if (err)
{
free_reverse_data(&reverse_data);
free_access_unit_context(&acontext);
return 1;
}
if (!quiet)
printf("\nOutputting in reverse order\n");
if (as_TS)
err = output_in_reverse_as_TS(es,output.ts_output,frequency,verbose,quiet,
-1,0,reverse_data);
else
err = output_in_reverse_as_ES(es,output.es_output,frequency,verbose,quiet,
-1,0,reverse_data);
if (!err && !quiet)
{
u_int32 final_index = reverse_data->index[reverse_data->first_written];
printf("\n");
printf("Summary\n");
printf("=======\n");
printf(" Considered Used Written\n");
printf("Access units %10d %10d (%4.1f%%) %10d (%4.1f%%)\n",
final_index,reverse_data->pictures_kept,
100*(((double)reverse_data->pictures_kept)/final_index),
reverse_data->pictures_written,
100*(((double)reverse_data->pictures_written)/final_index));
if (frequency != 0)
printf("Target (access units) . %10d (%4.1f%%) at requested"
" frequency %d\n",final_index/frequency,100.0/frequency,
frequency);
}
free_reverse_data(&reverse_data);
free_access_unit_context(&acontext);
return err;
}
static void print_usage()
{
printf(
"Usage: esreverse [switches] [<infile>] [<outfile>]\n"
"\n"
);
REPORT_VERSION("esreverse");
printf(
"\n"
" Output a reversed stream derived from the input H.264 (MPEG-4/AVC)\n"
" or H.262 (MPEG-2) elementary stream.\n"
"\n"
" If output is to an H.222 Transport Stream, then fixed values for\n"
" the PMT PID (0x66) and video PID (0x68) are used.\n"
"\n"
"Files:\n"
" <infile> is the input elementary stream.\n"
" <outfile> is the output stream, either an equivalent elementary\n"
" stream, or an H.222 Transport Stream (but see -stdout\n"
" and -host below).\n"
"\n"
"Switches:\n"
" -verbose, -v Output additional (debugging) messages\n"
" -quiet, -q Only output error messages\n"
" -stdout Write output to <stdout>, instead of a named file\n"
" Forces -quiet.\n"
" -host <host>, -host <host>:<port>\n"
" Writes output (over TCP/IP) to the named <host>,\n"
" instead of to a named file. If <port> is not\n"
" specified, it defaults to 88. Implies -tsout.\n"
" -max <n>, -m <n> Maximum number of frames to read\n"
" -freq <n> Specify the frequency of frames to try to keep\n"
" when reversing. Defaults to 8.\n"
" -tsout Output H.222 Transport Stream\n"
"\n"
" -pes, -ts The input file is TS or PS, to be read via the\n"
" PES->ES reading mechanisms\n"
" -server Also output as normal forward video as reversal\n"
" data is being collected. Implies -pes and -tsout.\n"
#if SHOW_REVERSE_DATA
"\n"
" -x Temporary extra debugging information\n"
#endif
"\n"
"Stream type:\n"
" If input is from a file, then the program will look at the start of\n"
" the file to determine if the stream is H.264 or H.262 data. This\n"
" process may occasionally come to the wrong conclusion, in which case\n"
" the user can override the choice using the following switches.\n"
"\n"
" -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
" -h262 Force the program to treat the input as MPEG-2.\n"
);
}
int main(int argc, char **argv)
{
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
int use_stdout = FALSE;
int use_tcpip = FALSE;
int port = 88; // Useful default port number
int err = 0;
ES_p es = NULL;
WRITER output;
int max = 0;
int as_TS = FALSE;
int frequency = 8; // The default as stated in the usage
int quiet = FALSE;
int verbose = FALSE;
int ii = 1;
int use_pes = FALSE;
int use_server = FALSE;
int want_data = VIDEO_H262;
int is_data;
int force_stream_type = FALSE;
byte stream_type;
if (argc < 2)
{
print_usage();
return 0;
}
output.es_output = NULL;
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) ||
!strcmp("-h",argv[ii]))
{
print_usage();
return 0;
}
#if SHOW_REVERSE_DATA
else if (!strcmp("-x",argv[ii]))
show_reverse_data = TRUE;
#endif
else if (!strcmp("-avc",argv[ii]) || !strcmp("-h264",argv[ii]))
{
force_stream_type = TRUE;
want_data = VIDEO_H264;
}
else if (!strcmp("-h262",argv[ii]))
{
force_stream_type = TRUE;
want_data = VIDEO_H262;
}
else if (!strcmp("-pes",argv[ii]) || !strcmp("-ts",argv[ii]))
use_pes = TRUE;
else if (!strcmp("-server",argv[ii]))
{
use_server = TRUE;
use_pes = TRUE;
as_TS = TRUE;
}
else if (!strcmp("-tsout",argv[ii]))
as_TS = TRUE;
else if (!strcmp("-stdout",argv[ii]))
{
had_output_name = TRUE; // more or less
use_stdout = TRUE;
}
else if (!strcmp("-host",argv[ii]))
{
CHECKARG("esreverse",ii);
err = host_value("esreverse",argv[ii],argv[ii+1],&output_name,&port);
if (err) return 1;
had_output_name = TRUE; // more or less
use_tcpip = TRUE;
as_TS = TRUE;
ii++;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("esreverse",ii);
err = int_value("esreverse",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-freq",argv[ii]))
{
CHECKARG("esreverse",ii);
err = int_value("esreverse",argv[ii],argv[ii+1],TRUE,10,&frequency);
if (err) return 1;
ii++;
}
else
{
fprintf(stderr,"### esreverse: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && had_output_name)
{
fprintf(stderr,"### esreverse: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name)
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### esreverse: No input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### esreverse: No output file specified\n");
return 1;
}
// Try to stop extraneous data ending up in our output stream
if (use_stdout)
{
verbose = FALSE;
quiet = TRUE;
}
err = open_input_as_ES(input_name,use_pes,quiet,
force_stream_type,want_data,&is_data,&es);
if (err)
{
fprintf(stderr,"### esreverse: Error opening input file\n");
return 1;
}
if (is_data == VIDEO_H262)
stream_type = MPEG2_VIDEO_STREAM_TYPE;
else if (is_data == VIDEO_H264)
stream_type = AVC_VIDEO_STREAM_TYPE;
else
{
fprintf(stderr,"### esreverse: Unexpected type of video data\n");
return 1;
}
if (as_TS)
{
if (use_stdout)
err = tswrite_open(TS_W_STDOUT,NULL,NULL,0,quiet,&(output.ts_output));
else if (use_tcpip)
err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&(output.ts_output));
else
err = tswrite_open(TS_W_FILE,output_name,NULL,0,quiet,&(output.ts_output));
if (err)
{
fprintf(stderr,"### esreverse: Unable to open %s\n",output_name);
(void) close_input_as_ES(input_name,&es);
return 1;
}
}
else
{
output.es_output = fopen(output_name,"wb");
if (output.es_output == NULL)
{
fprintf(stderr,"### esreverse: Unable to open output file %s: %s\n",
output_name,strerror(errno));
(void) close_input_as_ES(input_name,&es);
return 1;
}
if (!quiet)
printf("Writing to %s\n",output_name);
}
if (!quiet)
{
if (as_TS)
printf("Writing as Transport Stream\n");
printf("Filtering freqency %d\n",frequency);
if (max)
printf("Stopping as soon after %d %s as possible\n",max,
(is_data == VIDEO_H262?"MPEG2 items":"NAL units"));
}
if (use_pes)
{
#if SHOW_REVERSE_DATA
if (show_reverse_data)
es->reader->debug_read_packets = TRUE;
#endif
if (use_server)
{
// For testing purposes, let's try outputting video as we collect data
set_server_output(es->reader,output.ts_output,100);
es->reader->debug_read_packets = TRUE;
}
}
// If we're writing out TS data, start it off now
// (we mustn't do it after our forwards-processing function,
// because that itself may output some data...)
if (as_TS)
{
if (use_pes)
{
if (!quiet)
printf("Using transport stream id 1, PMT PID %#x, program 1 ="
" PID %#x\n",DEFAULT_PMT_PID,DEFAULT_VIDEO_PID);
set_PES_reader_program_data(es->reader,1,DEFAULT_PMT_PID,
DEFAULT_VIDEO_PID,
DEFAULT_AUDIO_PID, // not actually used
DEFAULT_VIDEO_PID); // video as PCR
// Note that (a) the server output will write program data for us,
// and (b) for the moment, the TS writer does not allow us to set the
// stream_type
}
else
{
if (!quiet)
printf("Using transport stream id 1, PMT PID %#x, program 1 ="
" PID %#x, stream type %#x\n",DEFAULT_PMT_PID,DEFAULT_VIDEO_PID,
stream_type);
err = write_TS_program_data(output.ts_output,
1,1,DEFAULT_PMT_PID,DEFAULT_VIDEO_PID,
stream_type);
if (err)
{
fprintf(stderr,"### esreverse: Error writing out TS program data\n");
(void) close_input_as_ES(input_name,&es);
if (as_TS)
(void) tswrite_close(output.ts_output,TRUE);
else if (had_output_name && !use_stdout)
{
err = fclose(output.es_output);
if (err)
fprintf(stderr,
"### esreverse: (Error closing output file %s: %s)\n",
output_name,strerror(errno));
}
return 1;
}
}
}
if (is_data == VIDEO_H262)
err = reverse_h262(es,output,max,frequency,as_TS,verbose,quiet);
else
err = reverse_access_units(es,output,max,frequency,as_TS,verbose,quiet);
if (err)
{
fprintf(stderr,"### esreverse: Error reversing input\n");
(void) close_input_as_ES(input_name,&es);
if (as_TS)
(void) tswrite_close(output.ts_output,TRUE);
else if (had_output_name && !use_stdout)
{
err = fclose(output.es_output);
if (err)
fprintf(stderr,"### esreverse: (Error closing output file %s: %s)\n",
output_name,strerror(errno));
}
return 1;
}
// And tidy up when we're finished
if (as_TS)
{
err = tswrite_close(output.ts_output,quiet);
if (err)
{
fprintf(stderr,"### esreverse: Error closing output file %s",
output_name);
(void) close_input_as_ES(input_name,&es);
return 1;
}
}
else if (!use_stdout)
{
errno = 0;
err = fclose(output.es_output);
if (err)
{
fprintf(stderr,"### esreverse: Error closing output file %s: %s\n",
output_name,strerror(errno));
(void) close_input_as_ES(input_name,&es);
return 1;
}
}
err = close_input_as_ES(input_name,&es);
if (err)
{
fprintf(stderr,"### esreverse: Error closing input file\n");
return 1;
}
return 0;
}

875
filter.c 100644
Wyświetl plik

@ -0,0 +1,875 @@
/*
* Support for "filtering" ES, outputting to either ES or TS.
*
* This provides the ability to "fast forward" through ES data.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "es_fns.h"
#include "ts_fns.h"
#include "accessunit_fns.h"
#include "h262_fns.h"
#include "misc_fns.h"
#include "filter_fns.h"
#define DEBUG 0
// ============================================================
// Managing H.262 filter contexts
// ============================================================
/*
* Build a new H.262 (MPEG-2 and also MPEG-1) filter context
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
static int new_h262_filter_context(h262_filter_context_p *fcontext)
{
h262_filter_context_p new = malloc(SIZEOF_H262_FILTER_CONTEXT);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate H.262 filter context\n");
return 1;
}
new->h262 = NULL;
new->last_seq_hdr = NULL;
new->new_seq_hdr = FALSE;
reset_h262_filter_context(new);
*fcontext = new;
return 0;
}
/*
* Build a new filter context for "stripping" H.262 data
*
* - `fcontext` is the new filter context
* - `h262` is the H.262 stream to read from
* - `all_IP` is true if the software should keep all I and P pictures
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h262_filter_context_strip(h262_filter_context_p *fcontext,
h262_context_p h262,
int all_IP)
{
int err = new_h262_filter_context(fcontext);
if (err) return 1;
(*fcontext)->h262 = h262;
(*fcontext)->filter = FALSE;
(*fcontext)->allref = all_IP;
return 0;
}
/*
* Build a new filter context for "filtering" H.262 data
*
* - `fcontext` is the new filter context
* - `h262` is the H.262 stream to read from
* - `freq` is the desired speed-up, or the frequency at which frames
* should (ideally) be kept
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h262_filter_context(h262_filter_context_p *fcontext,
h262_context_p h262,
int freq)
{
int err = new_h262_filter_context(fcontext);
if (err) return 1;
(*fcontext)->h262 = h262;
(*fcontext)->filter = TRUE;
(*fcontext)->freq = freq;
return 0;
}
/*
* Reset an H.262 filter context, ready to start filtering anew.
*/
extern void reset_h262_filter_context(h262_filter_context_p fcontext)
{
fcontext->pending_EOF = FALSE;
fcontext->last_was_slice = FALSE;
fcontext->had_previous_picture = FALSE;
if (fcontext->last_seq_hdr != NULL)
free_h262_picture(&fcontext->last_seq_hdr);
fcontext->new_seq_hdr = FALSE;
fcontext->count = 0;
fcontext->frames_seen = 0;
fcontext->frames_written = 0;
}
/*
* Free a filter context
*
* NOTE that this does *not* free the H.262 datastructure to which the
* filter context refers.
*
* - `fcontext` is the filter context, which will be freed, and returned
* as NULL.
*/
extern void free_h262_filter_context(h262_filter_context_p *fcontext)
{
if ((*fcontext) == NULL)
return;
// It's a little wasteful to call this, but on the other hand it is
// guaranteed to free everything we want freeing
reset_h262_filter_context(*fcontext);
// Just lose our reference to the H.262 datastructure, don't free it
(*fcontext)->h262 = NULL;
free(*fcontext);
*fcontext = NULL;
return;
}
// ============================================================
// Managing H.264 filter contexts
// ============================================================
/*
* Build a new H.264 filter context
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
static int new_h264_filter_context(h264_filter_context_p *fcontext)
{
h264_filter_context_p new = malloc(SIZEOF_H264_FILTER_CONTEXT);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate H.264 filter context\n");
return 1;
}
// Unset the important things - things we might otherwise try to free
// (or, for new->es, things that stop us doing anything until we're
// setup properly by the user)
new->access_unit_context = NULL;
reset_h264_filter_context(new);
*fcontext = new;
return 0;
}
/*
* Build a new filter context for "stripping" ES data
*
* - `fcontext` is the new filter context
* - `access` is the access unit context to read from
* - `allref` is true if the software should keep all reference pictures
* (H.264) or all I and P pictures (H.264)
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h264_filter_context_strip(h264_filter_context_p *fcontext,
access_unit_context_p access,
int allref)
{
int err = new_h264_filter_context(fcontext);
if (err) return 1;
(*fcontext)->access_unit_context = access;
(*fcontext)->filter = FALSE;
(*fcontext)->allref = allref;
return 0;
}
/*
* Build a new filter context for "filtering" ES data
*
* - `fcontext` is the new filter context
* - `access` is the access unit context to read from
* - `freq` is the desired speed-up, or the frequency at which frames
* should (ideally) be kept
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h264_filter_context(h264_filter_context_p *fcontext,
access_unit_context_p access,
int freq)
{
int err = new_h264_filter_context(fcontext);
if (err) return 1;
(*fcontext)->access_unit_context = access;
(*fcontext)->filter = TRUE;
(*fcontext)->freq = freq;
return 0;
}
/*
* Reset an H.264 filter context, ready to start filtering anew.
*/
extern void reset_h264_filter_context(h264_filter_context_p fcontext)
{
// `skipped_ref_pic` is TRUE if we've skipped any reference pictures
// since our last IDR (hmm - should it start off True or False?)
fcontext->skipped_ref_pic = FALSE;
// `last_accepted_was_not_IDR` is TRUE if the last frame kept (output)
// was not an IDR. We set it TRUE initially so that we will decide
// to output the first IDR we *do* find, regardless of the count.
fcontext->last_accepted_was_not_IDR = TRUE;
// And plainly we didn't have a previous access unit
fcontext->had_previous_access_unit = FALSE;
// Especially not an IDR
fcontext->not_had_IDR = TRUE;
fcontext->count = 0;
fcontext->frames_seen = 0;
fcontext->frames_written = 0;
}
/*
* Free an H.264 filter context
*
* NOTE that this does *not* free the access unit context to which the
* filter context refers.
*
* - `fcontext` is the filter context, which will be freed, and returned
* as NULL.
*/
extern void free_h264_filter_context(h264_filter_context_p *fcontext)
{
if ((*fcontext) == NULL)
return;
// It's a little wasteful to call this, but on the other hand it is
// guaranteed to free everything we want freeing
reset_h264_filter_context(*fcontext);
// Just lose our reference to the access unit context, don't free it
(*fcontext)->access_unit_context = NULL;
free(*fcontext);
*fcontext = NULL;
return;
}
// ============================================================
// Filtering H.262
// ============================================================
/*
* Retrieve the next I (and/or, if fcontext->allref, P) frame in this H.262 ES.
*
* Any sequence end "pictures" will be ignored.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `seq_hdr` is a sequence header, i.e., that used by the next frame to
* output. This will be NULL if the sequence header has not changed since
* the last call of this function.
*
* Note that the caller should *not* free this, and that it will not be
* maintained over calls of this function (i.e., it is a reference to a
* value within the `fcontext` which is altered by this function).
*
* - `frame` is the next frame to output.
*
* Note that it is the caller's responsibility to free this with
* `free_h262_picture()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of I and P frames (start code 0)
* found by this call of the function, including the item returned
* if appropriate.
*
* Returns 0 if it succeeds, EOF if end-of-file is read (or the last call
* returned a sequence end item), 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_stripped_h262_frame(h262_filter_context_p fcontext,
int verbose,
int quiet,
h262_picture_p *seq_hdr,
h262_picture_p *frame,
int *frames_seen)
{
int err;
// A picture is built up from several items - we start with none in hand
h262_picture_p this_picture = NULL;
*frames_seen = 0;
if (fcontext->filter)
{
fprintf(stderr,"### Calling get_next_stripped_h262_frame with a context"
" set for filtering\n");
return 1;
}
// Otherwise, look for something we want to keep
for (;;)
{
if (es_command_changed(fcontext->h262->es))
{
*frame = *seq_hdr = NULL;
return COMMAND_RETURN_CODE;
}
err = get_next_h262_frame(fcontext->h262,verbose,quiet,&this_picture);
if (err == EOF)
{
*frame = *seq_hdr = NULL;
return err;
}
else if (err)
{
fprintf(stderr,"### Error filtering H.262 frames\n");
return 1;
}
// Now to stripping
if (this_picture->is_picture)
{
(*frames_seen) ++;
if ((this_picture->picture_coding_type == 1) ||
(this_picture->picture_coding_type == 2 && fcontext->allref) )
{
*frame = this_picture;
if (fcontext->new_seq_hdr)
*seq_hdr = fcontext->last_seq_hdr;
else
*seq_hdr = NULL;
fcontext->new_seq_hdr = FALSE;
if (verbose) printf(">> %s picture \n",
(this_picture->picture_coding_type==1?"I":"P"));
return 0;
}
else
free_h262_picture(&this_picture);
}
else if (this_picture->is_sequence_header)
{
// We maybe want to remember this sequence header for the next picture
if (fcontext->last_seq_hdr == NULL)
{
fcontext->last_seq_hdr = this_picture;
fcontext->new_seq_hdr = TRUE;
if (verbose) printf(">> First sequence header\n");
}
else if (!same_h262_picture(this_picture,fcontext->last_seq_hdr))
{
if (verbose) printf(">> Different sequence header\n");
free_h262_picture(&fcontext->last_seq_hdr);
fcontext->last_seq_hdr = this_picture;
fcontext->new_seq_hdr = TRUE;
}
else
{
fcontext->new_seq_hdr = FALSE;
if (verbose) printf(">> Identical sequence header\n");
free_h262_picture(&this_picture);
}
}
}
}
/*
* Retrieve the next I frame, from the H.262 ES, aiming for an "apparent" kept
* frequency as stated.
*
* Any sequence end "pictures" will be ignored.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* (including the desired frequency)
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `seq_hdr` is a sequence header, i.e., that used by the next picture to
* output. This will be NULL if `frame` is NULL.
*
* Note that the caller should *not* free this, and that it will not be
* maintained over calls of this function (i.e., it is a reference to a
* value within the `fcontext` which is altered by this function).
*
* - `frame` is the next frame to output. This will be NULL if the last frame
* should be output again, to provide the requested apparent frequency.
*
* Note that it is the caller's responsibility to free this with
* `free_h262_picture()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of I and P frames found by this call of
* the function, including the item returned if appropriate.
*
* Returns 0 if it succeeds, EOF if end-of-file is read, or we've just read a
* sequence end item, or the last call ended a picture on a sequence end
* item, 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_filtered_h262_frame(h262_filter_context_p fcontext,
int verbose,
int quiet,
h262_picture_p *seq_hdr,
h262_picture_p *frame,
int *frames_seen)
{
int err;
// A picture is built up from several items - we start with none in hand
h262_picture_p this_picture = NULL;
*frames_seen = 0;
if (!fcontext->filter)
{
fprintf(stderr,"### Calling get_next_filtered_h262_frame with a context"
" set for stripping\n");
return 1;
}
// Otherwise, look for something we want to keep
for (;;)
{
if (es_command_changed(fcontext->h262->es))
{
*frame = *seq_hdr = NULL;
return COMMAND_RETURN_CODE;
}
// If the picture is an I picture, we want it to contain an appropriate
// AFD - so ask for that
fcontext->h262->add_fake_afd = TRUE;
err = get_next_h262_frame(fcontext->h262,verbose,quiet,&this_picture);
if (err == EOF)
{
*frame = *seq_hdr = NULL;
fcontext->h262->add_fake_afd = FALSE;
return err;
}
else if (err)
{
fprintf(stderr,"### Error filtering H.262 frames\n");
fcontext->h262->add_fake_afd = FALSE;
return 1;
}
// Reinstate normal "only include actual AFDs"
fcontext->h262->add_fake_afd = FALSE;
// Now to filtering
if (this_picture->is_picture)
{
fcontext->count ++;
(*frames_seen) ++;
fcontext->frames_seen ++;
if (this_picture->picture_coding_type == 1 &&
fcontext->count < fcontext->freq)
{
// It is an I picture, but it is too soon
if (verbose)
{
printf("+++ %d/%d DROP: Too soon\n",fcontext->count,fcontext->freq);
}
}
else if (this_picture->picture_coding_type != 1)
{
// It is not an I picture
if (verbose)
{
printf("+++ %d/%d DROP: %s picture\n",fcontext->count,fcontext->freq,
H262_PICTURE_CODING_STR(this_picture->picture_coding_type));
}
// But do we want to pad with (i.e., repeat) the previous I picture?
if (fcontext->freq > 0)
{
int pictures_wanted = fcontext->frames_seen / fcontext->freq;
int repeat = pictures_wanted - fcontext->frames_written;
if (repeat > 0 && fcontext->had_previous_picture)
{
if (verbose) printf(">>> output last picture again\n");
free_h262_picture(&this_picture);
*seq_hdr = NULL;
*frame = NULL;
fcontext->frames_written ++;
return 0;
}
}
}
else
{
// It was an I picture, and not too soon
if (verbose)
{
printf("+++ %d/%d KEEP\n",fcontext->count,fcontext->freq);
}
fcontext->count = 0;
fcontext->had_previous_picture = TRUE;
*seq_hdr = fcontext->last_seq_hdr;
*frame = this_picture;
fcontext->frames_written ++;
return 0;
}
free_h262_picture(&this_picture);
}
else if (this_picture->is_sequence_header)
{
// We want to remember the sequence header for the next picture
if (fcontext->last_seq_hdr != NULL)
free_h262_picture(&fcontext->last_seq_hdr);
fcontext->last_seq_hdr = this_picture;
}
}
}
// ============================================================
// Filtering H.264
// ============================================================
/*
* Return the next IDR or I (and maybe any reference) frame from this H.264 ES.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `frame` is the next frame to output.
* Note that it is the caller's responsibility to free this with
* `free_access_unit()`.
* If an error or EOF is returned, this value is undefined.
* - `frames_seen` is the number of frames found by this call
* of the function, including the frame returned.
*
* Returns 0 if it succeeds, EOF if end-of-file is read (or an an end of
* stream NAL unit has been passed), 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_stripped_h264_frame(h264_filter_context_p fcontext,
int verbose,
int quiet,
access_unit_p *frame,
int *frames_seen)
{
int err = 0;
int keep = FALSE; // Should we keep the current access unit?
access_unit_p this_access_unit = NULL;
*frames_seen = 0;
for (;;)
{
if (es_command_changed(fcontext->access_unit_context->nac->es))
return COMMAND_RETURN_CODE;
if (verbose)
printf("\n");
err = get_next_h264_frame(fcontext->access_unit_context,quiet,verbose,
&this_access_unit);
if (err == EOF)
return err;
else if (err)
return 1;
(*frames_seen) ++;
if (this_access_unit->primary_start == NULL)
{
// We don't have a primary picture - no VCL NAL
// There seems little point in keeping the access unit
keep = FALSE;
if (verbose)
printf("++ DROP: no primary picture\n");
}
else if (this_access_unit->primary_start->nal_ref_idc == 0)
{
// This is not a reference frame, so it's of no interest
keep = FALSE;
if (verbose)
printf("++ DROP: not reference\n");
}
else if (fcontext->allref)
{
// We want to keep all reference frames
if (this_access_unit->primary_start->nal_unit_type == NAL_IDR ||
this_access_unit->primary_start->nal_unit_type == NAL_NON_IDR)
{
keep = TRUE;
if (verbose)
printf("++ KEEP: reference picture\n");
}
else
{
keep = FALSE;
if (verbose)
printf("++ DROP: sequence or parameter set, etc.\n");
}
}
else
{
// We only want to keep IDR and I frames
if (this_access_unit->primary_start->nal_unit_type == NAL_IDR)
{
keep = TRUE;
if (verbose)
printf("++ KEEP: IDR picture\n");
}
else if (this_access_unit->primary_start->nal_unit_type == NAL_NON_IDR &&
all_slices_I(this_access_unit))
{
keep = TRUE;
if (verbose)
printf("++ KEEP: all slices I\n");
}
else
{
keep = FALSE;
if (verbose)
printf("++ DROP: not IDR or all slices I\n");
}
}
if (keep)
{
*frame = this_access_unit;
return 0;
}
// We've no further use for this access unit
free_access_unit(&this_access_unit);
}
}
/*
* Retrieve the next frame from the H.264 (MPEG-4/AVC) ES, aiming
* for an "apparent" kept frequency as stated.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* (including the desired frequency)
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `frame` is the next frame to output.
*
* If the function succeeds and `frame` is NULL, it means that the
* last frame should be output again.
*
* Note that it is the caller's responsibility to free this frame with
* `free_access_unit()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of frames found by this call of the function,
* including the frame returned.
*
* Returns 0 if all went well, 1 if something went wrong.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_filtered_h264_frame(h264_filter_context_p fcontext,
int verbose,
int quiet,
access_unit_p *frame,
int *frames_seen)
{
int err = 0;
int keep = FALSE; // Should we keep the current access unit?
access_unit_p this_access_unit = NULL;
*frames_seen = 0;
for (;;)
{
if (es_command_changed(fcontext->access_unit_context->nac->es))
return COMMAND_RETURN_CODE;
if (verbose)
printf("\n");
err = get_next_h264_frame(fcontext->access_unit_context,quiet,verbose,
&this_access_unit);
if (err == EOF)
return err;
else if (err)
return 1;
fcontext->count ++;
(*frames_seen) ++;
fcontext->frames_seen ++;
if (this_access_unit->primary_start == NULL)
{
// We don't have a primary picture - no VCL NAL
// There seems little point in keeping the access unit
keep = FALSE;
if (verbose)
printf("++ %d/%d DROP: no primary picture\n",
fcontext->count,fcontext->freq);
}
else if (this_access_unit->primary_start->nal_ref_idc == 0)
{
// This is not a reference frame, so it's of no interest
keep = FALSE;
if (verbose)
printf("++ %d/%d DROP: not a reference frame\n",
fcontext->count,fcontext->freq);
}
else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR &&
fcontext->last_accepted_was_not_IDR)
{
// This frame is an IDR, and the last frame kept was not, so
// we'll output it regardless - we don't expect to get enough
// IDR pictures that this will be a problem, and they're
// valuable because they're the "limit" for other frames that
// refer backwards
// (should we reset the count to zero? - seems sensible)
keep = TRUE;
fcontext->not_had_IDR = FALSE;
fcontext->skipped_ref_pic = FALSE;
fcontext->last_accepted_was_not_IDR = FALSE;
if (verbose)
printf("++ %d/%d KEEP: IDR and last was not\n",
fcontext->count,fcontext->freq);
}
else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR &&
fcontext->not_had_IDR)
{
// We haven't had an IDR yet in this filter run, so we had better
// output this one as a "good start"
keep = TRUE;
fcontext->skipped_ref_pic = FALSE;
fcontext->last_accepted_was_not_IDR = FALSE;
if (verbose)
printf("++ %d/%d KEEP: IDR and first IDR of filter run\n",
fcontext->count,fcontext->freq);
}
else if (fcontext->count < fcontext->freq)
{
// It's too soon, so ignore it - but notice that we *have*
// ignored a reference picture
keep = FALSE;
fcontext->skipped_ref_pic = TRUE;
if (verbose)
printf("++ %d/%d DROP: Too soon (skipping ref frame)\n",
fcontext->count,fcontext->freq);
}
else if (this_access_unit->primary_start->nal_unit_type == NAL_IDR)
{
// It's an IDR, so output it
keep = TRUE;
fcontext->skipped_ref_pic = FALSE;
fcontext->last_accepted_was_not_IDR = FALSE;
if (verbose)
printf("++ %d/%d KEEP: IDR\n",fcontext->count,fcontext->freq);
}
else if (all_slices_I(this_access_unit))
{
// It is an I picture (either it has all of its slices
// type "I", or it has a single slice which is of type "I")
keep = TRUE;
fcontext->last_accepted_was_not_IDR = TRUE;
if (verbose)
printf("++ %d/%d KEEP: I frame\n",fcontext->count,fcontext->freq);
}
else if (!fcontext->skipped_ref_pic && all_slices_I_or_P(this_access_unit))
{
// It is a P or I&P picture, but we know that we have output all
// the reference pictures since the last IDR, so it is
// safe to output it
keep = TRUE;
fcontext->last_accepted_was_not_IDR = TRUE;
if (verbose)
printf("++ %d/%d KEEP: P frame. no skipped ref frames\n",
fcontext->count,fcontext->freq);
}
else
{
keep = FALSE;
fcontext->skipped_ref_pic = TRUE;
if (verbose)
printf("++ %d/%d DROP: ref frame skipped earlier\n",
fcontext->count,fcontext->freq);
}
if (keep)
{
*frame = this_access_unit;
fcontext->had_previous_access_unit = TRUE;
fcontext->frames_written ++;
fcontext->count = 0;
return 0;
}
else
{
if (fcontext->freq > 0)
{
int access_units_wanted = fcontext->frames_seen / fcontext->freq;
int repeat = access_units_wanted - fcontext->frames_written;
if (repeat > 0 && fcontext->had_previous_access_unit)
{
if (verbose) printf(">>> output last access unit again\n");
free_access_unit(&this_access_unit);
*frame = NULL;
fcontext->frames_written ++;
return 0;
}
}
// We've no further use for this access unit
free_access_unit(&this_access_unit);
}
}
}

106
filter_defns.h 100644
Wyświetl plik

@ -0,0 +1,106 @@
/*
* Datastructures for filtering ES data ("fast forward") and writing to ES or
* TS.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _filter_defns
#define _filter_defns
#include "compat.h"
#include "es_defns.h"
#include "h262_defns.h"
#include "accessunit_defns.h"
#include "reverse_defns.h"
// Filtering comes in two varieties:
// - "stripping" means retaining just reference pictures. For H.262 this
// means the I pictures (and maybe the P pictures), for H.264 this means
// the IDR and I pictures (or maybe all reference pictures). This is simple
// to do, but the speedup resulting is very dependant on the data.
// - "filtering" means attempting to keep frames as a particular frequency,
// so, for instance, a frequency of 8 would mean trying to keep every 8th
// frame, or a speedup of 8x. This is harder to do as it depends rather
// crucially on the distribution of reference frames in the data.
// ------------------------------------------------------------
struct h262_filter_context
{
h262_context_p h262; // The H.262 stream we are reading from
int filter; // TRUE if filtering, FALSE if stripping
int freq; // Frequency of frames to try to keep if filtering
int allref; // Keep all I and P pictures if stripping?
// (the name `allref` is used for compatibility with the H.264 filter
// context - it's a little easier to have one name for both filters)
// For any operation on H.262, we want:
int pending_EOF; // next time a function is called, say we had EOF
// When filtering, we want:
int count; // a rolling count to compare with the desired frequency
int last_was_slice;
int had_previous_picture;
h262_picture_p last_seq_hdr;
// When stripping, we want:
int new_seq_hdr; // has the sequence header changed?
int frames_seen; // number of pictures seen this filter run
int frames_written; // number of pictures written (or, returned)
};
typedef struct h262_filter_context *h262_filter_context_p;
#define SIZEOF_H262_FILTER_CONTEXT sizeof(struct h262_filter_context)
// ------------------------------------------------------------
struct h264_filter_context
{
access_unit_context_p access_unit_context; // our "reader" for access units
int filter; // TRUE if filtering, FALSE if stripping
int freq; // Frequency of frames to try to keep if filtering
int allref; // Keep all reference pictures
// When filtering, we want:
// a rolling count to compare with the desired frequency
int count;
// `skipped_ref_pic` is TRUE if we've skipped any reference pictures
// since our last IDR.
int skipped_ref_pic;
// `last_accepted_was_not_IDR` is TRUE if the last frame kept (output)
// was not an IDR. We set it TRUE initially so that we will decide
// to output the first IDR we *do* find, regardless of the count.
int last_accepted_was_not_IDR;
int had_previous_access_unit;
// Have we had an IDR in this run of the filter?
int not_had_IDR;
int frames_seen; // number seen this filter run
int frames_written; // number written (or, returned)
};
typedef struct h264_filter_context *h264_filter_context_p;
#define SIZEOF_H264_FILTER_CONTEXT sizeof(struct h264_filter_context)
#endif // _filter_defns

262
filter_fns.h 100644
Wyświetl plik

@ -0,0 +1,262 @@
/*
* Functions for filtering ES data ("fast forward") and writing to ES or TS.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _filter_fns
#define _filter_fns
#include "filter_defns.h"
/*
* Build a new filter context for "stripping" H.262 data
*
* - `fcontext` is the new filter context
* - `h262` is the H.262 stream to read from
* - `all_IP` is true if the software should keep all I and P pictures
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h262_filter_context_strip(h262_filter_context_p *fcontext,
h262_context_p h262,
int all_IP);
/*
* Build a new filter context for "filtering" H.262 data
*
* - `fcontext` is the new filter context
* - `h262` is the H.262 stream to read from
* - `freq` is the desired speed-up, or the frequency at which frames
* should (ideally) be kept
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h262_filter_context(h262_filter_context_p *fcontext,
h262_context_p h262,
int freq);
/*
* Reset an H.262 filter context, ready to start filtering anew.
*/
extern void reset_h262_filter_context(h262_filter_context_p fcontext);
/*
* Free a filter context
*
* NOTE that this does *not* free the H.262 datastructure to which the
* filter context refers.
*
* - `fcontext` is the filter context, which will be freed, and returned
* as NULL.
*/
extern void free_h262_filter_context(h262_filter_context_p *fcontext);
/*
* Build a new filter context for "stripping" ES data
*
* - `fcontext` is the new filter context
* - `access` is the access unit context to read from
* - `allref` is true if the software should keep all reference pictures
* (H.264) or all I and P pictures (H.264)
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h264_filter_context_strip(h264_filter_context_p *fcontext,
access_unit_context_p access,
int allref);
/*
* Build a new filter context for "filtering" ES data
*
* - `fcontext` is the new filter context
* - `access` is the access unit context to read from
* - `freq` is the desired speed-up, or the frequency at which frames
* should (ideally) be kept
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int build_h264_filter_context(h264_filter_context_p *fcontext,
access_unit_context_p access,
int freq);
/*
* Reset an H.264 filter context, ready to start filtering anew.
*/
extern void reset_h264_filter_context(h264_filter_context_p fcontext);
/*
* Free an H.264 filter context
*
* NOTE that this does *not* free the access unit context to which the
* filter context refers.
*
* - `fcontext` is the filter context, which will be freed, and returned
* as NULL.
*/
extern void free_h264_filter_context(h264_filter_context_p *fcontext);
/*
* Retrieve the next I (and/or, if fcontext->allref, P) frame in this H.262 ES.
*
* Any sequence end "pictures" will be ignored.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `seq_hdr` is a sequence header, i.e., that used by the next frame to
* output. This will be NULL if the sequence header has not changed since
* the last call of this function.
*
* Note that the caller should *not* free this, and that it will not be
* maintained over calls of this function (i.e., it is a reference to a
* value within the `fcontext` which is altered by this function).
*
* - `frame` is the next frame to output.
*
* Note that it is the caller's responsibility to free this with
* `free_h262_picture()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of I and P frames (start code 0)
* found by this call of the function, including the item returned
* if appropriate.
*
* Returns 0 if it succeeds, EOF if end-of-file is read (or the last call
* returned a sequence end item), 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_stripped_h262_frame(h262_filter_context_p fcontext,
int verbose,
int quiet,
h262_picture_p *seq_hdr,
h262_picture_p *frame,
int *frames_seen);
/*
* Retrieve the next I frame, from the H.262 ES, aiming for an "apparent" kept
* frequency as stated.
*
* Any sequence end "pictures" will be ignored.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* (including the desired frequency)
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `seq_hdr` is a sequence header, i.e., that used by the next picture to
* output. This will be NULL if `frame` is NULL.
*
* Note that the caller should *not* free this, and that it will not be
* maintained over calls of this function (i.e., it is a reference to a
* value within the `fcontext` which is altered by this function).
*
* - `frame` is the next frame to output. This will be NULL if the last frame
* should be output again, to provide the requested apparent frequency.
*
* Note that it is the caller's responsibility to free this with
* `free_h262_picture()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of I and P frames found by this call of
* the function, including the item returned if appropriate.
*
* Returns 0 if it succeeds, EOF if end-of-file is read, or we've just read a
* sequence end item, or the last call ended a picture on a sequence end
* item, 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_filtered_h262_frame(h262_filter_context_p fcontext,
int verbose,
int quiet,
h262_picture_p *seq_hdr,
h262_picture_p *frame,
int *frames_seen);
/*
* Return the next IDR or I (and maybe any reference) frame from this H.264 ES.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `frame` is the next frame to output.
* Note that it is the caller's responsibility to free this with
* `free_access_unit()`.
* If an error or EOF is returned, this value is undefined.
* - `frames_seen` is the number of frames found by this call
* of the function, including the frame returned.
*
* Returns 0 if it succeeds, EOF if end-of-file is read (or an an end of
* stream NAL unit has been passed), 1 if some error occurs.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_stripped_h264_frame(h264_filter_context_p fcontext,
int verbose,
int quiet,
access_unit_p *frame,
int *frames_seen);
/*
* Retrieve the next frame from the H.264 (MPEG-4/AVC) ES, aiming
* for an "apparent" kept frequency as stated.
*
* Note that the ES data being read should be video-only.
*
* - `fcontext` is the information that tells us what to filter and how
* (including the desired frequency)
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* - `frame` is the next frame to output.
*
* If the function succeeds and `frame` is NULL, it means that the
* last frame should be output again.
*
* Note that it is the caller's responsibility to free this frame with
* `free_access_unit()`.
*
* If an error or EOF is returned, this value is undefined.
*
* - `frames_seen` is the number of frames found by this call of the function,
* including the frame returned.
*
* Returns 0 if all went well, 1 if something went wrong.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int get_next_filtered_h264_frame(h264_filter_context_p fcontext,
int verbose,
int quiet,
access_unit_p *frame,
int *frames_seen);
#endif // _filter_fns

131
fmtx.c 100644
Wyświetl plik

@ -0,0 +1,131 @@
/*
* Support for formatting time stamps
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include <string.h>
#include "compat.h"
#include "fmtx.h"
static TCHAR fmtx_buffers[FMTX_BUFFERS_COUNT][FMTX_BUFFER_SIZE];
static int fmtx_buf_no = 0;
TCHAR *
fmtx_alloc()
{
const int n = fmtx_buf_no++ % FMTX_BUFFERS_COUNT;
return fmtx_buffers[n];
}
int
frac_27MHz(int64 n)
{
return (int)((n < 0 ? -n : n) % 300LL);
}
const TCHAR *
fmtx_timestamp(int64 n, unsigned int flags)
{
TCHAR * buf = fmtx_alloc();
int64 n27 = n * ((flags & FMTX_TS_N_27MHz) != 0 ? 1LL : 300LL);
switch (flags & FMTX_TS_DISPLAY_MASK)
{
default:
case FMTX_TS_DISPLAY_90kHz_RAW:
_stprintf(buf, _T("%") I64FMT _T("dt"), n27 / 300LL);
break;
case FMTX_TS_DISPLAY_27MHz_RAW:
_stprintf(buf, _T("%") I64FMT _T("d:%03dt"), n27 / 300LL, frac_27MHz(n27));
break;
case FMTX_TS_DISPLAY_90kHz_32BIT:
{
int64 n90 = n27 / 300LL;
TCHAR * p = buf;
if (n90 < 0)
*p++ = _T('-');
_stprintf(p, _T("%ut"), (unsigned int)(n90 < 0 ? -n90 : n90));
break;
}
case FMTX_TS_DISPLAY_ms:
// No timestamp when converted into ms should exceed 32bits
_stprintf(buf, _T("%dms"), (int)(n27 / 27000LL));
break;
case FMTX_TS_DISPLAY_HMS:
{
unsigned int h, m, s, f;
int64 a27 = n27 < 0 ? -n27 : n27;
a27 /= I64K(27); //us
f = (unsigned int)(a27 % I64K(1000000));
a27 /= I64K(1000000);
s = (unsigned int)(a27 % I64K(60));
a27 /= I64K(60);
m = (unsigned int)(a27 % I64K(60));
h = (unsigned int)(a27 / I64K(60));
_stprintf(buf, "%s%u:%02u:%02u.%04u", n27 < 0 ? "-" : "", h, m, s, f/1000);
}
}
return (const TCHAR *)buf;
}
static const struct s2tsfss
{
const char * str;
int flags;
} s2tsf[] =
{
{"hms", FMTX_TS_DISPLAY_HMS},
{"ms", FMTX_TS_DISPLAY_ms},
{"90", FMTX_TS_DISPLAY_90kHz_RAW},
{"32", FMTX_TS_DISPLAY_90kHz_32BIT},
{"27", FMTX_TS_DISPLAY_27MHz_RAW},
{NULL, -1}
};
int
fmtx_str_to_timestamp_flags(const TCHAR * arg_str)
{
const struct s2tsfss * p;
for (p = s2tsf; p->str != NULL; ++p)
{
if (strcmp(p->str, arg_str) == 0)
break;
}
return p->flags;
}

58
fmtx.h 100644
Wyświetl plik

@ -0,0 +1,58 @@
/*
* Support for formatting time stamps
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#define FMTX_BUFFERS_COUNT 8
#define FMTX_BUFFER_SIZE 128
#ifdef _WIN32
#include <tchar.h>
#define I64FMT _T("I64")
#define I64K(x) x##I64
#else
typedef char TCHAR;
#define _T(x) x
#define I64FMT "ll"
#define I64K(x) x##LL
#define _stprintf sprintf
#define _tcscmp strcmp
#endif
// Flags to fmtx_time_stamp
#define FMTX_TS_N_90kHz 0 // Supplied time stamp is in 90kHz units
#define FMTX_TS_N_27MHz 1 // Supplied time stamp is in 27Mhz units
#define FMTX_TS_DISPLAY_MASK 0xff0
#define FMTX_TS_DISPLAY_90kHz_RAW 0
#define FMTX_TS_DISPLAY_90kHz_32BIT 0x10
#define FMTX_TS_DISPLAY_27MHz_RAW 0x20
#define FMTX_TS_DISPLAY_ms 0x30
#define FMTX_TS_DISPLAY_HMS 0x40
const TCHAR * fmtx_timestamp(int64 n, unsigned int flags);
int fmtx_str_to_timestamp_flags(const TCHAR * arg_str);

239
h222_defns.h 100644
Wyświetl plik

@ -0,0 +1,239 @@
/*
* Datastructures and definitions useful for working with H.222 data,
* whether it be Transport Stream or Program Stream
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _h222_defns
#define _h222_defns
// ------------------------------------------------------------
// H.222.0 Table 2-29: Stream type assignments, as amended by
// H.222.0 (2000) Amendment 3
//
// Value Description
// ===== ============================
// 00 ITU-T | ISO/IEC Reserved
// 01 ISO/IEC 11172-2 Video
// 02 ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2
// constrained parameter video stream
// 03 ISO/IEC 11172-3 Audio
// 04 ISO/IEC 13818-3 Audio
// 05 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections
// 06 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing
// private data -- traditionally DVB Dolby (AC-3)
// 07 ISO/IEC 13522 MHEG
// 08 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM CC
// 09 ITU-T Rec. H.222.1
// 0A ISO/IEC 13818-6 type A
// 0B ISO/IEC 13818-6 type B
// 0C ISO/IEC 13818-6 type C
// 0D ISO/IEC 13818-6 type D
// 0E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary
// 0F ISO/IEC 13818-7 Audio with ADTS transport syntax
// 10 ISO/IEC 14496-2 Visual
// 11 ISO/IEC 14496-3 Audio with the LATM transport syntax as defined
// in ISO/IEC 14496-3 / AMD 1
// 12 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried
// in PES packets
// 13 ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried
// in ISO/IEC14496_sections.
// 14 ISO/IEC 13818-6 Synchronized Download Protocol
// 15 Metadata carried in PES packets
// 16 Metadata carried in metadata_sections
// 17 Metadata carried in ISO/IEC 13818-6 Data Carousel
// 18 Metadata carried in ISO/IEC 13818-6 Object Carousel
// 19 Metadata carried in ISO/IEC 13818-6 Synchronized Download Protocol
// 1A IPMP stream (defined in ISO/IEC 13818-11, MPEG-2 IPMP)
// 1B AVC video stream as defined in ITU-T Rec. H.264 | ISO/IEC 14496-10
// Video
// 1C-7E ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
// 7F IPMP stream
// 80-FF User Private
// 81 Traditionally ATSC Dolby (AC-3)
#define H222_STREAM_TYPE_STR(s) \
((s)==0x00?"Reserved": \
(s)==0x01?"11172-2 video (MPEG-1)": \
(s)==0x02?"H.262/13818-2 video (MPEG-2) or 11172-2 constrained video": \
(s)==0x03?"11172-3 audio (MPEG-1)": \
(s)==0x04?"13818-3 audio (MPEG-2)": \
(s)==0x05?"H.222.0/13818-1 private sections": \
(s)==0x06?"H.222.0/13818-1 PES private data": \
(s)==0x07?"13522 MHEG": \
(s)==0x08?"H.222.0/13818-1 Annex A - DSM CC": \
(s)==0x09?"H.222.1": \
(s)==0x0A?"13818-6 type A": \
(s)==0x0B?"13818-6 type B": \
(s)==0x0C?"13818-6 type C": \
(s)==0x0D?"13818-6 type D": \
(s)==0x0E?"H.222.0/13818-1 auxiliary": \
(s)==0x0F?"13818-7 Audio with ADTS transport syntax": \
(s)==0x10?"14496-2 Visual (MPEG-4 part 2 video)": \
(s)==0x11?"14496-3 Audio with LATM transport syntax (14496-3/AMD 1)": \
(s)==0x12?"14496-1 SL-packetized or FlexMux stream in PES packets": \
(s)==0x13?"14496-1 SL-packetized or FlexMux stream in 14496 sections": \
(s)==0x14?"ISO/IEC 13818-6 Synchronized Download Protocol": \
(s)==0x15?"Metadata in PES packets": \
(s)==0x16?"Metadata in metadata_sections": \
(s)==0x17?"Metadata in 13818-6 Data Carousel": \
(s)==0x18?"Metadata in 13818-6 Object Carousel": \
(s)==0x19?"Metadata in 13818-6 Synchronized Download Protocol": \
(s)==0x1A?"13818-11 MPEG-2 IPMP stream": \
(s)==0x1B?"H.264/14496-10 video (MPEG-4/AVC)": \
(s)==0x42?"AVS Video": \
(0x1C < (s)) && ((s) < 0x7E)?"H.220.0/13818-1 reserved": \
(s)==0x7F?"IPMP stream": \
(s)==0x81?"User private": \
(0x80 <= (s)) && ((s) <= 0xFF)?"User private": \
"Unrecognised")
#define MPEG1_VIDEO_STREAM_TYPE 0x01
#define MPEG2_VIDEO_STREAM_TYPE 0x02 // H.262
#define AVC_VIDEO_STREAM_TYPE 0x1B // MPEG-4 part10 - H.264
#define AVS_VIDEO_STREAM_TYPE 0x42 // AVS -- Chinese standard
#define DVB_DOLBY_AUDIO_STREAM_TYPE 0x06 // [1]
#define ATSC_DOLBY_AUDIO_STREAM_TYPE 0x81 // [1]
#define MPEG2_AUDIO_STREAM_TYPE 0x04
#define MPEG1_AUDIO_STREAM_TYPE 0x03
#define ADTS_AUDIO_STREAM_TYPE 0x0F // AAC ADTS
#define MPEG4_PART2_VIDEO_STREAM_TYPE 0x10
#define LATM_AUDIO_STREAM_TYPE 0x11 // How much do we support this?
#define DOLBY_DVB_STREAM_TYPE 0x06 // [1]
#define DOLBY_ATSC_STREAM_TYPE 0x81 // [1]
// [1] In DVB (the European transmission standard) Dolby (AC-3) audio is
// carried in stream type 0x06, but in ATSC (the USA standard), stream
// type 0x81 is used. Note that both of these are essentially just saying
// that the data is a private stream, so technically one needs to set
// descriptors in the PMT as well to say we really mean Dolby (AC-3)
// Also, in DVB, other types of stream can be in 0x06.
#define IS_VIDEO_STREAM_TYPE(s) ((s)==MPEG1_VIDEO_STREAM_TYPE || \
(s)==MPEG2_VIDEO_STREAM_TYPE || \
(s)==AVC_VIDEO_STREAM_TYPE || \
(s)==AVS_VIDEO_STREAM_TYPE || \
(s)==MPEG4_PART2_VIDEO_STREAM_TYPE)
// Although I include Dolby in the "standard" audio types, beware that the
// stream type usage is not specified by H.222 itself - it is "convention"
// (albeit a standardised convention) how private streams are used to transmit
// Dolby. There is a case to be made that, at any one time, we should not
// recognise *both* potential Dolby stream types, but just one or the other
// (see [1] above) according to the standard the user is expecting. On the
// other hand, practice seems to be to use the stream types only in the
// expected manner.
#define IS_DOLBY_STREAM_TYPE(s) ((s)==DOLBY_DVB_STREAM_TYPE || \
(s)==DOLBY_ATSC_STREAM_TYPE)
#define IS_AUDIO_STREAM_TYPE(s) ((s)==MPEG1_AUDIO_STREAM_TYPE || \
(s)==MPEG2_AUDIO_STREAM_TYPE || \
(s)==ADTS_AUDIO_STREAM_TYPE || \
(s)==LATM_AUDIO_STREAM_TYPE || \
IS_DOLBY_STREAM_TYPE((s)))
// ------------------------------------------------------------
// Stream ids, as used in PES headers
// H.222.0 Table 2-18: Stream_id assignments, as amended by
// H.222.0 (2000) Amendment 3
//
// Note Hex stream_id stream coding
// ==== === ========= =============
// 1 BC 1011 1100 program_stream_map
// 2 BD 1011 1101 private_stream_1
// BE 1011 1110 padding_stream
// 3 BF 1011 1111 private_stream_2
// C0-DF 110x xxxx ISO/IEC 13818-3 or ISO/IEC 11172-3 or
// ISO/IEC 13818-7 or ISO/IEC 14496-3 audio stream
// number x xxxx
// Ex 1110 xxxx ITU-T Rec. H.262 | ISO/IEC 13818-2, ISO/IEC 11172-2,
// ISO/IEC 14496-2 or ITU-T Rec. H.264 | ISO/IEC
// 14496-10 video stream number xxxx
// 3 F0 1111 0000 ECM_stream
// F1 1111 0001 EMM_stream
// 5 F2 1111 0010 ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A or
// ISO/IEC 13818-6_DSMCC_stream
// 2 F3 1111 0011 ISO/IEC_13522_stream
// 6 F4 1111 0100 ITU-T Rec. H.222.1 type A
// 6 F5 1111 0101 ITU-T Rec. H.222.1 type B
// 6 F6 1111 0110 ITU-T Rec. H.222.1 type C
// 6 F7 1111 0111 ITU-T Rec. H.222.1 type D
// 6 F8 1111 1000 ITU-T Rec. H.222.1 type E
// 7 F9 1111 1001 ancillary_stream
// FA 1111 1010 ISO/IEC14496-1_SL-packetized_stream
// FB 1111 1011 ISO/IEC14496-1_FlexMux_stream
// FC 1111 1100 descriptive data stream
// FD 1111 1101 reserved data stream
// FE 1111 1110 reserved data stream
// 4 FF 1111 1111 program_stream_directory
//
// The notation x means that the values '0' or '1' are both permitted and
// results in the same stream type. The stream number is given by the values
// taken by the x's.
//
// NOTES
// 1 PES packets of type program_stream_map have unique syntax specified
// in 2.5.4.1.
// 2 PES packets of type private_stream_1 and ISO/IEC_13552_stream follow
// the same PES packet syntax as those for ITU-T Rec. H.262 | ISO/IEC
// 13818-2 video and ISO/IEC 13818-3 audio streams.
// 3 PES packets of type private_stream_2, ECM_stream and EMM_stream
// are similar to private_stream_1 except no syntax is specified after
// PES_packet_length field.
// 4 PES packets of type program_stream_directory have a unique syntax
// specified in 2.5.5.
// 5 PES packets of type DSM-CC_stream have a unique syntax specified
// in ISO/IEC 13818- 6.
// 6 This stream_id is associated with stream_type 0x09 in Table 2-29.
// 7 This stream_id is only used in PES packets, which carry data from
// a Program Stream or an ISO/IEC 11172-1 System Stream, in a Transport
// Stream (refer to 2.4.3.7).
#define PADDING_STREAM_ID 0xBE
#define PRIVATE1_AUDIO_STREAM_ID 0xBD
#define PRIVATE2_AUDIO_STREAM_ID 0xBF
#define DEFAULT_VIDEO_STREAM_ID 0xE0 // i.e., stream 0
#define DEFAULT_AUDIO_STREAM_ID 0xC0 // i.e., stream 0
#define IS_AUDIO_STREAM_ID(id) ((id)==0xBD || ((id) >= 0xC0 && (id) <= 0xDF))
#define IS_VIDEO_STREAM_ID(id) ((id) >= 0xE0 && (id) <= 0xEF)
// ------------------------------------------------------------
// Timing info (used in reporting on packets). Initialise to all zeroes...
struct timing
{
u_int64 first_pcr;
u_int64 last_pcr;
int first_pcr_packet;
int last_pcr_packet;
int had_first_pcr; // FALSE until we've started
};
typedef struct timing *timing_p;
#endif // _h222_defns

1198
h262.c 100644

Plik diff jest za duży Load Diff

248
h262_defns.h 100644
Wyświetl plik

@ -0,0 +1,248 @@
/*
* Datastructures for reading H.262 (MPEG-2) elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _h262_defns
#define _h262_defns
#include <stdio.h>
#include "compat.h"
#include "es_defns.h"
#include "ts_defns.h"
// Since reverse_data refers to h262 and acces_unit datastructures, and
// *they* refer to reverse_data, we need to break the circular referencing
// at some point
typedef struct h262_context *h262_context_p;
#include "reverse_defns.h"
// ------------------------------------------------------------
// An MPEG "item", the set of bytes that starts with a start code prefix.
struct _h262_item
{
struct ES_unit unit; // The actual data
// MPEG2 specific data
byte picture_coding_type; // only defined if unit.start_code == 0
};
typedef struct _h262_item *h262_item_p;
#define SIZEOF_H262_ITEM sizeof(struct _h262_item)
// ------------------------------------------------------------
// An H.262 "picture". This might be a picture or a sequence header (and
// its associated items).
struct _h262_picture
{
// The main thing we need is a list of the items that make up this picture
ES_unit_list_p list;
// An H.262 "picture" might be a "proper" picture, a sequence header,
// or (just) a sequence end item. It's useful to be able to identify
// the two more common cases easily
int is_picture;
int is_sequence_header;
// Data defined for a picture. When a picture is composed of the data
// from two fields, then these will be the values taken from the first
// field "picture".
byte picture_coding_type; // I, P or B
byte picture_structure; // top/bottom field or frame
u_int16 temporal_reference; // presentation order within a group
byte afd; // its "Active Format Description" value
// (NB: with 0xF0 bits set at top of byte)
byte is_real_afd; // was it a *real* AFD?
int was_two_fields; // TRUE if it's a frame merged from two fields
// Data defined for a sequence header/extension
// Note that H.262 requires that data given in one sequence extension
// shall be the same as that in all the others in the stream. Thus, in
// particular, we know that if fields are allowed by one sequence
// extension, they will be allowed by all.
byte progressive_sequence; // frames or frames and fields allowed?
// Data defined for both
// (in a frame, this is the value from the previous section header)
byte aspect_ratio_info; // its aspect ratio code
};
typedef struct _h262_picture *h262_picture_p;
#define SIZEOF_H262_PICTURE sizeof(struct _h262_picture)
// ------------------------------------------------------------
// Produce a nice string for the start code. `b` must be a byte.
#define H262_START_CODE_STR(b) \
((b)==0x00?"picture": \
(b)>=0x01 && b<=0xAF?"slice": \
(b)==0xB0?"reserved": \
(b)==0xB1?"reserved": \
(b)==0xB2?"user data": \
(b)==0xB3?"sequence header": \
(b)==0xB4?"sequence error": \
(b)==0xB5?"extension start": \
(b)==0xB6?"reserved": \
(b)==0xB7?"sequence end": \
(b)==0xB8?"group start": \
(b)>=0xB9?"system start":"???")
#define is_h262_picture_item(item) ((item)->unit.start_code==0x00)
#define is_h262_slice_item(item) ((item)->unit.start_code>=0x01 && \
(item)->unit.start_code<=0xAF)
#define is_h262_user_data_item(item) ((item)->unit.start_code==0xB2)
#define is_h262_seq_header_item(item) ((item)->unit.start_code==0xB3)
#define is_h262_seq_end_item(item) ((item)->unit.start_code==0xB7)
#define is_h262_group_start_item(item) ((item)->unit.start_code==0xB8)
#define is_h262_extension_start_item(item) ((item)->unit.start_code==0xB5)
#define H262_PICTURE_CODING_STR(s) \
((s)==0?"Forbidden": \
(s)==1?"I": \
(s)==2?"P": \
(s)==3?"B": \
(s)==4?"D":"Reserved")
// The following two macros can be used on H.262 items *or* pictures
#define is_I_picture(picture) ((picture)->picture_coding_type==1)
#define is_P_picture(picture) ((picture)->picture_coding_type==2)
#define H262_PICTURE_STRUCTURE_STR(s) \
((s)==0?"Reserved": \
(s)==1?"Top Field": \
(s)==2?"Bottom Field": \
(s)==3?"Frame":"???")
#define is_h262_field_picture(picture) ((picture)->is_picture && \
((picture)->picture_structure == 1 ||\
(picture)->picture_structure == 2))
#define is_h262_AFD_user_data_item(item) \
((item)->unit.start_code == 0xB2 && \
(item)->unit.data_len > 8 && \
(item)->unit.data[4] == 0x44 && \
(item)->unit.data[5] == 0x54 && \
(item)->unit.data[6] == 0x47 && \
(item)->unit.data[7] == 0x31)
#define UNSET_AFD_BYTE 0xF8 // i.e., '1111 1000'
// String values taken from ATSC Digital Television Standard, Rev C,
// (A/53C) 12 May 2004, which will hopefully be correct for DVB as well...
#define AFD_STR(afd) \
((afd)==0xF2?"ATSC: box 16:9 (top)": \
(afd)==0xF3?"ATSC: box 14:9 (top)": \
(afd)==0xF4?"ATSC: box > 16:9 (center)": \
(afd)==0xF8?"Active format as coded frame": \
(afd)==0xF9?"4:3 (centre)": \
(afd)==0xFA?"16:9 (centre)": \
(afd)==0xFB?"14:9 (centre)": \
(afd)==0xFC?"reserved": \
(afd)==0xFD?"4:3 (with shoot & protect 14:9 centre)": \
(afd)==0xFE?"16:9 (with shoot & protect 14:9 centre)": \
(afd)==0xFF?"16:9 (with shoot & protect 4:3 centre)":"reserved")
#define SHORT_AFD_STR(afd) \
((afd)==0xF2?"ATSC: box 16:9 (top)": \
(afd)==0xF3?"ATSC: box 14:9 (top)": \
(afd)==0xF4?"ATSC: box > 16:9 (center)": \
(afd)==0xF8?"As coded frame": \
(afd)==0xF9?"4:3 (centre)": \
(afd)==0xFA?"16:9 (centre)": \
(afd)==0xFB?"14:9 (centre)": \
(afd)==0xFC?"reserved": \
(afd)==0xFD?"4:3 (14:9)": \
(afd)==0xFE?"16:9 (14:9)": \
(afd)==0xFF?"16:9 (4:3)":"reserved")
// The standard does not define value FF, so I'm choosing it as an "unset"
// value, so that I get sensible diagnostics when a picture is reported
// on before a section header has been read
#define H262_UNSET_ASPECT_RATIO_INFO 0xFF
#define H262_ASPECT_RATIO_INFO_STR(rat) ((rat)==0xFF?"Unset": \
(rat)==0?"Forbidden aspect ratio": \
(rat)==1?"Square": \
(rat)==2?"4:3": \
(rat)==3?"16:9": \
"Reserved aspect ratio")
// ------------------------------------------------------------
// Context for looping over the H.262 items and pictures in an elementary
// stream
struct h262_context
{
ES_p es;
// We count all of the pictures as we read them (this is useful
// when we are building up reverse_data arrays). If functions
// move around in the data stream, we assume that they will
// (re)set this to a sensible value.
// The index of the first picture read is 1, and this value is
// incremented by each call of `get_next_h262_picture` (note that
// for this purpose, sequence headers are *not* considered pictures)
u_int32 picture_index; // The index of the last picture read
// We detect the end of an H.262 picture (or sequence header) by
// reading the first item that cannot be part of it. We then need
// to remember that item for *next* time we try to read a picture.
h262_item_p last_item;
// What was the aspect ratio code from the last sequence header?
byte last_aspect_ratio_info;
// When we are reading MPEG-2, we may encounter AFD (Active Format
// Definiton) user data. This sets the aspect ratio of the following
// picture(s), possibly overriding information present in a preceding
// sequence header. It should only be present for I pictures, really,
// but we remember it for all pictures.
// Once an AFD value has been set, it stands until another value is read.
// This all becomes important when reversing, which see.
// Anyway, in order to set the AFD on pictures which don't have it
// explicitly, we obviously need to remember the last value found.
// For simplicity of use, we remember the whole byte it came in,
// including the 0xF0 reserved bits at the top of the byte.
// Note that '1000' (8) is the "unset" value.
byte last_afd;
// When we are reading an H.262 picture back in, for reversing purposes, or
// reading frames for filtering, it is useful to *insist* that an AFD be
// found for the picture. Thus, if `add_fake_afd` is TRUE, a dummy AFD,
// containing the value in `last_afd`, will be inserted into the picture's
// ES unit `list` (of course, if the picture contains a *real* AFD, this is
// not necessary). Beware that the ES unit inserted won't stand up to close
// observation, as its start position (for instance) will clearly be
// wrong...
// (this is manipulated by the reversing and filtering code - it is not
// intended for use for any other purpose)
int add_fake_afd;
// If we are collecting reversing information, then we keep a reference
// to the reverse data here
reverse_data_p reverse_data;
// In the same context, we need to remember how long it is since the
// last sequence header
byte count_since_seq_hdr;
};
#define SIZEOF_H262_CONTEXT sizeof(struct h262_context)
#endif // _h262_defns

235
h262_fns.h 100644
Wyświetl plik

@ -0,0 +1,235 @@
/*
* Prototypes for reading H.262 (MPEG-2) elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _h262_fns
#define _h262_fns
#include "h262_defns.h"
/*
* Print out information derived from the start code, to the given stream.
*
* Note that if a "SYSTEM START" code is reported, then the data is
* likely to be PES or Transport Stream data, not Elementary Stream.
*
* Similarly, if a "TRANSPORT STREAM sync byte" is reported, then
* the stream is probably Transport Stream.
*
* If the stream is *not* Elementary Stream data, then it is possible
* that some of the apparent start code prefixes are actually false
* detections.
*/
extern void print_h262_start_code_str(FILE *stream,
byte start_code);
/*
* Build a new MPEG2 item datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_h262_item(h262_item_p *item);
/*
* Tidy up and free an MPEG2 item datastructure after we've finished with it.
*
* Empties the MPEG2 item datastructure, frees it, and sets `item` to NULL.
*
* If `item` is already NULL, does nothing.
*/
extern void free_h262_item(h262_item_p *item);
/*
* Print out useful information about this MPEG2 item, on the given stream.
*/
extern void report_h262_item(FILE *stream,
h262_item_p item);
// ------------------------------------------------------------
// MPEG2 item *data* stuff
// ------------------------------------------------------------
/*
* Find and read in the next MPEG2 item.
*
* Be careful if using this in conjunction with reading H.262 pictures
* via an `h262_context_p`, as it does not maintain the "last item read"
* information therein.
*
* - `es` is the elementary stream we're reading from.
* - `item` is the datastructure containing the MPEG2 item found, or NULL
* if there was none.
*
* Returns 0 if it succeeds, EOF if the end-of-file is read (i.e., there
* is no next MPEG2 item), otherwise 1 if some error occurs.
*/
extern int find_next_h262_item(ES_p es,
h262_item_p *item);
/*
* Build a new H.262 picture reading context.
*
* This acts as a "jacket" around the ES context, and is used when reading
* H.262 pictures with get_next_h262_picture(). It "remembers" the last
* item read, which is the first item that was not part of the picture.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_h262_context(ES_p es,
h262_context_p *context);
/*
* Free an H.262 picture reading context.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does not free any `reverse_data` datastructure.
*
* Does nothing if `context` is already NULL.
*/
extern void free_h262_context(h262_context_p *context);
/*
* Rewind a file being read as H.262 pictures
*
* This is a wrapper for `seek_ES` that also knows to unset things appropriate
* to the H.262 picture reading context.
*
* If a reverse context is attached to this context, it also will
* be "rewound" appropriately.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_h262_context(h262_context_p context);
/*
* Free an H.262 "picture".
*
* Clears the datastructure, frees it, and returns `picture` as NULL.
*
* Does nothing if `picture` is already NULL.
*/
extern void free_h262_picture(h262_picture_p *picture);
/*
* Compare two H.262 pictures. The comparison does not include the start
* position of the picture, but just the actual data - i.e., two pictures
* read from different locations in the input stream may be considered the
* same if their data content is identical.
*
* Returns TRUE if the lists contain identical content, FALSE otherwise.
*/
extern int same_h262_picture(h262_picture_p picture1,
h262_picture_p picture2);
/*
* Retrieve the the next H.262 "picture".
*
* The H.262 "picture" returned can be one of:
*
* 1. A field or frame, including its slices.
* 2. A sequence header, including its sequence extension, if any.
* 3. A sequence end.
*
* - `context` is the H.262 picture reading context.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `picture` is the H.262 "picture", containing a field or frame picture,
* a sequence header or a sequence end
*
* Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some
* error occurs.
*/
extern int get_next_h262_single_picture(h262_context_p context,
int verbose,
h262_picture_p *picture);
/*
* Retrieve the the next H.262 "picture".
*
* The H.262 "picture" returned can be one of:
*
* 1. A frame, including its slices. This may be the concatenation of two
* adjacent field pictures.
* 2. A sequence header, including its sequence extension, if any.
* 3. A sequence end.
*
* Specifically, the next H.262 "picture" is retrieved from the input stream.
*
* If that "picture" represents a sequence header or a frame, it is returned.
*
* If it represents a field, then the *following* "picture" is retrieved, and
* if that is the second field of its frame, it is merged into the first,
* and the resultant frame is returned.
*
* If a field with temporal reference A is followed by a field with temporal
* reference B, it is assumed that synchronisation has been lost. In this
* case, the first field (frame A) will be discarded, and an attempt made to
* read the second field of frame B.
*
* Similarly, if a frame or sequence header is found instead of the second
* field, the first field will be discarded and the frame returned.
*
* Note that if the context is associated with a reverse context,
* then appropriate frames/sequence headers will automatically be
* remembered therein.
*
* - `context` is the H.262 picture reading context.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `picture` is the H.262 "picture", containing a frame picture,
* a sequence header or a sequence end
*
* Returns 0 if it succeeds, EOF if we reach the end of file, or 1 if some
* error occurs.
*/
extern int get_next_h262_frame(h262_context_p context,
int verbose,
int quiet,
h262_picture_p *picture);
/*
* Write out an H.262 picture as TS
*
* - `tswriter` is TS the output stream
* - `picture` is the picture to write out
* - `pid` is the PID to use for the TS packets
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_h262_picture_as_TS(TS_writer_p tswriter,
h262_picture_p picture,
u_int32 pid);
/*
* Write out a picture (as stored in an ES unit list) as ES
*
* - `output` is the ES output file
* - `picture` is the picture to write out
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int write_h262_picture_as_ES(FILE *output,
h262_picture_p picture);
/*
* Report on an H.262 picture's contents.
*
* - `stream` is where to write the information
* - `picture` is the picture to report on
* - if `report_data`, then the component ES units will be printed out as well
*/
extern void report_h262_picture(FILE *stream,
h262_picture_p picture,
int report_data);
#endif // _h262_fns

375
l2audio.c 100644
Wyświetl plik

@ -0,0 +1,375 @@
/*
* Support for MPEG layer 2 audio streams.
*
* (actually, support for
*
* - MPEG-1 audio (described in ISO/IEC 11172-3), layers 1..3
* - MPEG-2 audio (described in ISO/IEC 13818-3), layer 2
* - unofficial MPEG-2.5
*
* but MPEG-2 layer 2 is the main target)
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include "compat.h"
#include "misc_fns.h"
#include "l2audio_fns.h"
#define DEBUG 0
// Bitrates by index, according to layer and protocol version
// Note that v3 is actually the mutant V2.5 protocol
static const int bitrate_v1l1[] =
{
0, 32, 64, 96, 128, 160, 192, 224,
256, 288, 320, 352, 384, 416, 448, 0
};
static const int bitrate_v1l2[] =
{
0, 32, 48, 56, 64, 80, 96, 112,
128, 160, 192, 224, 256, 320, 384, 0
};
static const int bitrate_v1l3[] =
{
0, 32, 40, 48, 56, 64, 80, 96,
112, 128, 160, 192, 224, 256, 320, 0
};
static const int bitrate_v2l1[] =
{
0, 32, 48, 56, 64, 80, 96, 112,
128, 144, 160, 176, 192, 224, 256, 0
};
static const int bitrate_v2l2[] =
{
0, 8, 16, 24, 32, 40, 48, 56,
64, 80, 96, 112, 128, 144, 160, 0
};
static const int * const bitrate_table[3][3] =
{
{ bitrate_v1l1, bitrate_v1l2, bitrate_v1l3 },
{ bitrate_v2l1, bitrate_v2l2, bitrate_v2l2 },
{ bitrate_v2l1, bitrate_v2l2, bitrate_v2l2 }
};
// Sample rates table
static const unsigned int sampling_table[3][3] =
{
{ 44100, 48000, 32000 },
{ 22050, 24000, 16000 },
{ 11025, 12000, 8000 }
};
// Decode frame header information
#define AUD_FRAME_RATE_N_0 96000
#define AUD_FRAME_RATE_N_1 88200
#define AUD_FRAME_RATE_N_2 64000
#define AUD_FRAME_RATE_N_3 48000
#define AUD_FRAME_RATE_N_4 44100
#define AUD_FRAME_RATE_N_5 32000
#define AUD_FRAME_RATE_N_6 24000
#define AUD_FRAME_RATE_N_7 22050
#define AUD_FRAME_RATE_N_8 16000
#define AUD_FRAME_RATE_N_9 12000
#define AUD_FRAME_RATE_N_10 11025
#define AUD_FRAME_RATE_N_11 8000
#define AUD_FRAME_RATE_N_12 7350
#define AUD_FRAME_RATE_N_13 0
#define AUD_FRAME_RATE_N_14 0
#define AUD_FRAME_RATE_N_15 0
const unsigned int aud_frame_rate_n[16] =
{
AUD_FRAME_RATE_N_0,
AUD_FRAME_RATE_N_1,
AUD_FRAME_RATE_N_2,
AUD_FRAME_RATE_N_3,
AUD_FRAME_RATE_N_4,
AUD_FRAME_RATE_N_5,
AUD_FRAME_RATE_N_6,
AUD_FRAME_RATE_N_7,
AUD_FRAME_RATE_N_8,
AUD_FRAME_RATE_N_9,
AUD_FRAME_RATE_N_10,
AUD_FRAME_RATE_N_11,
AUD_FRAME_RATE_N_12,
AUD_FRAME_RATE_N_13,
AUD_FRAME_RATE_N_14,
AUD_FRAME_RATE_N_15
};
/*
* Look at a frame header and try to deduce the length of the frame.
*
* Returns the frame length deduced therefrom, or -1 if it finds something
* wrong with the header data.
*/
static int peek_frame_header(const u_int32 header)
{
unsigned int version, layer, padding;
// byte protected, private;
// byte mode, modex, copyright, original, emphasis;
unsigned int bitrate_enc, sampling_enc;
unsigned int bitrate, sampling;
byte rate;
unsigned int framesize, framelen;
// Version:
// 00 - MPEG Version 2.5
// 01 - reserved
// 10 - MPEG Version 2 (ISO/IEC 13818-3)
// 11 - MPEG Version 1 (ISO/IEC 11172-3)
version = (header >> 19) & 0x03;
if (version == 1)
{
fprintf(stderr,"### Illegal version (1) in MPEG layer 2 audio header\n");
return -1;
}
version = (version == 3) ? 1: (version == 2) ? 2: 3;
// Layer:
// 00 - reserved
// 01 - Layer 3
// 10 - Layer 2
// 11 - Layer 1
layer = (header >> 17) & 0x03;
if (layer == 0)
{
fprintf(stderr,"### Illegal layer (0) in MPEG layer 2 audio header\n");
return -1;
}
layer = 4 - layer;
// protected (i.e. CRC present) field
// protected = ! ((header >> 16) & 0x01);
// bitrate field, whose meaning is dependent on version and layer
bitrate_enc = (header >> 12) & 0x0f;
if (bitrate_enc == 0x0f)
{
fprintf(stderr,"### Illegal bitrate_enc (0x0f) in MPEG layer 2 audio header\n");
return -1;
}
bitrate = (bitrate_table[version-1][layer-1])[bitrate_enc];
if (bitrate == 0) // bitrate now in kbits per channel
{
fprintf(stderr,"### Illegal bitrate (0 kbits/channel) in MPEG level 2"
" audio header\n");
return -1;
}
// sample rate field, whose meaning is dependent on version
sampling_enc = (header >> 10) & 0x03;
if (sampling_enc == 3)
{
fprintf(stderr,"### Illegal sampleing_enc (3) in MPEG layer 2 audio header\n");
return -1;
}
sampling = sampling_table[version-1][sampling_enc];
// Make an AAC rate number from the rate number
rate = (version * 3) + (sampling_enc & 2) + (sampling_enc == 0);
// padding and private fields
padding = (header >> 9) & 0x01;
// private = (header >> 8) & 0x01; // private doesn't get used
// mode and mode extension - these are also not used
// Channel Mode
// 00 - Stereo
// 01 - Joint stereo (Stereo)
// 10 - Dual channel (Stereo)
// 11 - Single channel (Mono)
// mode = (header >> 6) & 0x03;
// modex = (header >> 4) & 0x03;
// miscellaneous other things we ignore
// copyright = (header >> 3) & 0x01;
// original = (header >> 2) & 0x01;
// emphasis = (header >> 0) & 0x03;
// generate framesize and frame length
// (for the moment, we only *use* the frame length)
if (layer == 1)
{
framesize = 384; // samples
framelen = (12000 * bitrate / aud_frame_rate_n[rate] + padding) * 4;
}
else if (version == 1)
{
framesize = 1152; // samples
framelen = (144000 * bitrate / aud_frame_rate_n[rate] + padding);
}
else
{
framesize = 576; // samples
framelen = (72000 * bitrate / aud_frame_rate_n[rate] + padding);
}
return framelen;
}
/*
* Build a new layer2 audio frame datastructure
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
static inline int build_audio_frame(audio_frame_p *frame)
{
audio_frame_p new = malloc(SIZEOF_AUDIO_FRAME);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate audio frame datastructure\n");
return 1;
}
new->data = NULL;
new->data_len = 0;
*frame = new;
return 0;
}
/*
* Read the next audio frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the audio file to read from
* - `frame` is the audio frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_l2audio_frame(int file,
audio_frame_p *frame)
{
#define JUST_ENOUGH 6 // just enough to hold the bits of the headers we want
int err, ii;
byte header[JUST_ENOUGH];
byte *data = NULL;
int frame_length; // XXXX Really 626.94 on average
offset_t posn = tell_file(file);
#if DEBUG
printf("Offset: " OFFSET_T_FORMAT "\n",posn);
#endif
err = read_bytes(file,JUST_ENOUGH,header);
if (err == EOF)
return EOF;
else if (err)
{
fprintf(stderr,"### Error reading header bytes of MPEG layer 2 audio frame\n");
fprintf(stderr," (in frame starting at " OFFSET_T_FORMAT ")\n",posn);
free(data);
return 1;
}
#if DEBUG
printf("MPEG layer 2 frame\n");
print_data(stdout,"Start",header,JUST_ENOUGH,JUST_ENOUGH);
#endif
while (header[0] != 0xFF || (header[1] & 0xe0) != 0xe0)
{
int skip = JUST_ENOUGH;
fprintf(stderr,
"### MPEG layer 2 audio frame does not start with '1111 1111 111x'\n"
" syncword - lost synchronisation?\n"
" Found 0x%X%X%X instead of 0xFFE\n",
(header[0] & 0xF0) >> 4,
(header[0] & 0x0F),
(header[1] & 0xe0) >> 4);
fprintf(stderr," (in frame starting at " OFFSET_T_FORMAT ")\n",posn);
do
{
err = read_bytes(file,1,header);
skip++;
if (err == 0 && header[0] == 0xff)
{
err = read_bytes(file,1,header + 1);
skip++;
if (err == 0 && (header[1] & 0xe0) == 0xe0)
{
err = read_bytes(file,JUST_ENOUGH - 2, header + 2);
break;
}
}
} while (!err);
if (err) return 1;
fprintf(stderr,"#################### Resuming after %d skipped bytes\n",skip);
}
frame_length = peek_frame_header((header[1] << 16) | (header[2] << 8) | header[3]);
if (frame_length < 1)
{
fprintf(stderr,"### Bad MPEG layer 2 audio header\n");
return 1;
}
data = malloc(frame_length);
if (data == NULL)
{
fprintf(stderr,"### Unable to extend data buffer for MPEG layer 2 audio frame\n");
free(data);
return 1;
}
for (ii=0; ii<JUST_ENOUGH; ii++)
data[ii] = header[ii];
err = read_bytes(file,frame_length - JUST_ENOUGH,&(data[JUST_ENOUGH]));
if (err)
{
if (err == EOF)
fprintf(stderr,"### Unexpected EOF reading rest of MPEG layer 2 audio frame\n");
else
fprintf(stderr,"### Error reading rest of MPEG layer 2 audio frame\n");
free(data);
return 1;
}
err = build_audio_frame(frame);
if (err)
{
free(data);
return 1;
}
(*frame)->data = data;
(*frame)->data_len = frame_length;
return 0;
}

65
l2audio_fns.h 100644
Wyświetl plik

@ -0,0 +1,65 @@
/*
* Support for MPEG layer 2 audio streams.
*
* (actually, support for
*
* - MPEG-1 audio (described in ISO/IEC 11172-3), layers 1..3
* - MPEG-2 audio (described in ISO/IEC 13818-3), layer 2
* - unofficial MPEG-2.5
*
* but MPEG-2 layer 2 is the main target)
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _l2audio_fns
#define _l2audio_fns
#include "audio_defns.h"
/*
* Tidy up and free an audio frame datastructure when we've finished with it
*
* Empties the datastructure, frees it, and sets `frame` to NULL.
*
* If `frame` is already NULL, does nothing.
*/
extern void free_audio_frame(audio_frame_p *frame);
/*
* Read the next audio frame.
*
* Assumes that the input stream is synchronised - i.e., it does not
* try to cope if the next three bytes are not '1111 1111 1111'.
*
* - `file` is the file descriptor of the audio file to read from
* - `frame` is the audio frame that is read
*
* Returns 0 if all goes well, EOF if end-of-file is read, and 1 if something
* goes wrong.
*/
extern int read_next_l2audio_frame(int file,
audio_frame_p *frame);
#endif // _l2audio_fns

1400
misc.c 100644

Plik diff jest za duży Load Diff

58
misc_defns.h 100644
Wyświetl plik

@ -0,0 +1,58 @@
/*
* Miscellaneous useful definitions
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _misc_defns
#define _misc_defns
#include "tswrite_defns.h"
#include "video_defns.h"
// Some (internal) functions find it convenient to have a union of the
// possible output streams. Rather than duplicate the definition of these,
// we put them here...
union _writer
{
FILE *es_output; // output to an ES file
TS_writer_p ts_output; // output via a TS writer
};
typedef union _writer WRITER;
// In the programs that handle command lines, it's useful to have a simple
// macro for checking the presence of subsidiary arguments.
// Assumes that argc and argv have their normal names.
#define CHECKARG(program,argno) \
if ((argno)+1 == argc) \
{ \
fprintf(stderr,"### %s: missing argument to %s\n",program,argv[(argno)]); \
return 1; \
}
// A simple macro to return a bit from a bitfield, for use in printf()
#define ON(byt,msk) ((byt & msk)?1:0)
#endif // _misc_defns

412
misc_fns.h 100644
Wyświetl plik

@ -0,0 +1,412 @@
/*
* Miscellaneous useful functions.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _misc_fns
#define _misc_fns
#include "misc_defns.h"
#include "es_defns.h"
#ifdef _WIN32
#include <winsock2.h>
#endif // _WIN32
#define CRC32_POLY 0x04c11db7L
/*
* Compute CRC32 over a block of data, by table method.
*
* Returns a working value, suitable for re-input for further blocks
*
* Notes: Input value should be 0xffffffff for the first block,
* else return value from previous call (not sure if that
* needs complementing before being passed back in).
*/
extern u_int32 crc32_block(u_int32 crc, byte *pData, int blk_len);
/*
* Print out the bottom N bits from a byte on the given stream
*/
extern void print_bits(FILE *stream,
int num_bits,
byte value);
/*
* Print out (the first `max`) bytes of a byte array.
*
* - `stream` is the stream to print on.
* - `name` is identifying text to start the report with.
* - `data` is the byte data to print. This may be NULL.
* - `length` is its length
* - `max` is the maximum number of bytes to print
*
* Prints out::
*
* <name> (<length>): b1 b2 b3 b4 ...
*
* where no more than `max` bytes are to be printed (and "..." is printed
* if not all bytes were shown).
*/
extern void print_data(FILE *stream,
char *name,
byte data[],
int length,
int max);
/*
* Print out (the last `max`) bytes of a byte array.
*
* - `stream` is the stream to print on.
* - `name` is identifying text to start the report with.
* - `data` is the byte data to print. This may be NULL.
* - `length` is its length
* - `max` is the maximum number of bytes to print
*
* Prints out::
*
* <name> (<length>): ... b1 b2 b3 b4
*
* where no more than `max` bytes are to be printed (and "..." is printed
* if not all bytes were shown).
*/
extern void print_end_of_data(FILE *stream,
char *name,
byte data[],
int length,
int max);
/*
* Calculate log2 of `x` - for some reason this is missing from <math.h>
*/
extern double log2(double x);
// ============================================================
// Simple file I/O utilities
// ============================================================
/*
* Read a given number of bytes from a file.
*
* This is a jacket for `read`, allowing for the future possibility of
* buffered input, and simplifying error handling.
*
* - `input` is the file descriptor for the file
* - `num_bytes` is how many bytes to read
* - `data` is the buffer to read the bytes into
*
* Returns 0 if all goes well, EOF if end of file was read, or 1 if some
* other error occurred (in which case it will already have output a message
* on stderr about the problem).
*/
extern int read_bytes(int input,
int num_bytes,
byte *data);
/*
* Utility function to seek within a file
*
* - `filedes` is the file to seek within
* - `posn` is the position to which to seek
*
* This is a jacket for::
*
* new_posn = lseek(filedes,posn,SEEK_SET);
*
* Returns 0 if all went well, 1 if the seek failed (either because
* it returned -1, or because the position reached was not the position
* requested). If an error occurs, then an explanatory message will
* already have been written to stderr.
*/
extern int seek_file(int filedes,
offset_t posn);
/*
* Utility function to report the current location within a file
*
* - `filedes` is the file to seek within
*
* This is a jacket for::
*
* posn = lseek(filedes,0,SEEK_CUR);
*
* Returns the current position in the file if all went well, otherwise
* -1 (in which case an error message will already have been written
* on stderr)
*/
extern offset_t tell_file(int filedes);
/*
* Utility function to open a file (descriptor), and report any errors
*
* This is intended only for very simple usage, and is not mean to be
* a general purpose "open" replacement.
*
* - `filename` is the name of the file to open
* - `for_write` should be TRUE if the file is to be written to,
* in which case it will be opened with flags O_WRONLY|O_CREAT|O_TRUNC,
* or FALSE if the file is to be read, in which case it will be
* opened with flag O_RDONLY. In both cases, on Windows the flag
* O_BINARY will also be set.
*
* Returns the file descriptor for the file, or -1 if it failed to open
* the file.
*/
extern int open_binary_file(char *filename,
int for_write);
/*
* Utility function to close a file (descriptor), and report any errors
*
* Returns 0 if all went well, 1 if an error occurred.
*/
extern int close_file(int filedes);
// ============================================================
// More complex file I/O utilities
// ============================================================
/*
* Open an input file appropriately for reading as ES.
*
* - `name` is the name of the file, or NULL if standard input
* is to be read from (which is not allowed if `use_pes` is
* TRUE).
*
* - If `use_pes` is true then the input file is PS or TS and should
* be read via a PES reader.
*
* - If `quiet` is true then information about the file being read will
* not be written out. Otherwise, its name and what is decided about
* its content will be printed.
*
* - If `force_stream_type` is true, then the caller asserts that
* the input shall be read according to `want_data`, and not whatever
* might be deduced from looking at the file itself.
*
* - If `force_stream_type` is true, then `want_data` should be one of
* VIDEO_H262, VIDEO_H264 or VIDEO_AVS. `is_data` will then be
* returned with the same value.
*
* - If `force_stream_type` is false, then the function will attempt
* to determine what type of data it has, and `is_data` will be set
* to whatever is determined (presumably one of VIDEO_H262, VIDEO_H264
* or VIDEO_AVS).
*
* - If input is from standard input, and `force_stream_type` is FALSE,
* `is_data` will always be set to VIDEO_H262, which may be incorrect.
*
* - `es` is the new ES reader context.
*
* Returns 0 if all goes well, 1 if something goes wrong. In the latter case,
* suitable messages will have been written out to standard error.
*/
extern int open_input_as_ES(char *name,
int use_pes,
int quiet,
int force_stream_type,
int want_data,
int *is_data,
ES_p *es);
/*
* Close an input ES stream opened with `open_input_as_ES`.
*
* Specifically, this will close the ES stream and also any underlying PES
* reader and file (unless the input was standard input).
*
* - `name` is the name of the file, used for error reporting.
* - `es` is the ES stream to close. This will be set to NULL.
*
* Returns 0 if all goes well, 1 if something goes wrong. In the latter case,
* suitable messages will have been written out to standard error.
*/
extern int close_input_as_ES(char *name,
ES_p *es);
// ============================================================
// Command line "helpers"
// ============================================================
/*
* Read in an unsigned integer value, checking for extraneous characters.
*
* - `prefix` is an optional prefix for error messages, typically the
* name of the program. It may be NULL.
* - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
* which is used in error messages.
* - `str` is the string to read (typically ``argv[ii+1]``).
* - `base` is the base to read to. If it is 0, then the user can use
* C-style expressions like "0x68" to specify the base on the command line.
* - `value` is the value read.
*
* Returns 0 if all went well, 1 otherwise (in which case a message
* explaining will have been written to stderr).
*/
extern int unsigned_value(char *prefix,
char *cmd,
char *arg,
int base,
u_int32 *value);
/*
* Read in an integer value, checking for extraneous characters.
*
* - `prefix` is an optional prefix for error messages, typically the
* name of the program. It may be NULL.
* - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
* which is used in error messages.
* - `str` is the string to read (typically ``argv[ii+1]``).
* - if `positive` is true, then the number read must be positive (0 or more).
* - `base` is the base to read to. If it is 0, then the user can use
* C-style expressions like "0x68" to specify the base on the command line.
* - `value` is the value read.
*
* Returns 0 if all went well, 1 otherwise (in which case a message
* explaining will have been written to stderr).
*/
extern int int_value(char *prefix,
char *cmd,
char *str,
int positive,
int base,
int *value);
/*
* Read in an integer value, checking for extraneous characters and a range.
*
* - `prefix` is an optional prefix for error messages, typically the
* name of the program. It may be NULL.
* - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
* which is used in error messages.
* - `str` is the string to read (typically ``argv[ii+1]``).
* - `minimum` is the minimum value allowed.
* - `maximum` is the maximum value allowed.
* - `base` is the base to read to. If it is 0, then the user can use
* C-style expressions like "0x68" to specify the base on the command line.
* - `value` is the value read.
*
* Returns 0 if all went well, 1 otherwise (in which case a message
* explaining will have been written to stderr).
*/
extern int int_value_in_range(char *prefix,
char *cmd,
char *arg,
int minimum,
int maximum,
int base,
int *value);
/*
* Read in a double value, checking for extraneous characters.
*
* - `prefix` is an optional prefix for error messages, typically the
* name of the program. It may be NULL.
* - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
* which is used in error messages.
* - `str` is the string to read (typically ``argv[ii+1]``).
* - if `positive` is true, then the number read must be positive (0 or more).
* - `value` is the value read.
*
* Returns 0 if all went well, 1 otherwise (in which case a message
* explaining will have been written to stderr).
*/
extern int double_value(char *prefix,
char *cmd,
char *arg,
int positive,
double *value);
/*
* Read in a hostname and (optional) port
*
* - `prefix` is an optional prefix for error messages, typically the
* name of the program. It may be NULL.
* - `cmd` is the command switch we're reading for (typically ``argv[ii]``),
* which is used in error messages.
* - `arg` is the string to read (typically ``argv[ii+1]``).
* - `hostname` is the host name read
* - `port` is the port read (note that this is not touched if there is
* no port number, so it may be set to a default before calling this
* function)
*
* Note that this works by pointing `hostname` to the start of the `arg`
* string, and then if there is a ':' in `arg`, changing that colon to
* a '\0' delimiter, and interpreting the string thereafter as the port
* number. If *that* fails, it resets the '\0' as a ':'.
*
* Returns 0 if all went well, 1 otherwise (in which case a message
* explaining will have been written to stderr).
*/
extern int host_value(char *prefix,
char *cmd,
char *arg,
char **hostname,
int *port);
// ============================================================
// Sockets
// ============================================================
#ifdef _WIN32
/*
* Start up WINSOCK so we can use sockets.
*
* Note that each successful call of this *must* be matched by a call
* of winsock_cleanup().
*
* Returns 0 if it works, 1 if it fails.
*/
extern int winsock_startup();
/*
* Convert a WinSock error number into a string and print it out on stderr
*/
extern void print_winsock_err(int err);
#endif // _WIN32
/*
* Connect to a socket, to allow us to write to it, using TCP/IP.
*
* - `hostname` is the name of the host to connect to
* - `port` is the port to use
* - if `use_tcpip`, then a TCP/IP connection will be made, otherwise UDP.
* For UDP, multicast TTL will be enabled.
* - If the destination address (`hostname`) is multicast and `multicast_ifaddr`
* is supplied, it is used to select (by IP address) the network interface
* on which to send the multicasts. It may be NULL to use the default,
* or for non-multicast cases.
*
* A socket connected to via this function must be disconnected from with
* disconnect_socket().
*
* Returns a positive integer (the file descriptor for the socket) if it
* succeeds, or -1 if it fails, in which case it will have complained on
* stderr.
*/
extern int connect_socket(char *hostname,
int port,
int use_tcpip,
char *multicast_ifaddr);
/*
* Disconnect from a socket (close it).
*
* Returns 0 if all goes well, 1 otherwise.
*/
#ifdef _WIN32
extern int disconnect_socket(SOCKET socket);
#else // _WIN32
extern int disconnect_socket(int socket);
#endif // _WIN32
#endif // _misc_fns

1872
nalunit.c 100644

Plik diff jest za duży Load Diff

248
nalunit_defns.h 100644
Wyświetl plik

@ -0,0 +1,248 @@
/*
* Datastructures for manipulating NAL units in H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _nalunit_defns
#define _nalunit_defns
#include <stdio.h>
#include "compat.h"
#include "es_defns.h"
#include "bitdata_defns.h"
// ------------------------------------------------------------
// Constants and definitions
// ------------------------------------------------------------
enum NAL_UNIT_TYPE
{
NAL_UNSPECIFIED,
NAL_NON_IDR,
NAL_PARTITION_A,
NAL_PARTITION_B,
NAL_PARTITION_C,
NAL_IDR,
NAL_SEI,
NAL_SEQ_PARAM_SET,
NAL_PIC_PARAM_SET,
NAL_ACCESS_UNIT_DELIM,
NAL_END_OF_SEQ,
NAL_END_OF_STREAM,
NAL_FILLER
};
#define NAL_UNIT_TYPE_STR(a) \
((a)==NAL_UNSPECIFIED?"unspecified": \
(a)==NAL_NON_IDR?"non-IDR": \
(a)==NAL_PARTITION_A?"partition A": \
(a)==NAL_PARTITION_B?"partition B": \
(a)==NAL_PARTITION_C?"partition C": \
(a)==NAL_IDR?"IDR": \
(a)==NAL_SEI?"SEI": \
(a)==NAL_SEQ_PARAM_SET?"seq param set": \
(a)==NAL_PIC_PARAM_SET?"pic param set": \
(a)==NAL_ACCESS_UNIT_DELIM?"access unit delim": \
(a)==NAL_END_OF_SEQ?"end of seq": \
(a)==NAL_END_OF_STREAM?"end of stream": \
(a)==NAL_FILLER?"filler":"???")
#define SLICE_P 0
#define SLICE_B 1
#define SLICE_I 2
#define SLICE_SP 3
#define SLICE_SI 4
#define ALL_SLICES_P 5
#define ALL_SLICES_B 6
#define ALL_SLICES_I 7
#define ALL_SLICES_SP 8
#define ALL_SLICES_SI 9
#define NAL_SLICE_TYPE_STR(a) \
((a)==SLICE_P?"P": (a)==SLICE_B?"B": (a)==SLICE_I?"I": \
(a)==SLICE_SP?"SP": (a)==SLICE_SI?"SI": \
(a)==ALL_SLICES_P?"P &c": (a)==ALL_SLICES_B?"B &c": \
(a)==ALL_SLICES_I?"I &c": (a)==ALL_SLICES_SP?"SP &c": \
(a)==ALL_SLICES_SI?"SI &c":"??")
// ------------------------------------------------------------
// Datastructures
// ------------------------------------------------------------
// Data for a slice NAL unit
struct nal_slice_data
{
u_int32 seq_param_set_pic_order_cnt_type; // from the seq param set
u_int32 first_mb_in_slice;
u_int32 slice_type;
u_int32 pic_parameter_set_id;
u_int32 frame_num;
// From here onwards, the fields are not necessarily all present
byte field_pic_flag; // frame or field? 0 if absent
byte bottom_field_flag; // 0 if absent
int bottom_field_flag_present;
u_int32 idr_pic_id;
u_int32 pic_order_cnt_lsb;
int32 delta_pic_order_cnt_bottom;
int32 delta_pic_order_cnt[2];
u_int32 redundant_pic_cnt;
int redundant_pic_cnt_present;
};
typedef struct nal_slice_data *nal_slice_data_p;
#define SIZEOF_NAL_SLICE_DATA sizeof(struct nal_slice_data)
// ------------------------------------------------------------
// Data for a sequence parameter set
struct nal_seq_param_data
{
byte profile_idc;
byte constraint_set0_flag;
byte constraint_set1_flag;
byte constraint_set2_flag;
byte level_idc;
u_int32 seq_parameter_set_id; // our own id (0..31)
u_int32 log2_max_frame_num;
u_int32 pic_order_cnt_type;
u_int32 log2_max_pic_order_cnt_lsb;
byte delta_pic_order_always_zero_flag;
byte frame_mbs_only_flag;
};
typedef struct nal_seq_param_data *nal_seq_param_data_p;
#define SIZEOF_NAL_SEQ_PARAM_DATA sizeof(struct nal_seq_param_data)
// ------------------------------------------------------------
// Data for a picture parameter set
struct nal_pic_param_data
{
int pic_parameter_set_id; // our own id (0..255)
int seq_parameter_set_id; // we use this
byte entropy_coding_mode_flag;
byte pic_order_present_flag; // we use this
u_int32 num_slice_groups;
u_int32 slice_group_map_type;
// lots of ignored things
byte redundant_pic_cnt_present_flag; // this is mildly interesting
};
typedef struct nal_pic_param_data *nal_pic_param_data_p;
#define SIZEOF_NAL_PIC_PARAM_DATA sizeof(struct nal_pic_param_data)
// ------------------------------------------------------------
// An individual NAL unit might hold any one of those...
union nal_innards
{
struct nal_slice_data slice;
struct nal_seq_param_data seq;
struct nal_pic_param_data pic;
};
typedef union nal_innards *nal_innards_p;
#define SIZEOF_NAL_INNARDS sizeof(union nal_innards)
// ------------------------------------------------------------
// "Dictionaries" for finding a specific picture parameter set or
// sequence parameter set
// Picture parameter set ids are in the range 0..255
// Sequence parameter set ids are in the range 0..31
struct param_dict
{
int last_id; // The id of the last parameter set we wanted
int last_index; // and its index in the arrays
int *ids; // The ids for...
union nal_innards *params; // ...the data
ES_offset *posns; // Where each was read from...
u_int32 *data_lens; // ...and its size
int size, length; // of the arrays and their content
};
typedef struct param_dict *param_dict_p;
#define SIZEOF_PARAM_DICT sizeof(struct param_dict)
#define NAL_PIC_PARAM_START_SIZE 20
#define NAL_PIC_PARAM_INCREMENT 20
// ------------------------------------------------------------
// A single NAL unit
struct nal_unit
{
struct ES_unit unit; // The actual data
// For most purposes, it's simplest to think about the NAL unit's data as
// being the data *after* the start code prefix. Indeed, that is the way
// that it is presented in the standard. Thus we provide aliases here, which
// do so
// (NB: since these point "into" the .unit structure, they will only be
// meaningful after we've finished reading in a NAL unit's data - before
// then they're undefined)
byte *data; // The current NAL unit's data, excluding 00 00 01
int data_len; // And its length/size
// And for some processing, we need to work with the data after
// it has had its emulation 3 bytes removed
byte *rbsp; // The data with 00 00 03 bytes "fixed"
int rbsp_len;
bitdata_p bit_data; // And a view of that as bits
// Information obtained by inspection of the NAL units content
int nal_ref_idc;
enum NAL_UNIT_TYPE nal_unit_type;
int starts_picture_decided;
int starts_picture;
char *start_reason; // If it starts a picture, why
int decoded; // Have we "read" the innards of the NAL unit?
union nal_innards u; // Admittedly an unimaginative name, but short
};
typedef struct nal_unit *nal_unit_p;
#define SIZEOF_NAL_UNIT sizeof(struct nal_unit)
// ------------------------------------------------------------
// An expandable list of NAL units
struct nal_unit_list
{
nal_unit_p *array; // The current array of nal units */
int length; // How many there are
int size; // How big the array is
};
typedef struct nal_unit_list *nal_unit_list_p;
#define SIZEOF_NAL_UNIT_LIST sizeof(struct nal_unit_list)
#define NAL_UNIT_LIST_START_SIZE 20
#define NAL_UNIT_LIST_INCREMENT 20
// ------------------------------------------------------------
// A context for reading NAL units from an Elementary Stream
struct nal_unit_context
{
ES_p es;
int count;
// Sequence and picture parameter set "dictionaries"
param_dict_p seq_param_dict;
param_dict_p pic_param_dict;
// Show details of each NAL units content as it is read?
int show_nal_details;
};
typedef struct nal_unit_context *nal_unit_context_p;
#define SIZEOF_NAL_UNIT_CONTEXT sizeof(struct nal_unit_context)
#endif // _nalunit_defns

346
nalunit_fns.h 100644
Wyświetl plik

@ -0,0 +1,346 @@
/*
* Prototypes for manipulating NAL units in H.264 elementary streams.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _nalunit_fns
#define _nalunit_fns
#include "nalunit_defns.h"
/*
* Request details of the NAL unit contents as they are read
*/
extern void set_show_nal_reading_details(nal_unit_context_p context,
int show);
/*
* Build a new NAL unit context, for reading NAL units from an ES.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit_context(ES_p es,
nal_unit_context_p *context);
/*
* Free a NAL unit context datastructure.
*
* Clears the datastructure, frees it, and returns `context` as NULL.
*
* Does nothing if `context` is already NULL.
*/
extern void free_nal_unit_context(nal_unit_context_p *context);
/*
* Rewind a file being read as NAL units.
*
* A thin jacket for `seek_ES`.
*
* Doesn't unset the sequence and picture parameter dictionaries
* that have been built up when reading the file - this may possibly
* not be the desired behaviour, but should be OK for well behaved files.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int rewind_nal_unit_context(nal_unit_context_p context);
/*
* Build a new NAL unit datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit(nal_unit_p *nal);
/*
* Tidy up and free a NAL unit datastructure after we've finished with it.
*
* Empties the NAL unit datastructure, frees it, and sets `nal` to NULL.
*
* If `nal` is already NULL, does nothing.
*/
extern void free_nal_unit(nal_unit_p *nal);
/*
* Find and read in the next NAL unit.
*
* - `context` is the NAL unit context we're reading from
* - `verbose` is true if a brief report on the NAL unit should be given
* - `nal` is the datastructure containing the NAL unit found, or NULL
* if there was none.
*
* Returns:
* * 0 if it succeeds,
* * EOF if the end-of-file is read (i.e., there is no next NAL unit),
* * 2 if the NAL unit data does not make sense, so it should be ignored
* (specifically, if the NAL unit's RBSP data cannot be understood),
* * 1 if some other error occurs.
*/
extern int find_next_NAL_unit(nal_unit_context_p context,
int verbose,
nal_unit_p *nal);
/*
* Write (copy) the current NAL unit to the ES output stream.
*
* - `output` is the output stream (file descriptor) to write to
* - `nal` is the NAL unit to write
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int write_NAL_unit_as_ES(FILE *output,
nal_unit_p nal);
/*
* Write (copy) the current NAL unit to the output stream, wrapped up in a
* PES within TS.
*
* - `output` is the TS writer to write to
* - `nal` is the NAL unit to write
* - `video_pid` is the video PID to use
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int write_NAL_unit_as_TS(TS_writer_p tswriter,
nal_unit_p nal,
u_int32 video_pid);
/*
* Create a new "dictionary" for remembering picture or sequence
* parameter sets.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_param_dict(param_dict_p *param_dict);
/*
* Tidy up and free a parameters "dictionary" datastructure after we've
* finished with it.
*
* Empties the datastructure, frees it, and sets `param_dict` to NULL.
*
* Does nothing if `param_dict` is already NULL.
*/
extern void free_param_dict(param_dict_p *param_dict);
/*
* Remember parameter set data in a "dictionary".
*
* - `param_dict` should be an appropriate "dictionary" - i.e., one
* being used to store picture or sequence parameter set data, as
* appropriate.
* - `param_id` is the id for this picture or sequence parameter set.
* - `nal` is the NAL unit containing the parameter set data.
* Note that a copy will be taken of the parameter set data, which
* means that the caller may free the NAL unit.
*
* Any previous data for this picture or sequence parameter set id will be
* forgotten (overwritten).
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remember_param_data(param_dict_p param_dict,
u_int32 param_id,
nal_unit_p nal);
/*
* Retrieve the picture parameter set data for the given id.
*
* - `pic_param_dict` is a parameter "dictionary" of the appropriate type.
* - `pic_param_id` is the id to look up.
* - `pic_param_data` is the data for that id. Do not free this, it refers
* into the "dictionary" datastructure.
*
* Note that altering the "dictionary" (with `remember_param_data()`) may
* cause the underlying datastructures to be realloc'ed, which in turn means
* that the address returned as `pic_param_data` may not be valid after such
* an action.
*
* Returns 0 if it succeeds, 1 if the id is not recognised.
*/
extern int get_pic_param_data(param_dict_p pic_param_dict,
u_int32 pic_param_id,
nal_pic_param_data_p *pic_param_data);
/*
* Retrieve the sequence parameter set data for the given id.
*
* - `seq_param_dict` is a parameter "dictionary" of the appropriate type.
* - `seq_param_id` is the id to look up.
* - `seq_param_data` is the data for that id. Do not free this, it refers
* into the "dictionary" datastructure.
*
* Note that altering the "dictionary" (with `remember_param_data()`) may
* cause the underlying datastructures to be realloc'ed, which in turn means
* that the address returned as `seq_param_data` may not be valid after such
* an action.
*
* Returns 0 if it succeeds, 1 if the id is not recognised.
*/
extern int get_seq_param_data(param_dict_p seq_param_dict,
u_int32 seq_param_id,
nal_seq_param_data_p *seq_param_data);
/*
* Is this NAL unit a slice?
*
* Returns true if its ``nal_unit_type`` is 1 (coded slice of IDR picture)
* or 5 (coded slice of IDR picture).
*/
extern int nal_is_slice(nal_unit_p nal);
/*
* Is this NAL unit a picture parameter set?
*
* Returns true if its ``nal_unit_type`` is 8.
*/
extern int nal_is_pic_param_set(nal_unit_p nal);
/*
* Is this NAL unit a sequence parameter set?
*
* Returns true if its ``nal_unit_type`` is 7.
*/
extern int nal_is_seq_param_set(nal_unit_p nal);
/*
* Is this NAL unit marked as part of a redundant picture?
*/
extern int nal_is_redundant(nal_unit_p nal);
/*
* Is this VCL NAL unit the first of a new primary coded picture?
*
* - `nal` is the NAL unit we need to decide about.
* - `last` is a slice NAL unit from the last primary coded picture
* (likely to be the first NAL unit therefrom, in fact)
*
* Both `nal` and `last` must be VCL NALs representing slices of a reference
* picture - i.e., with nal_unit_type 1 or 5 (if we were supporting type A
* slice data partitions, we would have to take them into account as well).
*
* Both `nal` and `last` must have had their innards decoded with
* `read_slice_data`, which should have occurred automatically if they are
* both appropriate NAL units for this process.
*
* Acording to H.264 7.4.1.2.4 (from the JVT-J010d7 draft):
*
* The first NAL unit of a new primary code picture can be detected
* because:
*
* - its frame number differs in value from that of the last slice (NB:
* IDR pictures always have frame_num == 0)
*
* - its field_pic_flag differs in value (i.e., one is a field slice, and
* the other a frame slice)
*
* - the bottom_field_flag is present in both (determined by
* frame_mbs_only_flag in the sequence parameter set, and by
* field_pic_flag) and differs (i.e., both are field slices, but one
* is top and the other bottom) [*]_
*
* - nal_ref_idc differs in value, and one of them has nal_ref_idc == 0
* (i.e., one is a reference picture and the other is not)
*
* - pic_order_cnt_type (found in the sequence parameter set) == 0 for
* both and either pic_order_cnt_lsb differs in value or
* delta_pic_order_cnt_bottom differs in value [*]_
*
* - pic_order_cnt_type == 1 for both and either delta_pic_order_cnt[0]
* or delta_pic_order_cnt[1] differs in value [*]_
*
* - nal_unit_type == 5 for one and not in the other (i.e., one is IDR
* and the other is not)
*
* - nal_unit_type == 5 for both (i.e., both are IDR), and idr_pic_id
* differs (i.e., they're not the same IDR)
*
* It is possible that later drafts may alter/augment these criteria -
* that has already happened between JVT-G050r1 and JVT-J010d7.
*
* .. [*] For these three items, we need to have decoded the active
* sequence parameter set (which, for now, I'll assume to be the last
* set we found with the appropriate id).
*/
extern int nal_is_first_VCL_NAL(nal_unit_p nal,
nal_unit_p last);
// ------------------------------------------------------------
// Lists of NAL units
//
// This duplicates the functionality provided by ES unit lists
// in es.h, but it works at the higher level of NAL units,
// which is useful if one wants to report on the content of the
// lists *as* NAL units.
// ------------------------------------------------------------
/*
* Build a new list-of-nal-units datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_nal_unit_list(nal_unit_list_p *list);
/*
* Add a NAL unit to the end of the NAL unit list. Does not take a copy.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int append_to_nal_unit_list(nal_unit_list_p list,
nal_unit_p nal);
/*
* Reset (empty) a NAL unit list.
*
* If `deep` is true, then any NAL units in the list will be freed
* as well (this will be a Bad Thing if anywhere else is using them).
*/
extern void reset_nal_unit_list(nal_unit_list_p list,
int deep);
/*
* Tidy up and free a NAL unit list datastructure after we've finished with it.
*
* If `deep` is true, then any NAL units in the list will be freed
* as well (this will be a Bad Thing if anywhere else is using them).
*
* Clears the datastructure, frees it and returns `list` as NULL.
*
* Does nothing if `list` is already NULL.
*/
extern void free_nal_unit_list(nal_unit_list_p *list,
int deep);
/*
* Report on a NAL unit list's contents, to the given stream.
*/
extern void report_nal_unit_list(FILE *stream,
char *prefix,
nal_unit_list_p list);
/*
* Print out useful information about this NAL unit, on the given stream.
*
* This is intended as a single line of information.
*/
extern void report_nal(FILE *stream,
nal_unit_p nal);
#endif // _nalunit_fns

3791
pes.c 100644

Plik diff jest za duży Load Diff

217
pes_defns.h 100644
Wyświetl plik

@ -0,0 +1,217 @@
/*
* Datastructures for reading PES packets from TS or PS files
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _pes_defns
#define _pes_defns
#include "compat.h"
#include "pidint_defns.h"
#include "ps_defns.h"
#include "ts_defns.h"
#include "tswrite_defns.h"
// ------------------------------------------------------------
// A PES packet comes with some useful associated data
struct PES_packet_data
{
byte *data; // The actual packet data
int32 data_len; // The length of the `data` array [1]
int32 length; // Its length
offset_t posn; // The offset of its start in the file [2]
int is_video; // Is this video data? (as opposed to audio)
// For convenience, it's useful to be able to get at the PES packet's
// "payload" (i.e., the ES data) as if it were a separate array. This
// is, of course, just an offset into `data`
byte *es_data;
int32 es_data_len;
// The PES packet *does* tell us if its data starts with an ES packet
// (i.e., if the 00 00 01 bytes come as the first bytes in the data),
// so that's worth remembering
int data_alignment_indicator;
// Some applications want to know if a particular packet contains
// a PTS or not
int has_PTS;
};
// [1] For PS data, data_len and length will always be the same.
// For TS data, length is set when the first TS packet of the
// PES packet is read, and data_len gradually increases to length
// as "chunks" of the PES packet are read in
// [2] For TS data, this is actually the offset of the first TS packet
// containing the PES packet
typedef struct PES_packet_data *PES_packet_data_p;
#define SIZEOF_PES_PACKET_DATA sizeof(struct PES_packet_data)
// ------------------------------------------------------------
// An expandable list of PID vs. PES packet data
struct peslist
{
u_int32 *pid; // An array of the PIDs
PES_packet_data_p *data; // An array of the corresponding PES data
int length; // How many there are
int size; // How big the arrays are
};
typedef struct peslist *peslist_p;
#define SIZEOF_PESLIST sizeof(struct peslist)
#define PESLIST_START_SIZE 2 // Guess at one audio, one video
#define PESLIST_INCREMENT 1 // And a very conservative extension policy
// ------------------------------------------------------------
// A PES "reader" datastructure is the interface through which one reads
// PES packets from a TS or PS file
struct PES_reader
{
int is_TS; // Is it is TS (as opposed to PS)?
// If it is TS, we read via a TS read-ahead buffer
TS_reader_p tsreader;
// If it is PS, we read via a PS read-ahead buffer
PS_reader_p psreader;
int give_info; // Should information messages be output?
int give_warning; // Should warning messages be output (to stderr)?
PES_packet_data_p packet; // The current PES packet
// When reading PS packets, `posn` is the position of the current (or last)
// PS or TS packet.
offset_t posn;
// For PS data, we need to know if it is H.264 (MPEG-4/AVC) or not
int is_h264; // for backwards compatibility
int video_type; // the actual (believed) video type
// For PS and TS, we can choose to ignore audio entirely
int video_only;
// For PS, if we're not ignoring audio, we either look for a specific
// audio stream id (specified by the user), or we will take the first
// we find that is not Dolby. This latter is indicated by audio_stream
// being set to 0
byte audio_stream_id; // If not, the stream id of the audio we want
// When reading TS data, we need the program information to make sense
// of what is going on
int got_program_data; // Do we know our program data yet?
pmt_p program_map; // The content of the (current/last) PMT
// And from that, we can work out our video and audio (if any) pids, etc.
u_int32 video_pid; // Zero if not yet known
u_int32 audio_pid; // Ditto
u_int32 pcr_pid; // A copy of the value from the PMT
u_int16 program_number; // Which program are we reading? (0=first)
u_int32 pmt_pid; // What's the PMT PID?
// PMTs may be split over several TS packets, so we need a buffer
// to build them in
byte *pmt_data; // The buffer (NULL when not in use)
int pmt_data_len; // The buffers length = the PMT section length + 3
int pmt_data_used; // How much of said data we've already got
// In order to write out TS data, we also need program information.
// Obviously, the simplest case is when reading TS and writing it out
// again, with the same settings. However, we also have to cope with
// reading in PS data (which has no TS program information), and writing
// out TS data with *different* program information.
// If we're reading TS data, the default is to use the program data we
// find therein. If `override_program_data` is TRUE, then we ignore that,
// and use the values given by the user instead.
int override_program_data;
// Regardless, the following are the values to use when writing TS data out:
u_int32 output_video_pid;
u_int32 output_audio_pid;
u_int32 output_pcr_pid;
u_int16 output_program_number;
u_int32 output_pmt_pid;
// If we're reading Dolby (AC-3) audio, then there are two choices for the
// stream type. DVB uses stream type 0x06, and ATSC uses stream type 0x81.
byte dolby_stream_type; // The Dolby stream type we read (if any)
byte output_dolby_stream_type;
int override_dolby_stream_type; // Override whatever we read
// Before we can write out TS data, we need some basic program information.
// This is read in automatically if the input is TS, and must be supplied
// by the user (via set_PES_reader_program_data) if the input is PS.
// When reading a TS file, more than one PES packet may be being built
// at the same time. At any time, the "next" read PES packet will be the
// first one to be completely read in
peslist_p packets; // The packets currently being read
// If we are reading TS, and a PES packet has a declared length of 0,
// then it can only be ended by the *next* PES packet of the same PID
// (or by EOF, of course). In this case, we want to return the newly
// ended PES packet, and the *next* read request should continue
// with the PES packet we hadn't yet finished with. However, it is
// technically possible (although unlikely) that the new (just started)
// PES packet will end in its first TS packet. In that case, we want
// to return *it* next time we try to read a TS packet. To facilitate
// that, we can remember it here...
PES_packet_data_p deferred;
// If we ended such a packet on EOF, it's moderately convenient to
// remember that we had found EOF, rather than try to bump into it again
int had_eof;
// When being used by a server, we want PES packets to be written out
// as a "side effect" of reading them in to analyse their contents.
// Thus we provide:
int write_PES_packets; // TRUE if to write them out to:
TS_writer_p tswriter; // this TS writer context
int program_freq; // how often to write PAT/PMT out
int program_index; // how long since we last did so
// Sometimes, for instance when going from fast forwards to normal playing,
// we've already output the (end of) the current PES packet by hand, and
// thus don't want the automated server mechanism to output it for us.
// It's thus useful to have a flag indicating this (which will be unset
// as soon as the current PES packet has, indeed, not been written out)
// Since this is (definitely) an internal detail, it must be set explicitly.
int dont_write_current_packet;
// For benchmarking purposes (of the recipient), it can be useful to be able
// to "pad out" the data we're sending, so that it is <n> times as big. If
// ``expand`` is greater than 0, then ``expand`` "dummy" PES packets (of the
// same size as the real one) will be output for each real PES packet (but
// with an irrelevant stream id).
int pes_padding;
// Debugging: if this is set, and the appropriate code is compiled into
// pes.c (see DEBUG_READ_PACKETS), then report on each PES packet read
// and written. Even if DEBUG_READ_PACKETS is not defined, some output
// will be produced.
int debug_read_packets;
};
typedef struct PES_reader *PES_reader_p;
#define SIZEOF_PES_READER sizeof(struct PES_reader)
// Given the PES packet data (i.e., the data starting 00 00 01 <stream_id>
// <packet_length>), decide if this PES packet is MPEG-1 (11172-1) or
// H.222.0 (13818-1)
#define IS_H222_PES(data) ((data[6] & 0xC0) == 0x80)
#endif // _pes_defns

535
pes_fns.h 100644
Wyświetl plik

@ -0,0 +1,535 @@
/*
* Functions for reading PES packets from TS or PS files
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _pes_fns
#define _pes_fns
#include "pes_defns.h"
#include "es_defns.h"
/*
* Free a PES packet datastructure
*
* - `data` is the PES packet datastructure, which will be freed,
* and returned as NULL.
*/
extern void free_PES_packet_data(PES_packet_data_p *data);
/*
* Look at the start of a file to determine if it appears to be transport
* stream. Rewinds the file when it is finished.
*
* The file is assumed to be Transport Stream if it starts with 0x47 as
* the first byte, and 0x47 recurs at 188 byte intervals (in other words,
* it appears to start with several TS packets).
*
* - `input` is the file to check
* - `is_TS` is TRUE if it looks like TS, as described above.
*
* Returns 0 if all goes well, 1 if there was an error.
*/
extern int determine_if_TS_file(int input,
int *is_TS);
/*
* Build a PES reader datastructure for PS data
*
* - `ps` is the Program Stream to read the PES data from
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout).
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `reader` is the resulting PES reader
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_PS_PES_reader(PS_reader_p ps,
int give_info,
int give_warnings,
PES_reader_p *reader);
/*
* Build a PES reader datastructure for TS data
*
* - `tsreader` is the Transport Stream to read the PES data from
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout).
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `program_number` is only used for TS data, and identifies which program
* to read. If this is 0 then the first program encountered in the first PAT
* will be read.
* - `reader` is the resulting PES reader
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_TS_PES_reader(TS_reader_p tsreader,
int give_info,
int give_warnings,
u_int16 program_number,
PES_reader_p *reader);
/*
* Build a PES reader datastructure
*
* - `input` is the file to read the PES data from
* - `is_TS` should be TRUE if the data is TS, FALSE if it is PS
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout).
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `program_number` is only used for TS data, and identifies which program
* to read. If this is 0 then the first program encountered in the first PAT
* will be read.
* - `reader` is the resulting PES reader
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_PES_reader(int input,
int is_TS,
int give_info,
int give_warnings,
u_int16 program_number,
PES_reader_p *reader);
/*
* Open a Transport Stream file for PES packet reading
*
* - `filename` is the name of the file to open.
* - `program_number` identifies which program to read. If this is 0
* then the first program encountered in the first PAT will be read.
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout). If information messages are requested, and the
* program number is given as 0, the actual program number chosen will
* be reported as well.
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `reader` is the PES reader context corresponding to the newly
* opened file.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int open_PES_reader_for_TS(char *filename,
u_int16 program_number,
int give_info,
int give_warnings,
PES_reader_p *reader);
/*
* Open a Program Stream file for PES packet reading
*
* - `filename` is the name of the file to open.
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout).
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `reader` is the PES reader context corresponding to the newly
* opened file.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int open_PES_reader_for_PS(char *filename,
int give_info,
int give_warnings,
PES_reader_p *reader);
/*
* Open a Program Stream or Transport Stream file for PES packet reading
*
* - `filename` is the name of the file to open.
* - `give_info` is TRUE if information about program data, etc., should be
* output (to stdout).
* - `give_warnings` is TRUE if warnings (starting with "!!!") should be
* output (to stderr), FALSE if they should be suppressed.
* - `reader` is the PES reader context corresponding to the newly
* opened file.
*
* If the file is Transport Stream, then this is equivalent to a call
* of::
*
* err = open_PES_reader_for_TS(filename,0,give_info,give_warnings,&reader);
*
* i.e., the first program found is the program that will be read.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int open_PES_reader(char *filename,
int give_info,
int give_warnings,
PES_reader_p *reader);
/*
* Tell the PES reader whether we only want video data
*
* - `video_only` should be TRUE if audio is to be ignored, FALSE
* if audio should be retained.
*
* By default, the PES reader returns video data and a single audio
* stream (taken from the first audio stream encountered).
*/
extern void set_PES_reader_video_only(PES_reader_p reader,
int video_only);
/*
* Tell the PES reader which audio stream we want.
*
* By default, the PES reader returns video data and a single audio
* stream (taken from the first audio stream encountered).
*
* - `reader` is the PES reader context
* - `stream_number` is the number of the audio stream to read, from
* 0 to 31 (0x1F).
*
* This call only has effect if the input data is PS.
*
* Returns 0 if all went well, or 1 if there was an error (specifically,
* that `stream_number` was not in the range 0-31).
*/
extern int set_PES_reader_audio_stream(PES_reader_p reader,
int stream_number);
/*
* Tell the PES reader to get its audio stream from private stream 1
* (this is the stream that is conventionally used for Dolby in DVD data).
*
* By default, the PES reader returns video data and a single audio
* stream (taken from the first audio stream encountered).
*
* - `reader` is the PES reader context
*
* This call only has effect if the input data is PS.
*/
extern void set_PES_reader_audio_private1(PES_reader_p reader);
/*
* Tell the PES reader to "pretend" it has read a PAT and PMT with
* the given program information.
*
* This is intended for use in setting up sensible values when reading
* PS data (which does not contain such information). It will silently
* do nothing for TS data.
*
* Note that calling it more than once is allowed - it will happily
* overwrite any previous values.
*
* - `reader` is the PES reader context
* - `program_number` is the program number to assume. If this is 0,
* then 1 will be used.
* - `pmt_pid` is the PID for the PMT we've pretended to read.
* - `video_pid` is the PID to assume for the video data
* - `audio_pid` is the PID to assume for the audio data (if any)
* - `pcr_pid` is the PID to assume for the PCR data - this will often
* be the same as the `video_pid`
*/
extern void set_PES_reader_program_data(PES_reader_p reader,
u_int16 program_number,
u_int32 pmt_pid,
u_int32 video_pid,
u_int32 audio_pid,
u_int32 pcr_pid);
/*
* Tell the PES reader that the PS data it is reading is MPEG-4/AVC,
* as opposed to MPEG-1/MPEG-2.
*/
extern void set_PES_reader_h264(PES_reader_p reader);
/*
* Tell the PES reader that the PS data it is reading is of
* type `video_type` (which is assumed to be a legitimate value
* such as VIDEO_H264, etc.)
*/
extern void set_PES_reader_video_type(PES_reader_p reader,
int video_type);
/*
* Tell the PES reader whether to output any Dolby (AC-3) audio data
* it may read using the DVB stream type (0x06) or the ATSC stream
* type (0x81).
*
* If it is reading TS data, then the default is to use whatever stream type
* the Dolby audio was read with.
*
* If it is reading PS data, then the default is to assume DVB data.
*
* This call only has effect if Dolby audio data is actually selected.
*/
extern void set_PES_reader_dolby_stream_type(PES_reader_p reader,
int is_dvb);
/*
* Reposition the PES reader to an earlier packet
*
* It is the caller's responsibility to choose a sensible `posn` to seek to.
*
* Note that using this to reposition in a PES reader does not affect any
* "higher" reading context using this PES reader - specifically, if data
* is being read via an ES reader, then calling this function directly
* will result in the ES reader losing its positional information.
*
* In this case, `seek_ES` should be called.
*
* - `reader` is the PES reader context
* - `posn` is a packet position obtained from an earlier PES packet
* datastructure (this should *not* be a random offset in the input
* file, as that will not in general work).
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int set_PES_reader_position(PES_reader_p reader,
offset_t posn);
/*
* Free a PES reader, and the relevant datastructures. Does not close
* the underlying file.
*
* - `reader` is the PES reader context. This will be freed, and
* returned as NULL.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int free_PES_reader(PES_reader_p *reader);
/*
* Close a PES reader, and free the relevant datastructures.
*
* - `reader` is the PES reader context. This will be freed, and
* returned as NULL.
*
* Returns 0 if all goes well, 1 if something goes wrong with closing the
* file (although in that case, the `reader` will still have been freed).
*/
extern int close_PES_reader(PES_reader_p *reader);
/*
* Return the next PES packet from the input file
*
* - `reader` is a PES reader context
*
* Returns 0 if all goes well, EOF if end of file is read, and 1 if
* something goes wrong.
*/
extern int read_next_PES_packet(PES_reader_p reader);
/*
* Given an MPEG-1 PES packet, determine the offset of the ES data.
*
* - `data` is the PES packet data, starting "00 00 01 <stream_id>
* <packet_length>"
* - `data_len` is the actual length of the data therein
*
* Returns the required offset (i.e., packet[offset] is the first byte
* of the ES data within the PES packet).
*/
extern int calc_mpeg1_pes_offset(byte *data, int data_len);
/*
* Read in the next PES packet that contains ES data we are interested in
* Ignores non-video packets.
*
* - `reader` is a PES reader context
*
* Returns 0 if all goes well, EOF if end of file is read, and 1 if
* something goes wrong.
*/
extern int read_next_PES_ES_packet(PES_reader_p reader);
/*
* If the given PES packet data contains a PTS field, return it
*
* - `data` is the data for this PES packet
* - `data_len` is its length
* - `got_pts` is TRUE if a PTS field was found, in which case
* - `pts` is that PTS value
*
* Returns 0 if all went well, 1 if an error occurs.
*/
extern int find_PTS_in_PES(byte data[],
int32 data_len,
int *got_pts,
u_int64 *pts);
/*
* If the given PES packet data contains a DTS field, return it
*
* - `data` is the data for this PES packet
* - `data_len` is its length
* - `got_dts` is TRUE if a DTS field was found, in which case
* - `dts` is that DTS value
*
* Returns 0 if all went well, 1 if an error occurs.
*/
extern int find_DTS_in_PES(byte data[],
int32 data_len,
int *got_dts,
u_int64 *dts);
/*
* If the given PES packet data contains a PTS and/or DTS field, return it
*
* - `data` is the data for this PES packet
* - `data_len` is its length
* - `got_pts` is TRUE if a PTS field was found, in which case
* - `pts` is that PTS value
* - `got_dts` is TRUE if a DTS field was found, in which case
* - `dts` is that DTS value
*
* Returns 0 if all went well, 1 if an error occurs.
*/
extern int find_PTS_DTS_in_PES(byte data[],
int32 data_len,
int *got_pts,
u_int64 *pts,
int *got_dts,
u_int64 *dts);
/*
* If the given PES packet data contains an ESCR field, return it
*
* - `data` is the data for this PES packet
* - `data_len` is its length
* - `got_escr` is TRUE if an ESCR field was found, in which case
* - `escr` is that ESCR value
*
* Returns 0 if all went well, 1 if an error occurs.
*/
extern int find_ESCR_in_PES(byte data[],
int32 data_len,
int *got_escr,
u_int64 *escr);
/*
* Decode a PTS or DTS value.
*
* - `bytes` is the 5 bytes containing the encoded PTS or DTS value
* - `required_guard` should be 2 for a PTS alone, 3 for a PTS before
* a DTS, or 1 for a DTS after a PTS
* - `value` is the PTS or DTS value as decoded
*
* Returns 0 if the PTS/DTS value is decoded successfully, 1 if an error occurs
*/
extern int decode_pts_dts(byte data[],
int required_guard,
u_int64 *value);
/*
* Encode a PTS or DTS.
*
* - `data` is the array of 5 bytes into which to encode the PTS/DTS
* - `guard_bits` are the required guard bits: 2 for a PTS alone, 3 for
* a PTS before a DTS, or 1 for a DTS after a PTS
* - `value` is the PTS or DTS value to be encoded
*/
extern void encode_pts_dts(byte data[],
int guard_bits,
u_int64 value);
/*
* Does the given PES packet contain a PTS?
*
* - `packet` is the PES packet datastructure
*
* Returns TRUE if it does, FALSE if it does not (or is in error)
*/
extern int PES_packet_has_PTS(PES_packet_data_p packet);
/*
* Report on the content of a PES packet - specifically, its header data.
*
* - `prefix` is a string to put before each line of output
* - `data` is the packet data, and `data_len` its length
* - `show_data` should be TRUE if the start of the data for each packet should
* be shown
*
* Returns 0 if all went well, 1 if an error occurs.
*/
extern int report_PES_data_array(char *prefix,
byte *data,
int data_len,
int show_data);
/*
* Report on the content of a PES packet.
*
* This gives a longer form of report than report_PES_data_array(), and
* can also present substream data for audio stream_types.
*
* - `stream_type` is the stream type of the data, or -1 if it is not
* known (i.e., if this packet is from PS data).
* - `payload` is the packet data.
* - `payload_len` is the actual length of the payload (for a TS packet,
* this will generally be less than the PES packet's length).
* - if `show_data_len` is non-0 then the data for the PES packet will
* also be shown, up to that length
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern void report_PES_data_array2(int stream_type,
byte *payload,
int payload_len,
int show_data_len);
// ============================================================
// Server support
// ============================================================
/*
* Prepare for PES packets being written out to a TS writer, and
* request that they be written out.
*
* The effect is that, each time a new PES packet is read in, it will
* be written out to the TS output stream.
*
* - `reader` is our PES reader context
* - `tswriter` is the TS writer
* - `program_freq` is how often to write out program data (PAT/PMT)
*/
extern void set_server_output(PES_reader_p reader,
TS_writer_p tswriter,
int program_freq);
/*
* Start PES packets being written out to a TS writer (again).
*
* The effect is that, each time a new PES packet is read in, it will
* be written out to the TS output stream.
*
* If PES packets were already being written out, this does nothing.
*
* If `reader` is NULL, nothing is done.
*/
extern void start_server_output(PES_reader_p reader);
/*
* Stop PES packets being written out to a TS writer.
*
* If PES packets were already not being written out, this does nothing.
*
* If set_server_output() has not been called to define a TS writer
* context, this will have no effect.
*
* If `reader` is NULL, nothing is done.
*/
extern void stop_server_output(PES_reader_p reader);
/*
* When outputting PES packets in "normal play" mode, add ``extra`` PES
* packets (of the same size as each real packet) to the output. This
* makes the amount of data output be about ``extra``+1 times the amount
* read (the discrepancy is due to any program data being written).
*
* This "expansion" or "padding" of the data can be useful for benchmarking
* the recipient, as the extra data (which has an irrelevant stream id)
* will be ignored by the video processor, but not by preceding systems.
*
* - `reader` is our PES reader context
* - `extra` is how many extra packets to output per "real" packet.
*/
extern void set_server_padding(PES_reader_p reader,
int extra);
/*
* Write out TS program data based on the information we have within the given
* PES reader context (as amended by any calls of
* `set_PES_reader_program_data`).
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int write_program_data(PES_reader_p reader,
TS_writer_p output);
#endif // _pes_fns

769
pidint.c 100644
Wyświetl plik

@ -0,0 +1,769 @@
/*
* Support for lists (actually arrays) of PID versus integer
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "compat.h"
#include "pidint_fns.h"
#include "misc_fns.h"
#include "ts_fns.h"
#include "h222_defns.h"
// ============================================================================
// PIDINT LIST maintenance
// ============================================================================
/*
* Initialise a new pid/int list datastructure.
*/
extern int init_pidint_list(pidint_list_p list)
{
list->length = 0;
list->size = PIDINT_LIST_START_SIZE;
list->number = malloc(sizeof(int)*PIDINT_LIST_START_SIZE);
if (list->number == NULL)
{
fprintf(stderr,"### Unable to allocate array in program list datastructure\n");
return 1;
}
list->pid = malloc(sizeof(u_int32)*PIDINT_LIST_START_SIZE);
if (list->pid == NULL)
{
free(list->number);
fprintf(stderr,"### Unable to allocate array in program list datastructure\n");
return 1;
}
return 0;
}
/*
* Build a new pid/int list datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_pidint_list(pidint_list_p *list)
{
pidint_list_p new = malloc(SIZEOF_PIDINT_LIST);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate pid/int list datastructure\n");
return 1;
}
if (init_pidint_list(new))
return 1;
*list = new;
return 0;
}
/*
* Add a pid/integer pair to the end of the list
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int append_to_pidint_list(pidint_list_p list,
u_int32 pid,
int program)
{
if (list == NULL)
{
fprintf(stderr,"### Unable to append to NULL pid/int list\n");
return 1;
}
if (list->length == list->size)
{
int newsize = list->size + PIDINT_LIST_INCREMENT;
list->number = realloc(list->number,newsize*sizeof(int));
if (list->number == NULL)
{
fprintf(stderr,"### Unable to extend pid/int list array\n");
return 1;
}
list->pid = realloc(list->pid,newsize*sizeof(u_int32));
if (list->pid == NULL)
{
fprintf(stderr,"### Unable to extend pid/int list array\n");
return 1;
}
list->size = newsize;
}
list->number[list->length] = program;
list->pid[list->length] = pid;
list->length++;
return 0;
}
/*
* Remove a pid/integer pair from the list
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remove_from_pidint_list(pidint_list_p list,
u_int32 pid)
{
int index;
int ii;
if (list == NULL)
{
fprintf(stderr,"### Unable to remove entry from NULL pid/int list\n");
return 1;
}
index = pid_index_in_pidint_list(list,pid);
if (index == -1)
{
fprintf(stderr,"### Cannot remove PID %04x from pid/int list"
" - it is not there\n",pid);
return 1;
}
for (ii = index; ii < (list->length - 1); ii++)
{
list->pid[ii] = list->pid[ii+1];
list->number[ii] = list->number[ii+1];
}
(list->length) --;
return 0;
}
/*
* Tidy up and free a pid/int list datastructure after we've finished with it
*
* Clears the datastructure, frees it and returns `list` as NULL.
*
* Does nothing if `list` is already NULL.
*/
extern void free_pidint_list(pidint_list_p *list)
{
if (*list == NULL)
return;
if ((*list)->number != NULL)
{
free((*list)->number);
(*list)->number = NULL;
}
if ((*list)->pid != NULL)
{
free((*list)->pid);
(*list)->pid = NULL;
}
(*list)->length = 0;
(*list)->size = 0;
free(*list);
*list = NULL;
}
/*
* Report on a pid/int list's contents
*/
extern void report_pidint_list(pidint_list_p list,
char *list_name,
char *int_name,
int pid_first)
{
if (list == NULL)
printf("%s is NULL\n",list_name);
else if (list->length == 0)
printf("%s is empty\n",list_name);
else
{
int ii;
printf("%s:\n",list_name);
for (ii=0; ii<list->length; ii++)
{
if (pid_first)
printf(" PID %04x (%d) -> %s %d\n",
list->pid[ii],list->pid[ii],int_name,list->number[ii]);
else
printf(" %s %d -> PID %04x (%d)\n",
int_name,list->number[ii],list->pid[ii],list->pid[ii]);
}
}
}
/*
* Lookup a PID to find its index in a pid/int list.
*
* Note that if `list` is NULL, then -1 will be returned - this is to
* allow the caller to make a query before they have read a list from the
* bitstream.
*
* Returns its index (0 or more) if the PID is in the list, -1 if it is not.
*/
extern int pid_index_in_pidint_list(pidint_list_p list,
u_int32 pid)
{
int ii;
if (list == NULL)
return -1;
for (ii = 0; ii < list->length; ii++)
{
if (list->pid[ii] == pid)
return ii;
}
return -1;
}
/*
* Lookup a PID to find the corresponding integer value in a pid/int list.
*
* Returns 0 if the PID is in the list, -1 if it is not.
*/
extern int pid_int_in_pidint_list(pidint_list_p list,
u_int32 pid,
int *number)
{
int ii;
if (list == NULL)
return -1;
for (ii = 0; ii < list->length; ii++)
{
if (list->pid[ii] == pid)
{
*number = list->number[ii];
return 0;
}
}
return -1;
}
/*
* Lookup a PID to see if it is in a pid/int list.
*
* Note that if `list` is NULL, then FALSE will be returned - this is to
* allow the caller to make a query before they have read a list from the
* bitstream.
*
* Returns TRUE if the PID is in the list, FALSE if it is not.
*/
extern int pid_in_pidint_list(pidint_list_p list,
u_int32 pid)
{
return pid_index_in_pidint_list(list,pid) != -1;
}
/*
* Check if two pid/int lists have the same content.
*
* Note that:
*
* - a list always compares as the same as itself
* - two NULL lists compare as the same
* - the *order* of PID/int pairs in the lists does not matter
*
* Returns TRUE if the two have the same content, FALSE otherwise.
*/
extern int same_pidint_list(pidint_list_p list1,
pidint_list_p list2)
{
int ii;
if (list1 == list2)
return TRUE;
else if (list1 == NULL || list2 == NULL)
return FALSE;
else if (list1->length != list2->length)
return FALSE;
for (ii = 0; ii < list1->length; ii++)
{
u_int32 pid = list1->pid[ii];
int idx = pid_index_in_pidint_list(list2,pid);
if (idx == -1)
return FALSE;
else if (list1->number[ii] != list2->number[idx])
return FALSE;
}
return TRUE;
}
/*
* Report on a program stream list (a specialisation of report_pidint_list).
*
* - `list` is the stream list to report on
* - `prefix` is NULL or a string to put before each line printed
*/
extern void report_stream_list(pidint_list_p list,
char *prefix)
{
if (prefix!=NULL) printf(prefix);
if (list == NULL)
printf("Program stream list is NULL\n");
else if (list->length == 0)
printf("Program stream list is empty\n");
else
{
int ii;
printf("Program streams:\n");
for (ii=0; ii<list->length; ii++)
{
if (prefix!=NULL) printf(prefix);
printf(" PID %04x (%d) -> Stream type %3d (%s)\n",
list->pid[ii],list->pid[ii],list->number[ii],
H222_STREAM_TYPE_STR(list->number[ii]));
}
}
}
// ============================================================================
// PMT data maintenance
// ============================================================================
/*
* Initialise a PMT datastructure's stream lists
*/
static int init_pmt_streams(pmt_p pmt)
{
pmt->num_streams = 0;
pmt->streams_size = PMT_STREAMS_START_SIZE;
pmt->streams = malloc(SIZEOF_PMT_STREAM*PMT_STREAMS_START_SIZE);
if (pmt->streams == NULL)
{
fprintf(stderr,"### Unable to allocate streams in PMT datastructure\n");
return 1;
}
return 0;
}
/*
* Build a new PMT datastructure.
*
* `version_number` should be in the range 0-31, and will be treated as a
* number modulo 32 if it is not.
*
* `PCR_pid` should be a legitimate PCR PID - i.e., in the range 0x0010 to
* 0x1FFE, or 0x1FFF to indicate "unset". However, for convenience, the
* value 0 will also be accepted, and converted to 0x1FFF.
*
* Returns (a pointer to) the new PMT datastructure, or NULL if some error
* occurs.
*/
extern pmt_p build_pmt(u_int16 program_number, byte version_number,
u_int32 PCR_pid)
{
pmt_p new;
if (version_number > 31)
version_number = version_number % 32;
if (PCR_pid == 0)
PCR_pid = 0x1FFF; // unset
if (PCR_pid != 0x1FFF && (PCR_pid < 0x0010 || PCR_pid > 0x1ffe))
{
fprintf(stderr,"### Error building PMT datastructure\n"
" PCR PID %04x is outside legal program stream range\n",
PCR_pid);
return NULL;
}
new = malloc(SIZEOF_PMT);
if (new == NULL)
{
fprintf(stderr,"### Unable to allocate PMT datastructure\n");
return NULL;
}
new->program_number = program_number;
new->version_number = version_number;
new->PCR_pid = PCR_pid;
new->program_info_length = 0;
new->program_info = NULL;
if (init_pmt_streams(new))
{
free(new);
return NULL;
}
return new;
}
/*
* Set the descriptor data on a PMT. Specifically, 'program info',
* the descriptor data in the PMT "as a whole".
*
* Any previous program information for this PMT is lost.
*
* A copy of the program information bytes is taken.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int set_pmt_program_info(pmt_p pmt,
u_int16 program_info_length,
byte *program_info)
{
if (program_info_length > PMT_MAX_INFO_LENGTH)
{
fprintf(stderr,"### Program info length %d is more than %d\n",
program_info_length,PMT_MAX_INFO_LENGTH);
return 1;
}
if (pmt->program_info == NULL)
{
pmt->program_info = malloc(program_info_length);
if (pmt->program_info == NULL)
{
fprintf(stderr,"### Unable to allocate program info in PMT datastructure\n");
return 1;
}
}
else if (program_info_length != pmt->program_info_length)
{
// well, we might be shrinking it rather than growing it, but still
pmt->program_info = realloc(pmt->program_info,program_info_length);
if (pmt->program_info == NULL)
{
fprintf(stderr,"### Unable to extend program info in PMT datastructure\n");
return 1;
}
}
memcpy(pmt->program_info,program_info,program_info_length);
pmt->program_info_length = program_info_length;
return 0;
}
/*
* Add a program stream to a PMT datastructure
*
* If `ES_info_length` is greater than 0, then `ES_info` is copied.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int add_stream_to_pmt(pmt_p pmt,
u_int32 elementary_PID,
byte stream_type,
u_int16 ES_info_length,
byte *ES_info)
{
if (pmt == NULL)
{
fprintf(stderr,"### Unable to append to NULL PMT datastructure\n");
return 1;
}
if (elementary_PID < 0x0010 || elementary_PID > 0x1ffe)
{
fprintf(stderr,"### Error adding stream to PMT\n"
" Elementary PID %04x is outside legal program stream range\n",
elementary_PID);
return 1;
}
if (ES_info_length > PMT_MAX_INFO_LENGTH)
{
fprintf(stderr,"### ES info length %d is more than %d\n",
ES_info_length,PMT_MAX_INFO_LENGTH);
return 1;
}
if (pmt->num_streams == pmt->streams_size)
{
int newsize = pmt->streams_size + PMT_STREAMS_INCREMENT;
pmt->streams = realloc(pmt->streams,newsize*SIZEOF_PMT_STREAM);
if (pmt->streams == NULL)
{
fprintf(stderr,"### Unable to extend PMT streams array\n");
return 1;
}
pmt->streams_size = newsize;
}
pmt->streams[pmt->num_streams].stream_type = stream_type;
pmt->streams[pmt->num_streams].elementary_PID = elementary_PID;
pmt->streams[pmt->num_streams].ES_info_length = ES_info_length;
if (ES_info_length > 0)
{
pmt->streams[pmt->num_streams].ES_info = malloc(ES_info_length);
if (pmt->streams[pmt->num_streams].ES_info == NULL)
{
fprintf(stderr,"### Unable to allocate PMT stream ES info\n");
return 1;
}
memcpy(pmt->streams[pmt->num_streams].ES_info,ES_info,ES_info_length);
}
else
pmt->streams[pmt->num_streams].ES_info = NULL;
pmt->num_streams++;
return 0;
}
/*
* Free a PMT stream datastructure
*/
static void free_pmt_stream(pmt_stream_p stream)
{
if (stream == NULL) return;
if (stream->ES_info != NULL)
{
free(stream->ES_info);
stream->ES_info = NULL;
}
}
/*
* Remove a program stream from a PMT.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remove_stream_from_pmt(pmt_p pmt,
u_int32 pid)
{
int index;
int ii;
if (pmt == NULL)
{
fprintf(stderr,"### Unable to remove entry from NULL PMT datastructure\n");
return 1;
}
index = pid_index_in_pmt(pmt,pid);
if (index == -1)
{
fprintf(stderr,"### Cannot remove PID %04x from PMT datastructure"
" - it is not there\n",pid);
return 1;
}
free_pmt_stream(&pmt->streams[index]);
for (ii = index; ii < (pmt->num_streams - 1); ii++)
pmt->streams[ii] = pmt->streams[ii+1];
(pmt->num_streams) --;
return 0;
}
/*
* Tidy up and free a PMT datastructure after we've finished with it
*
* Clears the datastructure, frees it and returns `pmt` as NULL.
*
* Does nothing if `pmt` is already NULL.
*/
extern void free_pmt(pmt_p *pmt)
{
if (*pmt == NULL)
return;
if ((*pmt)->num_streams > 0)
{
int ii;
for (ii = 0; ii < (*pmt)->num_streams; ii++)
free_pmt_stream(&(*pmt)->streams[ii]);
(*pmt)->num_streams = 0;
}
if ((*pmt)->program_info != NULL)
{
free((*pmt)->program_info);
(*pmt)->program_info = NULL;
}
free((*pmt)->streams);
(*pmt)->program_info_length = 0;
free(*pmt);
*pmt = NULL;
}
/*
* Lookup a PID to find its index in a PMT datastructure.
*
* Note that if `pmt` is NULL, then -1 will be returned.
*
* Returns its index (0 or more) if the PID is in the list, -1 if it is not.
*/
extern int pid_index_in_pmt(pmt_p pmt,
u_int32 pid)
{
int ii;
if (pmt == NULL)
return -1;
for (ii = 0; ii < pmt->num_streams; ii++)
{
if (pmt->streams[ii].elementary_PID == pid)
return ii;
}
return -1;
}
/*
* Lookup a PID to find the corresponding program stream information.
*
* Returns a pointer to the stream information if the PID is in the list,
* NULL if it is not.
*/
extern pmt_stream_p pid_stream_in_pmt(pmt_p pmt,
u_int32 pid)
{
int ii;
if (pmt == NULL)
return NULL;
for (ii = 0; ii < pmt->num_streams; ii++)
{
if (pmt->streams[ii].elementary_PID == pid)
return &pmt->streams[ii];
}
return NULL;
}
/*
* Lookup a PID to see if it is in a PMT datastructure.
*
* Note that if `pmt` is NULL, then FALSE will be returned.
*
* Returns TRUE if the PID is in the PMT's stream list, FALSE if it is not.
*/
extern int pid_in_pmt(pmt_p pmt,
u_int32 pid)
{
return pid_index_in_pmt(pmt,pid) != -1;
}
/*
* Check if two PMT streams have the same content.
*
* Returns TRUE if the two have the same content, FALSE otherwise.
*/
static int same_pmt_stream(pmt_stream_p str1,
pmt_stream_p str2)
{
if (str1 == str2) // !!!
return TRUE;
else if (str1 == NULL || str2 == NULL) // !!!
return FALSE;
else if (str1->elementary_PID != str2->elementary_PID)
return FALSE;
else if (str1->ES_info_length != str2->ES_info_length)
return FALSE;
else if (memcmp(str1->ES_info,str2->ES_info,str1->ES_info_length))
return FALSE;
else
return TRUE;
}
/*
* Check if two PMT datastructures have the same content.
*
* Note that:
*
* - a PMT datastructure always compares as the same as itself
* - two NULL datastructures compare as the same
* - a different version number means a different PMT
* - the *order* of program streams in the PMTs does not matter
* - descriptors must be identical as well, and byte order therein
* does matter (this may need changing later on)
*
* Returns TRUE if the two have the same content, FALSE otherwise.
*/
extern int same_pmt(pmt_p pmt1,
pmt_p pmt2)
{
int ii;
if (pmt1 == pmt2)
return TRUE;
else if (pmt1 == NULL || pmt2 == NULL)
return FALSE;
else if (pmt1->PCR_pid != pmt2->PCR_pid)
return FALSE;
else if (pmt1->version_number != pmt2->version_number)
return FALSE;
else if (pmt1->program_info_length != pmt2->program_info_length)
return FALSE;
else if (pmt1->num_streams != pmt2->num_streams)
return FALSE;
else if (memcmp(pmt1->program_info,pmt2->program_info,
pmt1->program_info_length))
return FALSE;
for (ii = 0; ii < pmt1->num_streams; ii++)
{
u_int32 pid = pmt1->streams[ii].elementary_PID;
int idx = pid_index_in_pmt(pmt2,pid);
if (idx == -1)
return FALSE;
else if (!same_pmt_stream(&pmt1->streams[ii],&pmt2->streams[idx]))
return FALSE;
}
return TRUE;
}
/*
* Report on a PMT datastructure.
*
* - `stream` is the stream to write to
* - `prefix` is NULL or a string to put before each line printed
* - `pmt` is the PMT to report on
*/
extern void report_pmt(FILE *stream,
char *prefix,
pmt_p pmt)
{
if (prefix!=NULL) fprintf(stream,prefix);
if (pmt == NULL)
{
fprintf(stream,"PMT is NULL\n");
return;
}
else
fprintf(stream,"Program %d, version %d, PCR PID %04x (%d)\n",
pmt->program_number,pmt->version_number,pmt->PCR_pid,pmt->PCR_pid);
if (pmt->program_info_length > 0)
{
if (prefix!=NULL) fprintf(stream,prefix);
print_data(stream," Program info",pmt->program_info,
pmt->program_info_length,pmt->program_info_length);
print_descriptors(stream,prefix," ",pmt->program_info,
pmt->program_info_length);
}
if (pmt->num_streams > 0)
{
int ii;
if (prefix!=NULL) fprintf(stream,prefix);
fprintf(stream,"Program streams:\n");
for (ii=0; ii<pmt->num_streams; ii++)
{
if (prefix!=NULL) fprintf(stream,prefix);
fprintf(stream," PID %04x (%4d) -> Stream type %02x (%3d) %s\n",
pmt->streams[ii].elementary_PID,
pmt->streams[ii].elementary_PID,
pmt->streams[ii].stream_type,
pmt->streams[ii].stream_type,
H222_STREAM_TYPE_STR(pmt->streams[ii].stream_type));
if (pmt->streams[ii].ES_info_length > 0)
{
if (prefix!=NULL) fprintf(stream,prefix);
print_data(stream," ES info",
pmt->streams[ii].ES_info,
pmt->streams[ii].ES_info_length,
pmt->streams[ii].ES_info_length);
print_descriptors(stream,prefix," ",
pmt->streams[ii].ES_info,
pmt->streams[ii].ES_info_length);
}
}
}
}

87
pidint_defns.h 100644
Wyświetl plik

@ -0,0 +1,87 @@
/*
* Datastructures for working PID/integer lists
*
* The PAT is adequately represented by a transport_stream_id and a
* pidint_list of its program_number->PID mappings.
*
* A PMT requires a bit more structure, mainly to allow for the handling
* of descriptor information.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _pidint_defns
#define _pidint_defns
#include "compat.h"
// ----------------------------------------------------------------------------
// An expandable list of PID vs. integer
struct pidint_list
{
int *number; // The integers
u_int32 *pid; // The corresponding PIDs
int length; // How many there are
int size; // How big the arrays are
};
typedef struct pidint_list *pidint_list_p;
#define SIZEOF_PIDINT_LIST sizeof(struct pidint_list)
#define PIDINT_LIST_START_SIZE 5
#define PIDINT_LIST_INCREMENT 10
// ----------------------------------------------------------------------------
// PMT - a representation of a Program Map Table
struct _pmt_stream
{
byte stream_type;
u_int32 elementary_PID;
u_int16 ES_info_length;
byte *ES_info; // the descriptor data therefor
};
typedef struct _pmt_stream *pmt_stream_p;
#define SIZEOF_PMT_STREAM sizeof(struct _pmt_stream)
struct _pmt
{
u_int16 program_number;
byte version_number; // perhaps not strictly necessary
u_int32 PCR_pid;
u_int16 program_info_length;
byte *program_info; // the descriptor data therefor
int streams_size; // the size of the `streams` array
int num_streams; // the number of streams we know about
pmt_stream_p streams;
};
typedef struct _pmt *pmt_p;
#define SIZEOF_PMT sizeof(struct _pmt)
#define PMT_STREAMS_START_SIZE 5
#define PMT_STREAMS_INCREMENT 10
#define PMT_MAX_INFO_LENGTH 0x3FF // i.e., 12 bits with the top two zero
#endif // _pidint_defns

240
pidint_fns.h 100644
Wyświetl plik

@ -0,0 +1,240 @@
/*
* Functions for working PID/integer lists.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _pidint_fns
#define _pidint_fns
#include "pidint_defns.h"
// ============================================================================
// PIDINT LIST maintenance
// ============================================================================
/*
* Initialise a new pid/int list datastructure.
*/
extern int init_pidint_list(pidint_list_p list);
/*
* Build a new pid/int list datastructure.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_pidint_list(pidint_list_p *list);
/*
* Add a pid/integer pair to the end of the list
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int append_to_pidint_list(pidint_list_p list,
u_int32 pid,
int program);
/*
* Remove a pid/integer pair from the list
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remove_from_pidint_list(pidint_list_p list,
u_int32 pid);
/*
* Tidy up and free a pid/int list datastructure after we've finished with it
*
* Clears the datastructure, frees it and returns `list` as NULL.
*
* Does nothing if `list` is already NULL.
*/
extern void free_pidint_list(pidint_list_p *list);
/*
* Report on a pid/int list's contents
*/
extern void report_pidint_list(pidint_list_p list,
char *list_name,
char *int_name,
int pid_first);
/*
* Lookup a PID to find the corresponding integer value in a pid/int list.
*
* Returns 0 if the PID is in the list, -1 if it is not.
*/
extern int pid_int_in_pidint_list(pidint_list_p list,
u_int32 pid,
int *number);
/*
* Lookup a PID to find its index in a pid/int list.
*
* Note that if `list` is NULL, then -1 will be returned - this is to
* allow the caller to make a query before they have read a list from the
* bitstream.
*
* Returns its index (0 or more) if the PID is in the list, -1 if it is not.
*/
extern int pid_index_in_pidint_list(pidint_list_p list,
u_int32 pid);
/*
* Lookup a PID to see if it is in a pid/int list.
*
* Note that if `list` is NULL, then FALSE will be returned - this is to
* allow the caller to make a query before they have read a list from the
* bitstream.
*
* Returns TRUE if the PID is in the list, FALSE if it is not.
*/
extern int pid_in_pidint_list(pidint_list_p list,
u_int32 pid);
/*
* Check if two pid/int lists have the same content.
*
* Note that:
*
* - a list always compares as the same as itself
* - two NULL lists compare as the same
* - the *order* of PID/int pairs in the lists does not matter
*
* Returns TRUE if the two have the same content, FALSE otherwise.
*/
extern int same_pidint_list(pidint_list_p list1,
pidint_list_p list2);
/*
* Report on a program stream list (a specialisation of report_pidint_list).
*
* - `list` is the stream list to report on
* - `prefix` is NULL or a string to put before each line printed
*/
extern void report_stream_list(pidint_list_p list,
char *prefix);
// ============================================================================
// PMT data maintenance
// ============================================================================
/*
* Build a new PMT datastructure.
*
* `version_number` should be in the range 0-31, and will be treated as a
* number modulo 32 if it is not.
*
* `PCR_pid` should be a legitimate PCR PID - i.e., in the range 0x0010 to
* 0x1FFE, or 0x1FFF to indicate "unset". However, for convenience, the
* value 0 will also be accepted, and converted to 0x1FFF.
*
* Returns (a pointer to) the new PMT datastructure, or NULL if some error
* occurs.
*/
extern pmt_p build_pmt(u_int16 program_number, byte version_number,
u_int32 PCR_pid);
/*
* Set the descriptor data on a PMT. Specifically, 'program info',
* the descriptor data in the PMT "as a whole".
*
* Any previous program information for this PMT is lost.
*
* A copy of the program information bytes is taken.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int set_pmt_program_info(pmt_p pmt,
u_int16 program_info_length,
byte *program_info);
/*
* Add a program stream to a PMT datastructure
*
* If `ES_info_length` is greater than 0, then `ES_info` is copied.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int add_stream_to_pmt(pmt_p pmt,
u_int32 elementary_PID,
byte stream_type,
u_int16 ES_info_length,
byte *ES_info);
/*
* Remove a program stream from a PMT.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remove_stream_from_pmt(pmt_p pmt,
u_int32 pid);
/*
* Tidy up and free a PMT datastructure after we've finished with it
*
* Clears the datastructure, frees it and returns `pmt` as NULL.
*
* Does nothing if `pmt` is already NULL.
*/
extern void free_pmt(pmt_p *pmt);
/*
* Lookup a PID to find its index in a PMT datastructure.
*
* Note that if `pmt` is NULL, then -1 will be returned.
*
* Returns its index (0 or more) if the PID is in the list, -1 if it is not.
*/
extern int pid_index_in_pmt(pmt_p pmt,
u_int32 pid);
/*
* Lookup a PID to find the corresponding program stream information.
*
* Returns a pointer to the stream information if the PID is in the list,
* NULL if it is not.
*/
extern pmt_stream_p pid_stream_in_pmt(pmt_p pmt,
u_int32 pid);
/*
* Lookup a PID to see if it is in a PMT datastructure.
*
* Note that if `pmt` is NULL, then FALSE will be returned.
*
* Returns TRUE if the PID is in the PMT's stream list, FALSE if it is not.
*/
extern int pid_in_pmt(pmt_p pmt,
u_int32 pid);
/*
* Check if two PMT datastructures have the same content.
*
* Note that:
*
* - a PMT datastructure always compares as the same as itself
* - two NULL datastructures compare as the same
* - the *order* of program streams in the PMTs does not matter
* - descriptors must be identical as well, and byte order therein
* does matter (this may need changing later on)
*
* Returns TRUE if the two have the same content, FALSE otherwise.
*/
extern int same_pmt(pmt_p pmt1,
pmt_p pmt2);
/*
* Report on a PMT datastructure.
*
* - `stream` is the stream to write to
* - `prefix` is NULL or a string to put before each line printed
* - `pmt` is the PMT to report on
*/
extern void report_pmt(FILE *stream,
char *prefix,
pmt_p pmt);
#endif // _pidint_fns

1961
ps.c 100644

Plik diff jest za duży Load Diff

500
ps2ts.c 100644
Wyświetl plik

@ -0,0 +1,500 @@
/*
* Convert a Program Stream to Transport Stream.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "pes_fns.h"
#include "ps_fns.h"
#include "ts_fns.h"
#include "tswrite_fns.h"
#include "misc_fns.h"
#include "version.h"
static void print_usage()
{
printf(
"Usage: ps2ts [switches] [<infile>] [<outfile>]\n"
"\n"
);
REPORT_VERSION("ps2ts");
printf(
"\n"
" Convert an H.222 program stream to H.222 transport stream.\n"
"\n"
" This program does not make use of any Program Stream Map packets\n"
" in the data (mainly because I have yet to see data with any). This\n"
" means that the program has to determine the stream type of the data\n"
" based on the first few ES units.\n"
"\n"
" This program does not output more than one video and one audio\n"
" stream. If the program stream data contains more than one of each,\n"
" the first will be used, and the others ignored (with a message\n"
" indicating this).\n"
"\n"
" It is assumed that the video stream will contain DTS values in its\n"
" PES packets at reasonable intervals, which can be used as PCR values\n"
" in the transport stream, and thus the video stream's PID can be used\n"
" as the PCR PID in the transport stream.\n"
"\n"
"Files:\n"
" <infile> is a file containing the program stream data\n"
" (but see -stdin below)\n"
" <outfile> is an transport stream file\n"
" (but see -stdout and -host below)\n"
"\n"
"Input switches:\n"
" -stdin Take input from <stdin>, instead of a named file\n"
" -dvd The PS data is from a DVD. This is the default.\n"
" This switch has no effect on MPEG-1 PS data.\n"
" -notdvd, -nodvd The PS data is not from a DVD.\n"
" The DVD specification stores AC-3 (Dolby), DTS and\n"
" other audio in a specialised manner in private_stream_1.\n"
" -vstream <n> Take video from video stream <n> (0..7).\n"
" The default is the first video stream found.\n"
" -astream <n> Take audio from audio stream <n> (0..31).\n"
" The default is the first audio stream found\n"
" (this includes private_stream_1 on non-DVD streams).\n"
" -ac3stream <n> Take audio from AC3 substream <n> (0..7), from\n"
" private_stream_1. This implies -dvd.\n"
" (If audio is being taken from a substream, the user\n"
" is assumed to have determined which one is wanted,\n"
" e.g., using psreport)\n"
"\n"
"Output switches:\n"
" -stdout Write output to <stdout>, instead of a named file\n"
" Forces -quiet.\n"
" -host <host>, -host <host>:<port>\n"
" Writes output (over TCP/IP) to the named <host>,\n"
" instead of to a named file. If <port> is not\n"
" specified, it defaults to 88.\n"
" -vpid <pid> <pid> is the video PID to use for the data.\n"
" Use '-vpid 0x<pid>' to specify a hex value.\n"
" Defaults to 0x68.\n"
" -apid <pid> <pid> is the audio PID to use for the data.\n"
" Use '-apid 0x<pid>' to specify a hex value.\n"
" Defaults to 0x67.\n"
" -noaudio Don't output the audio data\n"
" -pmt <pid> <pid> is the PMT PID to use.\n"
" Use '-pmt 0x<pid>' to specify a hex value.\n"
" Defaults to 0x66\n"
" -prepeat <n> Output the program data (PAT/PMT) after every <n>\n"
" PS packs. Defaults to 100.\n"
" -pad <n> Pad the start with <n> filler TS packets, to allow\n"
" a TS reader to synchronize with the datastream.\n"
" Defaults to 8.\n"
"\n"
"General switches:\n"
" -verbose, -v Print a 'v' for each video packet and an 'a' for \n"
" each audio packet, as it is read\n"
" -quiet, -q Only output error messages\n"
" -max <n>, -m <n> Maximum number of PS packs to read\n"
"\n"
"Stream type:\n"
" When the TS data is being output, it is flagged to indicate whether\n"
" it conforms to H.262, H.264, etc. It is important to get this right, as\n"
" it will affect interpretation of the TS data.\n"
"\n"
" If input is from a file, then the program will look at the start of\n"
" the file to determine if the stream is H.264 or H.262 data. This\n"
" process may occasionally come to the wrong conclusion, in which case\n"
" the user can override the choice using the following switches.\n"
"\n"
" If input is from standard input (via -stdin), then it is not possible\n"
" for the program to make its own decision on the input stream type.\n"
" Instead, it defaults to H.262, and relies on the user indicating if\n"
" this is wrong.\n"
"\n"
" -h264, -avc Force the program to treat the input as MPEG-4/AVC.\n"
" -h262 Force the program to treat the input as MPEG-2.\n"
" -mp42 Force the program to treat the input as MPEG-4/Part 2.\n"
" -vtype <type> Force the program to treat the input as video of\n"
" stream type <type> (e.g., 0x42 means AVS video). It is\n"
" up to the user to specify a valid <type>.\n"
"\n"
" If the audio stream being output is Dolby (AC-3), then the stream type\n"
" used to output it differs for DVB (European) and ATSC (USA) data. It\n"
" may be specified as follows:\n"
"\n"
" -dolby dvb Use stream type 0x06 (the default)\n"
" -dolby atsc Use stream type 0x81\n"
);
}
int main(int argc, char **argv)
{
int use_stdin = FALSE;
int use_stdout = FALSE;
int use_tcpip = FALSE;
int port = 88; // Useful default port number
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
PS_reader_p ps = NULL;
TS_writer_p output = NULL;
int verbose = FALSE;
int quiet = FALSE;
int max = 0;
u_int32 pmt_pid = 0x66;
u_int32 video_pid = 0x68;
u_int32 pcr_pid = video_pid; // Use PCRs from the video stream
u_int32 audio_pid = 0x67;
int keep_audio = TRUE;
int repeat_program_every = 100;
int pad_start = 8;
int err = 0;
int ii = 1;
int video_type = VIDEO_H262; // hopefully a sensible default
int force_stream_type = FALSE;
int video_stream = -1;
int audio_stream = -1;
int want_ac3_audio = FALSE;
int input_is_dvd = TRUE;
int want_dolby_as_dvb = TRUE;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-help",argv[ii]) ||
!strcmp("-h",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-avc",argv[ii]) || !strcmp("-h264",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_H264;
}
else if (!strcmp("-h262",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_H262;
}
else if (!strcmp("-vtype",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,0,
&video_type);
if (err) return 1;
ii++;
force_stream_type = TRUE;
}
else if (!strcmp("-mp42",argv[ii]))
{
force_stream_type = TRUE;
video_type = VIDEO_MPEG4_PART2;
}
else if (!strcmp("-dolby",argv[ii]))
{
CHECKARG("ps2ts",ii);
if (!strcmp("dvb",argv[ii+1]))
want_dolby_as_dvb = TRUE;
else if (!strcmp("atsc",argv[ii+1]))
want_dolby_as_dvb = FALSE;
else
{
fprintf(stderr,"### ps2ts: -dolby must be followed by dvb or atsc\n");
return 1;
}
ii++;
}
else if (!strcmp("-stdin",argv[ii]))
{
had_input_name = TRUE; // more or less
use_stdin = TRUE;
}
else if (!strcmp("-stdout",argv[ii]))
{
had_output_name = TRUE; // more or less
use_stdout = TRUE;
}
else if (!strcmp("-dvd",argv[ii]))
{
input_is_dvd = TRUE;
}
else if (!strcmp("-notdvd",argv[ii]) || !strcmp("-nodvd",argv[ii]))
{
input_is_dvd = FALSE;
}
else if (!strcmp("-host",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = host_value("ps2ts",argv[ii],argv[ii+1],&output_name,&port);
if (err) return 1;
had_output_name = TRUE; // more or less
use_tcpip = TRUE;
ii++;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-prepeat",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10,
&repeat_program_every);
if (err) return 1;
ii++;
}
else if (!strcmp("-pad",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value("ps2ts",argv[ii],argv[ii+1],TRUE,10,&pad_start);
if (err) return 1;
ii++;
}
else if (!strcmp("-vpid",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&video_pid);
if (err) return 1;
ii++;
}
else if (!strcmp("-apid",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&audio_pid);
if (err) return 1;
ii++;
}
else if (!strcmp("-pmt",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = unsigned_value("ps2ts",argv[ii],argv[ii+1],0,&pmt_pid);
if (err) return 1;
ii++;
}
else if (!strcmp("-noaudio",argv[ii]))
{
keep_audio = FALSE;
}
else if (!strcmp("-vstream",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0xF,0,
&video_stream);
if (err) return 1;
ii++;
}
else if (!strcmp("-astream",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0x1F,0,
&audio_stream);
if (err) return 1;
want_ac3_audio = FALSE;
ii++;
}
else if (!strcmp("-ac3stream",argv[ii]))
{
CHECKARG("ps2ts",ii);
err = int_value_in_range("ps2ts",argv[ii],argv[ii+1],0,0x7,0,
&audio_stream);
if (err) return 1;
want_ac3_audio = TRUE;
input_is_dvd = TRUE;
ii++;
}
else
{
fprintf(stderr,"### ps2ts: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && had_output_name)
{
fprintf(stderr,"### ps2ts: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name)
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### ps2ts: No input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### ps2ts: No output file specified\n");
return 1;
}
// Try to stop extraneous data ending up in our output stream
if (use_stdout)
{
verbose = FALSE;
quiet = TRUE;
}
err = open_PS_file(input_name,quiet,&ps);
if (err)
{
fprintf(stderr,"### ps2ts: Unable to open input %s\n",
(use_stdin?"<stdin>":input_name));
return 1;
}
if (!quiet)
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
// Try to decide what sort of data stream we have
if (force_stream_type || use_stdin)
{
if (!quiet)
printf("Reading input as %s (0x%02x)\n",
H222_STREAM_TYPE_STR(video_type),video_type);
}
else
{
err = determine_PS_video_type(ps,&video_type);
if (err) return 1;
if (!quiet)
printf("Video appears to be %s (0x%02x)\n",
H222_STREAM_TYPE_STR(video_type),video_type);
}
if (!quiet)
{
if (input_is_dvd)
printf("Treating input as from DVD\n");
else
printf("Treating input as NOT from DVD\n");
printf("Reading video from ");
if (video_stream == -1)
printf("first stream found");
else
printf("stream %0#x (%d)",video_stream,video_stream);
if (keep_audio)
{
printf(", audio from ");
if (audio_stream == -1)
printf("first %s found",(want_ac3_audio?"AC3 stream":"stream"));
else
printf("%s %0#x (%d)",(want_ac3_audio?"AC3 stream":"stream"),
audio_stream,audio_stream);
printf("\n");
}
printf("Writing video with PID 0x%02x",video_pid);
if (keep_audio)
printf(", audio with PID 0x%02x,",audio_pid);
printf(" PMT PID 0x%02x, PCR PID 0x%02x\n",pmt_pid,pcr_pid);
if (max)
printf("Stopping after %d program stream packets\n",max);
}
if (use_stdout)
err = tswrite_open(TS_W_STDOUT,NULL,NULL,0,quiet,&output);
else if (use_tcpip)
err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&output);
else
err = tswrite_open(TS_W_FILE,output_name,NULL,0,quiet,&output);
if (err)
{
fprintf(stderr,"### ps2ts: Unable to open %s\n",output_name);
(void) close_PS_file(&ps);
return 1;
}
err = ps_to_ts(ps,output,pad_start,repeat_program_every,
video_type,input_is_dvd,
video_stream,audio_stream,want_ac3_audio,
want_dolby_as_dvb,pmt_pid,pcr_pid,video_pid,
keep_audio,audio_pid,max,verbose,quiet);
if (err)
{
fprintf(stderr,"### ps2ts: Error transferring data\n");
(void) close_PS_file(&ps);
(void) tswrite_close(output,TRUE);
return 1;
}
// And tidy up when we're finished
err = tswrite_close(output,quiet);
if (err)
fprintf(stderr,"### ps2ts: Error closing output %s: %s\n",output_name,
strerror(errno));
err = close_PS_file(&ps);
if (err)
fprintf(stderr,"### ps2ts: Error closing input %s\n",
(use_stdin?"<stdin>":input_name));
return 0;
}

137
ps_defns.h 100644
Wyświetl plik

@ -0,0 +1,137 @@
/*
* Datastructures for working with H.222 Program Stream packets - in
* particular, for reading PES packets.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _ps_defns
#define _ps_defns
#include "compat.h"
#include "h222_defns.h"
#include "tswrite_defns.h"
// ------------------------------------------------------------
// A program stream context, used to read PS and manage a read-ahead cache
#define PS_READ_AHEAD_SIZE 5000 // The number of bytes to read ahead
struct ps_reader
{
int input; // where we're reading from
offset_t start; // the offset at which our data starts
byte data[PS_READ_AHEAD_SIZE];
offset_t data_posn; // location of this data in the file
int32 data_len; // actual number of bytes in the buffer
byte *data_end; // off the end of `data`
byte *data_ptr; // which byte we're interested in (next)
};
typedef struct ps_reader *PS_reader_p;
#define SIZEOF_PS_READER sizeof(struct ps_reader)
// ------------------------------------------------------------
// A program stream pack header (not including the system header packets)
struct PS_pack_header
{
int id; // A number to identify this packet
byte data[10]; // The data excluding the leading 00 00 01 BA
u_int64 scr; // Formed from scr_base and scr_ext
u_int64 scr_base;
u_int32 scr_extn;
u_int32 program_mux_rate;
int pack_stuffing_length;
};
typedef struct PS_pack_header *PS_pack_header_p;
#define SIZEOF_PS_PACK_HEADER sizeof(struct PS_pack_header)
// ------------------------------------------------------------
// A program stream packet (specifically one that starts with six bytes
// organised as 00 00 01 <stream id> <packet length>)
struct PS_packet
{
int id; // A number to identify this packet
byte *data; // The data including the leading 00 00 01
int data_len; // Its length
byte stream_id; // Its stream id (i.e., data[4])
int packet_length; // The packet length (6 less than data_len)
};
typedef struct PS_packet *PS_packet_p;
#define SIZEOF_PS_PACKET sizeof(struct PS_packet)
// ------------------------------------------------------------
// Number of streams of various types
#define NUMBER_VIDEO_STREAMS 0x0F
#define NUMBER_AUDIO_STREAMS 0x1F
#define NUMBER_AC3_SUBSTREAMS 0x08
// DVD private_stream_1 substream identifiers
// (also used for non-DVD data when we have identified the private data
// appropriately)
#define SUBSTREAM_OTHER 0
#define SUBSTREAM_AC3 1 // AC-3 audio (Dolby 5.1)
#define SUBSTREAM_DTS 2 // DTS audio
#define SUBSTREAM_LPCM 3 // LPCM audio (CD audio)
#define SUBSTREAM_SUBPICTURES 4 // Sub-pictures
#define SUBSTREAM_ERROR 5 // Error in deciding
#define NUMBER_SUBSTREAM_TYPES 6 // useful for array sizing
#define SUBSTREAM_STR(what) ((what)==SUBSTREAM_OTHER?"other": \
(what)==SUBSTREAM_AC3 ?"AC3": \
(what)==SUBSTREAM_DTS ?"DTS": \
(what)==SUBSTREAM_LPCM ?"LPCM": \
(what)==SUBSTREAM_SUBPICTURES?"subpictures": \
"???")
#define SUBSTREAM_IS_AUDIO(what) ((what)==SUBSTREAM_AC3|| \
(what)==SUBSTREAM_DTS|| \
(what)==SUBSTREAM_LPCM)
#define BSMOD_STR(bsmod,acmod) \
((bsmod)==0?"main audio service: complete main (CM)": \
(bsmod)==1?"main audio service: music & effects (ME)": \
(bsmod)==2?"associated service: visually impaired (VI)": \
(bsmod)==3?"associated service: hearing impaired (HI)": \
(bsmod)==4?"associated service: dialogue (D)": \
(bsmod)==5?"associated service: commentary (C)": \
(bsmod)==6?"associated service: emergency (E)": \
(bsmod)==7 && (acmod)==1?"associated service: voice over (VO)": \
(bsmod)==7 && (acmod)>=2 && (acmod)<=7?"main audio service: karaoke": \
"???")
#define ACMOD_STR(acmod) ((acmod)==0?"1+1 Ch1,Ch2": \
(acmod)==1?"1/0 C": \
(acmod)==2?"2/0 L,R": \
(acmod)==3?"3/0 L,C,R": \
(acmod)==4?"2/1 L,R,S": \
(acmod)==5?"3/1 L,C,R,S": \
(acmod)==6?"2/2 L,R,SL,SR": \
(acmod)==7?"3/2 L,C,R,SL,SR":"???")
#endif // _ps_defns

351
ps_fns.h 100644
Wyświetl plik

@ -0,0 +1,351 @@
/*
* Functions for working with H.222 Program Stream packets - in particular,
* for reading PES packets.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _ps_fns
#define _ps_fns
#include "compat.h"
#include "h222_defns.h"
#include "tswrite_defns.h"
#include "ps_defns.h"
// ============================================================
// Program stream reading functions
// ============================================================
/*
* Build a program stream context attached to an input file. This handles
* read-ahead buffering for the PS.
*
* - `input` is the file stream to read from.
* - If `quiet`, then don't report on ignored bytes at the start of the file
* - `ps` is the new PS context
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int build_PS_reader(int input,
int quiet,
PS_reader_p *ps);
/*
* Tidy up the PS read-ahead context after we've finished with it.
*
* Specifically:
*
* - free the datastructure
* - set `ps` to NULL
*
* Does not close the associated file.
*/
extern void free_PS_reader(PS_reader_p *ps);
/*
* Open a PS file for reading.
*
* - `name` is the name of the file
* - If `quiet`, then don't report on ignored bytes at the start of the file
* - `ps` is the new PS context
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int open_PS_file(char *name,
int quiet,
PS_reader_p *ps);
/*
* Close a PS file, and free the reader context
*
* Returns 0 if all goes well, 1 otherwise.
*/
extern int close_PS_file(PS_reader_p *ps);
/*
* Given a program stream, attempt to determine if it holds H.262 or H.264
* data.
*
* Leaves the PS rewound to its "start".
*
* NOTE: It is probably better to use determine_PS_video_type().
*
* - `ps` is the program stream to check (assumed just to have been
* opened/built). This cannot be standard input, as it must be
* seekable.
* - `is_h264` is the result
*
* Returns 0 if all goes well, 1 if there was an error (including the
* stream not appearing to be either).
*/
extern int determine_if_PS_is_h264(PS_reader_p ps,
int *is_h264);
/*
* Given a program stream, attempt to determine what type of video data it
* contains.
*
* Leaves the PS rewound to its "start".
*
* - `ps` is the program stream to check (assumed just to have been
* opened/built). This cannot be standard input, as it must be
* seekable.
* - `video_type` is the result. Calls determine_PES_video_type().
*
* Returns 0 if all goes well, 1 if there was an error (including the
* stream not appearing to be either).
*/
extern int determine_PS_video_type(PS_reader_p ps,
int *video_type);
/*
* Seek within the PS file.
*
* Note that if the intent is to *rewind* to the start of the PS data,
* then `rewind_program_stream` should be used instead, as offset 0 is
* not necessarily the same as the start of the program stream.
*
* - `ps` is the PS read-ahead context
* - `posn` is the file offset to seek to
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int seek_using_PS_reader(PS_reader_p ps,
offset_t posn);
/*
* Rewind the PS context to the remembered "start of data"
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int rewind_program_stream(PS_reader_p ps);
/*
* Print out a stream id in a manner consistent with the PS usages
* of the stream id values.
*/
extern void print_stream_id(FILE *stream,
byte stream_id);
/*
* Look for the start (the first 4 bytes) of the next program stream packet.
*
* Assumes that (for some reason) alignment has been lost, and thus it is
* necessary to scan forwards to find the next 00 00 01 prefix.
*
* Otherwise equivalent to a call of `read_PS_packet_start`.
*
* - `ps` is the PS read-ahead context we're reading from
* - if `verbose`, then we want to explain what we're doing
* - if `max` is non-zero, then it is the maximum number of bytes
* to scan before giving up.
* - `posn` is the file offset of the start of the packet
* - `stream_id` is the identifying byte, after the 00 00 01 prefix. Note
* that this is set correctly if MPEG_program_end_code was read, and is
* 0 if an error occurred.
*
* Returns:
* * 0 if it succeeds,
* * EOF if EOF is read, or an MPEG_program_end_code is read, or
* * 1 if some error (including the first 3 bytes not being 00 00 01) occurs.
*/
extern int find_PS_packet_start(PS_reader_p ps,
int verbose,
u_int32 max,
offset_t *posn,
byte *stream_id);
/*
* Look for the next PS pack header.
*
* Equivalent to calling `find_PS_packet_start` until `stream_id` is 0xBA
* (in other words, equivalent to having read the pack header start with
* `read_PS_packet_start`).
*
* If you want to call `read_PS_packet_start` to read this pack header start
* in again, then call ``seek_using_PS_reader(ps,posn)`` to reposition ready
* to read it.
*
* - `ps` is the PS read-ahead context we're reading from
* - if `verbose`, then the 00 00 01 XX sequences found will be logged
* to stderr, indicating the progress of our search
* - if `max` is non-zero, then it is the maximum number of bytes
* to scan before giving up.
* - `posn` is the file offset of the start of the packet found
*
* Returns:
* * 0 if it succeeds,
* * EOF if EOF is read, or an MPEG_program_end_code is read, or
* * 1 if some error (including the first 3 bytes not being 00 00 01) occurs.
*/
extern int find_PS_pack_header_start(PS_reader_p ps,
int verbose,
u_int32 max,
offset_t *posn);
/*
* Read in (the rest of) a PS packet according to its length.
*
* Suitable for use reading PS PES packets and PS system header packets.
*
* NOTE that the `data` buffer in the `packet` is realloc'ed by this
* function. It is thus important to ensure that the `packet` datastructure
* contains a NULL pointer for said buffer before the first call of this
* function.
*
* - `ps` is the PS read-ahead context we're reading from
* - `stream_id` identifies what sort of packet it is
* - `packet` is the packet we're reading the PES packet into.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int read_PS_packet_body(PS_reader_p ps,
byte stream_id,
PS_packet_p packet);
/*
* Read in the body of the pack header (but *not* the system header packets
* therein).
*
* - `ps` is the PS read-ahead context we're reading from
* - `hdr` is the packet we've read
*
* Returns 0 if it succeeds, or 1 if some error occurs.
*/
extern int read_PS_pack_header_body(PS_reader_p ps,
PS_pack_header_p hdr);
/*
* Clear the contents of a PS packet datastructure. Frees the internal
* `data` array.
*/
extern void clear_PS_packet(PS_packet_p packet);
/*
* Tidy up and free a PS packet datastructure after we've finished with it.
*
* Empties the PS packet datastructure, frees it, and sets `unit` to NULL.
*
* If `unit` is already NULL, does nothing.
*/
extern void free_PS_packet(PS_packet_p *packet);
/*
* Read in the start (the first 4 bytes) of the next program stream packet.
*
* If the bytes read don't appear to be valid (i.e., they do not start with
* the 00 00 01 prefix), then the next pack header will be sought and read in.
*
* Note that sequences of 00 bytes before the 00 00 01 will be ignored.
*
* - `ps` is the PS read-ahead context we're reading from
* - if `verbose`, then we want to explain what we're doing
* - `posn` is the file offset of the start of the packet
* - `stream_id` is the identifying byte, after the 00 00 01 prefix. Note
* that this is set correctly if MPEG_program_end_code was read, and is
* 0 if an error occurred or reading was ended because `max` packets had
* been read.
*
* Returns:
* * 0 if it succeeds,
* * EOF if EOF is read, or an MPEG_program_end_code is read,
* * 2 if the bytes read are not 00 00 01 `stream_id`, or
* * 1 if some other error occurs.
*/
extern int read_PS_packet_start(PS_reader_p ps,
int verbose,
offset_t *posn,
byte *stream_id);
/*
* Inspect the given PS packet, and determine if it contains AC3 or DTS audio data.
*
* - `packet` is the packet's data, already established as private_data_1
* - `is_dvd` is true if the data should be interpreted as DVD data
* - if `verbose`, report on the details of what we find out
* - `substream_index` returns the substream's index, taken from the low
* nibble of the substream id, and adjusted to start at 0. This will be
* a value in the range 0-7 for DTS, AC3 and LPCM, and in the range 0-1F
* (0-31) for subpictures.
* - for AC3, `bsmod` and `acmod` return the appropriate quantities,
* otherwise they are 0.
*
* Returns one of the SUBSTREAM_* values.
*/
extern int identify_private1_data(struct PS_packet *packet,
int is_dvd,
int verbose,
int *substream_index,
byte *bsmod,
byte *acmod);
// ============================================================
// PS to TS functions
// ============================================================
/*
* Read program stream and write transport stream
*
* - `ps` is the program stream
* - `output` is the transport stream
* - `pad_start` is the number of filler TS packets to start the output
* with.
* - `program_repeat` is how often (after how many PS packs) to repeat
* the program information (PAT/PMT)
* - `video_type` indicates what type of video is being transferred. It should
* be VIDEO_H264, VIDEO_H262, etc.
* - `is_dvd` should be true if this input represents DVD data; i.e., with
* private_stream_1 used for AC-3/DTS/etc., and with substream headers
* therein.
* - `video_stream` indicates which video stream we want - i.e., the stream
* with id 0xE0 + <video_stream>. -1 means the first encountered.
* - `audio_stream` indicates which audio stream we want. If `want_ac3_audio`
* is false, then this will be the stream with id 0xC0 + <audio_stream>,
* or -1 for the first audio stream encountered.
* - if `want_ac3_audio` is true, then if `is_dvd` is true, then we want
* audio from private_stream_1 (0xBD) with substream id <audio_stream>,
* otherwise we ignore `audio_stream` and assume that all data in
* private_stream_1 is the audio we want.
* - `dolby_is_dvb` should be true if Dolby (AC-3) audio (if selected) should
* be output using the DVB stream type 0x06, false if using the ATSC stream
* type 0x81. This is ignored if the audio being output is not Dolby.
* - `pmt_pid` is the PID of the PMT to write
* - `pcr_pid` is the PID of the TS unit containing the PCR
* - `video_pid` is the PID for the video we write
* - `keep_audio` is true if the audio stream should be output, false if
* it should be ignored
* - `audio_pid` is the PID for the audio we write
* - if `max` is non-zero, then we want to stop reading after we've read
* `max` packs
* - if `verbose` then we want to output diagnostic information
* - if `quiet` then we want to be as quiet as we can
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int ps_to_ts(PS_reader_p ps,
TS_writer_p output,
int pad_start,
int program_repeat,
int video_type,
int is_dvd,
int video_stream,
int audio_stream,
int want_ac3_audio,
int dolby_is_dvb,
u_int32 pmt_pid,
u_int32 pcr_pid,
u_int32 video_pid,
int keep_audio,
u_int32 audio_pid,
int max,
int verbose,
int quiet);
#endif // _ps_fns

367
psdots.c 100644
Wyświetl plik

@ -0,0 +1,367 @@
/*
* Report on the content of an H.222 program stream (PS) file as a sequence
* of single characters, representing appropriate entities.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "ps_fns.h"
#include "misc_fns.h"
#include "version.h"
/*
* Report on the given file with characters representing packets
*
* - `ps` is the PS file we're reading
* - if `max` is more than zero, then it is the maximum number of PS packs
* we want to read
* - `verbose` is true if we want an explanation of the characters
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int report_ps_dots(PS_reader_p ps,
int max,
int verbose)
{
int err;
int count = 0;
int num_packs = 0;
offset_t posn; // The location in the input file of the current packet
byte stream_id; // The packet's stream id
int end_of_file = FALSE;
struct PS_packet packet = {0};
struct PS_pack_header header = {0};
if (verbose)
printf("Characters represent the following:\n"
" [ Pack header\n"
" H System header\n"
" ] MPEG_program_end_code\n"
" p<n> Private stream <n> (1 or 2)\n"
" v Video stream 0\n"
" v<n> Video stream <n> (>0)\n"
" a Audio stream 0\n"
" a<n> Audio stream <n> (>0)\n"
" M Program stream map\n"
" D Program stream directory\n"
" . Padding\n"
" ? Something else\n"
);
// Read the start of the first packet (we confidently expect this
// to be a pack header)
err = read_PS_packet_start(ps,FALSE,&posn,&stream_id);
if (err == EOF)
{
fprintf(stderr,"### Error reading first pack header\n");
fprintf(stderr," Unexpected end of PS at start of stream\n");
return 1;
}
else if (err)
{
fprintf(stderr,"### Error reading first pack header\n");
return 1;
}
if (stream_id != 0xba)
{
fprintf(stderr,"### Program stream does not start with pack header\n");
fprintf(stderr," First packet has stream id %02X (",stream_id);
print_stream_id(stdout,stream_id);
printf(")\n");
return 1;
}
// But given that, we can now happily loop reading in packs
for (;;)
{
int num_system_headers = 0;
if (max > 0 && num_packs >= max)
{
printf("\nStopping after %d packs\n",num_packs);
return 0;
}
num_packs ++;
printf("[");
fflush(stdout);
err = read_PS_pack_header_body(ps,&header);
if (err)
{
fprintf(stderr,
"### Error reading data for pack header starting at "
OFFSET_T_FORMAT "\n",posn);
return 1;
}
// Read (and, for the moment, at least, ignore) any system headers
for (;;)
{
err = read_PS_packet_start(ps,FALSE,&posn,&stream_id);
if (err == EOF)
{
end_of_file = TRUE;
if (stream_id == 0xB9)
{
printf("]");
fflush(stdout);
}
break;
}
else if (err)
return 1;
if (stream_id == 0xbb) // System header
{
printf("H");
fflush(stdout);
err = read_PS_packet_body(ps,stream_id,&packet);
if (err)
{
fprintf(stderr,
"### Error reading system header starting at "
OFFSET_T_FORMAT "\n",posn);
return 1;
}
// For the moment, just ignore the system header content
num_system_headers ++;
}
else
break;
}
if (end_of_file)
break;
// We've finished with system headers - onto data (one fondly hopes)
for (;;)
{
if (stream_id == 0xba) // Start of the next pack
break;
if (stream_id == 0xBC)
printf("M");
else if (stream_id == 0xFF)
printf("D");
else if (stream_id == 0xBD)
printf("p1");
else if (stream_id == 0xBE)
printf(".");
else if (stream_id == 0xBF)
printf("p2");
else if (stream_id >= 0xC0 && stream_id <=0xDF)
{
int number = stream_id & 0x1F;
if (number == 0)
printf("a");
else
printf("a%x",number);
}
else if (stream_id >= 0xE0 && stream_id <= 0xEF)
{
int number = stream_id & 0x0F;
if (number == 0)
printf("v");
else
printf("v%x",number);
}
else
printf("?");
fflush(stdout);
err = read_PS_packet_body(ps,stream_id,&packet);
if (err)
{
fprintf(stderr,"### Error reading PS packet starting at "
OFFSET_T_FORMAT "\n",posn);
return 1;
}
err = read_PS_packet_start(ps,FALSE,&posn,&stream_id);
if (err == EOF)
{
if (stream_id == 0xB9)
{
printf("]");
fflush(stdout);
}
end_of_file = TRUE;
break;
}
else if (err)
return 1;
}
if (end_of_file)
break;
}
clear_PS_packet(&packet);
printf("\nRead %d PS packet%s in %d pack%s\n",
count,(count==1?"":"s"),
num_packs,(num_packs==1?"":"s"));
return 0;
}
static void print_usage()
{
printf(
"Usage: psreport [switches] [<infile>]\n"
"\n"
);
REPORT_VERSION("psdots");
printf(
"\n"
" Present the content of a Program Stream file as a sequence of\n"
" characters, representing the packets.\n"
"\n"
"Files:\n"
" <infile> is an H.222 Program Stream file (but see -stdin)\n"
"\n"
"Switches:\n"
" -stdin Input from standard input, instead of a file\n"
" -verbose, -v Output a description of the characters used\n"
" -max <n>, -m <n> Maximum number of PS packets to read\n"
);
}
int main(int argc, char **argv)
{
int use_stdin = FALSE;
char *input_name = NULL;
int had_input_name = FALSE;
PS_reader_p ps; // The PS file we're reading
int max = 0; // The maximum number of PS packets to read (or 0)
int verbose = FALSE; // True => output diagnostic/progress messages
int err = 0;
int ii = 1;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("psdots",ii);
err = int_value("psdots",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-stdin",argv[ii]))
{
use_stdin = TRUE;
had_input_name = TRUE; // so to speak
}
else
{
fprintf(stderr,"### psdots: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name)
{
fprintf(stderr,"### psdots: Unexpected '%s'\n",argv[ii]);
return 1;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### psdots: No input file specified\n");
return 1;
}
err = open_PS_file(input_name,FALSE,&ps);
if (err)
{
fprintf(stderr,"### psdots: Unable to open input file %s\n",
(use_stdin?"<stdin>":input_name));
return 1;
}
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
if (max)
printf("Stopping after %d PS packets\n",max);
err = report_ps_dots(ps,max,verbose);
if (err)
fprintf(stderr,"### psdots: Error reporting on input stream\n");
err = close_PS_file(&ps);
if (err)
{
fprintf(stderr,"### psdots: Error closing input file %s\n",
(use_stdin?"<stdin>":input_name));
return 1;
}
return 0;
}

597
psreport.c 100644
Wyświetl plik

@ -0,0 +1,597 @@
/*
* Report on an H.222 program stream (PS) file.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "ps_fns.h"
#include "pes_fns.h"
#include "misc_fns.h"
#include "version.h"
/*
* Report on the given file
*
* - `ps` represents the PS file we're reading
* - if `is_dvd` is TRUE, then assume that data in private_stream_1 is
* stored using the DVD "substream" convention
* - if `max` is more than zero, then it is the maximum number of PS packs
* we want to read
* - if `verbose` is true, then we want reporting on each packet,
* otherwise just a summary of the number of packs/packets is output.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int report_ps(PS_reader_p ps,
int is_dvd,
int max,
int verbose)
{
int err;
offset_t posn = 0; // The location in the input file of the current packet
byte stream_id; // The packet's stream id
int end_of_file = FALSE;
struct PS_packet packet = {0};
struct PS_pack_header header = {0};
// Summary data
int count = 0;
int num_packs = 0;
int num_maps = 0;
int num_dirs = 0;
int num_video[NUMBER_VIDEO_STREAMS];
int min_video_size[NUMBER_VIDEO_STREAMS];
int max_video_size[NUMBER_VIDEO_STREAMS];
double sum_video_size[NUMBER_VIDEO_STREAMS];
int num_audio[NUMBER_AUDIO_STREAMS];
int min_audio_size[NUMBER_AUDIO_STREAMS];
int max_audio_size[NUMBER_AUDIO_STREAMS];
double sum_audio_size[NUMBER_AUDIO_STREAMS];
#define cPRIVATE1 0
#define cPRIVATE2 1
#define cPRIVATE_SIZE 2
int num_private[cPRIVATE_SIZE] = {0, 0};
int min_private_size[cPRIVATE_SIZE] = {INT_MAX, INT_MAX};
int max_private_size[cPRIVATE_SIZE] = {0, 0};
double sum_private_size[cPRIVATE_SIZE] = {0, 0};
#define cAC3 0
#define cDTS 1
#define cLPCM 2
#define cSUBPICTURES 3
#define cOTHER 4
// Our arrays are 5 wide (for the 4 types of data + other we know about) by
// <n> deep, where <n>=32 allows for 32 subpictures. This wastes space
// for the other datatypes, which can only go to 8, but is simple...
#define cSIZE 5
#define cDEPTH 32
int num_other[cSIZE][cDEPTH];
int min_other_size[cSIZE][cDEPTH];
int max_other_size[cSIZE][cDEPTH];
double sum_other_size[cSIZE][cDEPTH];
// AC3 data can have two other types of information we want to remember...
byte ac3_bsmod[cDEPTH] = {0};
byte ac3_acmod[cDEPTH] = {0};
int ii,jj;
for (jj=0; jj<NUMBER_VIDEO_STREAMS; jj++)
{
num_video[jj] = 0;
min_video_size[jj] = INT_MAX;
max_video_size[jj] = 0;
sum_video_size[jj] = 0.0;
}
for (jj=0; jj<NUMBER_AUDIO_STREAMS; jj++)
{
num_audio[jj] = 0;
min_audio_size[jj] = INT_MAX;
max_audio_size[jj] = 0;
sum_audio_size[jj] = 0.0;
}
for (ii=0; ii<cSIZE; ii++)
{
for (jj=0; jj<cDEPTH; jj++)
{
num_other[ii][jj] = 0;
min_other_size[ii][jj] = INT_MAX;
max_other_size[ii][jj] = 0;
sum_other_size[ii][jj] = 0.0;
}
}
// Read the start of the first packet (we confidently expect this
// to be a pack header)
err = read_PS_packet_start(ps,verbose,&posn,&stream_id);
if (err == EOF)
{
fprintf(stderr,"### Error reading first pack header\n");
fprintf(stderr," Unexpected end of PS at start of stream\n");
return 1;
}
else if (err)
{
fprintf(stderr,"### Error reading first pack header\n");
return 1;
}
count++;
if (stream_id != 0xba)
{
fprintf(stderr,"### Program stream does not start with pack header\n");
fprintf(stderr," First packet has stream id %02X (",stream_id);
print_stream_id(stdout,stream_id);
printf(")\n");
return 1;
}
// But given that, we can now happily loop reading in packs
for (;;)
{
int num_system_headers = 0;
if (max > 0 && num_packs >= max)
{
if (verbose)
printf("Stopping after %d packs\n",num_packs);
break;
}
num_packs ++;
err = read_PS_pack_header_body(ps,&header);
if (err)
{
fprintf(stderr,
"### Error reading data for pack header starting at "
OFFSET_T_FORMAT "\n",posn);
goto give_up;
}
if (verbose)
printf("\n" OFFSET_T_FORMAT_08
": Pack header: SCR " LLD_FORMAT " (" LLD_FORMAT
"/%d) mux rate %d\n",posn,header.scr,header.scr_base,
header.scr_extn,header.program_mux_rate);
// Read (and, for the moment, at least, ignore) any system headers
for (;;)
{
err = read_PS_packet_start(ps,verbose,&posn,&stream_id);
if (err == EOF)
{
end_of_file = TRUE;
break;
}
else if (err)
goto give_up;
count++;
if (stream_id == 0xbb) // System header
{
err = read_PS_packet_body(ps,stream_id,&packet);
if (err)
{
fprintf(stderr,
"### Error reading system header starting at "
OFFSET_T_FORMAT "\n",posn);
goto give_up;
}
// For the moment, just ignore the system header content
num_system_headers ++;
if (verbose)
printf(OFFSET_T_FORMAT_08 ": System header %d\n",
posn,num_system_headers);
}
else
break;
}
if (end_of_file)
break;
// We've finished with system headers - onto data (one fondly hopes)
for (;;)
{
if (stream_id == 0xba) // Start of the next pack
break;
err = read_PS_packet_body(ps,stream_id,&packet);
if (err)
{
fprintf(stderr,"### Error reading PS packet starting at "
OFFSET_T_FORMAT "\n",posn);
goto give_up;
}
// For the moment, just ignore its content
if (verbose)
{
printf(OFFSET_T_FORMAT_08 ": PS Packet %2d stream %02X (",
posn,count,stream_id);
print_stream_id(stdout,stream_id);
printf(")\n");
print_data(stdout," Packet",
packet.data,packet.data_len,20);
#if 1 // XXX
print_end_of_data(stdout," ",packet.data,packet.data_len,20);
#endif
if (IS_AUDIO_STREAM_ID(stream_id) || IS_VIDEO_STREAM_ID(stream_id))
#if 1 // XXX
report_PES_data_array2(-1,packet.data,packet.data_len,20);
#else
report_PES_data_array(" ",packet.data,packet.data_len,TRUE);
#endif
}
if (stream_id == 0xBC)
num_maps ++;
else if (stream_id == 0xFF)
num_dirs ++;
else if (stream_id == PRIVATE1_AUDIO_STREAM_ID)
{
int substream_index;
byte bsmod, acmod;
int what = identify_private1_data(&packet,is_dvd,verbose,
&substream_index,&bsmod,&acmod);
num_private[cPRIVATE1] ++;
sum_private_size[cPRIVATE1] += packet.data_len;
if (packet.data_len > max_private_size[cPRIVATE1])
max_private_size[cPRIVATE1] = packet.data_len;
if (packet.data_len < min_private_size[cPRIVATE1])
min_private_size[cPRIVATE1] = packet.data_len;
if (what != SUBSTREAM_ERROR)
{
int index;
if (substream_index < 0 || substream_index >= cDEPTH)
{
fprintf(stderr,"Internal error: got substream index %d"
" (instead, counting item wrongly as index %d)\n",
substream_index,cDEPTH-1);
substream_index = cDEPTH-1;
}
switch (what)
{
case SUBSTREAM_AC3:
index = cAC3;
ac3_bsmod[substream_index] = bsmod;
ac3_acmod[substream_index] = acmod;
break;
case SUBSTREAM_DTS: index = cDTS; break;
case SUBSTREAM_LPCM: index = cLPCM; break;
case SUBSTREAM_SUBPICTURES: index = cSUBPICTURES; break;
case SUBSTREAM_OTHER: index = cOTHER; break;
default: fprintf(stderr,"Internal error: got substream id %d"
" (instead, counting item wrongly as OTHER)\n",what);
index = cOTHER;
break;
}
num_other[index][substream_index] ++;
sum_other_size[index][substream_index] += packet.data_len;
if (packet.data_len > max_other_size[index][substream_index])
max_other_size[index][substream_index] = packet.data_len;
if (packet.data_len < min_other_size[index][substream_index])
min_other_size[index][substream_index] = packet.data_len;
}
}
else if (stream_id == PRIVATE2_AUDIO_STREAM_ID)
{
num_private[cPRIVATE2] ++;
sum_private_size[cPRIVATE2] += packet.data_len;
if (packet.data_len > max_private_size[cPRIVATE2])
max_private_size[cPRIVATE2] = packet.data_len;
if (packet.data_len < min_private_size[cPRIVATE2])
min_private_size[cPRIVATE2] = packet.data_len;
}
else if (IS_AUDIO_STREAM_ID(stream_id))
{
int num = stream_id & 0x1F;
num_audio[num] ++;
sum_audio_size[num] += packet.data_len;
if (packet.data_len > max_audio_size[num])
max_audio_size[num] = packet.data_len;
if (packet.data_len < min_audio_size[num])
min_audio_size[num] = packet.data_len;
}
else if (IS_VIDEO_STREAM_ID(stream_id))
{
int num = stream_id & 0x0F;
num_video[num] ++;
sum_video_size[num] += packet.data_len;
if (packet.data_len > max_video_size[num])
max_video_size[num] = packet.data_len;
if (packet.data_len < min_video_size[num])
min_video_size[num] = packet.data_len;
}
err = read_PS_packet_start(ps,verbose,&posn,&stream_id);
if (err == EOF)
{
end_of_file = TRUE;
break;
}
else if (err)
goto give_up;
count++;
}
if (end_of_file)
break;
}
give_up:
clear_PS_packet(&packet);
{
int ii;
printf("Packets (total): %8d\n",count);
printf("Packs: %8d\n",num_packs);
for (ii=0; ii<NUMBER_VIDEO_STREAMS; ii++)
if (num_video[ii] > 0)
{
printf("Video packets (stream %2d): %8d",ii,num_video[ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_video_size[ii],max_video_size[ii],
sum_video_size[ii]/num_video[ii]);
}
for (ii=0; ii<NUMBER_AUDIO_STREAMS; ii++)
if (num_audio[ii] > 0)
{
printf("Audio packets (stream %2d): %8d",ii,num_audio[ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_audio_size[ii],max_audio_size[ii],
sum_audio_size[ii]/num_audio[ii]);
}
if (num_private[cPRIVATE1] > 0)
{
int ii;
printf("Private1 packets: %8d",num_private[cPRIVATE1]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_private_size[cPRIVATE1],max_private_size[cPRIVATE1],
sum_private_size[cPRIVATE1]/num_private[cPRIVATE1]);
for (ii=0; ii<cDEPTH; ii++)
{
if (num_other[cAC3][ii] > 0)
{
printf(" AC3, index %2d: %8d",ii,num_other[cAC3][ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_other_size[cAC3][ii],max_other_size[cAC3][ii],
sum_other_size[cAC3][ii]/num_other[cAC3][ii]);
printf(" %s\n",
BSMOD_STR(ac3_bsmod[ii],ac3_acmod[ii]));
printf(" audio coding mode %s\n",
ACMOD_STR(ac3_acmod[ii]));
}
}
for (ii=0; ii<cDEPTH; ii++)
{
if (num_other[cDTS][ii] > 0)
{
printf(" DTS, index %2d: %8d",ii,num_other[cDTS][ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_other_size[cDTS][ii],max_other_size[cDTS][ii],
sum_other_size[cDTS][ii]/num_other[cDTS][ii]);
}
}
for (ii=0; ii<cDEPTH; ii++)
{
if (num_other[cLPCM][ii] > 0)
{
printf(" LPCM, index %2d: %8d",ii,num_other[cLPCM][ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_other_size[cLPCM][ii],max_other_size[cLPCM][ii],
sum_other_size[cLPCM][ii]/num_other[cLPCM][ii]);
}
}
for (ii=0; ii<cDEPTH; ii++)
{
if (num_other[cSUBPICTURES][ii] > 0)
{
printf(" SUBPICTURES, index %2d: %8d",ii,num_other[cSUBPICTURES][ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_other_size[cSUBPICTURES][ii],max_other_size[cSUBPICTURES][ii],
sum_other_size[cSUBPICTURES][ii]/num_other[cSUBPICTURES][ii]);
}
}
for (ii=0; ii<cDEPTH; ii++)
{
if (num_other[cOTHER][ii] > 0)
{
printf(" OTHER, index %2d: %8d",ii,num_other[cOTHER][ii]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_other_size[cOTHER][ii],max_other_size[cOTHER][ii],
sum_other_size[cOTHER][ii]/num_other[cOTHER][ii]);
}
}
}
if (num_private[cPRIVATE2] > 0)
{
printf("Private2 packets: %8d",num_private[cPRIVATE2]);
printf(" min size %5d, max size %5d, mean size %7.1f\n",
min_private_size[cPRIVATE2],max_private_size[cPRIVATE2],
sum_private_size[cPRIVATE2]/num_private[cPRIVATE2]);
}
printf("Program stream maps: %8d\n",num_maps);
printf("Program stream directories: %8d\n",num_maps);
}
return 0;
}
static void print_usage()
{
printf(
"Usage: psreport [switches] [<infile>]\n"
"\n"
);
REPORT_VERSION("psreport");
printf(
"\n"
" Report on the packets in a Program Stream.\n"
"\n"
"Files:\n"
" <infile> is an H.222 Program Stream file (but see -stdin)\n"
"\n"
"Switches:\n"
" -stdin Input from standard input, instead of a file\n"
" -verbose, -v Output packet data as well.\n"
" -max <n>, -m <n> Maximum number of PS packets to read\n"
" -dvd The PS data is from a DVD. This is the default.\n"
" This switch has no effect on MPEG-1 PS data.\n"
" -notdvd, -nodvd The PS data is not from a DVD.\n"
" The DVD specification stores AC-3 (Dolby), DTS and\n"
" other audio in a specialised manner in private_stream_1.\n"
);
}
int main(int argc, char **argv)
{
int use_stdin = FALSE;
char *input_name = NULL;
int had_input_name = FALSE;
PS_reader_p ps; // The PS file we're reading
int max = 0; // The maximum number of PS packets to read (or 0)
int verbose = FALSE; // True => output diagnostic/progress messages
int is_dvd = TRUE;
int err = 0;
int ii = 1;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
}
else if (!strcmp("-dvd",argv[ii]))
{
is_dvd = TRUE;
}
else if (!strcmp("-notdvd",argv[ii]) || !strcmp("-nodvd",argv[ii]))
{
is_dvd = FALSE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("psreport",ii);
err = int_value("psreport",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-stdin",argv[ii]))
{
use_stdin = TRUE;
had_input_name = TRUE; // so to speak
}
else
{
fprintf(stderr,"### psreport: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name)
{
fprintf(stderr,"### psreport: Unexpected '%s'\n",argv[ii]);
return 1;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### psreport: No input file specified\n");
return 1;
}
err = open_PS_file(input_name,FALSE,&ps);
if (err)
{
fprintf(stderr,"### psreport: Unable to open input file %s\n",
(use_stdin?"<stdin>":input_name));
return 1;
}
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
if (is_dvd)
printf("Assuming data is from a DVD\n");
else
printf("Assuming data is NOT from a DVD\n");
if (max)
printf("Stopping after %d PS packets\n",max);
err = report_ps(ps,is_dvd,max,verbose);
if (err)
fprintf(stderr,"### psreport: Error reporting on input stream\n");
err = close_PS_file(&ps);
if (err)
{
fprintf(stderr,"### psreport: Error closing input file %s\n",
(use_stdin?"<stdin>":input_name));
return 1;
}
return 0;
}

1593
reverse.c 100644

Plik diff jest za duży Load Diff

127
reverse_defns.h 100644
Wyświetl plik

@ -0,0 +1,127 @@
/*
* Support for reversing
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _reverse_defns
#define _reverse_defns
#include "compat.h"
#include "es_defns.h"
// Since reverse_data refers to h262 and acces_unit datastructures, and
// *they* refer to reverse_data, we need to break the circular referencing
// at some point
typedef struct reverse_data *reverse_data_p;
#include "h262_defns.h"
#include "accessunit_defns.h"
// ------------------------------------------------------------
// As the software progresses through the data stream forwards, it remembers
// the location, size and details for frames that it might want to output in
// reverse
struct reverse_data
{
int is_h264; // Dealing with H.264 or H.262 data?
// To do anything useful, we must then be linked to one or the other
// type of input context (a union would save 4 bytes, but it doesn't
// seem worth the bother at the moment).
h262_context_p h262;
access_unit_context_p h264;
// Information for managing our arrays. `use_seq_offset` will be TRUE
// for H.262 data, and FALSE for H.264 (MPEG-4/AVC)
int length; // Number of items in our arrays
int size; // How big our arrays are
u_int32 num_pictures; // How many pictures we have
// Four useful arrays (although the last is not used for H.264 data)
u_int32 *index; // Which picture this is, counted from the start
offset_t *start_file; // The start offset of an item in the input file
int32 *start_pkt; // and then within the PES packet (if needed)
int32 *data_len; // Its length in bytes
byte *seq_offset; // For MPEG-2, the offset backwards in the arrays
// to the nearest earlier sequence header, or 0
// for a sequence header entry
byte *afd_byte; // For MPEG-2, the AFD byte current for the picture
// @@@ To be added later: for H.264 it's useful to know if a particular
// entry is an IDR or not. Thus add a ``byte *`` value called something
// like `is_IDR`, and make it point to `seq_offset`, since that is
// not used for H.264 data.
// (as a precursor to this, the code in reverse.c already allocates
// and extends the seq_offset array whether it is H.262 or H.264 data)
// Is our "counting" in `index` going to last long enough? Well, if
// we assume (worst case) that every picture was remembered in our arrays,
// then we would have 2**32-1 pictures. At (another worst case) 50 frames
// per second, that gives us (2**32-1)/50 seconds, which is 85899345
// seconds (rounded down), or 23860 hours. Which I think should be enough
// (about two years - yes, that should be enough).
// When a function (output_in_reverse_as_XX) is called to output
// reversed data, the reversal can either start from a specific index,
// or from the "default", which is the current index. To make this easier,
// we need to remember the index of the last entry added (which may be
// less than the length of the array, since we might have rewound, and
// be adding pictures again). Note that this is undefined if `length`
// is 0.
//
// (There's probably a better name for this, since it's really more
// like the highwater mark for what we've played forwards since we last
// started playing forwards again, but I can't think of anything startlingly
// illustrative, so this name will stay for now.)
u_int32 last_posn_added;
// Do we want to output sequence headers or not, when reversing H.262?
int output_sequence_headers;
// If we're outputting TS packets, we need to know the PID and stream id
// to use. We *could* pass that down to each reverse call, but it's easier
// to set it once and for all.
u_int32 pid;
byte stream_id;
// When a function (output_in_reverse_as_XX) is called to output
// reversed data, some statistics are maintained by the call.
// Despite saying "picture", these also apply to H.264 access units as well
// (and in fact "frame" would be a better term).
int pictures_written; // The number of pictures written out
int pictures_kept; // The number of *different* pictures written
int first_written; // Which picture was the first written out
int last_written; // Which picture was the last written out
// (for both of the latter, the value is the index of said picture in
// our arrays). Remember that if forwards action is to be made after
// reversing, it is important to reset the picture index in the H.262
// or access_unit context to the picture index of the last written
// picture. This must be done by the caller.
};
#define SIZEOF_REVERSE_DATA sizeof(struct reverse_data)
#define REVERSE_ARRAY_START_SIZE 1000
#define REVERSE_ARRAY_INCREMENT_SIZE 500
#endif // _reverse_defns

333
reverse_fns.h 100644
Wyświetl plik

@ -0,0 +1,333 @@
/*
* Support for reversing
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _reverse_fns
#define _reverse_fns
#include "accessunit_defns.h"
#include "reverse_defns.h"
#include "h262_defns.h"
/*
* Build the internal arrays to remember video sequence bounds in,
* for reversing.
*
* Builds a new `reverse_data` datastructure. If `is_h264` is FALSE (i.e., the
* data to be reversed is not MPEG-1 or MPEG-2), then this datastructure may
* be smaller.
*
* To collect reversing data, attach this datastructure to an H.262 or access
* unit context (with add_h262/access_unit_reverse_context), and then use
* get_next_h262_frame() or get_next_h264_frame() to read through the data
* stream - appropriate pictures/access units will be remembered
* automatically.
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int build_reverse_data(reverse_data_p *reverse_data,
int is_h264);
/*
* Set the video PID and stream id for TS output.
*
* This need only be called if reverse data *is* being output as TS,
* and if the standard default values (DEFAULT_VIDEO_PID and
* DEFAULT_VIDEO_STREAM_ID) are not correct.
*/
extern void set_reverse_pid(reverse_data_p reverse_data,
u_int32 pid,
byte stream_id);
/*
* Add a reversing context to an H.262 context (and vice versa).
*
* Does not check if there is one present already.
*
* Returns 0 if all is well, 1 if something goes wrong.
*/
extern int add_h262_reverse_context(h262_context_p h262,
reverse_data_p reverse_data);
/*
* Add a reversing context to an access unit context (and vice versa).
*
* Does not check if there is one present already.
*
* Returns 0 if all is well, 1 if something goes wrong.
*/
extern int add_access_unit_reverse_context(access_unit_context_p context,
reverse_data_p reverse_data);
/*
* Free the datastructure we used to remember reversing data
*
* Sets `reverse_data` to NULL.
*/
extern void free_reverse_data(reverse_data_p *reverse_data);
/*
* Remember video sequence bounds for H.262 data
*
* - `reverse_data` is the datastructure we want to add our entry to
* - `index` indicates which picture (counted from the start of the file)
* this one is (i.e., we're assuming that not all pictures will be stored).
* If the entry is an H.262 sequence header, then this is ignored.
* - `start_posn` is the location of the start of the entry in the file,
* The entry will be ignored if `start_posn` comes before the last
* existing entry in the arrays.
* - `length` is the number of bytes in the entry
* - in H.262 data, `seq_offset` should be 0 for a sequence header, and is
* otherwise the offset backwards to the previous nearest sequence header
* (i.e., 1 if the sequence header is the previous entry).
* - `afd` is the effective AFD byte for this picture
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remember_reverse_h262_data(reverse_data_p reverse_data,
u_int32 index,
ES_offset start_posn,
u_int32 length,
byte seq_offset,
byte afd);
/*
* Remember video sequence bounds for H.264 data
*
* - `reverse_data` is the datastructure we want to add our entry to
* - `index` indicates which picture (counted from the start of the file)
* this one is (i.e., we're assuming that not all pictures will be stored).
* If the entry is an H.262 sequence header, then this is ignored.
* - `start_posn` is the location of the start of the entry in the file,
* The entry will be ignored if `start_posn` comes before the last
* existing entry in the arrays.
* - `length` is the number of bytes in the entry
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int remember_reverse_h264_data(reverse_data_p reverse_data,
u_int32 index,
ES_offset start_posn,
u_int32 length);
/*
* Retrieve video sequence bounds for entry `which`
*
* - `reverse_data` is the datastructure we want to get our entry from
* - `which` indicates which entry we'd like to retrieve. The first
* entry in the `reverse_data` is number 0.
* - `index` indicates which picture (counted from the start of the file)
* this one is (i.e., we're assuming that not all pictures will be stored).
* `index` may be passed as NULL if the value is of no interest - i.e.,
* typically when the entry is for an H.262 sequence header.
* - `start_posn` is the location of the start of the entry in the file,
* - `length` is the number of bytes in the entry
* - for H.262 data, if the entry is a picture, then `seq_offset` will
* be the offset backwards to the previous nearest sequence header
* (i.e., 1 if the sequence header is the previous entry), and if it is
* a sequence header, `seq_offset` will be 0. For H.264 data, the value
* will always be 0. `seq_offset` may be passed as NULL if the value is
* of no interest.
* - for H.262 data, if the entry is a picture, then `afd` will be its
* (effective) AFD byte. Otherwise it will be 0. `afd` may be passed as NULL
* if the value if of no interest.
*
* To clarify, all of the following are legitimate calls::
*
* err = get_reverse_data(reverse_data,10,&index,&start,&length,&offset,&afd);
* err = get_reverse_data(reverse_data,10,&index,&start,&length,NULL,NULL);
* err = get_reverse_data(reverse_data,10,NULL,&start,&length,NULL,NULL);
*
* Returns 0 if it succeeds, 1 if some error occurs.
*/
extern int get_reverse_data(reverse_data_p reverse_data,
int which,
u_int32 *index,
ES_offset *start_posn,
u_int32 *length,
byte *seq_offset,
byte *afd);
// ============================================================
// Collecting pictures
// ============================================================
/*
* Locate and remember sequence headers and I pictures, for later reversal.
*
* - `h262` is the H.262 stream reading context
* - if `max` is non-zero, then collecting will stop after `max` pictures
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* Returns 0 if all went well, EOF if the end of file is reached,
* and 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int collect_reverse_h262(h262_context_p h262,
int max,
int verbose,
int quiet);
/*
* Find IDR and I slices, and remember their access units for later output
* in reverse order.
*
* - `acontext` is the access unit reading context
* - if `max` is non-zero, then collecting will stop after `max` access units
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
*
* Returns 0 if all went well, EOF if the end of file is reached,
* and 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int collect_reverse_access_units(access_unit_context_p acontext,
int max,
int verbose,
int quiet);
/*
* Output the last picture (or an earlier one) from the reverse arrays.
* This version writes the data out as Transport Stream.
*
* This is expected to be used after the whole of the data stream has been
* played, so that the last picture in the reverse arrays is the last I or
* IDR picture in the data stream.
*
* - `es` is the input elementary stream
* - `tswriter` is the transport stream writer
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `offset` is the offset from the end of the array of the picture
* to output - so 0 means the last picture, 1 the picture before that,
* and so on. Sequence headers do not count for this purpose.
* - `reverse_data` is the reverse data context.
*
* Returns 0 if all went well, 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int output_from_reverse_data_as_TS(ES_p es,
TS_writer_p tswriter,
int verbose,
int quiet,
u_int32 offset,
reverse_data_p reverse_data);
/*
* Output the last picture (or an earlier one) from the reverse arrays.
* This version writes the data out as Elementary Stream.
*
* This is expected to be used after the whole of the data stream has been
* played, so that the last picture in the reverse arrays is the last I or
* IDR picture in the data stream.
*
* - `es` is the input elementary stream
* - `output` is the stream to write to
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `offset` is the offset from the end of the array of the picture
* to output - so 0 means the last picture, 1 the picture before that,
* and so on. Sequence headers do not count for this purpose.
* - `reverse_data` is the reverse data context.
*
* Returns 0 if all went well, 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int output_from_reverse_data_as_ES(ES_p es,
FILE *output,
int verbose,
int quiet,
u_int32 offset,
reverse_data_p reverse_data);
/*
* Output the H.262 pictures or H.264 access units we remembered earlier - but
* in reverse order. This version writes the data out as Transport Stream.
*
* - `es` is the input elementary stream
* - `tswriter` is the transport stream writer
* - if `frequency` is non-zero, then attempt to produce the effect of
* keeping every <frequency>th picture (similar to reversing at a
* multiplication factor of `frequency`) If 0, just output all the
* pictures that were remembered.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `start_with` is the index at which to start outputting from the
* reverse data arrays. The value -1 may be used to indicate the most
* recent picture in the arrays. If `start_with` is less than -1 then this
* function will do nothing. If `start_with` is off the end of the
* arrays, then reversing will start from the end of the arrays.
* - if `max` is non-zero, then output will stop after at least `max`
* pictures have been reversed past.
* - `reverse_data` contains the list of pictures/access units to reverse.
*
* Returns 0 if all went well, 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int output_in_reverse_as_TS(ES_p es,
TS_writer_p tswriter,
int frequency,
int verbose,
int quiet,
int32 start_with,
int max,
reverse_data_p reverse_data);
/*
* Output the H.262 pictures or H.264 access units we remembered earlier - but
* in reverse order. This version writes the data out as Elementary Stream.
*
* - `es` is the input elementary stream
* - `output` is the stream to write to
* - if `frequency` is non-zero, then attempt to produce the effect of
* keeping every <frequency>th picture (similar to reversing at a
* multiplication factor of `frequency`) If 0, just output all the
* pictures that were remembered.
* - if `verbose` is true, then extra information will be output
* - if `quiet` is true, then only errors will be reported
* - `start_with` is the index at which to start outputting from the
* reverse data arrays. The value -1 may be used to indicate the most
* recent in the arrays. If `start_with` is less than -1 then this
* function will do nothing. If `start_with` is off the end of the
* arrays, then reversing will start from the end of the arrays.
* - if `max` is non-zero, then output will stop after at least `max`
* pictures have been reversed past.
* - `reverse_data` contains the list of pictures/access units to reverse.
*
* Returns 0 if all went well, 1 if an error occurred.
*
* If command input is enabled, then it can also return COMMAND_RETURN_CODE
* if the current command has changed.
*/
extern int output_in_reverse_as_ES(ES_p es,
FILE *output,
int frequency,
int verbose,
int quiet,
int32 start_with,
int max,
reverse_data_p reverse_data);
#endif // _reverse_fns

87
sockread.py 100644
Wyświetl plik

@ -0,0 +1,87 @@
#! /usr/bin/env python
"""sockread.py -- a simple client to read from a socket
"""
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
import sys
import socket
class DoneException(Exception):
pass
def get_packet(sock,packet_size=188):
"""Read a packet from the socket, coping with partial reads.
"""
data = ""
total = 0
while total < packet_size:
data += sock.recv(packet_size - total)
if len(data) == 0:
raise DoneException
total += len(data)
return data
def read_next_packet(sock,f=None):
"""Read the next packet from the socket, checking and counting it.
"""
data = get_packet(sock)
if ord(data[0]) == 0x47 and len(data) == 188:
sys.stdout.write(".")
else:
sys.stdout.write("[%x]/%d"%(ord(data[0]),len(data)))
sys.stdout.flush()
if f:
f.write(data)
def main():
total_packets = 0
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "Waiting on port 8889"
sock.bind(("localhost",8889))
sock.listen(1)
conn, addr = sock.accept()
print 'Connected by', addr
#print "Writing to file temp.ts"
#stream = file("temp.ts","wb")
stream = None
try:
while 1:
read_next_packet(conn,stream)
total_packets += 1
except DoneException:
#stream.close()
pass
sys.stdout.write("\n")
sys.stdout.write("Total packets: %d\n"%total_packets)
sock.close()
if __name__ == "__main__":
# try:
main()
# except KeyboardInterrupt:
# print

211
socktest.py 100644
Wyświetl plik

@ -0,0 +1,211 @@
#! /usr/bin/env python
"""socktest.py -- a simple client to talk to tsserve
Command line - optionally:
-host <host> defaults to "localhost"
-port <port> defaults to 8889
-output <filename> defaults to None
-file <filename> the same
-nonblock the socket should operate in non-blocking mode
followed by zero or more commands, specified as:
<letter> <count> for n, F, f, r or R
or <letter> for any of the above, and also q, p, > and <, or 0 .. 9
If a <count> is given, it is the number of data packets to try to read before
issuing the next command.
The commands "n", "f", "F", r" and "R", and the "select channel and play" commands
"0" .. "9" may be given a count, in which case the command is given and then that
many data packets are read before the next command is given. If no count is given,
then data packets are read "forever".
The commands "p", ">" and "<" do not take a count, and do not read any data
packets.
The command "q" does not take a count, but reads the rest of the data packets.
If no commands are given, the default is "n" with no count.
"""
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is the MPEG TS, PS and ES tools.
#
# The Initial Developer of the Original Code is Amino Communications Ltd.
# Portions created by the Initial Developer are Copyright (C) 2008
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Amino Communications Ltd, Swavesey, Cambridge UK
#
# ***** END LICENSE BLOCK *****
import sys
import socket
class DoneException(Exception):
pass
global total_packets
def get_packet(sock,packet_size=188):
"""Read a packet from the socket, coping with partial reads.
"""
data = ""
total = 0
while total < packet_size:
data += sock.recv(packet_size - total)
if len(data) == 0:
raise DoneException
total += len(data)
return data
def read_next_packet(sock,file=None):
"""Read the next packet from the socket, checking and counting it.
"""
data = get_packet(sock)
if ord(data[0]) == 0x47 and len(data) == 188:
sys.stdout.write(".")
else:
sys.stdout.write("[%x]/%d"%(ord(data[0]),len(data)))
sys.stdout.flush()
global total_packets
total_packets += 1
if file:
file.write(data)
def give_command(sock,command="n",file=None,howmany=None):
"""Give the command specified, and then read data packets.
If `howmany` is specified, try to read that many packets (and return
thereafter), otherwise, just keep trying to read.
Raises DoneException if there is no more data to read.
"""
if howmany is None:
print "Sending command '%s' and listening"%command
else:
print "Sending command '%s' and listening for %d packets"%(command,
howmany)
sock.send(command)
if howmany is None:
while 1:
read_next_packet(sock,file)
else:
try:
for count in range(howmany):
read_next_packet(sock,file)
except DoneException:
sys.stdout.write("\n")
sys.stdout.write("Finished listening after %d packets"%count)
raise DoneException
except socket.error, val:
print "socket.error:",val
raise DoneException
print
def main():
global total_packets
total_packets = 0
host = "localhost"
port = 8889
stream = filename = None
nonblock = 0
argv = sys.argv[1:]
if len(argv) == 0:
print __doc__
return
while len(argv) > 0 and argv[0].startswith("-"):
if argv[0] in ("-h", "-help", "--help"):
print __doc__
return
elif argv[0] == "-host":
host = argv[1]
argv = argv[2:]
elif argv[0] == "-port":
port = int(argv[1])
argv = argv[2:]
elif argv[0] in ("-file", "-output"):
filename = argv[1]
argv = argv[2:]
elif argv[0] in ("-nonblock"):
nonblock = 1
argv = argv[1:]
else:
print "Unexpected switch",argv[0]
return
commands = []
if len(argv) == 0:
print "No commands specified - assuming 'n'ormal play"
commands = [("n",None)]
command = None
count = 0
for word in argv:
if command: # we have a command waiting for a count
try:
count = int(word)
except:
print "'%s' does not work as a count for command '%s'"%(word,command)
return
commands.append((command,count))
command = None
elif word in ("p", ">", "<"): # commands that don't take a count
commands.append((word,0))
command = None
elif word in ("q"): # commands that read the rest of input
commands.append((word,None))
command = None
elif word in ("n","F","f","r","R",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"): # commands that do take a count
command = word
else:
print "Unrecognised command '%s'"%word
if command:
commands.append((command,None))
print "Commands:", commands
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
print "Connecting to %s on port %d"%(host,port)
sock.connect((host,port))
if filename:
print "Writing output to file %s"%filename
stream = file(filename,"wb")
if nonblock:
sock.setblocking(0)
try:
for command,count in commands:
give_command(sock,command=command,file=stream,howmany=count)
except (KeyboardInterrupt, DoneException):
if stream:
stream.close()
sys.stdout.write("\n")
sys.stdout.write("Total packets: %d\n"%total_packets)
sock.close()
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print

511
stream_type.c 100644
Wyświetl plik

@ -0,0 +1,511 @@
/*
* Attempt to determine if an input stream is Transport Stream or Elementary
* Stream, and if the latter, if it is H.262 or H.264 (MPEG-2 or MPEG-4/AVC).
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#ifdef _WIN32
#include <stddef.h>
#include <io.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "es_fns.h"
#include "ts_fns.h"
#include "nalunit_fns.h"
#include "h262_fns.h"
#include "misc_fns.h"
#include "version.h"
#define STREAM_IS_TS 10
#define STREAM_IS_PS 11
#define STREAM_IS_H262 12
#define STREAM_IS_H264 14
#define STREAM_IS_AVS 15
#define STREAM_MAYBE_PES 5
#define STREAM_IS_UNSURE 9
#define STREAM_IS_ERROR 0
/*
* Look for an initial 0x47 sync byte, and check for TS
*
* Returns 0 if nothing went wrong, 1 if something did.
*/
static int check_if_TS(int input,
byte cur_byte,
int verbose,
int *decided,
int *result)
{
int ii;
if (verbose)
printf("Is it Transport Stream?\n");
// It may be enough to look at the first byte of the stream
if (cur_byte != 0x47)
{
if (verbose)
printf(" First byte in file is 0x%02X not 0x47, so it is not\n",cur_byte);
return 0;
}
// Transport Stream packets start with 0x47, so it's a good bet.
if (verbose)
printf(" First byte in file is 0x47, so it looks like Transport Stream\n");
// To check a bit, we can try looking at every 188th byte
if (verbose)
printf(" Checking next 500 packets to see if they start 0x47\n");
for (ii=0; ii<500; ii++)
{
byte buf[TS_PACKET_SIZE];
int err = read_bytes(input,TS_PACKET_SIZE,buf);
if (err)
{
fprintf(stderr,"### %s trying to read start of packet %d\n",
(err==EOF?"EOF":"Error"),ii+1);
return 1;
}
if (buf[TS_PACKET_SIZE-1] != 0x47)
{
if (verbose)
printf(" Packet %d does not start with 0x47 (%02x instead)\n",
ii+1,buf[TS_PACKET_SIZE-1]);
return 0;
}
}
if (verbose)
printf("The checked packets all start with 0x47 - looks like TS\n");
*decided = TRUE;
*result = STREAM_IS_TS;
return 0;
}
/*
* Try to decide if we *have* got program stream
*
* Returns 0 if nothing went wrong, 1 if something did.
*/
static int check_if_PS(int input,
int verbose,
int *decided,
int *result)
{
int err;
byte buf[10];
int stuffing_length;
if (verbose)
{
printf("Is it Program Stream?\n");
printf(" Trying to read pack header\n");
}
err = read_bytes(input,4,buf);
if (err)
{
fprintf(stderr,"### %s trying to read start of first PS packet\n",
(err==EOF?"EOF":"Error"));
return 1;
}
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 1 || buf[3] != 0xba)
{
if (verbose)
printf(" File starts %02X %02X %02X %02X, not 00 00 01 BA - not PS\n",
buf[0],buf[1],buf[2],buf[3]);
return 0;
}
if (verbose)
printf(" File starts 00 00 01 BA - could be PS,"
" reading pack header body\n");
err = read_bytes(input,8,buf);
if (err)
{
fprintf(stderr,"### %s trying to read body of PS pack header\n",
(err==EOF?"EOF":"Error"));
return 1;
}
if ((buf[0] & 0xF0) == 0x20)
{
if (verbose)
printf(" Looks like ISO/IEC 11171-1/MPEG-1 pack header\n");
}
else if ((buf[0] & 0xC0) == 0x40)
{
if (verbose)
printf(" Looks like ISO/IEC 13818-1/H.222.0 pack header\n");
err = read_bytes(input,2,&(buf[8]));
if (err)
{
fprintf(stderr,"### %s trying to read last 2 bytes of body of PS pack header\n",
(err==EOF?"EOF":"Error"));
return 1;
}
stuffing_length = buf[9] & 0x07;
// And ignore that many stuffing bytes...
if (stuffing_length > 0)
{
err = read_bytes(input,stuffing_length,buf);
if (err)
{
fprintf(stderr,"### %s trying to read PS pack header stuffing bytes\n",
(err==EOF?"EOF":"Error"));
return 1;
}
}
}
// We could check for reserved bits - maybe at another time
if (verbose)
printf(" OK, trying to read start of next packet\n");
err = read_bytes(input,4,buf);
if (err)
{
fprintf(stderr,"### %s trying to read start of next PS packet\n",
(err==EOF?"EOF":"Error"));
return 1;
}
if (buf[0] != 0 || buf[1] != 0 || buf[2] != 1)
{
if (verbose)
printf(" Next 'packet' starts %02X %02X %02X, not 00 00 01 - not PS\n",
buf[0],buf[1],buf[2]);
return 0;
}
if (verbose)
printf(" Start of second packet found at right place - looks like PS\n");
*decided = TRUE;
*result = STREAM_IS_PS;
return 0;
}
/*
* Look at the start of our "elementary" stream, and try to determine
* its actual type.
*
* - `input` is the input stream to inspect
* - if `verbose` is true, the caller wants details of how the decision
* is being made
* - `decided` is returned TRUE if the function believes it has identified
* the stream type, in which case:
* - `result` will an appropriate value indicating what we've decided
*
* Note that this function reads into the stream, and may attempt to
* rewind it.
*
* Returns 0 if nothing went wrong, 1 if an error occurred
*/
static int determine_packet_type(int input,
int verbose,
int *decided,
int *result)
{
int err;
#ifdef _WIN32
int length;
#else
ssize_t length;
#endif
byte first_byte;
int video_type;
length = read(input,&first_byte,1);
if (length == 0)
{
fprintf(stderr,"### EOF reading first byte\n");
return 1;
}
else if (length == -1)
{
fprintf(stderr,"### Error reading first byte: %s\n",strerror(errno));
return 1;
}
// Does it look like transport stream?
err = check_if_TS(input,first_byte,verbose,decided,result);
if (err)
{
close_file(input);
return 1;
}
if (*decided)
{
close_file(input);
return 0;
}
seek_file(input,0);
// Does it look like program stream?
err = check_if_PS(input,verbose,decided,result);
if (err)
{
close_file(input);
return 1;
}
if (*decided)
{
close_file(input);
return 0;
}
seek_file(input,0);
// Does it look like one of the types of ES we recognise?
if (verbose)
printf("Is it an Elementary Stream we recognise?\n");
err = decide_ES_file_video_type(input,!verbose,verbose,&video_type);
if (err)
{
close_file(input);
return 1;
}
switch (video_type)
{
case VIDEO_H264:
*result = STREAM_IS_H264;
*decided = TRUE;
break;
case VIDEO_H262:
*result = STREAM_IS_H262;
*decided = TRUE;
break;
case VIDEO_AVS:
*result = STREAM_IS_AVS;
*decided = TRUE;
break;
case VIDEO_UNKNOWN:
*result = STREAM_IS_UNSURE;
*decided = FALSE;
if (verbose) printf("Still not sure\n");
break;
default:
printf("### stream_type: Unexpected decision from"
" decide_ES_file_video_type: %d\n",video_type);
close_file(input);
return 1;
}
close_file(input);
return 0;
}
static void print_usage()
{
printf(
"Usage: stream_type [switches] <infile>\n"
"\n"
);
REPORT_VERSION("stream_type");
printf(
"\n"
" Attempt to determine if an input stream is Transport Stream,\n"
" Program Stream, or Elementary Stream, and if the latter, if it\n"
" is H.262 or H.264 (i.e., MPEG-2 or MPEG-4/AVC respectively)."
"\n"
" The mechanisms used are fairly crude, assuming that:\n"
" - data is byte aligned\n"
" - for TS, the first byte in the file will be the start of a NAL unit,\n"
" and PAT/PMT packets will be findable\n"
" - for PS, the first packet starts immediately at the start of the\n"
" file, and is a pack header\n"
" - if the first 1000 packets could be H.262 *or* H.264, then the data\n"
" is assumed to be H.264 (the program doesn't try to determine\n"
" sensible sequences of H.262/H.264 packets, so this is a reasonable\n"
" way of guessing)\n"
"\n"
" It is quite possible that data which is not relevant will be\n"
" misidentified\n"
"\n"
" The program exit value is:\n"
" * 10 if it detects Transport Stream,\n"
" * 11 if it detects Program Stream,\n"
" * 12 if it detects Elementary Stream containing H.262 (MPEG-2),\n"
" * 14 if it detects Elementary Stream containing H.264 (MPEG-4/AVC),\n"
" * 5 if it looks like it might be PES,\n"
" * 9 if it really cannot decide, or\n"
" * 0 if some error occurred\n"
"\n"
"Files:\n"
" <infile> is the file to analyse\n"
"\n"
"Switches:\n"
" -verbose, -v Output more detailed information about how it is\n"
" making its decision\n"
" -quiet, -q Only output error messages\n"
);
}
int main(int argc, char **argv)
{
char *input_name = NULL;
int had_input_name = FALSE;
int input = -1;
int verbose = FALSE;
int quiet = FALSE;
int err = 0;
int ii = 1;
int decided = FALSE;
int result = STREAM_IS_ERROR;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return STREAM_IS_ERROR;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else
{
fprintf(stderr,"### stream_type: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return STREAM_IS_ERROR;
}
}
else
{
if (had_input_name)
{
fprintf(stderr,"### stream_type: Unexpected '%s'\n",argv[ii]);
return STREAM_IS_ERROR;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### stream_type: No input file specified\n");
return STREAM_IS_ERROR;
}
input = open_binary_file(input_name,FALSE);
if (input == -1)
{
fprintf(stderr,"### stream_type: Unable to open input file %s\n",
input_name);
return 1;
}
if (!quiet)
printf("Reading from %s\n",input_name);
// Try to guess
err = determine_packet_type(input,verbose,&decided,&result);
if (err)
{
fprintf(stderr,"### Unable to decide on stream type due to error\n");
return STREAM_IS_ERROR;
}
if (!quiet)
{
if (!decided)
{
printf("Unable to decide\n");
result = STREAM_IS_UNSURE;
}
else
{
switch (result)
{
case STREAM_IS_TS:
printf("It appears to be Transport Stream\n");
break;
case STREAM_IS_PS:
printf("It appears to be Program Stream\n");
break;
case STREAM_IS_H262:
printf("It appears to be Elementary Stream, MPEG-2 (H.262)\n");
break;
case STREAM_IS_H264:
printf("It appears to be Elementary Stream, MPEG-4 (H.264)\n");
break;
case STREAM_IS_AVS:
printf("It appears to be Elementary Stream, AVS\n");
break;
case STREAM_MAYBE_PES:
printf("It looks likely to be PES\n");
break;
case STREAM_IS_UNSURE:
printf("It is not recognised\n");
break;
default:
printf("Unexpected decision value %d\n",result);
result = STREAM_IS_ERROR;
break;
}
}
}
return result;
}

151
test_es_unit_list.c 100644
Wyświetl plik

@ -0,0 +1,151 @@
/*
* A simple test for the ES unit lists from es.c
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include "compat.h"
#include "es_fns.h"
int main(int argc, char **argv)
{
int err, ii;
int max = ES_UNIT_LIST_START_SIZE + ES_UNIT_LIST_INCREMENT + 3;
ES_unit_list_p list = NULL;
ES_unit_p unit = NULL;
printf("Testing ES unit list\n");
printf("Test 1 - differing ES units\n");
err = build_ES_unit_list(&list);
if (err)
{
printf("Test failed - constructing list\n");
return 1;
}
for (ii=0; ii<max; ii++)
{
err = build_ES_unit(&unit);
if (err)
{
printf("Test failed - constructing ES unit\n");
return 1;
}
err = append_to_ES_unit_list(list,unit);
if (err)
{
printf("Test failed - appending ES unit %d\n",ii);
return 1;
}
if (list->length > list->size)
{
printf("Test failed - list length = %d, size = %d\n",
list->length,list->size);
return 1;
}
else if (list->length != ii+1)
{
printf("Test failed - list length is %d, expected %d\n",
list->length,ii+1);
return 1;
}
free_ES_unit(&unit);
}
printf("Test 1 - resetting list\n");
reset_ES_unit_list(list);
if (list->length != 0)
{
printf("Test failed - list length is %d, not 0\n",list->length);
return 1;
}
// And try populating the list again, but a bit further this time
for (ii=0; ii<max+ES_UNIT_LIST_INCREMENT; ii++)
{
err = build_ES_unit(&unit);
if (err)
{
printf("Test failed - constructing ES unit\n");
return 1;
}
err = append_to_ES_unit_list(list,unit);
if (err)
{
printf("Test failed - appending ES unit %d\n",ii);
return 1;
}
if (list->length > list->size)
{
printf("Test failed - list length = %d, size = %d\n",
list->length,list->size);
return 1;
}
else if (list->length != ii+1)
{
printf("Test failed - list length is %d, expected %d\n",
list->length,ii+1);
return 1;
}
free_ES_unit(&unit);
}
printf("Test 1 - clearing list\n");
free_ES_unit_list(&list);
printf("Test 1 succeeded\n");
printf("Test 2 - the same ES unit inserted multiple times\n");
err = build_ES_unit_list(&list);
if (err)
{
printf("Test failed - constructing list\n");
return 1;
}
err = build_ES_unit(&unit);
if (err)
{
printf("Test failed - constructing ES unit\n");
return 1;
}
// We aren't testing allocation limits this time round
for (ii=0; ii<5; ii++)
{
err = append_to_ES_unit_list(list,unit);
if (err)
{
printf("Test failed - appending ES unit %d\n",ii);
return 1;
}
}
printf("Test 2 - clearing list\n");
free_ES_unit_list(&list);
free_ES_unit(&unit);
printf("Test 2 succeeded\n");
return 0;
}

Wyświetl plik

@ -0,0 +1,167 @@
/*
* A simple test for the NAL unit lists from nalunit.c
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include "compat.h"
#include "nalunit_fns.h"
int main(int argc, char **argv)
{
int err, ii;
int max = NAL_UNIT_LIST_START_SIZE + NAL_UNIT_LIST_INCREMENT + 3;
nal_unit_list_p list = NULL;
nal_unit_p unit = NULL;
printf("NAL unit lists are now handled as NAL unit lists, so...\n");
printf("Testing NAL unit list\n");
printf("Test 1 - differing NAL units\n");
err = build_nal_unit_list(&list);
if (err)
{
printf("Test failed - constructing list\n");
return 1;
}
for (ii=0; ii<max; ii++)
{
err = build_nal_unit(&unit);
if (err)
{
printf("Test failed - constructing NAL unit\n");
return 1;
}
err = append_to_nal_unit_list(list,unit);
if (err)
{
printf("Test failed - appending NAL unit %d\n",ii);
return 1;
}
if (list->length > list->size)
{
printf("Test failed - list length = %d, size = %d\n",
list->length,list->size);
return 1;
}
else if (list->length != ii+1)
{
printf("Test failed - list length is %d, expected %d\n",
list->length,ii+1);
return 1;
}
else if (list->array[ii] != unit)
{
printf("Test failed - list->array[%d] is %p, expected %p\n",
ii,list->array[ii],unit);
return 1;
}
}
printf("Test 1 - resetting list\n");
reset_nal_unit_list(list,TRUE);
if (list->length != 0)
{
printf("Test failed - list length is %d, not 0\n",list->length);
return 1;
}
if (list->array[0] != NULL)
{
printf("Test failed - list->array[0] is %p, not NULL\n",list->array[0]);
return 1;
}
// And try populating the list again, but a bit further this time
for (ii=0; ii<max+NAL_UNIT_LIST_INCREMENT; ii++)
{
err = build_nal_unit(&unit);
if (err)
{
printf("Test failed - constructing NAL unit\n");
return 1;
}
err = append_to_nal_unit_list(list,unit);
if (err)
{
printf("Test failed - appending NAL unit %d\n",ii);
return 1;
}
if (list->length > list->size)
{
printf("Test failed - list length = %d, size = %d\n",
list->length,list->size);
return 1;
}
else if (list->length != ii+1)
{
printf("Test failed - list length is %d, expected %d\n",
list->length,ii+1);
return 1;
}
else if (list->array[ii] != unit)
{
printf("Test failed - list->array[%d] is %p, expected %p\n",
ii,list->array[ii],unit);
return 1;
}
}
printf("Test 1 - clearing list\n");
free_nal_unit_list(&list,TRUE);
printf("Test 1 succeeded\n");
printf("Test 2 - the same NAL unit inserted multiple times\n");
err = build_nal_unit_list(&list);
if (err)
{
printf("Test failed - constructing list\n");
return 1;
}
err = build_nal_unit(&unit);
if (err)
{
printf("Test failed - constructing NAL unit\n");
return 1;
}
// We aren't testing allocation limits this time round
for (ii=0; ii<5; ii++)
{
err = append_to_nal_unit_list(list,unit);
if (err)
{
printf("Test failed - appending NAL unit %d\n",ii);
return 1;
}
}
printf("Test 2 - clearing list\n");
free_nal_unit_list(&list,FALSE); // better only do a shallow free
free_nal_unit(&unit);
printf("Test 2 succeeded\n");
return 0;
}

501
test_pes.c 100644
Wyświetl plik

@ -0,0 +1,501 @@
/*
* Test the PES reading facilities
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "compat.h"
#include "pes_fns.h"
#include "pidint_fns.h"
#include "misc_fns.h"
#include "ps_fns.h"
#include "ts_fns.h"
#include "es_fns.h"
#include "h262_fns.h"
#include "tswrite_fns.h"
#include "version.h"
/*
* Write out TS program data based on the information we have
*/
static int write_program_data_A(PES_reader_p reader,
TS_writer_p output)
{
// We know we support at most two program streams for output
int num_progs = 0;
u_int32 prog_pids[2];
byte prog_type[2];
int err;
u_int32 pcr_pid, pmt_pid;
if (reader->is_TS)
{
// For TS, we can use the stream types from the PMT itself
int number;
if (reader->video_pid != 0)
{
pmt_stream_p stream = pid_stream_in_pmt(reader->program_map,
reader->video_pid);
if (stream == NULL)
{
fprintf(stderr,"### Cannot find video PID %04x in program map\n",
reader->video_pid);
return 1;
}
prog_pids[0] = reader->output_video_pid; // may not be the same
prog_type[0] = stream->stream_type;
num_progs = 1;
pcr_pid = reader->video_pid;
}
if (reader->audio_pid != 0)
{
pmt_stream_p stream = pid_stream_in_pmt(reader->program_map,
reader->audio_pid);
if (stream == NULL)
{
fprintf(stderr,"### Cannot find audio PID %04x in program map\n",
reader->audio_pid);
return 1;
}
prog_pids[num_progs] = reader->output_audio_pid; // may not be the same
prog_type[num_progs] = stream->stream_type;
num_progs++;
}
pmt_pid = reader->pmt_pid;
}
else
{
// For PS, we have to be given appropriate PIDs, and we need to
// deduce stream types from the stream ids. Which, unfortunately,
// we can't do.
// For now, avoid the whole issue and just force some values...
num_progs = 1;
prog_pids[0] = 0x68; // hard-wired for video
prog_type[0] = MPEG2_VIDEO_STREAM_TYPE; // hard-wired for now
pcr_pid = 0x68;
if (reader->audio_stream_id != 0)
{
prog_pids[1] = 0x67; // hard-wired again
prog_type[1] = MPEG2_AUDIO_STREAM_TYPE; // a random guess
num_progs = 2;
}
pmt_pid = 0x66;
}
err = write_TS_program_data2(output,
1, // transport stream id
reader->program_number,
pmt_pid,pcr_pid,
num_progs,prog_pids,prog_type);
if (err)
{
fprintf(stderr,"### Error writing out TS program data\n");
return 1;
}
return 0;
}
/*
* Read PES packets and write them out to the target
*
* Returns 0 if all went well, 1 if an error occurred.
*/
static int play_pes_packets(PES_reader_p reader,
TS_writer_p output)
{
int err;
int ii;
int pad_start = 8;
int index = 0;
ES_p es; // A view of our PES packets as ES units
// Start off our output with some null packets - this is in case the
// reader needs some time to work out its byte alignment before it starts
// looking for 0x47 bytes
for (ii=0; ii<pad_start; ii++)
{
err = write_TS_null_packet(output);
if (err) return 1;
}
// Wrap our PES stream up as an ES stream
err = build_elementary_stream_PES(reader,&es);
if (err)
{
fprintf(stderr,"### Error trying to build ES reader from PES reader\n");
return 1;
}
for (;;)
{
h262_item_p item;
if (index % 500 == 0)
{
// Write out program data as we come to know it
err = write_program_data_A(reader,output);
if (err) return 1;
}
// Iterate our count here so that the first item is numbered 1
index++;
err = find_next_h262_item(es,&item);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error copying NAL units\n");
return err;
}
err = write_ES_as_TS_PES_packet(output,item->unit.data,
item->unit.data_len,DEFAULT_VIDEO_PID,
DEFAULT_VIDEO_STREAM_ID);
if (err)
{
fprintf(stderr,"### Error writing MPEG2 item\n");
return err;
}
free_h262_item(&item);
}
close_elementary_stream(&es);
return 0;
}
static int test1(PES_reader_p reader,
int verbose)
{
PES_packet_data_p packet;
int ii;
int err;
byte *old_data;
u_int32 old_data_len;
if (verbose)
printf("-------------------------- Test 1 --------------------------\n");
for (ii = 0; ii < 10; ii++)
{
err = read_next_PES_packet(reader);
if (err == EOF)
{
if (reader->give_info) printf("EOF\n");
break;
}
else if (err)
{
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
return 1;
}
packet = reader->packet;
if (verbose)
{
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
packet->posn,packet->data[3]);
print_stream_id(stdout,packet->data[3]);
printf(")\n");
print_data(stdout," Data",packet->data,packet->data_len,20);
err = report_PES_data_array("",packet->data,packet->data_len,FALSE);
if (err) return 1;
}
}
err = read_next_PES_packet(reader);
if (err)
{
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
return 1;
}
packet = reader->packet;
if (verbose)
{
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
packet->posn,packet->data[3]);
print_stream_id(stdout,packet->data[3]);
printf(")\n");
print_data(stdout," Data",packet->data,packet->data_len,20);
}
old_data = malloc(packet->data_len);
if (old_data == NULL)
{
fprintf(stderr,"### Error allocating data array\n");
return 1;
}
memcpy(old_data,packet->data,packet->data_len);
old_data_len = packet->data_len;
if (verbose)
printf("\n** Rewinding to the start of said packet again\n");
err = set_PES_reader_position(reader,packet->posn);
if (err)
{
fprintf(stderr,"### test_pes: Error seeking to previous PES packet\n");
free(old_data);
return 1;
}
if (verbose)
printf("** Reading packet the second time\n");
err = read_next_PES_packet(reader);
if (err)
{
fprintf(stderr,"### test_pes: Error reading next PES packet\n");
free(old_data);
return 1;
}
packet = reader->packet;
if (verbose)
{
printf("\n>> PS packet at " OFFSET_T_FORMAT " is %02x (",
packet->posn,packet->data[3]);
print_stream_id(stdout,packet->data[3]);
printf(")\n");
print_data(stdout," Data",packet->data,packet->data_len,20);
}
if (packet->data_len != old_data_len)
{
fprintf(stderr,
"### Test1: first packet length %d, second packet length %d\n",
old_data_len,packet->data_len);
free(old_data);
return 1;
}
else if (memcmp(packet->data,old_data,packet->data_len))
{
fprintf(stderr,"### Test1: packet data differs\n");
print_data(stderr," Packet 1",old_data,old_data_len,50);
print_data(stderr," Packet 2",packet->data,packet->data_len,50);
free(old_data);
return 1;
}
if (verbose)
printf("------------------------------------------------------------\n");
// Even in a test it's a good idea to tidy up
free(old_data);
return 0;
}
static void print_usage()
{
printf(
"Usage: test_pes <input-file> <host>[:<port>]\n"
"\n"
);
REPORT_VERSION("test_pes");
printf(
"\n"
" Test the PES reading facilities. <input-file> should be a TS\n"
" (Transport Stream) or PS (Program Stream) file.\n"
"\n"
"Input:\n"
" <input-file> An H.222.0 TS or PS file.\n"
" <host> The host to which to write TS packets, over\n"
" TCP/IP. If <port> is not specified, it defaults\n"
" to 88.\n"
"\n"
"Switches:\n"
" -quiet, -q Suppress informational and warning messages.\n"
" -verbose, -v Output additional diagnostic messages\n"
" -noaudio Ignore any audio data\n"
" -nohost Don't try to connect to the host\n"
);
}
int main(int argc, char **argv)
{
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
int port = 88; // Useful default port number
PES_reader_p reader = NULL;
TS_writer_p output = NULL;
int quiet = FALSE;
int verbose = FALSE;
int video_only = FALSE;
int want_output = TRUE;
int err;
int ii = 1;
if (argc < 2)
{
print_usage();
return 1;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
quiet = TRUE;
verbose = FALSE;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-noaudio",argv[ii]))
{
video_only = TRUE;
}
else if (!strcmp("-nohost",argv[ii]))
{
want_output = FALSE;
}
else
{
fprintf(stderr,"### test_pes: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && (want_output && had_output_name))
{
fprintf(stderr,"### test_pes: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name && want_output)
{
err = host_value("test_pes",NULL,argv[ii],&output_name,&port);
if (err) return 1;
had_output_name = TRUE; // more or less
ii++;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### test_pes: No input file specified\n");
return 1;
}
if (want_output && !had_output_name)
{
fprintf(stderr,"### test_pes: No target host specified\n");
return 1;
}
err = open_PES_reader(input_name,!quiet,!quiet,&reader);
if (err)
{
fprintf(stderr,"### test_pes: Error opening file %s\n",input_name);
return 1;
}
if (!quiet)
printf("Opened file %s (as %s)\n",input_name,(reader->is_TS?"TS":"PS"));
set_PES_reader_video_only(reader,video_only);
if (want_output)
{
err = tswrite_open(TS_W_TCP,output_name,NULL,port,quiet,&output);
if (err)
{
(void) close_PES_reader(&reader);
fprintf(stderr,"### test_pes: Unable to connect to %s\n",output_name);
return 1;
}
}
err = test1(reader,verbose);
if (err)
{
fprintf(stderr,"### test_pes: Test 1 failed\n");
(void) close_PES_reader(&reader);
(void) tswrite_close(output,TRUE);
return 1;
}
if (!quiet)
printf("** Test 1 passed\n"
"** Rewinding\n");
err = set_PES_reader_position(reader,0);
if (err)
{
fprintf(stderr,"### test_pes: Error seeking to previous PES packet\n");
return 1;
}
if (want_output)
{
err = play_pes_packets(reader,output);
if (err)
{
fprintf(stderr,"### test_pes: Error playing PES packets\n");
(void) close_PES_reader(&reader);
(void) tswrite_close(output,TRUE);
return 1;
}
}
if (want_output)
{
err = tswrite_close(output,quiet);
if (err)
fprintf(stderr,"### test_pes: Error closing output %s: %s\n",output_name,
strerror(errno));
}
err = close_PES_reader(&reader);
if (err)
{
fprintf(stderr,"### test_pes: Error closing file %s\n",input_name);
return 1;
}
return 0;
}

3302
ts.c 100644

Plik diff jest za duży Load Diff

688
ts2es.c 100644
Wyświetl plik

@ -0,0 +1,688 @@
/*
* Given an H.222 transport stream (TS) file, extract elementary stream
* data therefrom (i.e., extract the ES from the PES packets within the
* TS).
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "ts_fns.h"
#include "misc_fns.h"
#include "pidint_fns.h"
#include "es_fns.h"
#include "pes_fns.h"
#include "version.h"
// A three-way choice for what to output by PID
enum pid_extract
{
EXTRACT_UNDEFINED,
EXTRACT_VIDEO, // Output the first "named" video stream
EXTRACT_AUDIO, // Ditto for audio
EXTRACT_PID, // Output an explicit PID
};
typedef enum pid_extract EXTRACT;
/*
* Extract all the TS packets for either a video or audio stream.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int extract_av_via_pes(char *input_name,
char *output_name,
int want_video,
int quiet)
{
int err;
PES_reader_p reader = NULL;
ES_p es = NULL;
FILE *output;
if (!want_video)
{
fprintf(stderr,
"### Audio output is not supported via PES in this utility\n");
return 1;
}
output = fopen(output_name,"wb");
if (output == NULL)
{
fprintf(stderr,"### Unable to open output file %s: %s\n",output_name,
strerror(errno));
return 1;
}
err = open_PES_reader(input_name,!quiet,!quiet,&reader);
if (err)
{
fprintf(stderr,"### Error opening file %s\n",input_name);
fclose(output);
return 1;
}
set_PES_reader_video_only(reader,TRUE);
// Wrap our PES stream up as an ES stream
err = build_elementary_stream_PES(reader,&es);
if (err)
{
fprintf(stderr,"### Error trying to build ES reader from PES reader\n");
(void) close_PES_reader(&reader);
(void) fclose(output);
return 1;
}
for (;;)
{
ES_unit_p unit;
err = find_and_build_next_ES_unit(es,&unit);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error reading next ES unit\n");
(void) fclose(output);
(void) close_PES_reader(&reader);
close_elementary_stream(&es);
return 1;
}
err = write_ES_unit(output,unit);
if (err)
{
fprintf(stderr,"### Error writing ES unit out to file\n");
free_ES_unit(&unit);
(void) fclose(output);
(void) close_PES_reader(&reader);
close_elementary_stream(&es);
return 1;
}
free_ES_unit(&unit);
}
(void) fclose(output); // naughtily ignore the return code
(void) close_PES_reader(&reader); // naughtily ignore the return code
close_elementary_stream(&es);
return 0;
}
/*
* Extract all the TS packets for a nominated PID to another file.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int extract_pid_packets(TS_reader_p tsreader,
FILE *output,
u_int32 pid_wanted,
int max,
int verbose,
int quiet)
{
int err;
int count = 0;
int extracted = 0;
int pes_packet_len = 0;
int got_pes_packet_len = FALSE;
// It doesn't make sense to start outputting data for our PID until we
// get the start of a packet
int need_packet_start = TRUE;
for (;;)
{
u_int32 pid;
int payload_unit_start_indicator;
byte *adapt, *payload;
int adapt_len, payload_len;
if (max > 0 && count >= max)
{
if (!quiet) printf("Stopping after %d packets\n",max);
break;
}
err = get_next_TS_packet(tsreader,&pid,
&payload_unit_start_indicator,
&adapt,&adapt_len,&payload,&payload_len);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error reading TS packet\n");
return 1;
}
count++;
// If the packet is empty, all we can do is ignore it
if (payload_len == 0)
continue;
if (pid == pid_wanted)
{
byte *data;
int data_len;
size_t written;
if (verbose)
{
printf("%4d: TS Packet PID %04x",count,pid);
if (payload_unit_start_indicator)
printf(" (start)");
else if (need_packet_start)
printf(" <ignored>");
printf("\n");
}
if (payload_unit_start_indicator)
{
// It's the start of a PES packet, so we need to drop the header
int offset;
if (need_packet_start)
need_packet_start = FALSE;
pes_packet_len = (payload[4] << 8) | payload[5];
if (verbose) printf("PES packet length %d\n",pes_packet_len);
got_pes_packet_len = (pes_packet_len > 0);
if (IS_H222_PES(payload))
{
// It's H.222.0 - payload[8] is the PES_header_data_length,
// so our ES data starts that many bytes after that field
offset = payload[8] + 9;
}
else
{
// We assume it's MPEG-1
offset = calc_mpeg1_pes_offset(payload,payload_len);
}
data = &payload[offset];
data_len = payload_len-offset;
if (verbose) print_data(stdout,"data",data,data_len,1000);
}
else
{
// If we haven't *started* a packet, we can't use this,
// since it will just look like random bytes when written out.
if (need_packet_start)
continue;
data = payload;
data_len = payload_len;
if (verbose) print_data(stdout,"Data",payload,payload_len,1000);
if (got_pes_packet_len)
{
// Try not to write more data than the PES packet declares
if (data_len > pes_packet_len)
{
data_len = pes_packet_len;
if (verbose) print_data(stdout,"Reduced data",data,data_len,1000);
pes_packet_len = 0;
}
else
pes_packet_len -= data_len;
}
}
if (data_len > 0)
{
// Windows doesn't seem to like writing 0 bytes, so be careful...
written = fwrite(data,data_len,1,output);
if (written != 1)
{
fprintf(stderr,"### Error writing TS packet - units written = %d\n",
written);
return 1;
}
}
extracted ++;
}
}
if (!quiet)
printf("Extracted %d of %d TS packet%s\n",
extracted,count,(count==1?"":"s"));
// If the user has forgotten to say -pid XX, or -video/-audio,
// and are piping the output to another program, it can be surprising
// if there is no data!
if (quiet && extracted == 0)
fprintf(stderr,"### No data extracted for PID %#04x (%d)\n",
pid_wanted,pid_wanted);
return 0;
}
/*
* Extract all the TS packets for either a video or audio stream.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int extract_av(int input,
FILE *output,
int want_video,
int max,
int verbose,
int quiet)
{
int err, ii;
int max_to_read = max;
int total_num_read = 0;
u_int32 pid = 0;
TS_reader_p tsreader = NULL;
pmt_p pmt = NULL;
// Turn our file into a TS reader
err = build_TS_reader(input,&tsreader);
if (err) return 1;
// First, find out what program streams we actually have
for (;;)
{
int num_read;
// Give up if we've read more than our limit
if (max > 0 && max_to_read <= 0)
break;
err = find_pmt(tsreader,max_to_read,verbose,quiet,&num_read,&pmt);
if (err == EOF)
{
if (!quiet)
printf("No program stream information in the input file\n");
free_TS_reader(&tsreader);
free_pmt(&pmt);
return 0;
}
else if (err)
{
fprintf(stderr,"### Error finding program stream information\n");
free_TS_reader(&tsreader);
free_pmt(&pmt);
return 1;
}
max_to_read -= num_read;
total_num_read += num_read;
// From that, find a stream of the type we want...
// Note that the audio detection will accept either DVB or ADTS Dolby (AC-3)
// stream types
for (ii=0; ii < pmt->num_streams; ii++)
{
if (( want_video && IS_VIDEO_STREAM_TYPE(pmt->streams[ii].stream_type)) ||
(!want_video && (IS_AUDIO_STREAM_TYPE(pmt->streams[ii].stream_type))))
{
pid = pmt->streams[ii].elementary_PID;
break;
}
}
free_pmt(&pmt);
// Did we find what we want? If not, go round again and look for the
// next PMT (subject to the number of records we're willing to search)
if (pid != 0)
break;
}
if (pid == 0)
{
fprintf(stderr,"### No %s stream specified in first %d TS packets in input file\n",
(want_video?"video":"audio"),max);
free_TS_reader(&tsreader);
return 1;
}
if (!quiet)
printf("Extracting %s PID %04x (%d)\n",(want_video?"video":"audio"),
pid,pid);
// Amend max to take account of the packets we've already read
max -= total_num_read;
// And do the extraction.
err = extract_pid_packets(tsreader,output,pid,max,verbose,quiet);
free_TS_reader(&tsreader);
return err;
}
/*
* Extract all the TS packets for a nominated PID to another file.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int extract_pid(int input,
FILE *output,
u_int32 pid_wanted,
int max,
int verbose,
int quiet)
{
int err;
TS_reader_p tsreader = NULL;
// Turn our file into a TS reader
err = build_TS_reader(input,&tsreader);
if (err) return 1;
err = extract_pid_packets(tsreader,output,pid_wanted,max,verbose,quiet);
free_TS_reader(&tsreader);
return err;
}
static void print_usage()
{
printf(
"Usage: ts2es [switches] [<infile>] [<outfile>]\n"
"\n"
);
REPORT_VERSION("ts2es");
printf(
"\n"
" Extract a single (elementary) program stream from a Transport Stream\n"
" (or Program Stream).\n"
"\n"
"Files:\n"
" <infile> is an H.222 Transport Stream file (but see -stdin and -pes)\n"
" <outfile> is a single elementary stream file (but see -stdout)\n"
"\n"
"Which stream to extract:\n"
" -pid <pid> Output data for the stream with the given\n"
" <pid>. Use -pid 0x<pid> to specify a hex value\n"
" -video Output data for the (first) video stream\n"
" named in the (first) PMT. This is the default.\n"
" -audio Output data for the (first) audio stream\n"
" named in the (first) PMT\n"
"\n"
"General switches:\n"
" -stdin Input from standard input, instead of a file\n"
" -stdout Output to standard output, instead of a file\n"
" Forces -quiet.\n"
" -verbose, -v Output informational/diagnostic messages\n"
" -quiet, -q Only output error messages\n"
" -max <n>, -m <n> Maximum number of TS packets to read\n"
"\n"
" -pes, -ps Use the PES interface to read ES units from\n"
" the input file. This allows PS data to be read\n"
" (there is no point in using this for TS data).\n"
" Does not support -pid, -stdin or -stdout.\n"
);
}
int main(int argc, char **argv)
{
int use_stdout = FALSE;
int use_stdin = FALSE;
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
char *action_switch = "None";
EXTRACT extract = EXTRACT_VIDEO; // What we're meant to extract
int input = -1; // Our input file descriptor
FILE *output = NULL; // The stream we're writing to (if any)
int max = 0; // The maximum number of TS packets to read (or 0)
u_int32 pid = 0; // The PID of the (single) stream to extract
int quiet = FALSE; // True => be as quiet as possible
int verbose = FALSE; // True => output diagnostic/progress messages
int use_pes = FALSE;
int err = 0;
int ii = 1;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("ts2es",ii);
err = int_value("ts2es",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-pes",argv[ii]) || !strcmp("-ps",argv[ii]))
{
use_pes = TRUE;
}
else if (!strcmp("-pid",argv[ii]))
{
CHECKARG("ts2es",ii);
err = unsigned_value("ts2es",argv[ii],argv[ii+1],0,&pid);
if (err) return 1;
ii++;
extract = EXTRACT_PID;
}
else if (!strcmp("-video",argv[ii]))
{
extract = EXTRACT_VIDEO;
}
else if (!strcmp("-audio",argv[ii]))
{
extract = EXTRACT_AUDIO;
}
else if (!strcmp("-stdin",argv[ii]))
{
use_stdin = TRUE;
had_input_name = TRUE; // so to speak
}
else if (!strcmp("-stdout",argv[ii]))
{
use_stdout = TRUE;
had_output_name = TRUE; // so to speak
}
else
{
fprintf(stderr,"### ts2es: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && had_output_name)
{
fprintf(stderr,"### ts2es: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name) // shouldn't do this if had -stdout
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### ts2es: No input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### ts2es: "
"No output file specified for %s\n",action_switch);
return 1;
}
// ============================================================
// Testing PES output
if (use_pes && extract == EXTRACT_PID)
{
fprintf(stderr,"### ts2es: -pid is not supported with -pes\n");
return 1;
}
if (use_pes && use_stdout)
{
fprintf(stderr,"### ts2es: -stdout is not supported with -pes\n");
return 1;
}
if (use_pes && use_stdin)
{
fprintf(stderr,"### ts2es: -stdin is not supported with -pes\n");
return 1;
}
if (use_pes)
{
err = extract_av_via_pes(input_name,output_name,(extract==EXTRACT_VIDEO),
quiet);
if (err)
{
fprintf(stderr,"### ts2es: Error writing via PES\n");
return 1;
}
return 0;
}
// ============================================================
// Try to stop extraneous data ending up in our output stream
if (use_stdout)
{
verbose = FALSE;
quiet = TRUE;
}
if (use_stdin)
input = STDIN_FILENO;
else
{
input = open_binary_file(input_name,FALSE);
if (input == -1)
{
fprintf(stderr,"### ts2es: Unable to open input file %s\n",input_name);
return 1;
}
}
if (!quiet)
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
if (had_output_name)
{
if (use_stdout)
output = stdout;
else
{
output = fopen(output_name,"wb");
if (output == NULL)
{
if (!use_stdin) (void) close_file(input);
fprintf(stderr,"### ts2es: "
"Unable to open output file %s: %s\n",output_name,
strerror(errno));
return 1;
}
}
if (!quiet)
printf("Writing to %s\n",(use_stdout?"<stdout>":output_name));
}
if (!quiet)
{
if (extract == EXTRACT_PID)
printf("Extracting packets for PID %04x (%d)\n",pid,pid);
else
printf("Extracting %s\n",(extract==EXTRACT_VIDEO?"video":"audio"));
}
if (max && !quiet)
printf("Stopping after %d TS packets\n",max);
if (extract == EXTRACT_PID)
err = extract_pid(input,output,pid,max,verbose,quiet);
else
err = extract_av(input,output,(extract==EXTRACT_VIDEO),
max,verbose,quiet);
if (err)
{
fprintf(stderr,"### ts2es: Error extracting data\n");
if (!use_stdin) (void) close_file(input);
if (!use_stdout) (void) fclose(output);
return 1;
}
// And tidy up when we're finished
if (!use_stdout)
{
errno = 0;
err = fclose(output);
if (err)
{
fprintf(stderr,"### ts2es: Error closing output file %s: %s\n",
output_name,strerror(errno));
(void) close_file(input);
return 1;
}
}
if (!use_stdin)
{
err = close_file(input);
if (err)
fprintf(stderr,"### ts2es: Error closing input file %s\n",input_name);
}
return 0;
}

484
ts2ps.c 100644
Wyświetl plik

@ -0,0 +1,484 @@
/*
* Given an H.222 transport stream (TS) file, extract PES data therefrom (i.e.,
* extract the PES packets within the TS) and construct PS.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "ps_fns.h"
#include "ts_fns.h"
#include "misc_fns.h"
#include "pidint_fns.h"
#include "pes_fns.h"
#include "version.h"
/*
* Write a PS program end code
*
* Returns 0 if all went well, 1 if something went wrong
*/
static int write_program_end_code(FILE *output)
{
static byte program_end_code[] = {0x00, 0x00, 0x01, 0xB9};
size_t count = fwrite(program_end_code,4,1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PS program end code\n");
return 1;
}
return 0;
}
/*
* Write a PS pack header.
*
* Returns 0 if all went well, 1 if something went wrong
*/
static int write_pack_header(FILE *output)
{
static byte pack_header[] = {0x00, 0x00, 0x01, 0xBA,
0x44, 0x00, 0x04, 0x00,
0x04, 0x01, 0x00, 0x00,
0x03, 0xF8};
size_t count;
// For the moment, just write out an "unset" pack header
// This is illegal because the mux rate is zero (the standard
// specifically forbids that)
count = fwrite(pack_header,sizeof(pack_header),1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PS pack header out to file\n");
return 1;
}
return 0;
}
/*
* Write a PES packet from the given data
*
* `data_len` must be at most 0xFFFF - 3, allowing for the 3 bytes of the
* PES header flags/header data length which we must output.
*/
static int write_PES_packet(FILE *output,
byte *data,
u_int16 data_len,
byte stream_id)
{
static byte header[] = {0x00, 0x00, 0x01,
0xFF, 0xFF, 0xFF, // replace 3 bytes
0x80, 0x00, 0x00}; // flags and header data len
size_t count;
u_int16 PES_packet_length = data_len + 3; // + 3 for the flags, etc.
header[3] = stream_id;
header[4] = (PES_packet_length & 0xFF00) >> 8;
header[5] = (PES_packet_length & 0x00FF);
count = fwrite(header,sizeof(header),1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PS PES packet header out to file\n");
return 1;
}
count = fwrite(data,data_len,1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PS PES packet data out to file\n");
return 1;
}
return 0;
}
/*
* Extract data and output it as PS
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int extract_data(int input,
FILE *output,
u_int16 program_number,
int max,
int verbose,
int quiet)
{
int err;
PES_reader_p reader;
err = build_PES_reader(input,TRUE,!quiet,!quiet,program_number,&reader);
if (err)
{
fprintf(stderr,"### Error building PES reader over input file\n");
return 1;
}
// Temporarily, just writes out PES packets, not a PS stream...
for (;;)
{
size_t count;
err = read_next_PES_packet(reader);
if (err == EOF)
break;
else if (err)
{
fprintf(stderr,"### Error reading next PES packet\n");
(void) free_PES_reader(&reader);
return 1;
}
err = write_pack_header(output);
if (err)
{
fprintf(stderr,"### Error writing PS pack header\n");
(void) free_PES_reader(&reader);
return 1;
}
// It is possible that the TS data for video might have specified a zero
// length in the PES. Our TS reader will have read all of the packet for
// us, but will not have "adjusted" said length at the start of the packet.
// It is thus up to us to catch this case and amend it before we output
// the data...
if (reader->packet->data[4] == 0 &&
reader->packet->data[5] == 0)
{
int32 PES_packet_length = reader->packet->data_len - 6;
byte *start = reader->packet->data;
// Our maximum length is determined by the maximum length we can
// indicate in the two bytes of the PES_packet_length. When we're
// *writing* data, we also have to allow for writing the two flag
// bytes and PES_header_data_length that come thereafter.
#define MAX_LENGTH 0xFFFF
if (PES_packet_length > MAX_LENGTH)
{
printf("PES packet of 'zero' length is really %6d - too long for one packet\n",
PES_packet_length);
// Output what we can of the original packet
reader->packet->data[4] = (MAX_LENGTH & 0xFF00) >> 8;
reader->packet->data[5] = (MAX_LENGTH & 0x00FF);
// Remember that we also write out the 6 bytes preceding those
// MAX_LENGTH bytes...
printf(".. writing out %5d (%5d total)\n",MAX_LENGTH,MAX_LENGTH+6);
count = fwrite(reader->packet->data,MAX_LENGTH+6,1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing (start of) PES packet out to file\n");
(void) free_PES_reader(&reader);
return 1;
}
PES_packet_length -= MAX_LENGTH;
start += MAX_LENGTH+6;
while (PES_packet_length > 0)
{
// Now, when writing out chunks of data as PES packets,
// we have 6 bytes of header (00 00 01 stream_id length/length)
// followed by two bytes of flags (81 00) and a zero
// PES_header_data_length (00). Those last three bytes have
// to be included in the PES_packet_length of the PES packet
// we write out, which means that the longest "chunk" of data
// we can write is three less than the (otherwise) maximum.
int this_length = min(MAX_LENGTH-3,PES_packet_length);
int err;
printf(".. writing out %5d\n",this_length);
err = write_PES_packet(output,start,this_length,
reader->packet->data[3]);
if (err)
{
fprintf(stderr,"### Error writing (part of) PES packet out to file\n");
(void) free_PES_reader(&reader);
return 1;
}
PES_packet_length -= this_length;
start += this_length;
}
}
else
{
printf("PES packet of 'zero' length, adjusting to %6d-6=%6d"
" (stream id %02x, 'length' %d)\n",
reader->packet->data_len,PES_packet_length,
reader->packet->data[3],reader->packet->length);
reader->packet->data[4] = (PES_packet_length & 0xFF00) >> 8;
reader->packet->data[5] = (PES_packet_length & 0x00FF);
count = fwrite(reader->packet->data,reader->packet->data_len,1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PES packet out to file\n");
(void) free_PES_reader(&reader);
return 1;
}
}
}
else
{
count = fwrite(reader->packet->data,reader->packet->data_len,1,output);
if (count != 1)
{
fprintf(stderr,"### Error writing PES packet out to file\n");
(void) free_PES_reader(&reader);
return 1;
}
}
}
err = write_program_end_code(output);
if (err)
{
(void) free_PES_reader(&reader);
return 1;
}
(void) free_PES_reader(&reader); // naughtily ignore the return code
return 0;
}
static void print_usage()
{
printf(
"Usage: ts2ps [switches] [<infile>] [<outfile>]\n"
"\n"
);
REPORT_VERSION("ts2ps");
printf(
"\n"
" Extract a single program stream from a Transport Stream.\n"
"\n"
" WARNING: This software does not yet generate legitimate PS data.\n"
" In particular, the PS pack header records are illegal.\n"
"\n"
"Files:\n"
" <infile> is an H.222 Transport Stream file (but see -stdin)\n"
" <outfile> is an H.222 Program Stream file (but see -stdout)\n"
"\n"
"General switches:\n"
" -stdin Input from standard input, instead of a file\n"
" -stdout Output to standard output, instead of a file\n"
" Forces -quiet.\n"
" -verbose, -v Output informational/diagnostic messages\n"
" -quiet, -q Only output error messages\n"
" -max <n>, -m <n> Maximum number of TS packets to read\n"
" (not currently used)\n"
" -prog <n> Choose program number <n> (default 0, which means\n"
" the first one found).\n"
);
}
int main(int argc, char **argv)
{
int use_stdout = FALSE;
int use_stdin = FALSE;
char *input_name = NULL;
char *output_name = NULL;
int had_input_name = FALSE;
int had_output_name = FALSE;
int input = -1; // Our input file descriptor
FILE *output = NULL; // The stream we're writing to (if any)
int max = 0; // The maximum number of TS packets to read (or 0)
int quiet = FALSE; // True => be as quiet as possible
int verbose = FALSE; // True => output diagnostic/progress messages
u_int16 program_number = 0;
int err = 0;
int ii = 1;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
quiet = FALSE;
}
else if (!strcmp("-quiet",argv[ii]) || !strcmp("-q",argv[ii]))
{
verbose = FALSE;
quiet = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("ts2ps",ii);
err = int_value("ts2ps",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-prog",argv[ii]))
{
int temp;
CHECKARG("ts2ps",ii);
err = int_value("ts2ps",argv[ii],argv[ii+1],TRUE,10,&temp);
if (err) return 1;
program_number = temp;
ii++;
}
else if (!strcmp("-stdin",argv[ii]))
{
use_stdin = TRUE;
had_input_name = TRUE; // so to speak
}
else if (!strcmp("-stdout",argv[ii]))
{
use_stdout = TRUE;
had_output_name = TRUE; // so to speak
}
else
{
fprintf(stderr,"### ts2ps: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name && had_output_name)
{
fprintf(stderr,"### ts2ps: Unexpected '%s'\n",argv[ii]);
return 1;
}
else if (had_input_name) // shouldn't do this if had -stdout
{
output_name = argv[ii];
had_output_name = TRUE;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### ts2ps: No input file specified\n");
return 1;
}
if (!had_output_name)
{
fprintf(stderr,"### ts2ps: No output file specified\n");
return 1;
}
// Try to stop extraneous data ending up in our output stream
if (use_stdout)
{
verbose = FALSE;
quiet = TRUE;
}
if (use_stdin)
input = STDIN_FILENO;
else
{
input = open_binary_file(input_name,FALSE);
if (input == -1)
{
fprintf(stderr,"### ts2ps: Unable to open input file %s\n",input_name);
return 1;
}
}
if (!quiet)
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
if (had_output_name)
{
if (use_stdout)
output = stdout;
else
{
output = fopen(output_name,"wb");
if (output == NULL)
{
if (!use_stdin) (void) close_file(input);
fprintf(stderr,"### ts2ps: "
"Unable to open output file %s: %s\n",output_name,
strerror(errno));
return 1;
}
}
if (!quiet)
printf("Writing to %s\n",(use_stdout?"<stdout>":output_name));
}
if (max && !quiet)
printf("Stopping after %d TS packets\n",max);
err = extract_data(input,output,program_number,max,verbose,quiet);
if (err)
{
fprintf(stderr,"### ts2ps: Error extracting data\n");
if (!use_stdin) (void) close_file(input);
if (!use_stdout) (void) fclose(output);
return 1;
}
// And tidy up when we're finished
if (!use_stdout)
{
errno = 0;
err = fclose(output);
if (err)
{
fprintf(stderr,"### ts2ps: Error closing output file %s: %s\n",
output_name,strerror(errno));
(void) close_file(input);
return 1;
}
}
if (!use_stdin)
{
err = close_file(input);
if (err)
fprintf(stderr,"### ts2ps: Error closing input file %s\n",input_name);
}
return 0;
}

63
ts_defns.h 100644
Wyświetl plik

@ -0,0 +1,63 @@
/*
* Definitions for working with H.222 Transport Stream packets
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _ts_defns
#define _ts_defns
// Transport Stream packets are always the same size
#define TS_PACKET_SIZE 188
// When we are putting data into a TS packet, we need the first four
// bytes for heading information, which means that we will have at most
// 184 bytes for our payload
#define MAX_TS_PAYLOAD_SIZE (TS_PACKET_SIZE-4)
// ------------------------------------------------------------
// The number of TS packets to read ahead
#define TS_READ_AHEAD_COUNT 1024 // aim for multiple of block boundary -- used to be 50
// Thus the number of bytes to read ahead
#define TS_READ_AHEAD_BYTES TS_READ_AHEAD_COUNT*TS_PACKET_SIZE
// A read-ahead buffer for reading TS packets.
//
// Note that `posn` always gives the file position of the *next* TS packet to
// be read from the file (so after reading a TS packet with
// `read_next_TS_packet`, the position of said packet is `posn`-TS_PACKET_SIZE)
struct _ts_reader
{
int file; // the file to read from
offset_t posn; // the position of the next-to-be-read TS packet
byte read_ahead[TS_READ_AHEAD_COUNT*TS_PACKET_SIZE];
byte *read_ahead_ptr; // location of next packet in said array
byte *read_ahead_end; // pointer just after the end of `read_ahead`
};
typedef struct _ts_reader *TS_reader_p;
#define SIZEOF_TS_READER sizeof(struct _ts_reader)
#endif // _ts_defns

767
ts_fns.h 100644
Wyświetl plik

@ -0,0 +1,767 @@
/*
* Functions for working with H.222 Transport Stream packets - in particular,
* for writing PES packets.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _ts_fns
#define _ts_fns
#include "compat.h"
#include "h222_defns.h"
#include "tswrite_defns.h"
#include "pidint_defns.h"
#include "ts_defns.h"
// ============================================================
// Writing a Transport Stream
// ============================================================
/*
* Write out a Transport Stream PAT and PMT.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `transport_stream_id` is the id for this particular transport stream.
* - `program_number` is the program number to use for the PID.
* - `pmt_pid` is the PID for the PMT.
* - `pid` is the PID of the stream to enter in the tables. This is also
* used as the PCR PID.
* - `stream_type` is the type of stream. MPEG-2 video is 0x01,
* MPEG-4/AVC (H.264) is 0x1b.
*
* Since we're outputting a TS representing a single ES, we only need to
* support a single program stream, containing a single PID.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_TS_program_data(TS_writer_p output,
u_int32 transport_stream_id,
u_int32 program_number,
u_int32 pmt_pid,
u_int32 pid,
byte stream_type);
/*
* Write out a Transport Stream PAT and PMT, for multiple streams.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `transport_stream_id` is the id for this particular transport stream.
* - `program_number` is the program number to use for the PMT PID.
* - `pmt_pid` is the PID for the PMT.
* - `pcr_pid` is the PID that contains the PCR.
* - `num_progs` is how many program streams are to be defined.
* - `prog_pid` is an array of audio/video PIDs
* - `prog_type` is an array of the corresponding stream types
*
* Note that if `num_progs` is 0, `pcr_pid` is ignored.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_TS_program_data2(TS_writer_p output,
u_int32 transport_stream_id,
u_int32 program_number,
u_int32 pmt_pid,
u_int32 pcr_pid,
int num_progs,
u_int32 prog_pid[],
byte prog_type[]);
/*
* Write out a Transport Stream PAT.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `transport_stream_id` is the id for this particular transport stream.
* - `prog_list` is a PIDINT list of program number / PID pairs.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_pat(TS_writer_p output,
u_int32 transport_stream_id,
pidint_list_p prog_list);
/*
* Write out a Transport Stream PMT, given a PMT datastructure
*
* - `output` is the TS output context returned by `tswrite_open`
* - `pmt_pid` is the PID for the PMT.
* - 'pmt' is the datastructure containing the PMT information
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_pmt(TS_writer_p output,
u_int32 pmt_pid,
pmt_p pmt);
/*
* Write out a Transport Stream PAT and PMT, given the appropriate
* datastructures
*
* - `output` is the TS output context returned by `tswrite_open`
* - `transport_stream_id` is the id for this particular transport stream.
* - `prog_list` is a PIDINT list of program number / PID pairs.
* - `pmt_pid` is the PID for the PMT.
* - 'pmt' is the datastructure containing the PMT information
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_pat_and_pmt(TS_writer_p output,
u_int32 transport_stream_id,
pidint_list_p prog_list,
u_int32 pmt_pid,
pmt_p pmt);
/*
* Write out a Transport Stream PAT, for a single program.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `transport_stream_id` is the id for this particular transport stream.
* - `program_number` is the program number to use for the PID.
* - `pmt_pid` is the PID for the PMT.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_single_program_pat(TS_writer_p output,
u_int32 transport_stream_id,
u_int32 program_number,
u_int32 pmt_pid);
/*
* Write out our ES data as a Transport Stream PES packet.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `data` is our ES data (e.g., a NAL unit)
* - `data_len` is its length
* - `pid` is the PID to use for this TS packet
* - `stream_id` is the PES packet stream id to use (e.g.,
* DEFAULT_VIDEO_STREAM_ID)
*
* If the data to be written is more than 65535 bytes long (i.e., the
* length will not fit into 2 bytes), then the PES packet written will
* have PES_packet_length set to zero (see ISO/IEC 13818-1 (H.222.0)
* 2.4.3.7, Semantic definitions of fields in PES packet). This is only
* allowed for video streams.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_ES_as_TS_PES_packet(TS_writer_p output,
byte data[],
u_int32 data_len,
u_int32 pid,
byte stream_id);
/*
* Write out our ES data as a Transport Stream PES packet, with PTS and/or DTS
* if we've got them, and some attempt to write out a sensible PCR.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `data` is our ES data (e.g., a NAL unit)
* - `data_len` is its length
* - `pid` is the PID to use for this TS packet
* - `stream_id` is the PES packet stream id to use (e.g.,
* DEFAULT_VIDEO_STREAM_ID)
* - `got_pts` is TRUE if we have a PTS value, in which case
* - `pts` is said PTS value
* - `got_dts` is TRUE if we also have DTS, in which case
* - `dts` is said DTS value.
*
* We also want to try to write out a sensible PCR value.
*
* PTS can go up as well as down (it is the time at which the next frame
* should be presented to the user, but frames do not necessarily occur
* in presentation order).
*
* DTS only goes up, since it is the time that the frame should be decoded.
*
* Thus, if we have it, the DTS is sensible to use for the PCR...
*
* If the data to be written is more than 65535 bytes long (i.e., the
* length will not fit into 2 bytes), then the PES packet written will
* have PES_packet_length set to zero (see ISO/IEC 13818-1 (H.222.0)
* 2.4.3.7, Semantic definitions of fields in PES packet). This is only
* allowed for video streams.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_ES_as_TS_PES_packet_with_pts_dts(TS_writer_p output,
byte data[],
u_int32 data_len,
u_int32 pid,
byte stream_id,
int got_pts,
u_int64 pts,
int got_dts,
u_int64 dts);
/*
* Write out our ES data as a Transport Stream PES packet, with PCR.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `data` is our ES data (e.g., a NAL unit)
* - `data_len` is its length
* - `pid` is the PID to use for this TS packet
* - `stream_id` is the PES packet stream id to use (e.g.,
* DEFAULT_VIDEO_STREAM_ID)
* - `pcr_base` and `pcr_extn` encode the PCR value.
*
* If the data to be written is more than 65535 bytes long (i.e., the
* length will not fit into 2 bytes), then the PES packet written will
* have PES_packet_length set to zero (see ISO/IEC 13818-1 (H.222.0)
* 2.4.3.7, Semantic definitions of fields in PES packet). This is only
* allowed for video streams.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_ES_as_TS_PES_packet_with_pcr(TS_writer_p output,
byte data[],
u_int32 data_len,
u_int32 pid,
byte stream_id,
u_int64 pcr_base,
u_int32 pcr_extn);
/*
* Write out a PES packet's data as a Transport Stream PES packet.
*
* - `output` is the TS output context returned by `tswrite_open`
* - `data` is our PES data (e.g., a program stream video data packet)
* - `data_len` is its length
* - `pid` is the PID to use for this TS packet
* - `stream_id` is the PES packet stream id to use (e.g.,
* DEFAULT_VIDEO_STREAM_ID)
* - `got_pcr` is TRUE if we have values for the PCR in this packet,
* in which case `pcr_base` and `pcr_extn` are the parts of the PCR.
*
* If the data to be written is more than 65535 bytes long (i.e., the
* length will not fit into 2 bytes), then the PES packet written will
* have PES_packet_length set to zero (see ISO/IEC 13818-1 (H.222.0)
* 2.4.3.7, Semantic definitions of fields in PES packet). This is only
* allowed for video streams.
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_PES_as_TS_PES_packet(TS_writer_p output,
byte data[],
u_int32 data_len,
u_int32 pid,
byte stream_id,
int got_pcr,
u_int64 pcr_base,
u_int32 pcr_extn);
/*
* Write out a Transport Stream Null packet.
*
* - `output` is the TS output context returned by `tswrite_open`
*
* Returns 0 if it worked, 1 if something went wrong.
*/
extern int write_TS_null_packet(TS_writer_p output);
// ============================================================
// Reading a Transport Stream
// ============================================================
// ------------------------------------------------------------
// File handling
// ------------------------------------------------------------
/*
* Build a TS packet reader, including its read-ahead buffer
*
* - `file` is the file that the TS packets will be read from
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int build_TS_reader(int file,
TS_reader_p *tsreader);
/*
* Open a file to read TS packets from.
*
* If `filename` is NULL, then the input will be taken from standard input.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int open_file_for_TS_read(char *filename,
TS_reader_p *tsreader);
/*
* Free a TS packet read-ahead buffer
*
* Sets `buffer` to NULL.
*/
extern void free_TS_reader(TS_reader_p *tsreader);
/*
* Free a TS packet read-ahead buffer and close the referenced file
* (if it is not standard input).
*
* Sets `buffer` to NULL, whether the file close succeeds or not.
*
* Returns 0 if all goes well, 1 if something goes wrong.
*/
extern int close_TS_reader(TS_reader_p *tsreader);
/*
* Seek to a given offset in the TS reader's file
*
* (This should be used in preference to just seeking on the "bare" file
* since it also unsets the read-ahead buffer. However, it is still just
* a wrapper around `seek_file`.)
*
* It is assumed (but not checked) that the seek will end up at an appropriate
* offset for reading a TS packet - i.e., presumably some multiple of
* TS_PACKET_SIZE.
*
* Returns 0 if all goes well, 1 if something goes wrong
*/
extern int seek_using_TS_reader(TS_reader_p tsreader,
offset_t posn);
/*
* Read the (rest of the) first TS packet, given its first four bytes
*
* This is intended for use after inspecting the first four bytes of the
* input file, to determine if the file is TS or PS.
*
* - `tsreader` is the TS packet reading context
* - `start` is the first four bytes of the file
* - `packet` is (a pointer to) the resultant TS packet.
*
* This is a pointer into the reader's read-ahead buffer, and so should not
* be freed. Note that this means that it may not persist after another call
* of this function (and will not persist after a call of
* `free_TS_reader`).
*
* Note that the caller is trusted to call this only when appropriate.
*
* Returns 0 if all goes well, EOF if end of file was read, or 1 if some
* other error occurred (in which case it will already have output a message
* on stderr about the problem).
*/
extern int read_rest_of_first_TS_packet(TS_reader_p tsreader,
byte start[4],
byte **packet);
/*
* Read the next TS packet.
*
* - `tsreader` is the TS packet reading context
* - `packet` is (a pointer to) the resultant TS packet.
*
* This is a pointer into the reader's read-ahead buffer, and so should not
* be freed. Note that this means that it may not persist after another call
* of this function (and will not persist after a call of
* `free_TS_reader`).
*
* Returns 0 if all goes well, EOF if end of file was read, or 1 if some
* other error occurred (in which case it will already have output a message
* on stderr about the problem).
*/
extern int read_next_TS_packet(TS_reader_p tsreader,
byte **packet);
// ------------------------------------------------------------
// Reading a transport stream with buffered timing
// Keeps a PCR in hand, so that it has accurate timing information
// for each TS packet
// ------------------------------------------------------------
/* Retrieve the first TS packet from the PCR read-ahead buffer,
* complete with its calculated PCR time.
*
* This should be called the first time a TS packet is to be read
* using the PCR read-ahead buffer. It "primes" the read-ahead mechanism.
*
* - `pcr_pid` is the PID within which we should look for PCR entries
* - `start_count` is the index of the current (last read) TS entry (which will
* generally be the PMT).
* - `data` returns a pointer to the TS packet data
* - `pid` is its PID
* - `pcr` is its PCR, calculated using the previous known PCR and
* the following known PCR.
* - `count` is the index of the returned TS packet in the file
*
* Note that, like read_next_TS_packet, we return a pointer to our data,
* and, similarly, warn that it will go away next time this function
* is called.
*
* Returns 0 if all went well, 1 if something went wrong, EOF if EOF was read.
*/
extern int read_first_TS_packet_from_buffer(TS_reader_p tsreader,
u_int32 pcr_pid,
u_int32 start_count,
byte *data[TS_PACKET_SIZE],
u_int32 *pid,
u_int64 *pcr,
u_int32 *count);
/* Retrieve the next TS packet from the PCR read-ahead buffer,
* complete with its calculated PCR time.
*
* - `data` returns a pointer to the TS packet data
* - `pid` is its PID
* - `pcr` is its PCR, calculated using the previous known PCR and
* the following known PCR.
*
* Note that, like read_next_TS_packet, we return a pointer to our data,
* and, similarly, warn that it might go away next time this function
* is called.
*
* Returns 0 if all went well, 1 if something went wrong, EOF if EOF was read.
*/
extern int read_next_TS_packet_from_buffer(TS_reader_p tsreader,
byte *data[TS_PACKET_SIZE],
u_int32 *pid,
u_int64 *pcr);
/* Let the "looping" buffered TS packet reader know what its PCR PID is
*
* Call this before the first call of read_buffered_TS_packet().
*
* - `pcr_pid` is the PID within which we should look for PCR entries
*
* Returns 0 if all went well, 1 if something went wrong, EOF if we got an
* unexpected EOF.
*/
extern void prime_read_buffered_TS_packet(u_int32 pcr_pid);
/*
* Read the next TS packet, coping with looping, etc.
*
* prime_read_buffered_TS_packet() should have been called first.
*
* This differs from ``read_TS_packet`` in that it assumes that the
* underlying code will already have read to the next PCR, so that
* it can know the *actual* (PCR-based) time for each TS packet.
*
* - `tsreader` is the TS reader context
* - `count` is a running count of TS packets read from this input
* - `data` is a pointer to the data for the packet
* - `pid` is the PID of the TS packet
* - `pcr` is the PCR value (possibly calculated) for this TS packet
* - if `max` is greater than zero, then at most `max` TS packets should
* be read from the input
* - if `loop`, play the input file repeatedly (up to `max` TS packets
* if applicable) - i.e., rewind to `start_posn` and start again if
* `count` reaches `max` (obviously only if `max` is greater than zero).
* - `start_count` is the value `count` should have after we've looped back
* to `start_posn`
* - if `quiet` is true, then only error messages should be written out
*
* Returns 0 if all went well, 1 if something went wrong, EOF if `loop` is
* false and either EOF was read, or `max` TS packets were read.
*/
extern int read_buffered_TS_packet(TS_reader_p tsreader,
u_int32 *count,
byte *data[TS_PACKET_SIZE],
u_int32 *pid,
u_int64 *pcr,
int max,
int loop,
offset_t start_posn,
u_int32 start_count,
int quiet);
// ------------------------------------------------------------
// Packet interpretation
// ------------------------------------------------------------
/*
* Retrieve the PCR (if any) from a TS packet's adaptation field
*
* - `adapt` is the adaptation field content
* - `adapt_len` is its length
* - `got_PCR` is TRUE if the adaptation field contains a PCR
* - `pcr` is then the PCR value itself
*/
extern void get_PCR_from_adaptation_field(byte adapt[],
int adapt_len,
int *got_pcr,
u_int64 *pcr);
/*
* Report on the contents of this TS packet's adaptation field
*
* - `adapt` is the adaptation field content
* - `adapt_len` is its length
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern void report_adaptation_field(byte adapt[],
int adapt_len);
/*
* Report on the timing information from this TS packet's adaptation field
*
* - if `times` is non-NULL, then timing information (derived from the PCR)
* will be calculated and reported
* - `adapt` is the adaptation field content
* - `adapt_len` is its length
* - `packet_count` is a count of how many TS packets up to now
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern void report_adaptation_timing(timing_p times,
byte adapt[],
int adapt_len,
int packet_count);
/*
* Report on the contents of this TS packet's payload. The packet is assumed
* to have a payload that is (part of) a PES packet.
*
* - if `show_data` then the data for the PES packet will also be shown
* - `stream_type` is the stream type of the data, or -1 if it is not
* known
* - `payload` is the payload of the TS packet. We know it can't be more
* than 184 bytes long, because of the packet header bytes.
* - regardless, `payload_len` is the actual length of the payload.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern void report_payload(int show_data,
int stream_type,
byte payload[MAX_TS_PAYLOAD_SIZE],
int payload_len,
int payload_unit_start_indicator);
/*
* Print out information about program descriptors
* (either from the PMT program info, or the PMT/stream ES info)
*
* - `stream` is the stream to print on
* - `leader1` and `leader2` are the text to write at the start of each line
* (either or both may be NULL)
* - `desc_data` is the data containing the descriptors
* - `desc_data_len` is its length
*
* Returns 0 if all went well, 1 if something went wrong
*/
extern int print_descriptors(FILE *stream,
char *leader1,
char *leader2,
byte *desc_data,
int desc_data_len);
/*
* Extract the program list from a PAT packet (PID 0x0000).
*
* Handles the result of calling build_psi_data() for this PAT.
*
* - if `verbose`, then report on what we're doing
* - `payload` is the payload of the TS packet. We know it can't be more
* than 184 bytes long, because of the packet header bytes.
* - regardless, `payload_len` is the actual length of the payload.
* - `prog_list` is the list of program numbers versus PIDs.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int extract_prog_list_from_pat(int verbose,
byte payload[MAX_TS_PAYLOAD_SIZE],
int payload_len,
pidint_list_p *prog_list);
/*
* Extract the stream list (and PCR PID) from a PMT packet.
*
* Handles the result of calling build_psi_data() for this PMT.
*
* - if `verbose`, then report on what we're doing
* - `payload` is the payload of the TS packet. We know it can't be more
* than 184 bytes long, because of the packet header bytes.
* - regardless, `payload_len` is the actual length of the payload.
* - `pid` is the PID of this TS packet.
* - `program_number` is the program number.
* - `pcr_pid` is the PID of packets containing the PCR, or 0.
* - `stream_list` is a list of stream versus PID.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int extract_stream_list_from_pmt(int verbose,
byte payload[MAX_TS_PAYLOAD_SIZE],
int payload_len,
u_int32 pid,
int *program_number,
u_int32 *pcr_pid,
pidint_list_p *stream_list);
/*
* Given a TS packet, extract the (next bit of) a PAT/PMT's data.
*
* - if `verbose`, then report on what we're doing
* - `payload` is the payload of the current TS packet. We know it can't be
* more than 184 bytes long, because of the packet header bytes.
* - regardless, `payload_len` is the actual length of the payload.
* - `pid` is the PID of this TS packet.
* - `data` is the data array for the whole of the data of this PSI.
* If it is passed as NULL, then the TS packet must be the first for
* this PSI, and this function will malloc an array of the appropriate
* length (and return it here). If it is non-NULL, then it is partially
* full.
* - `data_len` is the actual length of the `data` array -- if `data` is NULL
* then this will be set by the function.
* - `data_used` is how many bytes of data are already in the `data` array.
* This will be updated by this function - if it is returned as equal to
* `data_len`, then the PMT packet data is complete.
*
* Usage:
*
* If a PSI packet has PUSI set, then it is the first packet of said PSI
* (which, for our purposes, means PAT or PMT). If it does not, then it
* is a continuation. If PUSI was set, call this with ``data`` NULL, otherwise
* pass it some previous data to continue.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int build_psi_data(int verbose,
byte payload[MAX_TS_PAYLOAD_SIZE],
int payload_len,
u_int32 pid,
byte **data,
int *data_len,
int *data_used);
/*
* Extract the program map table from a PMT packet.
*
* Assumes that the whole content of the PMT is in this single packet.
*
* - `data` is the data for the PMT packet.
* - `data_len` is the length of said data.
* - `pid` is the PID of this PMT
* - `pmt` is the new PMT datastructure
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int extract_pmt(int verbose,
byte data[],
int data_len,
u_int32 pid,
pmt_p *pmt);
/*
* Split a TS packet into its main parts
*
* - `buf` is the data for the packet
* - `pid` is the PID of said data
* - `payload_unit_start_indicator` is TRUE if any payload in this
* packet forms the start of a PES packet. Its meaning is not significant
* if there is no payload, or if the payload is not (part of) a PES packet.
* - `adapt` is an offset into `buf`, acting as an array of the actual
* adaptation control bytes. It will be NULL if there are no adaptation
* controls.
* - `adapt_len` is the length of the adaptation controls (i.e., the
* number of bytes). It will be 0 if there are no adaptation controls.
* - `payload` is an offset into `buf`, acting as an array of the actual
* payload bytes. It will be NULL if there is no payload.
* - `payload_len` is the length of the payload *in this packet* (i.e., the
* number of bytes. It will be 0 if there is no payload.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int split_TS_packet(byte buf[TS_PACKET_SIZE],
u_int32 *pid,
int *payload_unit_start_indicator,
byte *adapt[],
int *adapt_len,
byte *payload[],
int *payload_len);
/*
* Return the next TS packet, as payload and adaptation controls.
*
* This is a convenience wrapping of `read_next_TS_packet` and
* `split_TS_packet`. Because of this, the data referenced by `adapt` and
* `payload` will generally not persist over further calls of this function
* and `read_next_TS_packet`, as it is held within the TS reader's read-ahead
* buffer.
*
* - `tsreader` is the TS packet reading context
* - `pid` is the PID of said data
* - `payload_unit_start_indicator` is TRUE if any payload in this
* packet forms the start of a PES packet. Its meaning is not significant
* if there is no payload, or if the payload is not (part of) a PES packet.
* - `adapt` is an offset into `buf`, acting as an array of the actual
* adaptation control bytes. It will be NULL if there are no adaptation
* controls.
* - `adapt_len` is the length of the adaptation controls (i.e., the
* number of bytes). It will be 0 if there are no adaptation controls.
* - `payload` is an offset into `buf`, acting as an array of the actual
* payload bytes. It will be NULL if there is no payload.
* - `payload_len` is the length of the payload *in this packet* (i.e., the
* number of bytes. It will be 0 if there is no payload.
*
* Returns 0 if all went well, EOF if there is no more data, 1 if something
* went wrong.
*/
extern int get_next_TS_packet(TS_reader_p tsreader,
u_int32 *pid,
int *payload_unit_start_indicator,
byte *adapt[],
int *adapt_len,
byte *payload[],
int *payload_len);
/*
* Find the first (next) PAT.
*
* - `tsreader` is the TS packet reading context
* - if `max` is non-zero, then it is the maximum number of TS packets to read
* - if `verbose` is true, then output extra information
* - if `quiet` is true, then don't output normal informational messages
* - `num_read` is the number of packets read to find the PAT (or before
* giving up)
* - `prog_list` is the program list from the PAT, or NULL if none was found
*
* Returns 0 if all went well, EOF if no PAT was found,
* 1 if something else went wrong.
*/
extern int find_pat(TS_reader_p tsreader,
int max,
int verbose,
int quiet,
int *num_read,
pidint_list_p *prog_list);
/*
* Find the next PMT, and report on it.
*
* - `tsreader` is the TS packet reading context
* - `pmt_pid` is the PID of the PMT we are looking for
* - if `max` is non-zero, then it is the maximum number of TS packets to read
* - if `verbose` is true, then output extra information
* - if `quiet` is true, then don't output normal informational messages
* - `num_read` is the number of packets read to find the PMT (or before
* giving up)
* - `pmt` is a new datastructure representing the PMT found
*
* Returns 0 if all went well, EOF if no PMT was found,
* 1 if something else went wrong.
*/
extern int find_next_pmt(TS_reader_p tsreader,
u_int32 pmt_pid,
int max,
int verbose,int quiet,
int *num_read,
pmt_p *pmt);
/*
* Find the next PAT, and from that the next PMT.
*
* Looks for the next PAT in the input stream, and then for the first
* PMT thereafter. If there is more than one program stream in the PAT,
* it looks for the PMT for the first.
*
* - `tsreader` is the TS packet reading context
* - if `max` is non-zero, then it is the maximum number of TS packets to read
* - if `verbose` is true, then output extra information
* - if `quiet` is true, then don't output normal informational messages
* - `num_read` is the number of packets read to find the PMT (or before
* giving up)
* - `pmt` is a new datastructure containing the information from the PMT.
*
* Returns 0 if all went well, EOF if no PAT or PMT was found (and thus
* no program stream), -2 if a PAT was found but it did not contain any
* programs, 1 if something else went wrong.
*/
extern int find_pmt(TS_reader_p tsreader,
int max,
int verbose,
int quiet,
int *num_read,
pmt_p *pmt);
#endif // _ts_fns

408
tsinfo.c 100644
Wyświetl plik

@ -0,0 +1,408 @@
/*
* Locate the PAT and PMT packets in an H.222 transport stream (TS),
* and report on their contents (i.e., the program and stream info).
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#ifdef _WIN32
#include <stddef.h>
#else // _WIN32
#include <unistd.h>
#endif // _WIN32
#include "compat.h"
#include "ts_fns.h"
#include "misc_fns.h"
#include "pidint_fns.h"
#include "version.h"
/*
* Report on the program streams, by looking at the PAT and PMT packets
* in the first `max` TS packets of the given input stream
*
* Returns 0 if all went well, 1 if something went wrong.
*/
static int report_streams(TS_reader_p tsreader,
int max,
int verbose)
{
int err;
int ii;
// TODO: Should really support multiple programs
// (some use of pidint_list to support program number -> PMT?)
pidint_list_p this_prog_list = NULL;
pidint_list_p last_prog_list = NULL;
pmt_p this_pmt = NULL;
pmt_p last_pmt = NULL;
u_int32 pmt_pid = 0; // which will get "masked" by the PAT pid
byte *pat_data = NULL;
int pat_data_len = 0;
int pat_data_used = 0;
byte *pmt_data = NULL;
int pmt_data_len = 0;
int pmt_data_used = 0;
int num_pats = 0;
int num_pmts = 0;
printf("Scanning %d TS packets\n",max);
for (ii=0; ii<max; ii++)
{
u_int32 pid;
int payload_unit_start_indicator;
byte *adapt, *payload;
int adapt_len, payload_len;
err = get_next_TS_packet(tsreader,&pid,
&payload_unit_start_indicator,
&adapt,&adapt_len,&payload,&payload_len);
if (err == EOF)
{
printf("EOF\n");
break;
}
else if (err)
{
fprintf(stderr,"### Error reading TS packet %d\n",ii+1);
if (pat_data) free(pat_data);
free_pidint_list(&last_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return 1;
}
if (pid == 0x0000)
{
num_pats++;
if (verbose)
printf("Packet %d is PAT\n",ii+1);
if (payload_len == 0)
{
printf("Packet %d is PAT, but has no payload\n",ii+1);
continue;
}
if (payload_unit_start_indicator && pat_data)
{
// This is the start of a new PAT packet, but we'd already
// started one, so throw its data away
fprintf(stderr,"!!! Discarding previous (uncompleted) PAT data\n");
free(pat_data);
pat_data = NULL; pat_data_len = 0; pat_data_used = 0;
}
else if (!payload_unit_start_indicator && !pat_data)
{
// This is the continuation of a PAT packet, but we hadn't
// started one yet
fprintf(stderr,"!!! Discarding PAT continuation, no PAT started\n");
continue;
}
err = build_psi_data(verbose,payload,payload_len,pid,
&pat_data,&pat_data_len,&pat_data_used);
if (err)
{
fprintf(stderr,"### Error %s PAT\n",
(payload_unit_start_indicator?"starting new":"continuing"));
if (pat_data) free(pat_data);
free_pidint_list(&last_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return 1;
}
// Do we need more data to complete this PAT?
if (pat_data_len > pat_data_used)
continue;
err = extract_prog_list_from_pat(verbose,pat_data,pat_data_len,
&this_prog_list);
if (err)
{
free_pidint_list(&last_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
free(pat_data);
return err;
}
free(pat_data);
pat_data = NULL; pat_data_len = 0; pat_data_used = 0;
num_pats++;
if (err)
{
free_pidint_list(&last_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return err;
}
if (!same_pidint_list(this_prog_list,last_prog_list))
{
if (last_prog_list != NULL)
printf("\nPacket %d is PAT - content changed\n",ii+1);
else if (!verbose)
printf("\nPacket %d is PAT\n",ii+1);
report_pidint_list(this_prog_list,"Program list","Program",FALSE);
if (this_prog_list->length == 0)
printf("No programs defined in PAT (packet %d)\n",ii+1);
else
{
if (this_prog_list->length > 1)
printf("Multiple programs in PAT - using the first\n");
pmt_pid = this_prog_list->pid[0];
}
}
free_pidint_list(&last_prog_list);
last_prog_list = this_prog_list;
}
else if (pid == pmt_pid)
{
if (verbose)
printf("Packet %d is PMT with PID %04x (%d)%s\n",ii+1,pid,pid,
(payload_unit_start_indicator?"[pusi]":""));
if (payload_len == 0)
{
printf("Packet %d is PMT, but has no payload\n",ii+1);
continue;
}
if (payload_unit_start_indicator && pmt_data)
{
// This is the start of a new PMT packet, but we'd already
// started one, so throw its data away
fprintf(stderr,"!!! Discarding previous (uncompleted) PMT data\n");
free(pmt_data);
pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0;
}
else if (!payload_unit_start_indicator && !pmt_data)
{
// This is the continuation of a PMT packet, but we hadn't
// started one yet
fprintf(stderr,"!!! Discarding PMT continuation, no PMT started\n");
continue;
}
err = build_psi_data(verbose,payload,payload_len,pid,
&pmt_data,&pmt_data_len,&pmt_data_used);
if (err)
{
fprintf(stderr,"### Error %s PMT\n",
(payload_unit_start_indicator?"starting new":"continuing"));
free_pidint_list(&this_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return 1;
}
// Do we need more data to complete this PMT?
if (pmt_data_len > pmt_data_used)
continue;
err = extract_pmt(verbose,pmt_data,pmt_data_len,pid,&this_pmt);
if (err)
{
free_pidint_list(&this_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return err;
}
free(pmt_data);
pmt_data = NULL; pmt_data_len = 0; pmt_data_used = 0;
num_pmts++;
if (same_pmt(this_pmt,last_pmt)) // Nothing to do
{
free_pmt(&this_pmt);
continue;
}
if (last_pmt != NULL)
printf("\nPacket %d is PMT with PID %04x (%d)"
" - content changed\n",ii+1,pid,pid);
else if (!verbose)
printf("\nPacket %d is PMT with PID %04x (%d)\n",ii+1,pid,pid);
report_pmt(stdout," ",this_pmt);
free_pmt(&last_pmt);
last_pmt = this_pmt;
}
}
printf("\nFound %d PAT packet%s and %d PMT packet%s in %d TS packets\n",
num_pats,(num_pats==1?"":"s"),
num_pmts,(num_pmts==1?"":"s"),max);
free_pidint_list(&last_prog_list);
free_pmt(&last_pmt);
if (pmt_data) free(pmt_data);
return 0;
}
static void print_usage()
{
printf(
"Usage: tsinfo [switches] [<infile>]\n"
"\n"
);
REPORT_VERSION("tsinfo");
printf(
"\n"
" Report on the program streams in a Transport Stream.\n"
"\n"
"Files:\n"
" <infile> is an H.222 Transport Stream file (but see -stdin)\n"
"\n"
"Switches:\n"
" -stdin Input from standard input, instead of a file\n"
" -verbose, -v Output extra information about packets\n"
" -max <n>, -m <n> Number of TS packets to scan. Defaults to 1000.\n"
" -repeat <n> Look for <n> PMT packets, and report on each\n"
);
}
int main(int argc, char **argv)
{
int use_stdin = FALSE;
char *input_name = NULL;
int had_input_name = FALSE;
int max = 1000;
int verbose = FALSE; // True => output diagnostic/progress messages
int lookfor = 1;
int err = 0;
TS_reader_p tsreader = NULL;
int ii = 1;
if (argc < 2)
{
print_usage();
return 0;
}
while (ii < argc)
{
if (argv[ii][0] == '-')
{
if (!strcmp("--help",argv[ii]) || !strcmp("-h",argv[ii]) ||
!strcmp("-help",argv[ii]))
{
print_usage();
return 0;
}
else if (!strcmp("-verbose",argv[ii]) || !strcmp("-v",argv[ii]))
{
verbose = TRUE;
}
else if (!strcmp("-max",argv[ii]) || !strcmp("-m",argv[ii]))
{
CHECKARG("tsinfo",ii);
err = int_value("tsinfo",argv[ii],argv[ii+1],TRUE,10,&max);
if (err) return 1;
ii++;
}
else if (!strcmp("-repeat",argv[ii]))
{
CHECKARG("tsinfo",ii);
err = int_value("tsinfo",argv[ii],argv[ii+1],TRUE,10,&lookfor);
if (err) return 1;
ii++;
}
else if (!strcmp("-stdin",argv[ii]))
{
use_stdin = TRUE;
had_input_name = TRUE; // so to speak
}
else
{
fprintf(stderr,"### tsinfo: "
"Unrecognised command line switch '%s'\n",argv[ii]);
return 1;
}
}
else
{
if (had_input_name)
{
fprintf(stderr,"### tsinfo: Unexpected '%s'\n",argv[ii]);
return 1;
}
else
{
input_name = argv[ii];
had_input_name = TRUE;
}
}
ii++;
}
if (!had_input_name)
{
fprintf(stderr,"### tsinfo: No input file specified\n");
return 1;
}
err = open_file_for_TS_read((use_stdin?NULL:input_name),&tsreader);
if (err)
{
fprintf(stderr,"### tsinfo: Unable to open input file %s for reading TS\n",
use_stdin?"<stdin>":input_name);
return 1;
}
printf("Reading from %s\n",(use_stdin?"<stdin>":input_name));
err = report_streams(tsreader,max,verbose);
if (err)
{
fprintf(stderr,"### tsinfo: Error reporting on stream\n");
(void) close_TS_reader(&tsreader);
return 1;
}
err = close_TS_reader(&tsreader);
if (err) return 1;
return 0;
}

1537
tsplay.c 100644

Plik diff jest za duży Load Diff

1157
tsreport.c 100644

Plik diff jest za duży Load Diff

3884
tsserve.c 100644

Plik diff jest za duży Load Diff

3162
tswrite.c 100644

Plik diff jest za duży Load Diff

329
tswrite_defns.h 100644
Wyświetl plik

@ -0,0 +1,329 @@
/*
* Support for writing out TS packets, to file, or over TCP/IP or UDP
*
* When writing asynchronously, provides automated producer/consumer
* behaviour via a circular buffer, optionally taking timing from the
* TS PCR entries.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _tswrite_defns
#define _tswrite_defns
#include "compat.h"
#include "ts_defns.h"
#include "h222_defns.h"
#ifdef _WIN32
#include <winsock2.h> // for definition of SOCKET
#else
typedef int SOCKET; // for compatibility with Windows
#include <termios.h> // for struct termios
#endif
// ============================================================
// CIRCULAR BUFFER
// ============================================================
// We default to using a "packet" of 7 transport stream packets because 7*188 =
// 1316, but 8*188 = 1504, and we would like to output as much data as we can
// that is guaranteed to fit into a single ethernet packet, size 1500.
#define DEFAULT_TS_PACKETS_IN_ITEM 7
// For simplicity, we'll have a maximum on that (it allows us to have static
// array sizes in some places). This should be a big enough size to more than
// fill a jumbo packet on a gigabit network.
#define MAX_TS_PACKETS_IN_ITEM 100
// ------------------------------------------------------------
// A circular buffer, usable as a queue
//
// We "waste" one buffer item so that we don't have to maintain a count
// of items in the buffer
//
// To get an understanding of how it works, choose a small BUFFER_SIZE
// (e.g., 11), enable DISPLAY_BUFFER, and select --visual - this will show the
// reading/writing of the circular buffer in action, including the
// "unused item".
//
// The data for the circular buffer
// Each circular buffer item "contains" (up to) N TS packets (where N defaults
// to 7, and is specified as `item_size` in the circular buffer header), and a
// time (in microseconds) when we would like it to be output (relative to the
// time for the first packet "sent").
//
// Said data is stored at the address indicated by the circular buffer
// "header", as `item_data`.
//
struct circular_buffer_item
{
u_int32 time; // when we would like this data output
int discontinuity; // TRUE if our timeline has "broken"
int length; // number of bytes of data in the array
};
typedef struct circular_buffer_item *circular_buffer_item_p;
#define SIZEOF_CIRCULAR_BUFFER_ITEM sizeof(struct circular_buffer_item)
// ------------------------------------------------------------
// The header for the circular buffer
//
// Note that `start` is only ever written to by the child process, and this is
// the only thing that the child process ever changes in the circular buffer.
//
// `maxnowait` is the maximum number of packets to send to the target host
// without forcing an intermediate wait - required to stop us "swamping" the
// target with too much data, and overrunning its buffers.
struct circular_buffer
{
int start; // start of data "pointer"
int end; // end of data "pointer" (you guessed)
int size; // the actual length of the `item` array
int TS_in_item; // max number of TS packets in a circular buffer item
int item_size; // and thus the size of said item's data array
int maxnowait; // max number consecutive packets to send with no wait
int waitfor; // the number of microseconds to wait thereafter
// The location of the packet data for the circular buffer items
byte *item_data;
// The "header" data for each circular buffer item
struct circular_buffer_item item[];
};
typedef struct circular_buffer *circular_buffer_p;
// Note that size doesn't include the final `item`
#define SIZEOF_CIRCULAR_BUFFER sizeof(struct circular_buffer)
#define DEFAULT_CIRCULAR_BUFFER_SIZE 1024 // used to be 100
// ============================================================
// BUFFERED OUTPUT
// ============================================================
// Information about each TS packet in our circular buffer item
struct TS_packet_info
{
int index;
u_int32 pid; // do we need the PIDs?
int got_pcr;
u_int64 pcr;
};
typedef struct TS_packet_info *TS_packet_info_p;
#define SIZEOF_TS_PACKET_INFO sizeof(struct TS_packet_info);
// If we're going to support output via our circular buffer in a manner
// similar to that for output to a file or socket, then we need a structure
// to maintain the relevant information. It seems a bit wasteful to burden
// the circular buffer itself with this, particularly as only the writer
// cares about this data, so it needn't be shared.
struct buffered_TS_output
{
circular_buffer_p buffer;
int which; // Which buffer index we're writing to
int started; // TRUE if we've started writing therein
// For each TS packet in the circular buffer, remember its `count`
// within the input stream, whether it had a PCR, and if so what that
// PCR was. To make it simpler to access these arrays, also keep a fill
// index into them (the alternative would be to always re-zero the
// `got_pcr` values whenever we start a new circular buffer entry,
// which would be a pain...)
int num_packets; // how many TS packets we've got
struct TS_packet_info packet[MAX_TS_PACKETS_IN_ITEM];
// `rate` is the rate (in bytes per second) we would like to output data at
u_int32 rate;
// `pcr_scale` is a multiplier for PCRs - each PCR found gets its value
// multiplied by this
double pcr_scale;
// `use_pcrs` indicates if we should use PCRs in the data to drive our
// timing, rather than use the specified byte rate directly. The `priming`
// values are only relevant if `use_pcrs` is true.
int use_pcrs;
// 'prime_size' is the amount of space/time to 'prime' the circular buffer
// output timing mechanism with. This is effectively multiples of the
// size of a circular buffer item.
int prime_size;
// Percentage "too fast" speedup for our priming rate
int prime_speedup;
};
typedef struct buffered_TS_output *buffered_TS_output_p;
#define SIZEOF_BUFFERED_TS_OUTPUT sizeof(struct buffered_TS_output)
// ============================================================
// EXTERNAL DATASTRUCTURES - these are *intended* for external use
// ============================================================
// Our supported target types
// On Unix-type systems, there is little distinction between file and
// socket, but on Windows this becomes more interesting
enum TS_writer_type
{
TS_W_UNDEFINED,
TS_W_STDOUT, // standard output
TS_W_FILE, // a file
TS_W_TCP, // a socket, over TCP/IP
TS_W_UDP, // a socket, over UDP
};
typedef enum TS_writer_type TS_WRITER_TYPE;
// ------------------------------------------------------------
// So, *is* it a file or a socket?
union TS_writer_output
{
FILE *file;
SOCKET socket;
};
// ------------------------------------------------------------
// A datastructure to allow us to write to various different types of target
//
// When writing to a file, "how" will be TS_W_STDOUT or TS_W_FILE, and
// "where" will be the appropriate file interface. "writer" is not necessary
// (there's no point in putting a circular buffer and other stuff above
// the file writes), and no child process is needed.
//
// When writing over UDP, "how" will be TS_W_UDP, and "where" will be the
// socket that is being written to. For UDP, timing needs to be managed, and
// thus the circular buffer support is necessary, so "writer" should be
// set to a buffered output context. Since the circular buffer is being
// used, there will also be a child process.
//
// When writing over TCP/IP, "how" will be TS_W_TCP, and "where" will be the
// socket that is being written to. Timing is not an issue, so "writer" will
// not be needed, and nor will there be a child process. However, it is
// possible that we will want to respond to commands (over the same or another
// socket (or, on Linux/BSD, file descriptor)), so "commander" may be set.
struct TS_writer
{
enum TS_writer_type how; // what type of output we want
union TS_writer_output where; // where it's going to
buffered_TS_output_p writer; // our buffered output interface, if needed
int count; // a count of how many TS packets written
// Support for the child fork/thread, which actually does the writing when
// buffered output is enabled.
#ifdef _WIN32
HANDLE child; // the handle for the child thread (if any)
#else // _WIN32
pid_t child; // the PID of the child process (if any)
#endif // _WIN32
int quiet; // Should the child be as quiet as possible?
// Support for "commands" being sent to us via a socket (or, on Linux/BSD,
// from any other file descriptor). The "normal" way this is used is for
// our application (tsserve) to act as a server, listening on a socket
// for an incoming connection, and then both playing data to that
// connection, and listening for commands from it.
int server; // are we acting as a server?
SOCKET command_socket; // where to read commands from/through
// When the user sends a new command (a different character than is
// currently in `command`), the underpinnings of tswrite_write() set
// `command` to that command letter, and `command_changed` to TRUE.
// Various key functions that write to TS check `command_changed`, and
// return COMMAND_RETURN_CODE if it is true.
// Note, however, that it is left up to the top level to *unset*
// `command_changed` again.
byte command; // A single character "command" for what to do
int command_changed; // Has it changed?
// Some commands (notably, the "skip" commands) want to be atomic - that
// is, they should not be interrupted by the user "typing ahead". Since
// the fast forward and reverse mechanisms (used for skipping as well)
// call tswrite_command_changed() to tell if there is a new command that
// should interrup them, we can provide a flag to say "don't do that"...
int atomic_command;
// Should some TS packets be thrown away every <n> packets? This can be
// useful for debugging other applications
int drop_packets; // 0 to keep all packets, otherwise keep <n> packets
int drop_number; // and then drop this many
};
typedef struct TS_writer *TS_writer_p;
#define SIZEOF_TS_WRITER sizeof(struct TS_writer)
// ------------------------------------------------------------
// Command letters
#define COMMAND_NOT_A_COMMAND '_' // A guaranteed non-command letter
#define COMMAND_QUIT 'q' // quit/exit
#define COMMAND_NORMAL 'n' // normal playing speed
#define COMMAND_PAUSE 'p' // pause until another command
#define COMMAND_FAST 'f' // fast forward
#define COMMAND_FAST_FAST 'F' // faster forward
#define COMMAND_REVERSE 'r' // reverse/rewind
#define COMMAND_FAST_REVERSE 'R' // faster reverse/rewind
#define COMMAND_SKIP_FORWARD '>' // aim at 10s
#define COMMAND_SKIP_BACKWARD '<' // ditto
#define COMMAND_SKIP_FORWARD_LOTS ']' // aim at 100s
#define COMMAND_SKIP_BACKWARD_LOTS '[' // ditto
#define COMMAND_SELECT_FILE_0 '0'
#define COMMAND_SELECT_FILE_1 '1'
#define COMMAND_SELECT_FILE_2 '2'
#define COMMAND_SELECT_FILE_3 '3'
#define COMMAND_SELECT_FILE_4 '4'
#define COMMAND_SELECT_FILE_5 '5'
#define COMMAND_SELECT_FILE_6 '6'
#define COMMAND_SELECT_FILE_7 '7'
#define COMMAND_SELECT_FILE_8 '8'
#define COMMAND_SELECT_FILE_9 '9'
// And a "return code" that means "the command character has changed"
#define COMMAND_RETURN_CODE -999
// ------------------------------------------------------------
// Context for use in decoding command line - see `tswrite_process_args()`
struct TS_context
{
// Values used in setting up buffered output
int circ_buf_size; // number of buffer entries (+1) for circular buffer
int TS_in_item; // number of TS packets in each circular buffer item
int maxnowait; // max number of packets to send without waiting
int waitfor; // the number of microseconds to wait thereafter
int bitrate; // suggested bit rate (byterate*8) - both are given
int byterate; // suggested byte rate (bitrate/8) - for convenience
int use_pcrs; // use PCRs for timing information?
int prime_size; // initial priming size for buffered output
int prime_speedup; // percentage of normal speed to prime with
double pcr_scale; // multiplier for PCRs -- see buffered_TS_output
};
typedef struct TS_context *TS_context_p;
// Arguments processed by tswrite_process_args are set to:
#define TSWRITE_PROCESSED "<processed>"
#endif // _tswrite_defns

345
tswrite_fns.h 100644
Wyświetl plik

@ -0,0 +1,345 @@
/*
* Support for writing out TS packets, to file, or over TCP/IP or UDP
*
* When writing asynchronously, provides automated producer/consumer
* behaviour via a circular buffer, optionally taking timing from the
* TS PCR entries.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _tswrite_fns
#define _tswrite_fns
#include "tswrite_defns.h"
/*
* Open a file for TS output.
*
* - `how` is how to open the file or connect to the host
* - `name` is the name of the file or host to open/connect to
* (this is ignored if `how` is TS_W_STDOUT)
* - if `how` is TS_W_UDP, and `name` is a multicast address,
* then `multicast_if` may be the IP address of the network
* address to use, or NULL if the default interface should
* be used.
* - if it is a socket (i.e., if `how` is TS_W_TCP or TS_W_UDP),
* then `port` is the port to use, otherwise this is ignored
* - `quiet` is true if only error messages should be printed
* - `tswriter` is the new context to use for writing TS output,
* which should be closed using `tswrite_close`.
*
* For TS_W_STDOUT, there is no need to open anything.
*
* For TS_W_FILE, ``open(name,O_CREAT|O_WRONLY|O_TRUNC|O_BINARY,00777)``
* is used - i.e., the file is opened so that anyone may read/write/execute
* it. If ``O_BINARY`` is not defined (e.g., on Linux), then it is
* omitted.
*
* For TS_W_TCP and TS_W_UDP, the ``connect_socket`` function is called,
* which uses ``socket`` and ``connect``.
*
* In all cases (even when using TS_W_STDOUT), the `tswriter` should be
* closed using `tswrite_stdout`.
*
* For TS_W_UDP, the ``tswrite_startt_buffering`` function must be called
* before any output is written via the `tswriter`. For other forms of output,
* this is optional.
*
* Returns 0 if all goes well, 1 if something went wrong.
*/
extern int tswrite_open(TS_WRITER_TYPE how,
char *name,
char *multicast_if,
int port,
int quiet,
TS_writer_p *tswriter);
/*
* Open a network connection for TS output.
*
* This is a convenience wrapper around `tswrite_open`.
*
* - `name` is the name of the host to connect to
* - `port` is the port to connect to
* - `use_tcp` is TRUE if TCP/IP should be use, FALSE if UDP should be used
* - `quiet` is true if only error messages should be printed
* - `tswriter` is the new context to use for writing TS output,
* which should be closed using `tswrite_close`.
*
* In all cases, the `tswriter` should be closed using `tswrite_stdout`.
*
* For TS_W_UDP, the ``tswrite_start_buffering`` function must be called
* before any output is written via the `tswriter`. For other forms of output,
* this is optional.
*
* Returns 0 if all goes well, 1 if something went wrong.
*/
extern int tswrite_open_connection(int use_tcp,
char *name,
int port,
int quiet,
TS_writer_p *tswriter);
/*
* Open a file for TS output.
*
* This is a convenience wrapper around `tswrite_open`.
*
* - `name` is the name of the file to open, or NULL if stdout should be used
* - `quiet` is true if only error messages should be printed
* - `tswriter` is the new context to use for writing TS output,
* which should be closed using `tswrite_close`.
*
* In all cases (even when using TS_W_STDOUT), the `tswriter` should be
* closed using `tswrite_stdout`.
*
* Returns 0 if all goes well, 1 if something went wrong.
*/
extern int tswrite_open_file(char *name,
int quiet,
TS_writer_p *tswriter);
/*
* Wait for a client to connect and then both write TS data to it and
* listen for command from it. Uses TCP/IP.
*
* - `server_socket` is the socket on which we will listen for a connection
* - `quiet` is true if only error messages should be printed
* - `tswriter` is the new context to use for writing TS output,
* which should be closed using `tswrite_close`.
*
* Returns 0 if all goes well, 1 if something went wrong.
*/
extern int tswrite_wait_for_client(int server_socket,
int quiet,
TS_writer_p *tswriter);
/*
* Set up internal buffering for TS output. This is necessary for UDP
* output, and optional otherwise.
*
* Builds the internal circular buffer and other datastructures, and
* forks a child proces to send data over the socket.
*
* (This is *intended* for use when outputting via a socket, but there
* is nothing actually stopping it from being used to output to a file.
* This is unlikely to be useful for other than testing purposes, however.)
*
* See also `tswrite_start_buffering_from_context`, which uses the `context`
* datastructure that is prepared by `tswrite_process_args`.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
* - `circ_buf_size` is the number of buffer entries (plus one) we would
* like in the underlying circular buffer.
* - `TS_in_packet` is the number of TS packets to allow in each network
* packet.
* - `maxnowait` is the maximum number of packets to send to the target
* host with no wait between packets
* - `waitfor` is the number of microseconds to wait for thereafter
* - `byterate` is the (initial) rate at which we'd like to output our data
* - `use_pcrs` is TRUE if PCRs in the data stream are to be used for
* timing output (the normal case), otherwise the specified byte rate
* will be used directly.
* - `prime_size` is how much to prime the circular buffer output timer
* - `prime_speedup` is the percentage of "normal speed" to use for the priming
* rate. This should normally be set to 100 (i.e., no effect).
* - `pcr_scale` determines how much to "accelerate" each PCR - see the
* notes elsewhere on how this works.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int tswrite_start_buffering(TS_writer_p tswriter,
int circ_buf_size,
int TS_in_packet,
int maxnowait,
int waitfor,
int byterate,
int use_pcrs,
int prime_size,
int prime_speedup,
double pcr_scale);
/*
* Set up internal buffering for TS output. This is necessary for UDP output,
* and optional otherwise.
*
* This alternative takes the `context` datastructure that is prepared
* by `tswrite_process_args`.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
* - `context` contains the necessary information, as given by the user
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int tswrite_start_buffering_from_context(TS_writer_p tswriter,
TS_context_p context);
/*
* Indicate to a TS output context that `input` is to be used as
* command input.
*
* This function may only be used if output is via TCP/IP.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
* - `input` is the socket (or, on Linux/BSD, file descriptor) on which
* to listen for commands.
*
* Note that this should either be ``tswriter->where.socket`` or
* STDIN_FILENO - no other values are currently supported (particularly
* since no attempt is made to close this socket when things are finished,
* which doesn't matter for the given values).
*
* This function:
*
* - makes the socket on which data will be written non-blocking
* (i.e., if the socket is not ready to be written to, it will not
* accept input and block until it can be used, which means that it
* becomes our responsibility to ask if the socket is ready for output)
* - makes tswrite_write "look" on the `input` to see if a (single
* character) command has been given, and if it has, put it into
* the `tswriter` datastructure for use
*
* The command state is set to 'p'ause - i.e., as if the client had sent
* a COMMAND_PAUSE command.
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int tswrite_start_input(TS_writer_p tswriter,
SOCKET input);
/*
* Set/unset "atomic" status - i.e., whether a command may be interrupted
* by the next command.
*
* Most commands (normal play, fast forwards, etc.) should be interrupted
* by a new command. However, some (the skip forwards and backwards commands)
* make sense only if they will always complete. This function allows that
* state to be toggled.
*/
extern void tswrite_set_command_atomic(TS_writer_p tswriter,
int atomic);
/*
* Ask a TS writer if changed input is available.
*
* If the TS writer is enabled for command input, then if the command
* currently being executed has declared itself "atomic" (i.e., not able to be
* interrupted), it returns FALSE, otherwise it returns TRUE if the command
* character has changed.
*/
extern int tswrite_command_changed(TS_writer_p tswriter);
/*
* Close a file or socket opened using `tswrite_open`, and if necessary,
* send the child process used for output buffering an end-of-file
* indicator, and wait for it to finish.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
* - if `quiet` is true, then waiting for the child to exit should
* not be reported on (i.e., only errors should produce output)
*
* Returns 0 if all goes well, 1 if something went wrong.
*/
extern int tswrite_close(TS_writer_p tswriter,
int quiet);
/*
* Wait for a new command after 'p'ausing.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
*
* Returns 0 if all went well, 1 if something went wrong.
*/
extern int wait_for_command(TS_writer_p tswriter);
/*
* Write a Transport Stream packet out via the TS writer.
*
* - `tswriter` is the TS output context returned by `tswrite_open`
* - `packet` is the TS packet
* - if the packets payload_unit_start_indicator is set, then
* `pid` is the PID for this packet, `got_pcr` is TRUE if it
* contains a PCR in its adaptation field, and `pcr` contains
* said PCR. These values are only used when outputting via
* buffered output.
*
* Returns 0 if all goes well, 1 if something went wrong, and EOF if command
* input is enabled (only allowed for TCP/IP output) and the 'q'uit command
* has been given (in which case, no further commands will be read, and no
* more output will be written, by any subsequent calls of this function).
*/
extern int tswrite_write(TS_writer_p tswriter,
byte packet[TS_PACKET_SIZE],
u_int32 pid,
int got_pcr,
u_int64 pcr);
/*
* Write a usage string (to standard output) describing the tuning
* options processed by tswrite_process_args.
*/
extern void tswrite_help_tuning();
/*
* Write a usage string (to standard output) describing the testing
* options processed by tswrite_process_args.
*/
extern void tswrite_help_testing();
/*
* Write a usage string (to standard output) describing the
* debugging options processed by tswrite_process_args.
*/
extern void tswrite_help_debug();
/*
* Report on the values within our argument context.
*
* Also reports on the various global/debug values.
*
* Note that it is up to the caller to ensure that they *use* all
* the values reported on here!
*/
extern void tswrite_report_args(TS_context_p context);
/*
* Various command line switches that are useful for tswrite are really
* only interpretable by tswrite itself. Thus we provide a function that
* will process such switches.
*
* This function extracts appropriate switches from `argv`, and returns it
* altered appropriately.
*
* - `prefix` is a prefix for any error messages - typically the
* short name of the program running.
* - `argc` and `argv` are as passed to `main`. After
* this function has finished, any arguments that it has processed will have
* had their `argv` array elements changed to point to the string
* "<processed>" (this is defined as the string TSWRITE_PROCESSED in the
* tswrite.h header file).
* - values are set in `context` to indicate the user's requests,
* and also any appropriate defaults.
*
* Note that `tswrite_print_usage` may be used to print out a description of
* the switches processed by this function.
*
* Returns 0 if all goes well, 1 if there was an error. Note that not
* specifying an output file or host counts as an error.
*/
extern int tswrite_process_args(char *prefix,
int argc,
char *argv[],
TS_context_p context);
#endif // _tswrite_fns

41
version.h 100644
Wyświetl plik

@ -0,0 +1,41 @@
/*
* The official version number of this set of software.
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _version
#define _version
const char software_version[] = "1.10";
// The following is intended to be output as part of the main help text for
// each program. ``program_name`` is thus the name of the program.
#define REPORT_VERSION(program_name) \
printf(" TS tools version %s, %s built %s %s\n", \
software_version,(program_name), \
__DATE__,__TIME__)
#endif // _version

42
video_defns.h 100644
Wyświetl plik

@ -0,0 +1,42 @@
/*
* Support for generic video streams
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the MPEG TS, PS and ES tools.
*
* The Initial Developer of the Original Code is Amino Communications Ltd.
* Portions created by the Initial Developer are Copyright (C) 2008
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Amino Communications Ltd, Swavesey, Cambridge UK
*
* ***** END LICENSE BLOCK *****
*/
#ifndef _video_defns
#define _video_defns
#include "h222_defns.h"
// Recognised types of video input
// These are convenience names, defined in terms of the H222 values
#define VIDEO_UNKNOWN 0 // which is a reserved value
#define VIDEO_H262 MPEG2_VIDEO_STREAM_TYPE
#define VIDEO_H264 AVC_VIDEO_STREAM_TYPE
#define VIDEO_AVS AVS_VIDEO_STREAM_TYPE
#define VIDEO_MPEG4_PART2 MPEG4_PART2_VIDEO_STREAM_TYPE
#endif // _video_defns