kopia lustrzana https://github.com/F5OEO/tstools
Initial import to berlios.de
--HG-- extra : convert_revision : svn%3Aeff31bef-be4a-0410-a8fe-e47997df2690/trunk%401issue20
commit
70e9b431a7
|
@ -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
|
|
@ -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
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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
|
||||
"main" 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
|
||||
<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
|
||||
</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 -> 32ms
|
||||
44.1kHz -> approx 34.83ms
|
||||
32 kHz -> 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 ("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.
|
||||
</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 -- <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
|
||||
</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 "License"); 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 "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.</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>
|
|
@ -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 *****
|
|
@ -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 < 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
|
||||
</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 "License"); 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 "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.</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>
|
|
@ -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 *****
|
||||
|
|
@ -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()
|
|
@ -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 }
|
|
@ -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
|
|
@ -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 "License"); 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 "AS IS" 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"># 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> <font color="#008b8b">get_next_access_unit</font>(context):<br>
|
||||
<span style="background-color: #f2f2f2"><font color="#ff00ff">"""Retrieve the next access unit from the file described by `context`.</font></span><br>
|
||||
<span style="background-color: #f2f2f2"><font color="#ff00ff"> """</font></span><br>
|
||||
access_unit = build_access_unit()<br>
|
||||
<font color="#a52a2a"><b>if</b></font> context.pending_nal: <font color="#0000ff"># i.e., we already had a NAL to start this unit</font><br>
|
||||
access_unit.append(context.pending_nal,TRUE,context.pending_list)<br>
|
||||
context.pending_nal = NULL<br>
|
||||
context.pending_list.reset(FALSE)<br>
|
||||
<br>
|
||||
<font color="#a52a2a"><b>while</b></font> <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span>:<br>
|
||||
<font color="#a52a2a"><b>try</b></font>:<br>
|
||||
nal = context.find_next_NAL_unit()<br>
|
||||
<font color="#a52a2a"><b>except</b></font> EOF:<br>
|
||||
context.no_more_data = TRUE; <font color="#0000ff"># prevent future reads on this stream</font><br>
|
||||
<font color="#a52a2a"><b>break</b></font><br>
|
||||
<font color="#a52a2a"><b>except</b></font> BrokenNALUnit:<br>
|
||||
WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">"!!! 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">"</font></span>)<br>
|
||||
access_unit.ignored_broken_NAL_units += <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span><br>
|
||||
<font color="#a52a2a"><b>continue</b></font><br>
|
||||
<br>
|
||||
<font color="#a52a2a"><b>if</b></font> nal.is_slice():<br>
|
||||
<font color="#a52a2a"><b>if</b></font> <font color="#a52a2a"><b>not</b></font> access_unit.started_primary_picture:<br>
|
||||
<font color="#0000ff"># We're in a new access unit, but we haven't had a slice</font><br>
|
||||
<font color="#0000ff"># yet, so we can be lazy and assume that this must be the</font><br>
|
||||
<font color="#0000ff"># first slice</font><br>
|
||||
nal.start_reason = <span style="background-color: #f2f2f2"><font color="#ff00ff">"First slice of new access unit"</font></span><br>
|
||||
access_unit.append(nal,TRUE,context.pending_list)<br>
|
||||
context.pending_list.reset(FALSE)<br>
|
||||
context.remember_earlier_primary_start(nal)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.is_first_VCL_NAL(context.earlier_primary_start):<br>
|
||||
<font color="#0000ff"># Regardless of what we determine next, we need to remember</font><br>
|
||||
<font color="#0000ff"># that the NAL started (what may later be the previous) access</font><br>
|
||||
<font color="#0000ff"># unit</font><br>
|
||||
context.remember_earlier_primary_start(nal)<br>
|
||||
<font color="#a52a2a"><b>if</b></font> access_unit.started_primary_picture:<br>
|
||||
<font color="#0000ff"># We were already in an access unit with a primary</font><br>
|
||||
<font color="#0000ff"># picture, so this NAL unit must start a new access unit.</font><br>
|
||||
<font color="#0000ff"># Remember it for next time, and return the access unit so</font><br>
|
||||
<font color="#0000ff"># far.</font><br>
|
||||
context.pending_nal = nal<br>
|
||||
<font color="#a52a2a"><b>break</b></font>; <font color="#0000ff"># Ready to return the access unit</font><br>
|
||||
<font color="#a52a2a"><b>else</b></font>:<br>
|
||||
<font color="#0000ff"># This access unit was waiting for its primary picture</font><br>
|
||||
access_unit.append(nal,TRUE,context.pending_list)<br>
|
||||
context.pending_list.reset(FALSE)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> <font color="#a52a2a"><b>not</b></font> access_unit.started_primary_picture:<br>
|
||||
<font color="#0000ff"># But this is not a NAL unit that may start a new</font><br>
|
||||
<font color="#0000ff"># access unit. So what should we do? Ignore it?</font><br>
|
||||
<font color="#a52a2a"><b>if</b></font> <font color="#a52a2a"><b>not</b></font> quiet:<br>
|
||||
WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">"!!! Ignoring VCL NAL that cannot start a new"</font></span><br>
|
||||
<span style="background-color: #f2f2f2"><font color="#ff00ff">" primary picture: "</font></span><br>
|
||||
nal.report(stderr)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal_is_redundant(nal):<br>
|
||||
<font color="#0000ff"># printf(" ignoring redundant NAL unit\n")</font><br>
|
||||
<font color="#a52a2a"><b>pass</b></font><br>
|
||||
<font color="#a52a2a"><b>else</b></font>:<br>
|
||||
<font color="#0000ff"># We're part of the same access unit, but not special</font><br>
|
||||
access_unit.append(nal,FALSE,context.pending_list)<br>
|
||||
context.pending_list.reset(FALSE)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.nal_unit_type == NAL_ACCESS_UNIT_DELIM:<br>
|
||||
<font color="#0000ff"># An access unit delimiter always starts a new access unit</font><br>
|
||||
<font color="#a52a2a"><b>if</b></font> access_unit.started_primary_picture:<br>
|
||||
context.pending_list.append(nal)<br>
|
||||
<font color="#a52a2a"><b>break</b></font> <font color="#0000ff"># Ready to return the "previous" access unit</font><br>
|
||||
<font color="#a52a2a"><b>else</b></font>:<br>
|
||||
<font color="#0000ff"># The current access unit doesn't yet have any VCL NALs</font><br>
|
||||
<font color="#a52a2a"><b>if</b></font> context.pending_list.length > <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
|
||||
WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">"!!! Ignoring items after last VCL NAL and"</font></span><br>
|
||||
<span style="background-color: #f2f2f2"><font color="#ff00ff">" 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">"</font></span>)<br>
|
||||
context.pending_list.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">" "</font></span>,NULL,)<br>
|
||||
context.pending_list.reset(TRUE)<br>
|
||||
<font color="#a52a2a"><b>if</b></font> access_unit.nal_units.length > <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
|
||||
WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">"!!! 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">"</font></span>)<br>
|
||||
access_unit.nal_units.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">" "</font></span>,NULL,)<br>
|
||||
access_unit.nal_units.reset(TRUE)<br>
|
||||
access_unit.append(nal,FALSE,NULL)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.nal_unit_type == NAL_SEI:<br>
|
||||
<font color="#0000ff"># SEI units always precede the primary coded picture</font><br>
|
||||
<font color="#0000ff"># - so they also implicitly end any access unit that has already</font><br>
|
||||
<font color="#0000ff"># started its primary picture</font><br>
|
||||
<font color="#a52a2a"><b>if</b></font> access_unit.started_primary_picture:<br>
|
||||
context.pending_list.append(nal)<br>
|
||||
<font color="#a52a2a"><b>break</b></font> <font color="#0000ff"># Ready to return the "previous" access unit</font><br>
|
||||
<font color="#a52a2a"><b>else</b></font>:<br>
|
||||
context.pending_list.append(nal)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.nal_unit_type <font color="#a52a2a"><b>in</b></font> [NAL_SEQ_PARAM_SET, NAL_PIC_PARAM_SET,<br>
|
||||
<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>
|
||||
<font color="#0000ff"># These start a new access unit *if* they come after the last VCL</font><br>
|
||||
<font color="#0000ff"># NAL of an access unit. But we can only *tell* that they are</font><br>
|
||||
<font color="#0000ff"># after the last VCL NAL of an access unit when we start the next</font><br>
|
||||
<font color="#0000ff"># access unit - so we need to hold them in hand until we know that</font><br>
|
||||
<font color="#0000ff"># we need them. (i.e., they'll get added to an access unit just</font><br>
|
||||
<font color="#0000ff"># before the next "more determined" NAL unit we add to an access</font><br>
|
||||
<font color="#0000ff"># unit)</font><br>
|
||||
context.pending_list.append(nal)<br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.nal_unit_type == NAL_END_OF_SEQ:<br>
|
||||
<font color="#a52a2a"><b>if</b></font> context.pending_list.length > <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
|
||||
WARNING(<span style="background-color: #f2f2f2"><font color="#ff00ff">"!!! Ignoring items after last VCL NAL and"</font></span><br>
|
||||
<span style="background-color: #f2f2f2"><font color="#ff00ff">" 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">"</font></span>)<br>
|
||||
context.pending_list.report(stderr,<span style="background-color: #f2f2f2"><font color="#ff00ff">" "</font></span>,NULL,)<br>
|
||||
context.pending_list.reset(TRUE)<br>
|
||||
<font color="#0000ff"># And remember this as the End of Sequence marker</font><br>
|
||||
context.end_of_sequence = nal<br>
|
||||
<font color="#a52a2a"><b>break</b></font><br>
|
||||
<font color="#a52a2a"><b>elif</b></font> nal.nal_unit_type == NAL_END_OF_STREAM:<br>
|
||||
<font color="#0000ff"># And remember this as the End of Stream marker</font><br>
|
||||
context.end_of_stream = nal<br>
|
||||
<font color="#0000ff"># Which means there's no point in reading more from this stream</font><br>
|
||||
<font color="#0000ff"># (setting no_more_data like this means that *next* time this</font><br>
|
||||
<font color="#0000ff"># function is called, it will return EOF)</font><br>
|
||||
context.no_more_data = TRUE<br>
|
||||
<font color="#0000ff"># And we're done</font><br>
|
||||
<font color="#a52a2a"><b>break</b></font><br>
|
||||
<font color="#a52a2a"><b>else</b></font>:<br>
|
||||
<font color="#0000ff"># It's not a slice, or an access unit delimiter, or an</font><br>
|
||||
<font color="#0000ff"># end of sequence or stream, or a sequence or picture</font><br>
|
||||
<font color="#0000ff"># parameter set, or various other odds and ends, so it</font><br>
|
||||
<font color="#0000ff"># looks like we can ignore it.</font><br>
|
||||
<font color="#a52a2a"><b>pass</b></font><br>
|
||||
<br>
|
||||
<font color="#0000ff"># Check for an immediate "end of file with no data"</font><br>
|
||||
<font color="#0000ff"># - i.e., we read EOF or end of stream, and there was nothing</font><br>
|
||||
<font color="#0000ff"># between the last access unit and such reading</font><br>
|
||||
<font color="#a52a2a"><b>if</b></font> context.no_more_data <font color="#a52a2a"><b>and</b></font> access_unit.nal_units.length == <span style="background-color: #f2f2f2"><font color="#ff00ff">0</font></span>:<br>
|
||||
<font color="#a52a2a"><b>raise</b></font> EOF<br>
|
||||
<br>
|
||||
<font color="#0000ff"># Otherwise, finish off and return the access unit we have in hand</font><br>
|
||||
access_unit.end(context,show_details)<br>
|
||||
<br>
|
||||
<font color="#0000ff"># Remember to count it</font><br>
|
||||
context.access_unit_index += <span style="background-color: #f2f2f2"><font color="#ff00ff">1</font></span><br>
|
||||
<br>
|
||||
<font color="#a52a2a"><b>return</b></font> access_unit<br>
|
||||
</font></body>
|
||||
</html>
|
|
@ -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> </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> </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 "License"); 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 "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.</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>
|
|
@ -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 *****
|
|
@ -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 "dictionaries" 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 "reversing"
|
||||
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 "over" 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,&acontext);
|
||||
free_access_unit_context(&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,&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,&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 "frame".</p>
|
||||
<p>Regardless, the same function is used to free the resultant datastructure:</p>
|
||||
<pre class="literal-block">
|
||||
free_access_unit(&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,&context);
|
||||
free_h262_context(&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,&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,&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 "frame".</p>
|
||||
<p>Regardless, the same function is used to free the resultant datastructure:</p>
|
||||
<pre class="literal-block">
|
||||
free_h262_picture(&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->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,&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,&nal);
|
||||
free_nal_unit(&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,&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 "bare" ES data:</p>
|
||||
<pre class="literal-block">
|
||||
err = open_elementary_stream(filename,&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,&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(&es);
|
||||
</pre>
|
||||
<p>(this will not "close" 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,&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,&reader);
|
||||
err = open_PES_reader_for_TS(filename,program_number,
|
||||
give_info,give_warnings,&reader);
|
||||
</pre>
|
||||
<p>(the latter must also be used if one wants a different program number than the
|
||||
"first found" in TS data). The function:</p>
|
||||
<pre class="literal-block">
|
||||
err = determine_if_TS_file(filedesc,&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,&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,
|
||||
&reader);
|
||||
err = build_PS_PES_reader(psreader,give_info,give_warnings,&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(&reader);
|
||||
err = close_PES_reader(&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 "normal" 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 "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:</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 "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).</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 "bare" 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 "highest level" 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 "black box" manner. A
|
||||
reverse data context must be built:</p>
|
||||
<pre class="literal-block">
|
||||
err = build_reverse_data(&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 "current" 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(&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);
|
||||
</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,
|
||||
&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);
|
||||
</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 "License"); 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 "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.</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>
|
|
@ -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 *****
|
|
@ -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 "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.</p>
|
||||
<p>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).</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 "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.</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 "read and remember the GOP" 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 "properly" 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 "relevant" sequence headers</p>
|
||||
</li>
|
||||
<li><p class="first">Remember (as now) which sequence header that "corresponds" 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 "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.</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 & 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
|
||||
</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 & 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
|
||||
</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">@@@</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
|
||||
"notice" 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
|
||||
"truly" 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 "should not happen"???</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 "picture" or "access unit"
|
||||
that <em>should</em> say "frame" actually say "frame".</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 "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.</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 "License"); 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 "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.</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>
|
|
@ -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 *****
|
Plik diff jest za duży
Load Diff
|
@ -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 *****
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
||||
}
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
Plik diff jest za duży
Load Diff
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Ładowanie…
Reference in New Issue