diff options
Diffstat (limited to 'gst/rtp')
164 files changed, 41846 insertions, 0 deletions
diff --git a/gst/rtp/Makefile.am b/gst/rtp/Makefile.am new file mode 100755 index 0000000..441c52f --- /dev/null +++ b/gst/rtp/Makefile.am @@ -0,0 +1,182 @@ +plugin_LTLIBRARIES = libgstrtp.la + +libgstrtp_la_SOURCES = \ + dboolhuff.c \ + fnv1hash.c \ + gstrtp.c \ + gstrtpchannels.c \ + gstrtpac3depay.c \ + gstrtpac3pay.c \ + gstrtpbvdepay.c \ + gstrtpbvpay.c \ + gstrtpceltdepay.c \ + gstrtpceltpay.c \ + gstrtpdvdepay.c \ + gstrtpdvpay.c \ + gstrtpgstdepay.c \ + gstrtpgstpay.c \ + gstrtpilbcdepay.c \ + gstrtpilbcpay.c \ + gstrtpmpadepay.c \ + gstrtpmpapay.c \ + gstrtpmparobustdepay.c \ + gstrtpmpvdepay.c \ + gstrtpmpvpay.c \ + gstrtppcmadepay.c \ + gstrtppcmudepay.c \ + gstrtppcmupay.c \ + gstrtppcmapay.c \ + gstrtpg722depay.c \ + gstrtpg722pay.c \ + gstrtpg723depay.c \ + gstrtpg723pay.c \ + gstrtpg726pay.c \ + gstrtpg726depay.c \ + gstrtpg729pay.c \ + gstrtpg729depay.c \ + gstrtpgsmdepay.c \ + gstrtpgsmpay.c \ + gstrtpamrdepay.c \ + gstrtpamrpay.c \ + gstrtph263pdepay.c \ + gstrtph263ppay.c \ + gstrtph263depay.c \ + gstrtph263pay.c \ + gstrtph264depay.c \ + gstrtph264pay.c \ + gstrtpj2kdepay.c \ + gstrtpj2kpay.c \ + gstrtpjpegdepay.c \ + gstrtpjpegpay.c \ + gstrtpL16depay.c \ + gstrtpL16pay.c \ + gstrtpL24depay.c \ + gstrtpL24pay.c \ + gstasteriskh263.c \ + gstrtpmp1sdepay.c \ + gstrtpmp2tdepay.c \ + gstrtpmp2tpay.c \ + gstrtpmp4vdepay.c \ + gstrtpmp4vpay.c \ + gstrtpmp4gdepay.c \ + gstrtpmp4gpay.c \ + gstrtpmp4adepay.c \ + gstrtpmp4apay.c \ + gstrtpqcelpdepay.c \ + gstrtpqdmdepay.c \ + gstrtpsbcdepay.c \ + gstrtpsbcpay.c \ + gstrtpsirenpay.c \ + gstrtpsirendepay.c \ + gstrtpspeexdepay.c \ + gstrtpspeexpay.c \ + gstrtpsv3vdepay.c \ + gstrtptheoradepay.c \ + gstrtptheorapay.c \ + gstrtpvorbisdepay.c \ + gstrtpvorbispay.c \ + gstrtpvp8depay.c \ + gstrtpvp8pay.c \ + gstrtpvrawdepay.c \ + gstrtpvrawpay.c \ + gstrtpstreampay.c \ + gstrtpstreamdepay.c + +libgstrtp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) -Dvp8_norm=gst_rtpvp8_vp8_norm \ + -Dvp8dx_start_decode=gst_rtpvp8_vp8dx_start_decode \ + -Dvp8dx_bool_decoder_fill=gst_rtpvp8_vp8dx_bool_decoder_fill + +libgstrtp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstaudio-@GST_API_VERSION@ \ + -lgstvideo-@GST_API_VERSION@ \ + -lgsttag-@GST_API_VERSION@ \ + -lgstrtp-@GST_API_VERSION@ \ + -lgstpbutils-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) \ + $(LIBM) +libgstrtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstrtp_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) + +noinst_HEADERS = \ + dboolhuff.h \ + fnv1hash.h \ + gstrtpchannels.h \ + gstrtpL16depay.h \ + gstrtpL16pay.h \ + gstrtpL24depay.h \ + gstrtpL24pay.h \ + gstrtpac3depay.h \ + gstrtpac3pay.h \ + gstrtpbvdepay.h \ + gstrtpbvpay.h \ + gstrtpceltpay.h \ + gstrtpceltdepay.h \ + gstrtpdvdepay.h \ + gstrtpdvpay.h \ + gstrtpamrdepay.h \ + gstrtpamrpay.h \ + gstrtpgstdepay.h \ + gstrtpgstpay.h \ + gstrtpilbcdepay.h \ + gstrtpilbcpay.h \ + gstrtppcmadepay.h \ + gstrtppcmudepay.h \ + gstrtppcmupay.h \ + gstrtppcmapay.h \ + gstrtpg722depay.h \ + gstrtpg722pay.h \ + gstrtpg723depay.h\ + gstrtpg723pay.h \ + gstrtpg726depay.h \ + gstrtpg726pay.h \ + gstrtpg729depay.h \ + gstrtpg729pay.h \ + gstrtpgsmdepay.h \ + gstrtpgsmpay.h \ + gstrtpmpadepay.h \ + gstrtpmparobustdepay.h \ + gstrtpmpapay.h \ + gstrtpmpvdepay.h \ + gstrtpmpvpay.h \ + gstrtph263pdepay.h \ + gstrtph263ppay.h \ + gstrtph263depay.h \ + gstrtph263pay.h \ + gstrtph264depay.h \ + gstrtph264pay.h \ + gstrtpj2kdepay.h \ + gstrtpj2kpay.h \ + gstrtpjpegdepay.h \ + gstrtpjpegpay.h \ + gstrtpmp1sdepay.h \ + gstrtpmp2tdepay.h \ + gstrtpmp2tpay.h \ + gstrtpmp4vdepay.h \ + gstrtpmp4vpay.h \ + gstrtpmp4gdepay.h \ + gstrtpmp4gpay.h \ + gstrtpmp4adepay.h \ + gstrtpmp4apay.h \ + gstasteriskh263.h \ + gstrtpqcelpdepay.h \ + gstrtpqdmdepay.h \ + gstrtpsbcdepay.h \ + gstrtpsbcpay.h \ + gstrtpsirenpay.h \ + gstrtpsirendepay.h \ + gstrtpspeexdepay.h \ + gstrtpspeexpay.h \ + gstrtpsv3vdepay.h \ + gstrtptheoradepay.h \ + gstrtptheorapay.h \ + gstrtpvorbisdepay.h \ + gstrtpvorbispay.h \ + gstrtpvp8depay.h \ + gstrtpvp8pay.h \ + gstrtpvrawdepay.h \ + gstrtpvrawpay.h \ + gstrtpstreampay.h \ + gstrtpstreamdepay.h + +EXTRA_DIST = dboolhuff.LICENSE diff --git a/gst/rtp/Makefile.in b/gst/rtp/Makefile.in new file mode 100755 index 0000000..a1ae6ff --- /dev/null +++ b/gst/rtp/Makefile.in @@ -0,0 +1,1743 @@ +# Makefile.in generated by automake 1.14.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2013 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)' +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = gst/rtp +DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \ + $(top_srcdir)/depcomp $(noinst_HEADERS) README TODO +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/common/m4/as-ac-expand.m4 \ + $(top_srcdir)/common/m4/as-auto-alt.m4 \ + $(top_srcdir)/common/m4/as-compiler-flag.m4 \ + $(top_srcdir)/common/m4/as-gcc-inline-assembly.m4 \ + $(top_srcdir)/common/m4/as-libtool.m4 \ + $(top_srcdir)/common/m4/as-version.m4 \ + $(top_srcdir)/common/m4/ax_create_stdint_h.m4 \ + $(top_srcdir)/common/m4/gst-arch.m4 \ + $(top_srcdir)/common/m4/gst-args.m4 \ + $(top_srcdir)/common/m4/gst-check.m4 \ + $(top_srcdir)/common/m4/gst-default.m4 \ + $(top_srcdir)/common/m4/gst-dowhile.m4 \ + $(top_srcdir)/common/m4/gst-error.m4 \ + $(top_srcdir)/common/m4/gst-feature.m4 \ + $(top_srcdir)/common/m4/gst-gettext.m4 \ + $(top_srcdir)/common/m4/gst-glib2.m4 \ + $(top_srcdir)/common/m4/gst-package-release-datetime.m4 \ + $(top_srcdir)/common/m4/gst-platform.m4 \ + $(top_srcdir)/common/m4/gst-plugin-docs.m4 \ + $(top_srcdir)/common/m4/gst-plugindir.m4 \ + $(top_srcdir)/common/m4/gst-x11.m4 \ + $(top_srcdir)/common/m4/gst.m4 \ + $(top_srcdir)/common/m4/gtk-doc.m4 \ + $(top_srcdir)/common/m4/orc.m4 $(top_srcdir)/common/m4/pkg.m4 \ + $(top_srcdir)/m4/aalib.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gst-fionread.m4 $(top_srcdir)/m4/iconv.m4 \ + $(top_srcdir)/m4/intlmacosx.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(plugindir)" +LTLIBRARIES = $(plugin_LTLIBRARIES) +am__DEPENDENCIES_1 = +libgstrtp_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) +am_libgstrtp_la_OBJECTS = libgstrtp_la-dboolhuff.lo \ + libgstrtp_la-fnv1hash.lo libgstrtp_la-gstrtp.lo \ + libgstrtp_la-gstrtpchannels.lo libgstrtp_la-gstrtpac3depay.lo \ + libgstrtp_la-gstrtpac3pay.lo libgstrtp_la-gstrtpbvdepay.lo \ + libgstrtp_la-gstrtpbvpay.lo libgstrtp_la-gstrtpceltdepay.lo \ + libgstrtp_la-gstrtpceltpay.lo libgstrtp_la-gstrtpdvdepay.lo \ + libgstrtp_la-gstrtpdvpay.lo libgstrtp_la-gstrtpgstdepay.lo \ + libgstrtp_la-gstrtpgstpay.lo libgstrtp_la-gstrtpilbcdepay.lo \ + libgstrtp_la-gstrtpilbcpay.lo libgstrtp_la-gstrtpmpadepay.lo \ + libgstrtp_la-gstrtpmpapay.lo \ + libgstrtp_la-gstrtpmparobustdepay.lo \ + libgstrtp_la-gstrtpmpvdepay.lo libgstrtp_la-gstrtpmpvpay.lo \ + libgstrtp_la-gstrtppcmadepay.lo \ + libgstrtp_la-gstrtppcmudepay.lo libgstrtp_la-gstrtppcmupay.lo \ + libgstrtp_la-gstrtppcmapay.lo libgstrtp_la-gstrtpg722depay.lo \ + libgstrtp_la-gstrtpg722pay.lo libgstrtp_la-gstrtpg723depay.lo \ + libgstrtp_la-gstrtpg723pay.lo libgstrtp_la-gstrtpg726pay.lo \ + libgstrtp_la-gstrtpg726depay.lo libgstrtp_la-gstrtpg729pay.lo \ + libgstrtp_la-gstrtpg729depay.lo libgstrtp_la-gstrtpgsmdepay.lo \ + libgstrtp_la-gstrtpgsmpay.lo libgstrtp_la-gstrtpamrdepay.lo \ + libgstrtp_la-gstrtpamrpay.lo libgstrtp_la-gstrtph263pdepay.lo \ + libgstrtp_la-gstrtph263ppay.lo libgstrtp_la-gstrtph263depay.lo \ + libgstrtp_la-gstrtph263pay.lo libgstrtp_la-gstrtph264depay.lo \ + libgstrtp_la-gstrtph264pay.lo libgstrtp_la-gstrtpj2kdepay.lo \ + libgstrtp_la-gstrtpj2kpay.lo libgstrtp_la-gstrtpjpegdepay.lo \ + libgstrtp_la-gstrtpjpegpay.lo libgstrtp_la-gstrtpL16depay.lo \ + libgstrtp_la-gstrtpL16pay.lo libgstrtp_la-gstrtpL24depay.lo \ + libgstrtp_la-gstrtpL24pay.lo libgstrtp_la-gstasteriskh263.lo \ + libgstrtp_la-gstrtpmp1sdepay.lo \ + libgstrtp_la-gstrtpmp2tdepay.lo libgstrtp_la-gstrtpmp2tpay.lo \ + libgstrtp_la-gstrtpmp4vdepay.lo libgstrtp_la-gstrtpmp4vpay.lo \ + libgstrtp_la-gstrtpmp4gdepay.lo libgstrtp_la-gstrtpmp4gpay.lo \ + libgstrtp_la-gstrtpmp4adepay.lo libgstrtp_la-gstrtpmp4apay.lo \ + libgstrtp_la-gstrtpqcelpdepay.lo \ + libgstrtp_la-gstrtpqdmdepay.lo libgstrtp_la-gstrtpsbcdepay.lo \ + libgstrtp_la-gstrtpsbcpay.lo libgstrtp_la-gstrtpsirenpay.lo \ + libgstrtp_la-gstrtpsirendepay.lo \ + libgstrtp_la-gstrtpspeexdepay.lo \ + libgstrtp_la-gstrtpspeexpay.lo libgstrtp_la-gstrtpsv3vdepay.lo \ + libgstrtp_la-gstrtptheoradepay.lo \ + libgstrtp_la-gstrtptheorapay.lo \ + libgstrtp_la-gstrtpvorbisdepay.lo \ + libgstrtp_la-gstrtpvorbispay.lo libgstrtp_la-gstrtpvp8depay.lo \ + libgstrtp_la-gstrtpvp8pay.lo libgstrtp_la-gstrtpvrawdepay.lo \ + libgstrtp_la-gstrtpvrawpay.lo libgstrtp_la-gstrtpstreampay.lo \ + libgstrtp_la-gstrtpstreamdepay.lo +libgstrtp_la_OBJECTS = $(am_libgstrtp_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libgstrtp_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link \ + $(CCLD) $(libgstrtp_la_CFLAGS) $(CFLAGS) \ + $(libgstrtp_la_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libgstrtp_la_SOURCES) +DIST_SOURCES = $(libgstrtp_la_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +AALIB_CFLAGS = @AALIB_CFLAGS@ +AALIB_CONFIG = @AALIB_CONFIG@ +AALIB_LIBS = @AALIB_LIBS@ +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AS = @AS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BZ2_LIBS = @BZ2_LIBS@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_AUDIOSINK = @DEFAULT_AUDIOSINK@ +DEFAULT_AUDIOSRC = @DEFAULT_AUDIOSRC@ +DEFAULT_VIDEOSINK = @DEFAULT_VIDEOSINK@ +DEFAULT_VIDEOSRC = @DEFAULT_VIDEOSRC@ +DEFAULT_VISUALIZER = @DEFAULT_VISUALIZER@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DEPRECATED_CFLAGS = @DEPRECATED_CFLAGS@ +DIRECTSOUND_CFLAGS = @DIRECTSOUND_CFLAGS@ +DIRECTSOUND_LDFLAGS = @DIRECTSOUND_LDFLAGS@ +DIRECTSOUND_LIBS = @DIRECTSOUND_LIBS@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +DV1394_CFLAGS = @DV1394_CFLAGS@ +DV1394_LIBS = @DV1394_LIBS@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +ERROR_CFLAGS = @ERROR_CFLAGS@ +ERROR_CXXFLAGS = @ERROR_CXXFLAGS@ +ERROR_OBJCFLAGS = @ERROR_OBJCFLAGS@ +EXEEXT = @EXEEXT@ +FFLAGS = @FFLAGS@ +FGREP = @FGREP@ +FLAC_CFLAGS = @FLAC_CFLAGS@ +FLAC_LIBS = @FLAC_LIBS@ +GCOV = @GCOV@ +GCOV_CFLAGS = @GCOV_CFLAGS@ +GCOV_LIBS = @GCOV_LIBS@ +GDK_PIXBUF_CFLAGS = @GDK_PIXBUF_CFLAGS@ +GDK_PIXBUF_LIBS = @GDK_PIXBUF_LIBS@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GETTEXT_PACKAGE = @GETTEXT_PACKAGE@ +GIO_CFLAGS = @GIO_CFLAGS@ +GIO_LDFLAGS = @GIO_LDFLAGS@ +GIO_LIBS = @GIO_LIBS@ +GLIB_CFLAGS = @GLIB_CFLAGS@ +GLIB_EXTRA_CFLAGS = @GLIB_EXTRA_CFLAGS@ +GLIB_GENMARSHAL = @GLIB_GENMARSHAL@ +GLIB_LIBS = @GLIB_LIBS@ +GLIB_MKENUMS = @GLIB_MKENUMS@ +GLIB_PREFIX = @GLIB_PREFIX@ +GLIB_REQ = @GLIB_REQ@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GREP = @GREP@ +GSTPB_PLUGINS_DIR = @GSTPB_PLUGINS_DIR@ +GSTPB_PREFIX = @GSTPB_PREFIX@ +GST_AGE = @GST_AGE@ +GST_ALL_LDFLAGS = @GST_ALL_LDFLAGS@ +GST_API_VERSION = @GST_API_VERSION@ +GST_BASE_CFLAGS = @GST_BASE_CFLAGS@ +GST_BASE_LIBS = @GST_BASE_LIBS@ +GST_CFLAGS = @GST_CFLAGS@ +GST_CHECK_CFLAGS = @GST_CHECK_CFLAGS@ +GST_CHECK_LIBS = @GST_CHECK_LIBS@ +GST_CONTROLLER_CFLAGS = @GST_CONTROLLER_CFLAGS@ +GST_CONTROLLER_LIBS = @GST_CONTROLLER_LIBS@ +GST_CURRENT = @GST_CURRENT@ +GST_CXXFLAGS = @GST_CXXFLAGS@ +GST_LEVEL_DEFAULT = @GST_LEVEL_DEFAULT@ +GST_LIBS = @GST_LIBS@ +GST_LIBVERSION = @GST_LIBVERSION@ +GST_LICENSE = @GST_LICENSE@ +GST_LT_LDFLAGS = @GST_LT_LDFLAGS@ +GST_NET_CFLAGS = @GST_NET_CFLAGS@ +GST_NET_LIBS = @GST_NET_LIBS@ +GST_OBJCFLAGS = @GST_OBJCFLAGS@ +GST_OPTION_CFLAGS = @GST_OPTION_CFLAGS@ +GST_OPTION_CXXFLAGS = @GST_OPTION_CXXFLAGS@ +GST_OPTION_OBJCFLAGS = @GST_OPTION_OBJCFLAGS@ +GST_PACKAGE_NAME = @GST_PACKAGE_NAME@ +GST_PACKAGE_ORIGIN = @GST_PACKAGE_ORIGIN@ +GST_PLUGINS_ALL = @GST_PLUGINS_ALL@ +GST_PLUGINS_BASE_CFLAGS = @GST_PLUGINS_BASE_CFLAGS@ +GST_PLUGINS_BASE_DIR = @GST_PLUGINS_BASE_DIR@ +GST_PLUGINS_BASE_LIBS = @GST_PLUGINS_BASE_LIBS@ +GST_PLUGINS_DIR = @GST_PLUGINS_DIR@ +GST_PLUGINS_NONPORTED = @GST_PLUGINS_NONPORTED@ +GST_PLUGINS_SELECTED = @GST_PLUGINS_SELECTED@ +GST_PLUGIN_LDFLAGS = @GST_PLUGIN_LDFLAGS@ +GST_PLUGIN_LIBTOOLFLAGS = @GST_PLUGIN_LIBTOOLFLAGS@ +GST_PREFIX = @GST_PREFIX@ +GST_REVISION = @GST_REVISION@ +GST_TOOLS_DIR = @GST_TOOLS_DIR@ +GTKDOC_CHECK = @GTKDOC_CHECK@ +GTKDOC_DEPS_CFLAGS = @GTKDOC_DEPS_CFLAGS@ +GTKDOC_DEPS_LIBS = @GTKDOC_DEPS_LIBS@ +GTKDOC_MKPDF = @GTKDOC_MKPDF@ +GTKDOC_REBASE = @GTKDOC_REBASE@ +GTK_CFLAGS = @GTK_CFLAGS@ +GTK_LIBS = @GTK_LIBS@ +GTK_X11_CFLAGS = @GTK_X11_CFLAGS@ +GTK_X11_LIBS = @GTK_X11_LIBS@ +GUDEV_CFLAGS = @GUDEV_CFLAGS@ +GUDEV_LIBS = @GUDEV_LIBS@ +HAVE_AVC1394 = @HAVE_AVC1394@ +HAVE_CXX = @HAVE_CXX@ +HAVE_DIRECTSOUND = @HAVE_DIRECTSOUND@ +HAVE_ROM1394 = @HAVE_ROM1394@ +HAVE_SPEEX = @HAVE_SPEEX@ +HAVE_X = @HAVE_X@ +HAVE_XSHM = @HAVE_XSHM@ +HAVE_ZLIB = @HAVE_ZLIB@ +HTML_DIR = @HTML_DIR@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +JACK_0_120_1_CFLAGS = @JACK_0_120_1_CFLAGS@ +JACK_0_120_1_LIBS = @JACK_0_120_1_LIBS@ +JACK_1_9_7_CFLAGS = @JACK_1_9_7_CFLAGS@ +JACK_1_9_7_LIBS = @JACK_1_9_7_LIBS@ +JACK_CFLAGS = @JACK_CFLAGS@ +JACK_LIBS = @JACK_LIBS@ +JPEG_LIBS = @JPEG_LIBS@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBCACA_CFLAGS = @LIBCACA_CFLAGS@ +LIBCACA_LIBS = @LIBCACA_LIBS@ +LIBDV_CFLAGS = @LIBDV_CFLAGS@ +LIBDV_LIBS = @LIBDV_LIBS@ +LIBICONV = @LIBICONV@ +LIBIEC61883_CFLAGS = @LIBIEC61883_CFLAGS@ +LIBIEC61883_LIBS = @LIBIEC61883_LIBS@ +LIBINTL = @LIBINTL@ +LIBM = @LIBM@ +LIBOBJS = @LIBOBJS@ +LIBPNG_CFLAGS = @LIBPNG_CFLAGS@ +LIBPNG_LIBS = @LIBPNG_LIBS@ +LIBRT = @LIBRT@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIBV4L2_CFLAGS = @LIBV4L2_CFLAGS@ +LIBV4L2_LIBS = @LIBV4L2_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LOCALEDIR = @LOCALEDIR@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJC = @OBJC@ +OBJCDEPMODE = @OBJCDEPMODE@ +OBJCFLAGS = @OBJCFLAGS@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +ORCC = @ORCC@ +ORCC_FLAGS = @ORCC_FLAGS@ +ORC_CFLAGS = @ORC_CFLAGS@ +ORC_LIBS = @ORC_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PACKAGE_VERSION_MAJOR = @PACKAGE_VERSION_MAJOR@ +PACKAGE_VERSION_MICRO = @PACKAGE_VERSION_MICRO@ +PACKAGE_VERSION_MINOR = @PACKAGE_VERSION_MINOR@ +PACKAGE_VERSION_NANO = @PACKAGE_VERSION_NANO@ +PACKAGE_VERSION_RELEASE = @PACKAGE_VERSION_RELEASE@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PKG_CONFIG = @PKG_CONFIG@ +PLUGINDIR = @PLUGINDIR@ +POSUB = @POSUB@ +PROFILE_CFLAGS = @PROFILE_CFLAGS@ +PULSE_CFLAGS = @PULSE_CFLAGS@ +PULSE_LIBS = @PULSE_LIBS@ +PYTHON = @PYTHON@ +PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@ +PYTHON_PLATFORM = @PYTHON_PLATFORM@ +PYTHON_PREFIX = @PYTHON_PREFIX@ +PYTHON_VERSION = @PYTHON_VERSION@ +RANLIB = @RANLIB@ +RAW1394_CFLAGS = @RAW1394_CFLAGS@ +RAW1394_LIBS = @RAW1394_LIBS@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SHOUT2_CFLAGS = @SHOUT2_CFLAGS@ +SHOUT2_LIBS = @SHOUT2_LIBS@ +SOUP_CFLAGS = @SOUP_CFLAGS@ +SOUP_LIBS = @SOUP_LIBS@ +SPEEX_CFLAGS = @SPEEX_CFLAGS@ +SPEEX_LIBS = @SPEEX_LIBS@ +STRIP = @STRIP@ +TAGLIB_CFLAGS = @TAGLIB_CFLAGS@ +TAGLIB_CXXFLAGS = @TAGLIB_CXXFLAGS@ +TAGLIB_LIBS = @TAGLIB_LIBS@ +USE_NLS = @USE_NLS@ +VALGRIND_CFLAGS = @VALGRIND_CFLAGS@ +VALGRIND_LIBS = @VALGRIND_LIBS@ +VALGRIND_PATH = @VALGRIND_PATH@ +VERSION = @VERSION@ +VPX_130_CFLAGS = @VPX_130_CFLAGS@ +VPX_130_LIBS = @VPX_130_LIBS@ +VPX_CFLAGS = @VPX_CFLAGS@ +VPX_LIBS = @VPX_LIBS@ +WARNING_CFLAGS = @WARNING_CFLAGS@ +WARNING_CXXFLAGS = @WARNING_CXXFLAGS@ +WARNING_OBJCFLAGS = @WARNING_OBJCFLAGS@ +WAVPACK_CFLAGS = @WAVPACK_CFLAGS@ +WAVPACK_LIBS = @WAVPACK_LIBS@ +XDAMAGE_CFLAGS = @XDAMAGE_CFLAGS@ +XDAMAGE_LIBS = @XDAMAGE_LIBS@ +XFIXES_CFLAGS = @XFIXES_CFLAGS@ +XFIXES_LIBS = @XFIXES_LIBS@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +XMKMF = @XMKMF@ +XSHM_LIBS = @XSHM_LIBS@ +X_CFLAGS = @X_CFLAGS@ +X_EXTRA_LIBS = @X_EXTRA_LIBS@ +X_LIBS = @X_LIBS@ +X_PRE_LIBS = @X_PRE_LIBS@ +ZLIB_LIBS = @ZLIB_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +ac_ct_OBJC = @ac_ct_OBJC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgpyexecdir = @pkgpyexecdir@ +pkgpythondir = @pkgpythondir@ +plugindir = @plugindir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +pyexecdir = @pyexecdir@ +pythondir = @pythondir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +plugin_LTLIBRARIES = libgstrtp.la +libgstrtp_la_SOURCES = \ + dboolhuff.c \ + fnv1hash.c \ + gstrtp.c \ + gstrtpchannels.c \ + gstrtpac3depay.c \ + gstrtpac3pay.c \ + gstrtpbvdepay.c \ + gstrtpbvpay.c \ + gstrtpceltdepay.c \ + gstrtpceltpay.c \ + gstrtpdvdepay.c \ + gstrtpdvpay.c \ + gstrtpgstdepay.c \ + gstrtpgstpay.c \ + gstrtpilbcdepay.c \ + gstrtpilbcpay.c \ + gstrtpmpadepay.c \ + gstrtpmpapay.c \ + gstrtpmparobustdepay.c \ + gstrtpmpvdepay.c \ + gstrtpmpvpay.c \ + gstrtppcmadepay.c \ + gstrtppcmudepay.c \ + gstrtppcmupay.c \ + gstrtppcmapay.c \ + gstrtpg722depay.c \ + gstrtpg722pay.c \ + gstrtpg723depay.c \ + gstrtpg723pay.c \ + gstrtpg726pay.c \ + gstrtpg726depay.c \ + gstrtpg729pay.c \ + gstrtpg729depay.c \ + gstrtpgsmdepay.c \ + gstrtpgsmpay.c \ + gstrtpamrdepay.c \ + gstrtpamrpay.c \ + gstrtph263pdepay.c \ + gstrtph263ppay.c \ + gstrtph263depay.c \ + gstrtph263pay.c \ + gstrtph264depay.c \ + gstrtph264pay.c \ + gstrtpj2kdepay.c \ + gstrtpj2kpay.c \ + gstrtpjpegdepay.c \ + gstrtpjpegpay.c \ + gstrtpL16depay.c \ + gstrtpL16pay.c \ + gstrtpL24depay.c \ + gstrtpL24pay.c \ + gstasteriskh263.c \ + gstrtpmp1sdepay.c \ + gstrtpmp2tdepay.c \ + gstrtpmp2tpay.c \ + gstrtpmp4vdepay.c \ + gstrtpmp4vpay.c \ + gstrtpmp4gdepay.c \ + gstrtpmp4gpay.c \ + gstrtpmp4adepay.c \ + gstrtpmp4apay.c \ + gstrtpqcelpdepay.c \ + gstrtpqdmdepay.c \ + gstrtpsbcdepay.c \ + gstrtpsbcpay.c \ + gstrtpsirenpay.c \ + gstrtpsirendepay.c \ + gstrtpspeexdepay.c \ + gstrtpspeexpay.c \ + gstrtpsv3vdepay.c \ + gstrtptheoradepay.c \ + gstrtptheorapay.c \ + gstrtpvorbisdepay.c \ + gstrtpvorbispay.c \ + gstrtpvp8depay.c \ + gstrtpvp8pay.c \ + gstrtpvrawdepay.c \ + gstrtpvrawpay.c \ + gstrtpstreampay.c \ + gstrtpstreamdepay.c + +libgstrtp_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) \ + $(GST_CFLAGS) -Dvp8_norm=gst_rtpvp8_vp8_norm \ + -Dvp8dx_start_decode=gst_rtpvp8_vp8dx_start_decode \ + -Dvp8dx_bool_decoder_fill=gst_rtpvp8_vp8dx_bool_decoder_fill + +libgstrtp_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + -lgstaudio-@GST_API_VERSION@ \ + -lgstvideo-@GST_API_VERSION@ \ + -lgsttag-@GST_API_VERSION@ \ + -lgstrtp-@GST_API_VERSION@ \ + -lgstpbutils-@GST_API_VERSION@ \ + $(GST_BASE_LIBS) $(GST_LIBS) \ + $(LIBM) + +libgstrtp_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) +libgstrtp_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS) +noinst_HEADERS = \ + dboolhuff.h \ + fnv1hash.h \ + gstrtpchannels.h \ + gstrtpL16depay.h \ + gstrtpL16pay.h \ + gstrtpL24depay.h \ + gstrtpL24pay.h \ + gstrtpac3depay.h \ + gstrtpac3pay.h \ + gstrtpbvdepay.h \ + gstrtpbvpay.h \ + gstrtpceltpay.h \ + gstrtpceltdepay.h \ + gstrtpdvdepay.h \ + gstrtpdvpay.h \ + gstrtpamrdepay.h \ + gstrtpamrpay.h \ + gstrtpgstdepay.h \ + gstrtpgstpay.h \ + gstrtpilbcdepay.h \ + gstrtpilbcpay.h \ + gstrtppcmadepay.h \ + gstrtppcmudepay.h \ + gstrtppcmupay.h \ + gstrtppcmapay.h \ + gstrtpg722depay.h \ + gstrtpg722pay.h \ + gstrtpg723depay.h\ + gstrtpg723pay.h \ + gstrtpg726depay.h \ + gstrtpg726pay.h \ + gstrtpg729depay.h \ + gstrtpg729pay.h \ + gstrtpgsmdepay.h \ + gstrtpgsmpay.h \ + gstrtpmpadepay.h \ + gstrtpmparobustdepay.h \ + gstrtpmpapay.h \ + gstrtpmpvdepay.h \ + gstrtpmpvpay.h \ + gstrtph263pdepay.h \ + gstrtph263ppay.h \ + gstrtph263depay.h \ + gstrtph263pay.h \ + gstrtph264depay.h \ + gstrtph264pay.h \ + gstrtpj2kdepay.h \ + gstrtpj2kpay.h \ + gstrtpjpegdepay.h \ + gstrtpjpegpay.h \ + gstrtpmp1sdepay.h \ + gstrtpmp2tdepay.h \ + gstrtpmp2tpay.h \ + gstrtpmp4vdepay.h \ + gstrtpmp4vpay.h \ + gstrtpmp4gdepay.h \ + gstrtpmp4gpay.h \ + gstrtpmp4adepay.h \ + gstrtpmp4apay.h \ + gstasteriskh263.h \ + gstrtpqcelpdepay.h \ + gstrtpqdmdepay.h \ + gstrtpsbcdepay.h \ + gstrtpsbcpay.h \ + gstrtpsirenpay.h \ + gstrtpsirendepay.h \ + gstrtpspeexdepay.h \ + gstrtpspeexpay.h \ + gstrtpsv3vdepay.h \ + gstrtptheoradepay.h \ + gstrtptheorapay.h \ + gstrtpvorbisdepay.h \ + gstrtpvorbispay.h \ + gstrtpvp8depay.h \ + gstrtpvp8pay.h \ + gstrtpvrawdepay.h \ + gstrtpvrawpay.h \ + gstrtpstreampay.h \ + gstrtpstreamdepay.h + +EXTRA_DIST = dboolhuff.LICENSE +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu gst/rtp/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu gst/rtp/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(plugindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(plugindir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \ + } + +uninstall-pluginLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \ + done + +clean-pluginLTLIBRARIES: + -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES) + @list='$(plugin_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libgstrtp.la: $(libgstrtp_la_OBJECTS) $(libgstrtp_la_DEPENDENCIES) $(EXTRA_libgstrtp_la_DEPENDENCIES) + $(AM_V_CCLD)$(libgstrtp_la_LINK) -rpath $(plugindir) $(libgstrtp_la_OBJECTS) $(libgstrtp_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-dboolhuff.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-fnv1hash.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstasteriskh263.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtp.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpL16depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpL16pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpL24depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpL24pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpac3depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpac3pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpamrdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpamrpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpbvdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpbvpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpceltdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpceltpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpchannels.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpdvdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpdvpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg722depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg722pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg723depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg723pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg726depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg726pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg729depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpg729pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpgsmdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpgsmpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpgstdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpgstpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph263depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph263pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph263pdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph263ppay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph264depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtph264pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpilbcdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpilbcpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpj2kdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpj2kpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpjpegdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpjpegpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp1sdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp2tdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp2tpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4adepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4apay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4gdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4gpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4vdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmp4vpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmpadepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmpapay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmparobustdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmpvdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpmpvpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtppcmadepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtppcmapay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtppcmudepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtppcmupay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpqcelpdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpqdmdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpsbcdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpsbcpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpsirendepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpsirenpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpspeexdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpspeexpay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpstreamdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpstreampay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpsv3vdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtptheoradepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtptheorapay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvorbisdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvorbispay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvp8depay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvp8pay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvrawdepay.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libgstrtp_la-gstrtpvrawpay.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libgstrtp_la-dboolhuff.lo: dboolhuff.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-dboolhuff.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-dboolhuff.Tpo -c -o libgstrtp_la-dboolhuff.lo `test -f 'dboolhuff.c' || echo '$(srcdir)/'`dboolhuff.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-dboolhuff.Tpo $(DEPDIR)/libgstrtp_la-dboolhuff.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dboolhuff.c' object='libgstrtp_la-dboolhuff.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-dboolhuff.lo `test -f 'dboolhuff.c' || echo '$(srcdir)/'`dboolhuff.c + +libgstrtp_la-fnv1hash.lo: fnv1hash.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-fnv1hash.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-fnv1hash.Tpo -c -o libgstrtp_la-fnv1hash.lo `test -f 'fnv1hash.c' || echo '$(srcdir)/'`fnv1hash.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-fnv1hash.Tpo $(DEPDIR)/libgstrtp_la-fnv1hash.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='fnv1hash.c' object='libgstrtp_la-fnv1hash.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-fnv1hash.lo `test -f 'fnv1hash.c' || echo '$(srcdir)/'`fnv1hash.c + +libgstrtp_la-gstrtp.lo: gstrtp.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtp.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtp.Tpo -c -o libgstrtp_la-gstrtp.lo `test -f 'gstrtp.c' || echo '$(srcdir)/'`gstrtp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtp.Tpo $(DEPDIR)/libgstrtp_la-gstrtp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtp.c' object='libgstrtp_la-gstrtp.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtp.lo `test -f 'gstrtp.c' || echo '$(srcdir)/'`gstrtp.c + +libgstrtp_la-gstrtpchannels.lo: gstrtpchannels.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpchannels.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpchannels.Tpo -c -o libgstrtp_la-gstrtpchannels.lo `test -f 'gstrtpchannels.c' || echo '$(srcdir)/'`gstrtpchannels.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpchannels.Tpo $(DEPDIR)/libgstrtp_la-gstrtpchannels.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpchannels.c' object='libgstrtp_la-gstrtpchannels.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpchannels.lo `test -f 'gstrtpchannels.c' || echo '$(srcdir)/'`gstrtpchannels.c + +libgstrtp_la-gstrtpac3depay.lo: gstrtpac3depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpac3depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpac3depay.Tpo -c -o libgstrtp_la-gstrtpac3depay.lo `test -f 'gstrtpac3depay.c' || echo '$(srcdir)/'`gstrtpac3depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpac3depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpac3depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpac3depay.c' object='libgstrtp_la-gstrtpac3depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpac3depay.lo `test -f 'gstrtpac3depay.c' || echo '$(srcdir)/'`gstrtpac3depay.c + +libgstrtp_la-gstrtpac3pay.lo: gstrtpac3pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpac3pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpac3pay.Tpo -c -o libgstrtp_la-gstrtpac3pay.lo `test -f 'gstrtpac3pay.c' || echo '$(srcdir)/'`gstrtpac3pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpac3pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpac3pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpac3pay.c' object='libgstrtp_la-gstrtpac3pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpac3pay.lo `test -f 'gstrtpac3pay.c' || echo '$(srcdir)/'`gstrtpac3pay.c + +libgstrtp_la-gstrtpbvdepay.lo: gstrtpbvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpbvdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpbvdepay.Tpo -c -o libgstrtp_la-gstrtpbvdepay.lo `test -f 'gstrtpbvdepay.c' || echo '$(srcdir)/'`gstrtpbvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpbvdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpbvdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpbvdepay.c' object='libgstrtp_la-gstrtpbvdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpbvdepay.lo `test -f 'gstrtpbvdepay.c' || echo '$(srcdir)/'`gstrtpbvdepay.c + +libgstrtp_la-gstrtpbvpay.lo: gstrtpbvpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpbvpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpbvpay.Tpo -c -o libgstrtp_la-gstrtpbvpay.lo `test -f 'gstrtpbvpay.c' || echo '$(srcdir)/'`gstrtpbvpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpbvpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpbvpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpbvpay.c' object='libgstrtp_la-gstrtpbvpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpbvpay.lo `test -f 'gstrtpbvpay.c' || echo '$(srcdir)/'`gstrtpbvpay.c + +libgstrtp_la-gstrtpceltdepay.lo: gstrtpceltdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpceltdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpceltdepay.Tpo -c -o libgstrtp_la-gstrtpceltdepay.lo `test -f 'gstrtpceltdepay.c' || echo '$(srcdir)/'`gstrtpceltdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpceltdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpceltdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpceltdepay.c' object='libgstrtp_la-gstrtpceltdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpceltdepay.lo `test -f 'gstrtpceltdepay.c' || echo '$(srcdir)/'`gstrtpceltdepay.c + +libgstrtp_la-gstrtpceltpay.lo: gstrtpceltpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpceltpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpceltpay.Tpo -c -o libgstrtp_la-gstrtpceltpay.lo `test -f 'gstrtpceltpay.c' || echo '$(srcdir)/'`gstrtpceltpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpceltpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpceltpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpceltpay.c' object='libgstrtp_la-gstrtpceltpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpceltpay.lo `test -f 'gstrtpceltpay.c' || echo '$(srcdir)/'`gstrtpceltpay.c + +libgstrtp_la-gstrtpdvdepay.lo: gstrtpdvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpdvdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpdvdepay.Tpo -c -o libgstrtp_la-gstrtpdvdepay.lo `test -f 'gstrtpdvdepay.c' || echo '$(srcdir)/'`gstrtpdvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpdvdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpdvdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpdvdepay.c' object='libgstrtp_la-gstrtpdvdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpdvdepay.lo `test -f 'gstrtpdvdepay.c' || echo '$(srcdir)/'`gstrtpdvdepay.c + +libgstrtp_la-gstrtpdvpay.lo: gstrtpdvpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpdvpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpdvpay.Tpo -c -o libgstrtp_la-gstrtpdvpay.lo `test -f 'gstrtpdvpay.c' || echo '$(srcdir)/'`gstrtpdvpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpdvpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpdvpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpdvpay.c' object='libgstrtp_la-gstrtpdvpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpdvpay.lo `test -f 'gstrtpdvpay.c' || echo '$(srcdir)/'`gstrtpdvpay.c + +libgstrtp_la-gstrtpgstdepay.lo: gstrtpgstdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpgstdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpgstdepay.Tpo -c -o libgstrtp_la-gstrtpgstdepay.lo `test -f 'gstrtpgstdepay.c' || echo '$(srcdir)/'`gstrtpgstdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpgstdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpgstdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpgstdepay.c' object='libgstrtp_la-gstrtpgstdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpgstdepay.lo `test -f 'gstrtpgstdepay.c' || echo '$(srcdir)/'`gstrtpgstdepay.c + +libgstrtp_la-gstrtpgstpay.lo: gstrtpgstpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpgstpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpgstpay.Tpo -c -o libgstrtp_la-gstrtpgstpay.lo `test -f 'gstrtpgstpay.c' || echo '$(srcdir)/'`gstrtpgstpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpgstpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpgstpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpgstpay.c' object='libgstrtp_la-gstrtpgstpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpgstpay.lo `test -f 'gstrtpgstpay.c' || echo '$(srcdir)/'`gstrtpgstpay.c + +libgstrtp_la-gstrtpilbcdepay.lo: gstrtpilbcdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpilbcdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpilbcdepay.Tpo -c -o libgstrtp_la-gstrtpilbcdepay.lo `test -f 'gstrtpilbcdepay.c' || echo '$(srcdir)/'`gstrtpilbcdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpilbcdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpilbcdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpilbcdepay.c' object='libgstrtp_la-gstrtpilbcdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpilbcdepay.lo `test -f 'gstrtpilbcdepay.c' || echo '$(srcdir)/'`gstrtpilbcdepay.c + +libgstrtp_la-gstrtpilbcpay.lo: gstrtpilbcpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpilbcpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpilbcpay.Tpo -c -o libgstrtp_la-gstrtpilbcpay.lo `test -f 'gstrtpilbcpay.c' || echo '$(srcdir)/'`gstrtpilbcpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpilbcpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpilbcpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpilbcpay.c' object='libgstrtp_la-gstrtpilbcpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpilbcpay.lo `test -f 'gstrtpilbcpay.c' || echo '$(srcdir)/'`gstrtpilbcpay.c + +libgstrtp_la-gstrtpmpadepay.lo: gstrtpmpadepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmpadepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmpadepay.Tpo -c -o libgstrtp_la-gstrtpmpadepay.lo `test -f 'gstrtpmpadepay.c' || echo '$(srcdir)/'`gstrtpmpadepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmpadepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmpadepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmpadepay.c' object='libgstrtp_la-gstrtpmpadepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmpadepay.lo `test -f 'gstrtpmpadepay.c' || echo '$(srcdir)/'`gstrtpmpadepay.c + +libgstrtp_la-gstrtpmpapay.lo: gstrtpmpapay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmpapay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmpapay.Tpo -c -o libgstrtp_la-gstrtpmpapay.lo `test -f 'gstrtpmpapay.c' || echo '$(srcdir)/'`gstrtpmpapay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmpapay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmpapay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmpapay.c' object='libgstrtp_la-gstrtpmpapay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmpapay.lo `test -f 'gstrtpmpapay.c' || echo '$(srcdir)/'`gstrtpmpapay.c + +libgstrtp_la-gstrtpmparobustdepay.lo: gstrtpmparobustdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmparobustdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmparobustdepay.Tpo -c -o libgstrtp_la-gstrtpmparobustdepay.lo `test -f 'gstrtpmparobustdepay.c' || echo '$(srcdir)/'`gstrtpmparobustdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmparobustdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmparobustdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmparobustdepay.c' object='libgstrtp_la-gstrtpmparobustdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmparobustdepay.lo `test -f 'gstrtpmparobustdepay.c' || echo '$(srcdir)/'`gstrtpmparobustdepay.c + +libgstrtp_la-gstrtpmpvdepay.lo: gstrtpmpvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmpvdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmpvdepay.Tpo -c -o libgstrtp_la-gstrtpmpvdepay.lo `test -f 'gstrtpmpvdepay.c' || echo '$(srcdir)/'`gstrtpmpvdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmpvdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmpvdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmpvdepay.c' object='libgstrtp_la-gstrtpmpvdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmpvdepay.lo `test -f 'gstrtpmpvdepay.c' || echo '$(srcdir)/'`gstrtpmpvdepay.c + +libgstrtp_la-gstrtpmpvpay.lo: gstrtpmpvpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmpvpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmpvpay.Tpo -c -o libgstrtp_la-gstrtpmpvpay.lo `test -f 'gstrtpmpvpay.c' || echo '$(srcdir)/'`gstrtpmpvpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmpvpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmpvpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmpvpay.c' object='libgstrtp_la-gstrtpmpvpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmpvpay.lo `test -f 'gstrtpmpvpay.c' || echo '$(srcdir)/'`gstrtpmpvpay.c + +libgstrtp_la-gstrtppcmadepay.lo: gstrtppcmadepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtppcmadepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtppcmadepay.Tpo -c -o libgstrtp_la-gstrtppcmadepay.lo `test -f 'gstrtppcmadepay.c' || echo '$(srcdir)/'`gstrtppcmadepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtppcmadepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtppcmadepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtppcmadepay.c' object='libgstrtp_la-gstrtppcmadepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtppcmadepay.lo `test -f 'gstrtppcmadepay.c' || echo '$(srcdir)/'`gstrtppcmadepay.c + +libgstrtp_la-gstrtppcmudepay.lo: gstrtppcmudepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtppcmudepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtppcmudepay.Tpo -c -o libgstrtp_la-gstrtppcmudepay.lo `test -f 'gstrtppcmudepay.c' || echo '$(srcdir)/'`gstrtppcmudepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtppcmudepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtppcmudepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtppcmudepay.c' object='libgstrtp_la-gstrtppcmudepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtppcmudepay.lo `test -f 'gstrtppcmudepay.c' || echo '$(srcdir)/'`gstrtppcmudepay.c + +libgstrtp_la-gstrtppcmupay.lo: gstrtppcmupay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtppcmupay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtppcmupay.Tpo -c -o libgstrtp_la-gstrtppcmupay.lo `test -f 'gstrtppcmupay.c' || echo '$(srcdir)/'`gstrtppcmupay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtppcmupay.Tpo $(DEPDIR)/libgstrtp_la-gstrtppcmupay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtppcmupay.c' object='libgstrtp_la-gstrtppcmupay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtppcmupay.lo `test -f 'gstrtppcmupay.c' || echo '$(srcdir)/'`gstrtppcmupay.c + +libgstrtp_la-gstrtppcmapay.lo: gstrtppcmapay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtppcmapay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtppcmapay.Tpo -c -o libgstrtp_la-gstrtppcmapay.lo `test -f 'gstrtppcmapay.c' || echo '$(srcdir)/'`gstrtppcmapay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtppcmapay.Tpo $(DEPDIR)/libgstrtp_la-gstrtppcmapay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtppcmapay.c' object='libgstrtp_la-gstrtppcmapay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtppcmapay.lo `test -f 'gstrtppcmapay.c' || echo '$(srcdir)/'`gstrtppcmapay.c + +libgstrtp_la-gstrtpg722depay.lo: gstrtpg722depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg722depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg722depay.Tpo -c -o libgstrtp_la-gstrtpg722depay.lo `test -f 'gstrtpg722depay.c' || echo '$(srcdir)/'`gstrtpg722depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg722depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg722depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg722depay.c' object='libgstrtp_la-gstrtpg722depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg722depay.lo `test -f 'gstrtpg722depay.c' || echo '$(srcdir)/'`gstrtpg722depay.c + +libgstrtp_la-gstrtpg722pay.lo: gstrtpg722pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg722pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg722pay.Tpo -c -o libgstrtp_la-gstrtpg722pay.lo `test -f 'gstrtpg722pay.c' || echo '$(srcdir)/'`gstrtpg722pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg722pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg722pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg722pay.c' object='libgstrtp_la-gstrtpg722pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg722pay.lo `test -f 'gstrtpg722pay.c' || echo '$(srcdir)/'`gstrtpg722pay.c + +libgstrtp_la-gstrtpg723depay.lo: gstrtpg723depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg723depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg723depay.Tpo -c -o libgstrtp_la-gstrtpg723depay.lo `test -f 'gstrtpg723depay.c' || echo '$(srcdir)/'`gstrtpg723depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg723depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg723depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg723depay.c' object='libgstrtp_la-gstrtpg723depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg723depay.lo `test -f 'gstrtpg723depay.c' || echo '$(srcdir)/'`gstrtpg723depay.c + +libgstrtp_la-gstrtpg723pay.lo: gstrtpg723pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg723pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg723pay.Tpo -c -o libgstrtp_la-gstrtpg723pay.lo `test -f 'gstrtpg723pay.c' || echo '$(srcdir)/'`gstrtpg723pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg723pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg723pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg723pay.c' object='libgstrtp_la-gstrtpg723pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg723pay.lo `test -f 'gstrtpg723pay.c' || echo '$(srcdir)/'`gstrtpg723pay.c + +libgstrtp_la-gstrtpg726pay.lo: gstrtpg726pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg726pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg726pay.Tpo -c -o libgstrtp_la-gstrtpg726pay.lo `test -f 'gstrtpg726pay.c' || echo '$(srcdir)/'`gstrtpg726pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg726pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg726pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg726pay.c' object='libgstrtp_la-gstrtpg726pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg726pay.lo `test -f 'gstrtpg726pay.c' || echo '$(srcdir)/'`gstrtpg726pay.c + +libgstrtp_la-gstrtpg726depay.lo: gstrtpg726depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg726depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg726depay.Tpo -c -o libgstrtp_la-gstrtpg726depay.lo `test -f 'gstrtpg726depay.c' || echo '$(srcdir)/'`gstrtpg726depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg726depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg726depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg726depay.c' object='libgstrtp_la-gstrtpg726depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg726depay.lo `test -f 'gstrtpg726depay.c' || echo '$(srcdir)/'`gstrtpg726depay.c + +libgstrtp_la-gstrtpg729pay.lo: gstrtpg729pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg729pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg729pay.Tpo -c -o libgstrtp_la-gstrtpg729pay.lo `test -f 'gstrtpg729pay.c' || echo '$(srcdir)/'`gstrtpg729pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg729pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg729pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg729pay.c' object='libgstrtp_la-gstrtpg729pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg729pay.lo `test -f 'gstrtpg729pay.c' || echo '$(srcdir)/'`gstrtpg729pay.c + +libgstrtp_la-gstrtpg729depay.lo: gstrtpg729depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpg729depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpg729depay.Tpo -c -o libgstrtp_la-gstrtpg729depay.lo `test -f 'gstrtpg729depay.c' || echo '$(srcdir)/'`gstrtpg729depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpg729depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpg729depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpg729depay.c' object='libgstrtp_la-gstrtpg729depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpg729depay.lo `test -f 'gstrtpg729depay.c' || echo '$(srcdir)/'`gstrtpg729depay.c + +libgstrtp_la-gstrtpgsmdepay.lo: gstrtpgsmdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpgsmdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpgsmdepay.Tpo -c -o libgstrtp_la-gstrtpgsmdepay.lo `test -f 'gstrtpgsmdepay.c' || echo '$(srcdir)/'`gstrtpgsmdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpgsmdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpgsmdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpgsmdepay.c' object='libgstrtp_la-gstrtpgsmdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpgsmdepay.lo `test -f 'gstrtpgsmdepay.c' || echo '$(srcdir)/'`gstrtpgsmdepay.c + +libgstrtp_la-gstrtpgsmpay.lo: gstrtpgsmpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpgsmpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpgsmpay.Tpo -c -o libgstrtp_la-gstrtpgsmpay.lo `test -f 'gstrtpgsmpay.c' || echo '$(srcdir)/'`gstrtpgsmpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpgsmpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpgsmpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpgsmpay.c' object='libgstrtp_la-gstrtpgsmpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpgsmpay.lo `test -f 'gstrtpgsmpay.c' || echo '$(srcdir)/'`gstrtpgsmpay.c + +libgstrtp_la-gstrtpamrdepay.lo: gstrtpamrdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpamrdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpamrdepay.Tpo -c -o libgstrtp_la-gstrtpamrdepay.lo `test -f 'gstrtpamrdepay.c' || echo '$(srcdir)/'`gstrtpamrdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpamrdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpamrdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpamrdepay.c' object='libgstrtp_la-gstrtpamrdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpamrdepay.lo `test -f 'gstrtpamrdepay.c' || echo '$(srcdir)/'`gstrtpamrdepay.c + +libgstrtp_la-gstrtpamrpay.lo: gstrtpamrpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpamrpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpamrpay.Tpo -c -o libgstrtp_la-gstrtpamrpay.lo `test -f 'gstrtpamrpay.c' || echo '$(srcdir)/'`gstrtpamrpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpamrpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpamrpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpamrpay.c' object='libgstrtp_la-gstrtpamrpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpamrpay.lo `test -f 'gstrtpamrpay.c' || echo '$(srcdir)/'`gstrtpamrpay.c + +libgstrtp_la-gstrtph263pdepay.lo: gstrtph263pdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph263pdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph263pdepay.Tpo -c -o libgstrtp_la-gstrtph263pdepay.lo `test -f 'gstrtph263pdepay.c' || echo '$(srcdir)/'`gstrtph263pdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph263pdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph263pdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph263pdepay.c' object='libgstrtp_la-gstrtph263pdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph263pdepay.lo `test -f 'gstrtph263pdepay.c' || echo '$(srcdir)/'`gstrtph263pdepay.c + +libgstrtp_la-gstrtph263ppay.lo: gstrtph263ppay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph263ppay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph263ppay.Tpo -c -o libgstrtp_la-gstrtph263ppay.lo `test -f 'gstrtph263ppay.c' || echo '$(srcdir)/'`gstrtph263ppay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph263ppay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph263ppay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph263ppay.c' object='libgstrtp_la-gstrtph263ppay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph263ppay.lo `test -f 'gstrtph263ppay.c' || echo '$(srcdir)/'`gstrtph263ppay.c + +libgstrtp_la-gstrtph263depay.lo: gstrtph263depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph263depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph263depay.Tpo -c -o libgstrtp_la-gstrtph263depay.lo `test -f 'gstrtph263depay.c' || echo '$(srcdir)/'`gstrtph263depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph263depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph263depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph263depay.c' object='libgstrtp_la-gstrtph263depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph263depay.lo `test -f 'gstrtph263depay.c' || echo '$(srcdir)/'`gstrtph263depay.c + +libgstrtp_la-gstrtph263pay.lo: gstrtph263pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph263pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph263pay.Tpo -c -o libgstrtp_la-gstrtph263pay.lo `test -f 'gstrtph263pay.c' || echo '$(srcdir)/'`gstrtph263pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph263pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph263pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph263pay.c' object='libgstrtp_la-gstrtph263pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph263pay.lo `test -f 'gstrtph263pay.c' || echo '$(srcdir)/'`gstrtph263pay.c + +libgstrtp_la-gstrtph264depay.lo: gstrtph264depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph264depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph264depay.Tpo -c -o libgstrtp_la-gstrtph264depay.lo `test -f 'gstrtph264depay.c' || echo '$(srcdir)/'`gstrtph264depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph264depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph264depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph264depay.c' object='libgstrtp_la-gstrtph264depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph264depay.lo `test -f 'gstrtph264depay.c' || echo '$(srcdir)/'`gstrtph264depay.c + +libgstrtp_la-gstrtph264pay.lo: gstrtph264pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtph264pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtph264pay.Tpo -c -o libgstrtp_la-gstrtph264pay.lo `test -f 'gstrtph264pay.c' || echo '$(srcdir)/'`gstrtph264pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtph264pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtph264pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtph264pay.c' object='libgstrtp_la-gstrtph264pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtph264pay.lo `test -f 'gstrtph264pay.c' || echo '$(srcdir)/'`gstrtph264pay.c + +libgstrtp_la-gstrtpj2kdepay.lo: gstrtpj2kdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpj2kdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpj2kdepay.Tpo -c -o libgstrtp_la-gstrtpj2kdepay.lo `test -f 'gstrtpj2kdepay.c' || echo '$(srcdir)/'`gstrtpj2kdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpj2kdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpj2kdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpj2kdepay.c' object='libgstrtp_la-gstrtpj2kdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpj2kdepay.lo `test -f 'gstrtpj2kdepay.c' || echo '$(srcdir)/'`gstrtpj2kdepay.c + +libgstrtp_la-gstrtpj2kpay.lo: gstrtpj2kpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpj2kpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpj2kpay.Tpo -c -o libgstrtp_la-gstrtpj2kpay.lo `test -f 'gstrtpj2kpay.c' || echo '$(srcdir)/'`gstrtpj2kpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpj2kpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpj2kpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpj2kpay.c' object='libgstrtp_la-gstrtpj2kpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpj2kpay.lo `test -f 'gstrtpj2kpay.c' || echo '$(srcdir)/'`gstrtpj2kpay.c + +libgstrtp_la-gstrtpjpegdepay.lo: gstrtpjpegdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpjpegdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpjpegdepay.Tpo -c -o libgstrtp_la-gstrtpjpegdepay.lo `test -f 'gstrtpjpegdepay.c' || echo '$(srcdir)/'`gstrtpjpegdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpjpegdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpjpegdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpjpegdepay.c' object='libgstrtp_la-gstrtpjpegdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpjpegdepay.lo `test -f 'gstrtpjpegdepay.c' || echo '$(srcdir)/'`gstrtpjpegdepay.c + +libgstrtp_la-gstrtpjpegpay.lo: gstrtpjpegpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpjpegpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpjpegpay.Tpo -c -o libgstrtp_la-gstrtpjpegpay.lo `test -f 'gstrtpjpegpay.c' || echo '$(srcdir)/'`gstrtpjpegpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpjpegpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpjpegpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpjpegpay.c' object='libgstrtp_la-gstrtpjpegpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpjpegpay.lo `test -f 'gstrtpjpegpay.c' || echo '$(srcdir)/'`gstrtpjpegpay.c + +libgstrtp_la-gstrtpL16depay.lo: gstrtpL16depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpL16depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpL16depay.Tpo -c -o libgstrtp_la-gstrtpL16depay.lo `test -f 'gstrtpL16depay.c' || echo '$(srcdir)/'`gstrtpL16depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpL16depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpL16depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpL16depay.c' object='libgstrtp_la-gstrtpL16depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpL16depay.lo `test -f 'gstrtpL16depay.c' || echo '$(srcdir)/'`gstrtpL16depay.c + +libgstrtp_la-gstrtpL16pay.lo: gstrtpL16pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpL16pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpL16pay.Tpo -c -o libgstrtp_la-gstrtpL16pay.lo `test -f 'gstrtpL16pay.c' || echo '$(srcdir)/'`gstrtpL16pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpL16pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpL16pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpL16pay.c' object='libgstrtp_la-gstrtpL16pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpL16pay.lo `test -f 'gstrtpL16pay.c' || echo '$(srcdir)/'`gstrtpL16pay.c + +libgstrtp_la-gstrtpL24depay.lo: gstrtpL24depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpL24depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpL24depay.Tpo -c -o libgstrtp_la-gstrtpL24depay.lo `test -f 'gstrtpL24depay.c' || echo '$(srcdir)/'`gstrtpL24depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpL24depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpL24depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpL24depay.c' object='libgstrtp_la-gstrtpL24depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpL24depay.lo `test -f 'gstrtpL24depay.c' || echo '$(srcdir)/'`gstrtpL24depay.c + +libgstrtp_la-gstrtpL24pay.lo: gstrtpL24pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpL24pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpL24pay.Tpo -c -o libgstrtp_la-gstrtpL24pay.lo `test -f 'gstrtpL24pay.c' || echo '$(srcdir)/'`gstrtpL24pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpL24pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpL24pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpL24pay.c' object='libgstrtp_la-gstrtpL24pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpL24pay.lo `test -f 'gstrtpL24pay.c' || echo '$(srcdir)/'`gstrtpL24pay.c + +libgstrtp_la-gstasteriskh263.lo: gstasteriskh263.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstasteriskh263.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstasteriskh263.Tpo -c -o libgstrtp_la-gstasteriskh263.lo `test -f 'gstasteriskh263.c' || echo '$(srcdir)/'`gstasteriskh263.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstasteriskh263.Tpo $(DEPDIR)/libgstrtp_la-gstasteriskh263.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstasteriskh263.c' object='libgstrtp_la-gstasteriskh263.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstasteriskh263.lo `test -f 'gstasteriskh263.c' || echo '$(srcdir)/'`gstasteriskh263.c + +libgstrtp_la-gstrtpmp1sdepay.lo: gstrtpmp1sdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp1sdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp1sdepay.Tpo -c -o libgstrtp_la-gstrtpmp1sdepay.lo `test -f 'gstrtpmp1sdepay.c' || echo '$(srcdir)/'`gstrtpmp1sdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp1sdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp1sdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp1sdepay.c' object='libgstrtp_la-gstrtpmp1sdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp1sdepay.lo `test -f 'gstrtpmp1sdepay.c' || echo '$(srcdir)/'`gstrtpmp1sdepay.c + +libgstrtp_la-gstrtpmp2tdepay.lo: gstrtpmp2tdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp2tdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp2tdepay.Tpo -c -o libgstrtp_la-gstrtpmp2tdepay.lo `test -f 'gstrtpmp2tdepay.c' || echo '$(srcdir)/'`gstrtpmp2tdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp2tdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp2tdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp2tdepay.c' object='libgstrtp_la-gstrtpmp2tdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp2tdepay.lo `test -f 'gstrtpmp2tdepay.c' || echo '$(srcdir)/'`gstrtpmp2tdepay.c + +libgstrtp_la-gstrtpmp2tpay.lo: gstrtpmp2tpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp2tpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp2tpay.Tpo -c -o libgstrtp_la-gstrtpmp2tpay.lo `test -f 'gstrtpmp2tpay.c' || echo '$(srcdir)/'`gstrtpmp2tpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp2tpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp2tpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp2tpay.c' object='libgstrtp_la-gstrtpmp2tpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp2tpay.lo `test -f 'gstrtpmp2tpay.c' || echo '$(srcdir)/'`gstrtpmp2tpay.c + +libgstrtp_la-gstrtpmp4vdepay.lo: gstrtpmp4vdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4vdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4vdepay.Tpo -c -o libgstrtp_la-gstrtpmp4vdepay.lo `test -f 'gstrtpmp4vdepay.c' || echo '$(srcdir)/'`gstrtpmp4vdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4vdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4vdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4vdepay.c' object='libgstrtp_la-gstrtpmp4vdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4vdepay.lo `test -f 'gstrtpmp4vdepay.c' || echo '$(srcdir)/'`gstrtpmp4vdepay.c + +libgstrtp_la-gstrtpmp4vpay.lo: gstrtpmp4vpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4vpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4vpay.Tpo -c -o libgstrtp_la-gstrtpmp4vpay.lo `test -f 'gstrtpmp4vpay.c' || echo '$(srcdir)/'`gstrtpmp4vpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4vpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4vpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4vpay.c' object='libgstrtp_la-gstrtpmp4vpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4vpay.lo `test -f 'gstrtpmp4vpay.c' || echo '$(srcdir)/'`gstrtpmp4vpay.c + +libgstrtp_la-gstrtpmp4gdepay.lo: gstrtpmp4gdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4gdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4gdepay.Tpo -c -o libgstrtp_la-gstrtpmp4gdepay.lo `test -f 'gstrtpmp4gdepay.c' || echo '$(srcdir)/'`gstrtpmp4gdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4gdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4gdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4gdepay.c' object='libgstrtp_la-gstrtpmp4gdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4gdepay.lo `test -f 'gstrtpmp4gdepay.c' || echo '$(srcdir)/'`gstrtpmp4gdepay.c + +libgstrtp_la-gstrtpmp4gpay.lo: gstrtpmp4gpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4gpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4gpay.Tpo -c -o libgstrtp_la-gstrtpmp4gpay.lo `test -f 'gstrtpmp4gpay.c' || echo '$(srcdir)/'`gstrtpmp4gpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4gpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4gpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4gpay.c' object='libgstrtp_la-gstrtpmp4gpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4gpay.lo `test -f 'gstrtpmp4gpay.c' || echo '$(srcdir)/'`gstrtpmp4gpay.c + +libgstrtp_la-gstrtpmp4adepay.lo: gstrtpmp4adepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4adepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4adepay.Tpo -c -o libgstrtp_la-gstrtpmp4adepay.lo `test -f 'gstrtpmp4adepay.c' || echo '$(srcdir)/'`gstrtpmp4adepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4adepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4adepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4adepay.c' object='libgstrtp_la-gstrtpmp4adepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4adepay.lo `test -f 'gstrtpmp4adepay.c' || echo '$(srcdir)/'`gstrtpmp4adepay.c + +libgstrtp_la-gstrtpmp4apay.lo: gstrtpmp4apay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpmp4apay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpmp4apay.Tpo -c -o libgstrtp_la-gstrtpmp4apay.lo `test -f 'gstrtpmp4apay.c' || echo '$(srcdir)/'`gstrtpmp4apay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpmp4apay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpmp4apay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpmp4apay.c' object='libgstrtp_la-gstrtpmp4apay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpmp4apay.lo `test -f 'gstrtpmp4apay.c' || echo '$(srcdir)/'`gstrtpmp4apay.c + +libgstrtp_la-gstrtpqcelpdepay.lo: gstrtpqcelpdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpqcelpdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpqcelpdepay.Tpo -c -o libgstrtp_la-gstrtpqcelpdepay.lo `test -f 'gstrtpqcelpdepay.c' || echo '$(srcdir)/'`gstrtpqcelpdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpqcelpdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpqcelpdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpqcelpdepay.c' object='libgstrtp_la-gstrtpqcelpdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpqcelpdepay.lo `test -f 'gstrtpqcelpdepay.c' || echo '$(srcdir)/'`gstrtpqcelpdepay.c + +libgstrtp_la-gstrtpqdmdepay.lo: gstrtpqdmdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpqdmdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpqdmdepay.Tpo -c -o libgstrtp_la-gstrtpqdmdepay.lo `test -f 'gstrtpqdmdepay.c' || echo '$(srcdir)/'`gstrtpqdmdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpqdmdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpqdmdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpqdmdepay.c' object='libgstrtp_la-gstrtpqdmdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpqdmdepay.lo `test -f 'gstrtpqdmdepay.c' || echo '$(srcdir)/'`gstrtpqdmdepay.c + +libgstrtp_la-gstrtpsbcdepay.lo: gstrtpsbcdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpsbcdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpsbcdepay.Tpo -c -o libgstrtp_la-gstrtpsbcdepay.lo `test -f 'gstrtpsbcdepay.c' || echo '$(srcdir)/'`gstrtpsbcdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpsbcdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpsbcdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpsbcdepay.c' object='libgstrtp_la-gstrtpsbcdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpsbcdepay.lo `test -f 'gstrtpsbcdepay.c' || echo '$(srcdir)/'`gstrtpsbcdepay.c + +libgstrtp_la-gstrtpsbcpay.lo: gstrtpsbcpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpsbcpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpsbcpay.Tpo -c -o libgstrtp_la-gstrtpsbcpay.lo `test -f 'gstrtpsbcpay.c' || echo '$(srcdir)/'`gstrtpsbcpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpsbcpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpsbcpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpsbcpay.c' object='libgstrtp_la-gstrtpsbcpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpsbcpay.lo `test -f 'gstrtpsbcpay.c' || echo '$(srcdir)/'`gstrtpsbcpay.c + +libgstrtp_la-gstrtpsirenpay.lo: gstrtpsirenpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpsirenpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpsirenpay.Tpo -c -o libgstrtp_la-gstrtpsirenpay.lo `test -f 'gstrtpsirenpay.c' || echo '$(srcdir)/'`gstrtpsirenpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpsirenpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpsirenpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpsirenpay.c' object='libgstrtp_la-gstrtpsirenpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpsirenpay.lo `test -f 'gstrtpsirenpay.c' || echo '$(srcdir)/'`gstrtpsirenpay.c + +libgstrtp_la-gstrtpsirendepay.lo: gstrtpsirendepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpsirendepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpsirendepay.Tpo -c -o libgstrtp_la-gstrtpsirendepay.lo `test -f 'gstrtpsirendepay.c' || echo '$(srcdir)/'`gstrtpsirendepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpsirendepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpsirendepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpsirendepay.c' object='libgstrtp_la-gstrtpsirendepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpsirendepay.lo `test -f 'gstrtpsirendepay.c' || echo '$(srcdir)/'`gstrtpsirendepay.c + +libgstrtp_la-gstrtpspeexdepay.lo: gstrtpspeexdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpspeexdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpspeexdepay.Tpo -c -o libgstrtp_la-gstrtpspeexdepay.lo `test -f 'gstrtpspeexdepay.c' || echo '$(srcdir)/'`gstrtpspeexdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpspeexdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpspeexdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpspeexdepay.c' object='libgstrtp_la-gstrtpspeexdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpspeexdepay.lo `test -f 'gstrtpspeexdepay.c' || echo '$(srcdir)/'`gstrtpspeexdepay.c + +libgstrtp_la-gstrtpspeexpay.lo: gstrtpspeexpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpspeexpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpspeexpay.Tpo -c -o libgstrtp_la-gstrtpspeexpay.lo `test -f 'gstrtpspeexpay.c' || echo '$(srcdir)/'`gstrtpspeexpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpspeexpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpspeexpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpspeexpay.c' object='libgstrtp_la-gstrtpspeexpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpspeexpay.lo `test -f 'gstrtpspeexpay.c' || echo '$(srcdir)/'`gstrtpspeexpay.c + +libgstrtp_la-gstrtpsv3vdepay.lo: gstrtpsv3vdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpsv3vdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpsv3vdepay.Tpo -c -o libgstrtp_la-gstrtpsv3vdepay.lo `test -f 'gstrtpsv3vdepay.c' || echo '$(srcdir)/'`gstrtpsv3vdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpsv3vdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpsv3vdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpsv3vdepay.c' object='libgstrtp_la-gstrtpsv3vdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpsv3vdepay.lo `test -f 'gstrtpsv3vdepay.c' || echo '$(srcdir)/'`gstrtpsv3vdepay.c + +libgstrtp_la-gstrtptheoradepay.lo: gstrtptheoradepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtptheoradepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtptheoradepay.Tpo -c -o libgstrtp_la-gstrtptheoradepay.lo `test -f 'gstrtptheoradepay.c' || echo '$(srcdir)/'`gstrtptheoradepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtptheoradepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtptheoradepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtptheoradepay.c' object='libgstrtp_la-gstrtptheoradepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtptheoradepay.lo `test -f 'gstrtptheoradepay.c' || echo '$(srcdir)/'`gstrtptheoradepay.c + +libgstrtp_la-gstrtptheorapay.lo: gstrtptheorapay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtptheorapay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtptheorapay.Tpo -c -o libgstrtp_la-gstrtptheorapay.lo `test -f 'gstrtptheorapay.c' || echo '$(srcdir)/'`gstrtptheorapay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtptheorapay.Tpo $(DEPDIR)/libgstrtp_la-gstrtptheorapay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtptheorapay.c' object='libgstrtp_la-gstrtptheorapay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtptheorapay.lo `test -f 'gstrtptheorapay.c' || echo '$(srcdir)/'`gstrtptheorapay.c + +libgstrtp_la-gstrtpvorbisdepay.lo: gstrtpvorbisdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvorbisdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvorbisdepay.Tpo -c -o libgstrtp_la-gstrtpvorbisdepay.lo `test -f 'gstrtpvorbisdepay.c' || echo '$(srcdir)/'`gstrtpvorbisdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvorbisdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvorbisdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvorbisdepay.c' object='libgstrtp_la-gstrtpvorbisdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvorbisdepay.lo `test -f 'gstrtpvorbisdepay.c' || echo '$(srcdir)/'`gstrtpvorbisdepay.c + +libgstrtp_la-gstrtpvorbispay.lo: gstrtpvorbispay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvorbispay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvorbispay.Tpo -c -o libgstrtp_la-gstrtpvorbispay.lo `test -f 'gstrtpvorbispay.c' || echo '$(srcdir)/'`gstrtpvorbispay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvorbispay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvorbispay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvorbispay.c' object='libgstrtp_la-gstrtpvorbispay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvorbispay.lo `test -f 'gstrtpvorbispay.c' || echo '$(srcdir)/'`gstrtpvorbispay.c + +libgstrtp_la-gstrtpvp8depay.lo: gstrtpvp8depay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvp8depay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvp8depay.Tpo -c -o libgstrtp_la-gstrtpvp8depay.lo `test -f 'gstrtpvp8depay.c' || echo '$(srcdir)/'`gstrtpvp8depay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvp8depay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvp8depay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvp8depay.c' object='libgstrtp_la-gstrtpvp8depay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvp8depay.lo `test -f 'gstrtpvp8depay.c' || echo '$(srcdir)/'`gstrtpvp8depay.c + +libgstrtp_la-gstrtpvp8pay.lo: gstrtpvp8pay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvp8pay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvp8pay.Tpo -c -o libgstrtp_la-gstrtpvp8pay.lo `test -f 'gstrtpvp8pay.c' || echo '$(srcdir)/'`gstrtpvp8pay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvp8pay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvp8pay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvp8pay.c' object='libgstrtp_la-gstrtpvp8pay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvp8pay.lo `test -f 'gstrtpvp8pay.c' || echo '$(srcdir)/'`gstrtpvp8pay.c + +libgstrtp_la-gstrtpvrawdepay.lo: gstrtpvrawdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvrawdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvrawdepay.Tpo -c -o libgstrtp_la-gstrtpvrawdepay.lo `test -f 'gstrtpvrawdepay.c' || echo '$(srcdir)/'`gstrtpvrawdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvrawdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvrawdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvrawdepay.c' object='libgstrtp_la-gstrtpvrawdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvrawdepay.lo `test -f 'gstrtpvrawdepay.c' || echo '$(srcdir)/'`gstrtpvrawdepay.c + +libgstrtp_la-gstrtpvrawpay.lo: gstrtpvrawpay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpvrawpay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpvrawpay.Tpo -c -o libgstrtp_la-gstrtpvrawpay.lo `test -f 'gstrtpvrawpay.c' || echo '$(srcdir)/'`gstrtpvrawpay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpvrawpay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpvrawpay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpvrawpay.c' object='libgstrtp_la-gstrtpvrawpay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpvrawpay.lo `test -f 'gstrtpvrawpay.c' || echo '$(srcdir)/'`gstrtpvrawpay.c + +libgstrtp_la-gstrtpstreampay.lo: gstrtpstreampay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpstreampay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpstreampay.Tpo -c -o libgstrtp_la-gstrtpstreampay.lo `test -f 'gstrtpstreampay.c' || echo '$(srcdir)/'`gstrtpstreampay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpstreampay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpstreampay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpstreampay.c' object='libgstrtp_la-gstrtpstreampay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpstreampay.lo `test -f 'gstrtpstreampay.c' || echo '$(srcdir)/'`gstrtpstreampay.c + +libgstrtp_la-gstrtpstreamdepay.lo: gstrtpstreamdepay.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -MT libgstrtp_la-gstrtpstreamdepay.lo -MD -MP -MF $(DEPDIR)/libgstrtp_la-gstrtpstreamdepay.Tpo -c -o libgstrtp_la-gstrtpstreamdepay.lo `test -f 'gstrtpstreamdepay.c' || echo '$(srcdir)/'`gstrtpstreamdepay.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libgstrtp_la-gstrtpstreamdepay.Tpo $(DEPDIR)/libgstrtp_la-gstrtpstreamdepay.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='gstrtpstreamdepay.c' object='libgstrtp_la-gstrtpstreamdepay.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(libgstrtp_la_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libgstrtp_la_CFLAGS) $(CFLAGS) -c -o libgstrtp_la-gstrtpstreamdepay.lo `test -f 'gstrtpstreamdepay.c' || echo '$(srcdir)/'`gstrtpstreamdepay.c + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(plugindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-pluginLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-pluginLTLIBRARIES + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-pluginLTLIBRARIES + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-pluginLTLIBRARIES cscopelist-am ctags \ + ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pluginLTLIBRARIES install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-pluginLTLIBRARIES + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/gst/rtp/README b/gst/rtp/README new file mode 100755 index 0000000..5042bd5 --- /dev/null +++ b/gst/rtp/README @@ -0,0 +1,398 @@ +This directory contains some RTP payloaders/depayloaders for different payload +types. Use one payloader/depayloder pair per payload. If several payloads can be +payloaded/depayloaded by the same element, make different copies of it, one for +each payload. + +The application/x-rtp mime type +------------------------------- + +For valid RTP packets encapsulated in GstBuffers, we use the caps with +mime type application/x-rtp. + +The following fields can or must (*) be specified in the structure: + + * media: (String) [ "audio", "video", "application", "data", "control" ] + Defined in RFC 2327 in the SDP media announcement field. + Converted to lower case. + + * payload: (int) [0, 127] + For audio and video, these will normally be a media payload type as + defined in the RTP Audio/Video Profile. For dynamicaly allocated + payload types, this value will be >= 96 and the encoding-name must be + set. + + * clock-rate: (int) [0 - MAXINT] + The RTP clock rate. + + encoding-name: (String) ANY + typically second part of the mime type. ex. MP4V-ES. only required if + payload type >= 96. Converted to upper case. + + encoding-params: (String) ANY + extra encoding parameters (as in the SDP a=rtpmap: field). only required + if different from the default of the encoding-name. + Converted to lower-case. + + ssrc: (uint) [0 - MAXINT] + The ssrc value currently in use. (default = the SSRC of the first RTP + packet) + + timestamp-offset: (uint) [0 - MAXINT] + The RTP time representing time npt-start. (default = rtptime of first RTP + packet). + + seqnum-offset: (uint) [0 - MAXINT] + The RTP sequence number representing the first rtp packet. When this + parameter is given, all sequence numbers below this seqnum should be + ignored. (default = seqnum of first RTP packet). + + npt-start: (uint64) [0 - MAXINT] + The Normal Play Time for clock-base. This is the position in the stream and + is between 0 and the duration of the stream. This value is expressed in + nanoseconds GstClockTime. (default = 0) + + npt-stop: (uint64) [0 - MAXINT] + The last position in the stream. This value is expressed in nanoseconds + GstClockTime. (default = -1, stop unknown) + + play-speed: (gdouble) [-MIN - MAX] + The intended playback speed of the stream. The client is delivered data at + the adjusted speed. The client should adjust its playback speed with this + value and thus corresponds to the GStreamer rate field in the NEWSEGMENT + event. (default = 1.0) + + play-scale: (gdouble) [-MIN - MAX] + The rate already applied to the stream. The client is delivered a stream + that is scaled by this amount. This value is used to adjust position + reporting and corresponds to the GStream applied-rate field in the + NEWSEGMENT event. (default = 1.0) + + maxptime: (uint) [0, MAX] + The maxptime as defined in RFC 4566, this defines the maximum size of a + packet. It overrides the max-ptime property of payloaders. + + Optional parameters as key/value pairs, media type specific. The value type + should be of type G_TYPE_STRING. The key is converted to lower-case. The + value is left in its original case. + A parameter with no value is converted to <param>=1. + + Example: + + "application/x-rtp", + "media", G_TYPE_STRING, "audio", -. + "payload", G_TYPE_INT, 96, | - required + "clock-rate", G_TYPE_INT, 8000, -' + "encoding-name", G_TYPE_STRING, "AMR", -. - required since payload >= 96 + "encoding-params", G_TYPE_STRING, "1", -' - optional param for AMR + "octet-align", G_TYPE_STRING, "1", -. + "crc", G_TYPE_STRING, "0", | + "robust-sorting", G_TYPE_STRING, "0", | AMR specific params. + "interleaving", G_TYPE_STRING, "0", -' + + Mapping of caps to and from SDP fields: + + m=<media> <udp port> RTP/AVP <payload> -] media and payload from caps + a=rtpmap:<payload> <encoding-name>/<clock-rate>[/<encoding-params>] + -> when <payload> >= 96 + a=fmtp:<payload> <param>=<value>;... + + For above caps: + + m=audio <udp port> RTP/AVP 96 + a=rtpmap:96 AMR/8000/1 + a=fmtp:96 octet-align=1;crc=0;robust-sorting=0;interleaving=0 + + Attributes are converted as follows: + + IANA registered attribute names are prepended with 'a-' before putting them in + the caps. Unregistered keys (starting with 'x-') are copied directly into the + caps. + + in RTSP, the SSRC is also sent. + + The optional parameters in the SDP fields are case insensitive. In the caps we + always use the lowercase names so that the SDP -> caps mapping remains + possible. + + Mapping of caps to NEWSEGMENT: + + rate: <play-speed> + applied-rate: <play-scale> + format: GST_FORMAT_TIME + start: <clock-base> * GST_SECOND / <clock-rate> + stop: if <ntp-stop> != -1 + <npt-stop> - <npt-start> + start + else + -1 + time: <npt-start> + + +Timestamping +------------ + +RTP in GStreamer uses a combination of the RTP timestamps and GStreamer buffer +timestamps to ensure proper synchronisation at the sender and the receiver end. + +In RTP applications, the synchronisation is most complex at the receiver side. + +At the sender side, the RTP timestamps are generated in the payloaders based on +GStreamer timestamps. At the receiver, GStreamer timestamps are reconstructed +from the RTP timestamps and the GStreamer timestamps in the jitterbuffer. This +process is explained in more detail below. + += synchronisation at the sender + +Individual streams at the sender are synchronised using GStreamer timestamps. +The payloader at the sender will convert the GStreamer timestamp into an RTP +timestamp using the following formula: + + RTP = ((RT - RT-base) * clock-rate / GST_SECOND) + RTP-offset + + RTP: the RTP timestamp for the stream. This value is truncated to + 32 bits. + RT: the GStreamer running time corresponding to the timestamp of the + packet to payload + RT-base: the GStreamer running time of the first packet encoded + clock-rate: the clock-rate of the stream + RTP-offset: a random RTP offset + +The RTP timestamp corresponding to RT-base is the clock-base (see caps above). + +In addition to setting an RTP timestamp in the RTP packet, the payloader is also +responsible for putting the GStreamer timestamp on the resulting output buffer. +This timestamp is used for further synchronisation at the sender pipeline, such +as for sending out the packet on the network. + +Notice that the absolute timing information is lost; if the sender is sending +multiple streams, the RTP timestamps in the packets do not contain enough +information to synchronize them in the receiver. The receiver can however use +the RTP timestamps to reconstruct the timing of the stream as it was created by +the sender according to the sender's clock. + +Because the payloaded packet contains both an RTP timestamp and a GStreamer +timestamp, it is possible for an RTP session manager to derive the relation +between the RTP and GST timestamps. This information is used by a session +manager to create SR reports. The NTP time in the report will contain the +running time converted to NTP time and the corresponding RTP timestamp. + +Note that at the sender side, the RTP and GStreamer timestamp both increment at +the same rate, the sender rate. This rate depends on the global pipeline clock +of the sender. + +Some pipelines to illustrate the process: + + gst-launch-1.0 v4l2src ! videoconvert ! avenc_h263p ! rtph263ppay ! udpsink + + v4l2src puts a GStreamer timestamp on the video frames base on the current + running_time. The encoder encodes and passed the timestamp on. The payloader + generates an RTP timestamp using the above formula and puts it in the RTP + packet. It also copies the incomming GStreamer timestamp on the output RTP + packet. udpsink synchronizes on the gstreamer timestamp before pushing out the + packet. + + += synchronisation at the receiver + +The receiver is responsible for timestamping the received RTP packet with the +running_time of the clock at the time the packet was received. This GStreamer +timestamp reflects the receiver rate and depends on the global pipeline clock of +the receiver. The gstreamer timestamp of the received RTP packet contains a +certain amount of jitter introduced by the network. + +The most simple option for the receiver is to depayload the RTP packet and play +it back as soon as possible, this is with the timestamp when it was received +from the network. For the above sender pipeline this would be done with the +following pipeline: + + gst-launch-1.0 udpsrc caps="application/x-rtp, media=(string)video, + clock-rate=(int)90000, encoding-name=(string)H263-1998" ! rtph263pdepay ! + avdec_h263 ! autovideosink + +It is important that the depayloader copies the incomming GStreamer timestamp +directly to the depayloaded output buffer. It should never attempt to perform +any logic with the RTP timestamp, this task is for the jitterbuffer as we will +see next. + +The above pipeline does not attempt to deal with reordered packets or network +jitter, which could result in jerky playback in the case of high jitter or +corrupted video in the case of packet loss or reordering. This functionality is +performed by the gstrtpjitterbuffer in GStreamer. + +The task of the gstrtpjitterbuffer element is to: + + - deal with reordered packets based on the seqnum + - calculate the drift between the sender and receiver clocks using the + GStreamer timestamps (receiver clock rate) and RTP timestamps (sender clock + rate). + +To deal with reordered packet, the jitterbuffer holds on to the received RTP +packets in a queue for a configurable amount of time, called the latency. + +The jitterbuffer also eliminates network jitter and then tracks the drift +between the local clock (as expressed in the GStreamer timestamps) and the +remote clock (as expressed in the RTP timestamps). It will remove the jitter +and will apply the drift correction to the GStreamer timestamp before pushing +the buffer downstream. The result is that the depayloader receives a smoothed +GStreamer timestamp on the RTP packet, which is copied to the depayloaded data. + +The following pipeline illustrates a receiver with a jitterbuffer. + + gst-launch-1.0 udpsrc caps="application/x-rtp, media=(string)video, + clock-rate=(int)90000, encoding-name=(string)H263-1998" ! + rtpjitterbuffer latency=100 ! rtph263pdepay ! avdec_h263 ! autovideosink + +The latency property on the jitterbuffer controls the amount of delay (in +milliseconds) to apply to the outgoing packets. A higher latency will produce +smoother playback in networks with high jitter but cause a higher latency. +Choosing a good value for the latency is a tradeoff between the quality and +latency. The better the network, the lower the latency can be set. + + +usage with UDP +-------------- + +To correctly and completely use the RTP payloaders on the sender and the +receiver you need to write an application. It is not possible to write a full +blown RTP server with a single gst-launch-1.0 line. + +That said, it is possible to do something functional with a few gst-launch +lines. The biggest problem when constructing a correct gst-launch-1.0 line lies on +the receiver end. + +The receiver needs to know about the type of the RTP data along with a set of +RTP configuration parameters. This information is usually transmitted to the +client using some sort of session description language (SDP) over some reliable +channel (HTTP/RTSP/...). + +All of the required parameters to connect and use the RTP session on the +server can be found in the caps on the server end. The client receives this +information in some way (caps are converted to and from SDP, as explained above, +for example). + +Some gst-launch-1.0 lines: + + gst-launch-1.0 -v videotestsrc ! videoconvert ! avenc_h263p ! rtph263ppay ! udpsink + + Setting pipeline to PAUSED ... + /pipeline0/videotestsrc0.src: caps = video/x-raw, format=(string)I420, + width=(int)320, height=(int)240, framerate=(fraction)30/1 + Pipeline is PREROLLING ... + .... + /pipeline0/udpsink0.sink: caps = application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)H263-1998, + ssrc=(guint)527842345, clock-base=(guint)1150776941, seqnum-base=(guint)30982 + .... + Pipeline is PREROLLED ... + Setting pipeline to PLAYING ... + New clock: GstSystemClock + + Write down the caps on the udpsink and set them as the caps of the UDP + receiver: + + gst-launch-1.0 -v udpsrc caps="application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)H263-1998, + ssrc=(guint)527842345, clock-base=(guint)1150776941, seqnum-base=(guint)30982" + ! rtph263pdepay ! avdec_h263 ! autovideosink + + The receiver now displays an h263 image. Since there is no jitterbuffer in the + pipeline, frames will be displayed at the time when they are received. This can + result in jerky playback in the case of high network jitter or currupted video + when packets are dropped or reordered. + + Stream a quicktime file with mpeg4 video and AAC audio on port 5000 and port + 5002. + + gst-launch-1.0 -v filesrc location=~/data/sincity.mp4 ! qtdemux name=d ! queue ! rtpmp4vpay ! udpsink port=5000 + d. ! queue ! rtpmp4gpay ! udpsink port=5002 + .... + /pipeline0/udpsink0.sink: caps = application/x-rtp, media=(string)video, + payload=(int)96, clock-rate=(int)90000, encoding-name=(string)MP4V-ES, + ssrc=(guint)1162703703, clock-base=(guint)816135835, seqnum-base=(guint)9294, + profile-level-id=(string)3, config=(string)000001b003000001b50900000100000001200086c5d4c307d314043c1463000001b25876694430303334 + /pipeline0/udpsink1.sink: caps = application/x-rtp, media=(string)audio, + payload=(int)96, clock-rate=(int)44100, encoding-name=(string)MPEG4-GENERIC, + ssrc=(guint)3246149898, clock-base=(guint)4134514058, seqnum-base=(guint)57633, + encoding-params=(string)2, streamtype=(string)5, profile-level-id=(string)1, + mode=(string)aac-hbr, config=(string)1210, sizelength=(string)13, + indexlength=(string)3, indexdeltalength=(string)3 + .... + + Again copy the caps on both sinks to the receiver launch line + + gst-launch-1.0 + udpsrc port=5000 caps="application/x-rtp, media=(string)video, payload=(int)96, + clock-rate=(int)90000, encoding-name=(string)MP4V-ES, ssrc=(guint)1162703703, + clock-base=(guint)816135835, seqnum-base=(guint)9294, profile-level-id=(string)3, + config=(string)000001b003000001b50900000100000001200086c5d4c307d314043c1463000001b25876694430303334" + ! rtpmp4vdepay ! ffdec_mpeg4 ! autovideosink sync=false + udpsrc port=5002 caps="application/x-rtp, media=(string)audio, payload=(int)96, + clock-rate=(int)44100, encoding-name=(string)MPEG4-GENERIC, ssrc=(guint)3246149898, + clock-base=(guint)4134514058, seqnum-base=(guint)57633, encoding-params=(string)2, + streamtype=(string)5, profile-level-id=(string)1, mode=(string)aac-hbr, + config=(string)1210, sizelength=(string)13, indexlength=(string)3, + indexdeltalength=(string)3" + ! rtpmp4gdepay ! faad ! alsasink sync=false + + The caps on the udpsinks can be retrieved when the server pipeline prerolled to + PAUSED. + + The above pipeline sets sync=false on the audio and video sink which means that + no synchronisation will be performed in the sinks, they play the data when it + arrives. If you want to enable synchronisation in the sinks it is highly + recommended to use a gstrtpjitterbuffer after the udpsrc elements. + + Even when sync is enabled, the two different streams will not play synchronised + against eachother because the receiver does not have enough information to + perform this task. For this you need to add the rtpbin element in both the + sender and receiver pipeline and use additional sources and sinks to transmit + RTCP packets used for inter-stream synchronisation. + + The caps on the receiver side can be set on the UDP source elements when the + pipeline went to PAUSED. In that state no data is received from the UDP sources + as they are live sources and only produce data in PLAYING. + + +Relevant RFCs +------------- + +3550 RTP: A Transport Protocol for Real-Time Applications. ( 1889 Obsolete ) + +2198 RTP Payload for Redundant Audio Data. +3119 A More Loss-Tolerant RTP Payload Format for MP3 Audio. + +2793 RTP Payload for Text Conversation. + +2032 RTP Payload Format for H.261 Video Streams. +2190 RTP Payload Format for H.263 Video Streams. +2250 RTP Payload Format for MPEG1/MPEG2 Video. +2343 RTP Payload Format for Bundled MPEG. +2429 RTP Payload Format for the 1998 Version of ITU-T Rec. H.263 Video +2431 RTP Payload Format for BT.656 Video Encoding. +2435 RTP Payload Format for JPEG-compressed Video. +3016 RTP Payload Format for MPEG-4 Audio/Visual Streams. +3047 RTP Payload Format for ITU-T Recommendation G.722.1. +3189 RTP Payload Format for DV (IEC 61834) Video. +3190 RTP Payload Format for 12-bit DAT Audio and 20- and 24-bit Linear Sampled Audio. +3389 Real-time Transport Protocol (RTP) Payload for Comfort Noise (CN) +2733 An RTP Payload Format for Generic Forward Error Correction. +2833 RTP Payload for DTMF Digits, Telephony Tones and Telephony + Signals. +2862 RTP Payload Format for Real-Time Pointers. +3351 RTP Profile for Audio and Video Conferences with Minimal Control. ( 1890 Obsolete ) +3555 MIME Type Registration of RTP Payload Formats. + +2508 Compressing IP/UDP/RTP Headers for Low-Speed Serial Links. +1305 Network Time Protocol (Version 3) Specification, Implementation and Analysis. +3339 Date and Time on the Internet: Timestamps. +2246 The TLS Protocol Version 1.0 +3546 Transport Layer Security (TLS) Extensions. ( Updates 2246 ) + +do we care? +----------- + +2029 RTP Payload Format of Sun's CellB Video Encoding. + +usefull +------- + +http://www.iana.org/assignments/rtp-parameters diff --git a/gst/rtp/TODO b/gst/rtp/TODO new file mode 100755 index 0000000..8065a9c --- /dev/null +++ b/gst/rtp/TODO @@ -0,0 +1,15 @@ +* MPEG4 header + - ffmpeg mpeg4 decoder gives error message when sending only the config + string, parsing is OK, error just means no picture was found in the + stream. + +* compare H263 encoders and H263+ + +* better RTP packetizing for h263 + +* bitrate tuning in ffmpeg + - fixed the qmax values so we can quantize more. + +* make ffmpeg negotiate only with accepted framerates + + diff --git a/gst/rtp/dboolhuff.LICENSE b/gst/rtp/dboolhuff.LICENSE new file mode 100755 index 0000000..83e4e6f --- /dev/null +++ b/gst/rtp/dboolhuff.LICENSE @@ -0,0 +1,29 @@ +Copyright (c) 2010, Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/gst/rtp/dboolhuff.c b/gst/rtp/dboolhuff.c new file mode 100755 index 0000000..0e1fd6e --- /dev/null +++ b/gst/rtp/dboolhuff.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the dboolhuff.LICENSE file in this directory. + * See the libvpx original distribution for more information, + * including patent information, and author information. + */ + + +#include "dboolhuff.h" + +const unsigned char vp8_norm[256] __attribute__ ((aligned (16))) = { +0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + +int +vp8dx_start_decode (BOOL_DECODER * br, + const unsigned char *source, unsigned int source_sz) +{ + br->user_buffer_end = source + source_sz; + br->user_buffer = source; + br->value = 0; + br->count = -8; + br->range = 255; + + if (source_sz && !source) + return 1; + + /* Populate the buffer */ + vp8dx_bool_decoder_fill (br); + + return 0; +} + + +void +vp8dx_bool_decoder_fill (BOOL_DECODER * br) +{ + const unsigned char *bufptr; + const unsigned char *bufend; + VP8_BD_VALUE value; + int count; + bufend = br->user_buffer_end; + bufptr = br->user_buffer; + value = br->value; + count = br->count; + + VP8DX_BOOL_DECODER_FILL (count, value, bufptr, bufend); + + br->user_buffer = bufptr; + br->value = value; + br->count = count; +} diff --git a/gst/rtp/dboolhuff.h b/gst/rtp/dboolhuff.h new file mode 100755 index 0000000..41b0f5d --- /dev/null +++ b/gst/rtp/dboolhuff.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2010 The WebM project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the dboolhuff.LICENSE file in this directory. + * See the libvpx original distribution for more information, + * including patent information, and author information. + */ + + +#ifndef DBOOLHUFF_H +#define DBOOLHUFF_H +#include <stddef.h> +#include <limits.h> +#include <glib.h> + +typedef size_t VP8_BD_VALUE; + +# define VP8_BD_VALUE_SIZE ((int)sizeof(VP8_BD_VALUE)*CHAR_BIT) +/*This is meant to be a large, positive constant that can still be efficiently + loaded as an immediate (on platforms like ARM, for example). + Even relatively modest values like 100 would work fine.*/ +# define VP8_LOTS_OF_BITS (0x40000000) + +typedef struct +{ + const unsigned char *user_buffer_end; + const unsigned char *user_buffer; + VP8_BD_VALUE value; + int count; + unsigned int range; +} BOOL_DECODER; + +extern const unsigned char vp8_norm[256] __attribute__((aligned(16))); + +int vp8dx_start_decode(BOOL_DECODER *br, + const unsigned char *source, + unsigned int source_sz); + +void vp8dx_bool_decoder_fill(BOOL_DECODER *br); + +/*The refill loop is used in several places, so define it in a macro to make + sure they're all consistent. + An inline function would be cleaner, but has a significant penalty, because + multiple BOOL_DECODER fields must be modified, and the compiler is not smart + enough to eliminate the stores to those fields and the subsequent reloads + from them when inlining the function.*/ +#define VP8DX_BOOL_DECODER_FILL(_count,_value,_bufptr,_bufend) \ + do \ + { \ + int shift = VP8_BD_VALUE_SIZE - 8 - ((_count) + 8); \ + int loop_end, x; \ + size_t bits_left = ((_bufend)-(_bufptr))*CHAR_BIT; \ + \ + x = shift + CHAR_BIT - bits_left; \ + loop_end = 0; \ + if(x >= 0) \ + { \ + (_count) += VP8_LOTS_OF_BITS; \ + loop_end = x; \ + if(!bits_left) break; \ + } \ + while(shift >= loop_end) \ + { \ + (_count) += CHAR_BIT; \ + (_value) |= (VP8_BD_VALUE)*(_bufptr)++ << shift; \ + shift -= CHAR_BIT; \ + } \ + } \ + while(0) \ + + +static int vp8dx_decode_bool(BOOL_DECODER *br, int probability) { + unsigned int bit = 0; + VP8_BD_VALUE value; + unsigned int split; + VP8_BD_VALUE bigsplit; + int count; + unsigned int range; + + split = 1 + (((br->range - 1) * probability) >> 8); + + if(br->count < 0) + vp8dx_bool_decoder_fill(br); + + value = br->value; + count = br->count; + + bigsplit = (VP8_BD_VALUE)split << (VP8_BD_VALUE_SIZE - 8); + + range = split; + + if (value >= bigsplit) + { + range = br->range - split; + value = value - bigsplit; + bit = 1; + } + + { + register unsigned int shift = vp8_norm[range]; + range <<= shift; + value <<= shift; + count -= shift; + } + br->value = value; + br->count = count; + br->range = range; + + return bit; +} + +static G_GNUC_UNUSED int vp8_decode_value(BOOL_DECODER *br, int bits) +{ + int z = 0; + int bit; + + for (bit = bits - 1; bit >= 0; bit--) + { + z |= (vp8dx_decode_bool(br, 0x80) << bit); + } + + return z; +} + +static G_GNUC_UNUSED int vp8dx_bool_error(BOOL_DECODER *br) +{ + /* Check if we have reached the end of the buffer. + * + * Variable 'count' stores the number of bits in the 'value' buffer, minus + * 8. The top byte is part of the algorithm, and the remainder is buffered + * to be shifted into it. So if count == 8, the top 16 bits of 'value' are + * occupied, 8 for the algorithm and 8 in the buffer. + * + * When reading a byte from the user's buffer, count is filled with 8 and + * one byte is filled into the value buffer. When we reach the end of the + * data, count is additionally filled with VP8_LOTS_OF_BITS. So when + * count == VP8_LOTS_OF_BITS - 1, the user's data has been exhausted. + */ + if ((br->count > VP8_BD_VALUE_SIZE) && (br->count < VP8_LOTS_OF_BITS)) + { + /* We have tried to decode bits after the end of + * stream was encountered. + */ + return 1; + } + + /* No error. */ + return 0; +} +#endif diff --git a/gst/rtp/fnv1hash.c b/gst/rtp/fnv1hash.c new file mode 100755 index 0000000..9885bb2 --- /dev/null +++ b/gst/rtp/fnv1hash.c @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) 2007 Thomas Vander Stichele <thomas at apestaart dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> + +#include "fnv1hash.h" + +/* This file implements FNV-1 hashing used in the Ogg payload encoders + * to generate the 24-bit ident value based on the header pages. + * See http://isthe.com/chongo/tech/comp/fnv/ + */ + +#define MASK_24 (((guint32) 1 << 24) -1) + +#define FNV1_HASH_32_INIT ((guint32) 0x811C9DC5L) +//2166136261L) +#define FNV1_HASH_32_PRIME 16777619 + +guint32 +fnv1_hash_32_new (void) +{ + return FNV1_HASH_32_INIT; +} + +guint32 +fnv1_hash_32_update (guint32 hash, const guchar * data, guint length) +{ + guint i; + const guchar *p = data; + + for (i = 0; i < length; ++i, ++p) { + hash *= FNV1_HASH_32_PRIME; + hash ^= *p; + } + + return hash; +} + +guint32 +fnv1_hash_32_to_24 (guint32 hash) +{ + return (hash >> 24) ^ (hash & MASK_24); +} diff --git a/gst/rtp/fnv1hash.h b/gst/rtp/fnv1hash.h new file mode 100755 index 0000000..7047067 --- /dev/null +++ b/gst/rtp/fnv1hash.h @@ -0,0 +1,36 @@ +/* GStreamer + * Copyright (C) 2007 Thomas Vander Stichele <thomas at apestaart dot org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_FNV1_HASH_H__ +#define __GST_FNV1_HASH_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +guint32 fnv1_hash_32_new (void); +guint32 fnv1_hash_32_update (guint32 hash, const guchar *data, guint length); +guint32 fnv1_hash_32_to_24 (guint32 hash); + +G_END_DECLS + +#endif /* __GST_FNV1_HASH_H__ */ + diff --git a/gst/rtp/gstasteriskh263.c b/gst/rtp/gstasteriskh263.c new file mode 100755 index 0000000..72fe656 --- /dev/null +++ b/gst/rtp/gstasteriskh263.c @@ -0,0 +1,230 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstasteriskh263.h" + +#define GST_ASTERISKH263_HEADER_LEN 6 + +typedef struct _GstAsteriskH263Header +{ + guint32 timestamp; /* Timestamp */ + guint16 length; /* Length */ +} GstAsteriskH263Header; + +#define GST_ASTERISKH263_HEADER_TIMESTAMP(data) (((GstAsteriskH263Header *)(data))->timestamp) +#define GST_ASTERISKH263_HEADER_LENGTH(data) (((GstAsteriskH263Header *)(data))->length) + +static GstStaticPadTemplate gst_asteriskh263_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-asteriskh263") + ); + +static GstStaticPadTemplate gst_asteriskh263_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) [ 96, 127 ], " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263-1998\"") + ); + +static void gst_asteriskh263_finalize (GObject * object); + +static GstFlowReturn gst_asteriskh263_chain (GstPad * pad, GstObject * parent, + GstBuffer * buffer); + +static GstStateChangeReturn gst_asteriskh263_change_state (GstElement * + element, GstStateChange transition); + +#define gst_asteriskh263_parent_class parent_class +G_DEFINE_TYPE (GstAsteriskh263, gst_asteriskh263, GST_TYPE_ELEMENT); + +static void +gst_asteriskh263_class_init (GstAsteriskh263Class * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + + gobject_class->finalize = gst_asteriskh263_finalize; + + gstelement_class->change_state = gst_asteriskh263_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_asteriskh263_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_asteriskh263_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Asterisk H263 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts H263 video from RTP and encodes in Asterisk H263 format", + "Neil Stratford <neils@vipadia.com>"); +} + +static void +gst_asteriskh263_init (GstAsteriskh263 * asteriskh263) +{ + asteriskh263->srcpad = + gst_pad_new_from_static_template (&gst_asteriskh263_src_template, "src"); + gst_element_add_pad (GST_ELEMENT (asteriskh263), asteriskh263->srcpad); + + asteriskh263->sinkpad = + gst_pad_new_from_static_template (&gst_asteriskh263_sink_template, + "sink"); + gst_pad_set_chain_function (asteriskh263->sinkpad, gst_asteriskh263_chain); + gst_element_add_pad (GST_ELEMENT (asteriskh263), asteriskh263->sinkpad); + + asteriskh263->adapter = gst_adapter_new (); +} + +static void +gst_asteriskh263_finalize (GObject * object) +{ + GstAsteriskh263 *asteriskh263; + + asteriskh263 = GST_ASTERISK_H263 (object); + + g_object_unref (asteriskh263->adapter); + asteriskh263->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static GstFlowReturn +gst_asteriskh263_chain (GstPad * pad, GstObject * parent, GstBuffer * buf) +{ + GstAsteriskh263 *asteriskh263; + GstBuffer *outbuf; + GstFlowReturn ret; + + asteriskh263 = GST_ASTERISK_H263 (parent); + + { + gint payload_len; + guint8 *payload; + gboolean M; + guint32 timestamp; + guint32 samples; + guint16 asterisk_len; + GstRTPBuffer rtp = { NULL }; + GstMapInfo map; + + if (!gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp)) + goto bad_packet; + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + M = gst_rtp_buffer_get_marker (&rtp); + timestamp = gst_rtp_buffer_get_timestamp (&rtp); + + gst_rtp_buffer_unmap (&rtp); + + outbuf = gst_buffer_new_and_alloc (payload_len + + GST_ASTERISKH263_HEADER_LEN); + + /* build the asterisk header */ + asterisk_len = payload_len; + if (M) + asterisk_len |= 0x8000; + if (!asteriskh263->lastts) + asteriskh263->lastts = timestamp; + samples = timestamp - asteriskh263->lastts; + asteriskh263->lastts = timestamp; + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + GST_ASTERISKH263_HEADER_TIMESTAMP (map.data) = g_htonl (samples); + GST_ASTERISKH263_HEADER_LENGTH (map.data) = g_htons (asterisk_len); + + /* copy the data into place */ + memcpy (map.data + GST_ASTERISKH263_HEADER_LEN, payload, payload_len); + + gst_buffer_unmap (outbuf, &map); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + if (!gst_pad_has_current_caps (asteriskh263->srcpad)) { + GstCaps *caps; + + caps = gst_pad_get_pad_template_caps (asteriskh263->srcpad); + gst_pad_set_caps (asteriskh263->srcpad, caps); + gst_caps_unref (caps); + } + + ret = gst_pad_push (asteriskh263->srcpad, outbuf); + + gst_buffer_unref (buf); + } + + return ret; + +bad_packet: + { + GST_DEBUG ("Packet does not validate"); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } +} + +static GstStateChangeReturn +gst_asteriskh263_change_state (GstElement * element, GstStateChange transition) +{ + GstAsteriskh263 *asteriskh263; + GstStateChangeReturn ret; + + asteriskh263 = GST_ASTERISK_H263 (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (asteriskh263->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + /* + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + */ + return ret; +} + +gboolean +gst_asteriskh263_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "asteriskh263", + GST_RANK_SECONDARY-1, GST_TYPE_ASTERISK_H263); +} diff --git a/gst/rtp/gstasteriskh263.h b/gst/rtp/gstasteriskh263.h new file mode 100755 index 0000000..1c9523d --- /dev/null +++ b/gst/rtp/gstasteriskh263.h @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_ASTERISK_H263_H__ +#define __GST_ASTERISK_H263_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_ASTERISK_H263 \ + (gst_asteriskh263_get_type()) +#define GST_ASTERISK_H263(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ASTERISK_H263,GstAsteriskh263)) +#define GST_ASTERISK_H263_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ASTERISK_H263,GstAsteriskh263Class)) +#define GST_IS_ASTERISK_H263(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ASTERISK_H263)) +#define GST_IS_ASTERISK_H263_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ASTERISK_H263)) + +typedef struct _GstAsteriskh263 GstAsteriskh263; +typedef struct _GstAsteriskh263Class GstAsteriskh263Class; + +struct _GstAsteriskh263 +{ + GstElement element; + + GstPad *sinkpad; + GstPad *srcpad; + + GstAdapter *adapter; + + guint32 lastts; +}; + +struct _GstAsteriskh263Class +{ + GstElementClass parent_class; +}; + +GType gst_asteriskh263_get_type (void); + +gboolean gst_asteriskh263_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_ASTERISK_H263_H__ */ diff --git a/gst/rtp/gstrtp.c b/gst/rtp/gstrtp.c new file mode 100755 index 0000000..a3cbccd --- /dev/null +++ b/gst/rtp/gstrtp.c @@ -0,0 +1,343 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <gst/tag/tag.h> + +#include "gstrtpac3depay.h" +#include "gstrtpac3pay.h" +#include "gstrtpbvdepay.h" +#include "gstrtpbvpay.h" +#include "gstrtpceltdepay.h" +#include "gstrtpceltpay.h" +#include "gstrtpdvdepay.h" +#include "gstrtpdvpay.h" +#include "gstrtpgstdepay.h" +#include "gstrtpgstpay.h" +#include "gstrtpilbcdepay.h" +#include "gstrtpilbcpay.h" +#include "gstrtppcmupay.h" +#include "gstrtppcmapay.h" +#include "gstrtppcmadepay.h" +#include "gstrtppcmudepay.h" +#include "gstrtpg722depay.h" +#include "gstrtpg722pay.h" +#include "gstrtpg723depay.h" +#include "gstrtpg723pay.h" +#include "gstrtpg726depay.h" +#include "gstrtpg726pay.h" +#include "gstrtpg729depay.h" +#include "gstrtpg729pay.h" +#include "gstrtpgsmpay.h" +#include "gstrtpgsmdepay.h" +#include "gstrtpamrpay.h" +#include "gstrtpamrdepay.h" +#include "gstrtpmpapay.h" +#include "gstrtpmpadepay.h" +#include "gstrtpmparobustdepay.h" +#include "gstrtpmpvdepay.h" +#include "gstrtpmpvpay.h" +#include "gstrtph263pdepay.h" +#include "gstrtph263ppay.h" +#include "gstrtph263depay.h" +#include "gstrtph263pay.h" +#include "gstrtph264depay.h" +#include "gstrtph264pay.h" +#include "gstrtpj2kdepay.h" +#include "gstrtpj2kpay.h" +#include "gstrtpjpegdepay.h" +#include "gstrtpjpegpay.h" +#include "gstrtpL16depay.h" +#include "gstrtpL16pay.h" +#include "gstrtpL24depay.h" +#include "gstrtpL24pay.h" +#include "gstasteriskh263.h" +#include "gstrtpmp1sdepay.h" +#include "gstrtpmp2tdepay.h" +#include "gstrtpmp2tpay.h" +#include "gstrtpmp4vdepay.h" +#include "gstrtpmp4vpay.h" +#include "gstrtpmp4adepay.h" +#include "gstrtpmp4apay.h" +#include "gstrtpmp4gdepay.h" +#include "gstrtpmp4gpay.h" +#include "gstrtpqcelpdepay.h" +#include "gstrtpqdmdepay.h" +#include "gstrtpsbcdepay.h" +#include "gstrtpsbcpay.h" +#include "gstrtpsirenpay.h" +#include "gstrtpsirendepay.h" +#include "gstrtpspeexpay.h" +#include "gstrtpspeexdepay.h" +#include "gstrtpsv3vdepay.h" +#include "gstrtptheoradepay.h" +#include "gstrtptheorapay.h" +#include "gstrtpvorbisdepay.h" +#include "gstrtpvorbispay.h" +#include "gstrtpvp8depay.h" +#include "gstrtpvp8pay.h" +#include "gstrtpvrawdepay.h" +#include "gstrtpvrawpay.h" +#include "gstrtpstreampay.h" +#include "gstrtpstreamdepay.h" + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gst_tag_image_type_get_type (); + + if (!gst_rtp_ac3_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_ac3_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_bv_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_bv_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_celt_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_celt_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_dv_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_dv_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_gst_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_gst_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_ilbc_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_ilbc_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g722_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g722_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g723_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g723_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g726_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g726_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g729_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_g729_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_gsm_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_gsm_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_amr_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_amr_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_pcma_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_pcmu_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_pcmu_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_pcma_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mpa_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mpa_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mpa_robust_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mpv_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mpv_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h263p_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h263p_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h263_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h263_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h264_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_h264_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_j2k_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_j2k_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_jpeg_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_jpeg_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_L16_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_L16_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_L24_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_L24_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_asteriskh263_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp1s_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp2t_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp2t_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4v_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4v_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4a_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4a_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4g_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_mp4g_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_qcelp_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_qdm2_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_sbc_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_sbc_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_siren_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_siren_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_speex_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_speex_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_sv3v_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_theora_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_theora_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vorbis_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vorbis_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vp8_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vp8_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vraw_depay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_vraw_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_stream_pay_plugin_init (plugin)) + return FALSE; + + if (!gst_rtp_stream_depay_plugin_init (plugin)) + return FALSE; + + return TRUE; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + rtp, + "Real-time protocol plugins", + plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/gst/rtp/gstrtpL16depay.c b/gst/rtp/gstrtpL16depay.c new file mode 100755 index 0000000..667a64a --- /dev/null +++ b/gst/rtp/gstrtpL16depay.c @@ -0,0 +1,288 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpL16depay + * @see_also: rtpL16pay + * + * Extract raw audio from RTP packets according to RFC 3551. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3551.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 udpsrc caps='application/x-rtp, media=(string)audio, clock-rate=(int)44100, encoding-name=(string)L16, encoding-params=(string)1, channels=(int)1, payload=(int)96' ! rtpL16depay ! pulsesink + * ]| This example pipeline will depayload an RTP raw audio stream. Refer to + * the rtpL16pay example to create the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gst/audio/audio.h> + +#include "gstrtpL16depay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpL16depay_debug); +#define GST_CAT_DEFAULT (rtpL16depay_debug) + +static GstStaticPadTemplate gst_rtp_L16_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) S16BE, " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_L16_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " "clock-rate = (int) [ 1, MAX ], " + /* "channels = (int) [1, MAX]" */ + /* "emphasis = (string) ANY" */ + /* "channel-order = (string) ANY" */ + "encoding-name = (string) \"L16\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) { " GST_RTP_PAYLOAD_L16_STEREO_STRING ", " + GST_RTP_PAYLOAD_L16_MONO_STRING " }," "clock-rate = (int) [ 1, MAX ]" + /* "channels = (int) [1, MAX]" */ + /* "emphasis = (string) ANY" */ + /* "channel-order = (string) ANY" */ + ) + ); + +#define gst_rtp_L16_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpL16Depay, gst_rtp_L16_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_L16_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_L16_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_L16_depay_class_init (GstRtpL16DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->set_caps = gst_rtp_L16_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_L16_depay_process; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L16_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L16_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts raw audio from RTP packets", + "Zeeshan Ali <zak147@yahoo.com>," "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpL16depay_debug, "rtpL16depay", 0, + "Raw Audio RTP Depayloader"); +} + +static void +gst_rtp_L16_depay_init (GstRtpL16Depay * rtpL16depay) +{ +} + +static gint +gst_rtp_L16_depay_parse_int (GstStructure * structure, const gchar * field, + gint def) +{ + const gchar *str; + gint res; + + if ((str = gst_structure_get_string (structure, field))) + return atoi (str); + + if (gst_structure_get_int (structure, field, &res)) + return res; + + return def; +} + +static gboolean +gst_rtp_L16_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpL16Depay *rtpL16depay; + gint clock_rate, payload; + gint channels; + GstCaps *srccaps; + gboolean res; + const gchar *channel_order; + const GstRTPChannelOrder *order; + GstAudioInfo *info; + + rtpL16depay = GST_RTP_L16_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + payload = 96; + gst_structure_get_int (structure, "payload", &payload); + switch (payload) { + case GST_RTP_PAYLOAD_L16_STEREO: + channels = 2; + clock_rate = 44100; + break; + case GST_RTP_PAYLOAD_L16_MONO: + channels = 1; + clock_rate = 44100; + break; + default: + /* no fixed mapping, we need clock-rate */ + channels = 0; + clock_rate = 0; + break; + } + + /* caps can overwrite defaults */ + clock_rate = + gst_rtp_L16_depay_parse_int (structure, "clock-rate", clock_rate); + if (clock_rate == 0) + goto no_clockrate; + + channels = + gst_rtp_L16_depay_parse_int (structure, "encoding-params", channels); + if (channels == 0) { + channels = gst_rtp_L16_depay_parse_int (structure, "channels", channels); + if (channels == 0) { + /* channels defaults to 1 otherwise */ + channels = 1; + } + } + + depayload->clock_rate = clock_rate; + + info = &rtpL16depay->info; + gst_audio_info_init (info); + info->finfo = gst_audio_format_get_info (GST_AUDIO_FORMAT_S16BE); + info->rate = clock_rate; + info->channels = channels; + info->bpf = (info->finfo->width / 8) * channels; + + /* add channel positions */ + channel_order = gst_structure_get_string (structure, "channel-order"); + + order = gst_rtp_channels_get_by_order (channels, channel_order); + rtpL16depay->order = order; + if (order) { + memcpy (info->position, order->pos, + sizeof (GstAudioChannelPosition) * channels); + gst_audio_channel_positions_to_valid_order (info->position, info->channels); + } else { + GST_ELEMENT_WARNING (rtpL16depay, STREAM, DECODE, + (NULL), ("Unknown channel order '%s' for %d channels", + GST_STR_NULL (channel_order), channels)); + /* create default NONE layout */ + gst_rtp_channels_create_default (channels, info->position); + } + + srccaps = gst_audio_info_to_caps (info); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +no_clockrate: + { + GST_ERROR_OBJECT (depayload, "no clock-rate specified"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_L16_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpL16Depay *rtpL16depay; + GstBuffer *outbuf; + gint payload_len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + rtpL16depay = GST_RTP_L16_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len <= 0) + goto empty_packet; + + GST_DEBUG_OBJECT (rtpL16depay, "got payload of %d bytes", payload_len); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + + if (marker) { + /* mark talk spurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + outbuf = gst_buffer_make_writable (outbuf); + if (rtpL16depay->order && + !gst_audio_buffer_reorder_channels (outbuf, + rtpL16depay->info.finfo->format, rtpL16depay->info.channels, + rtpL16depay->info.position, rtpL16depay->order->pos)) { + goto reorder_failed; + } + + gst_rtp_buffer_unmap (&rtp); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpL16depay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +reorder_failed: + { + GST_ELEMENT_ERROR (rtpL16depay, STREAM, DECODE, + ("Channel reordering failed."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_L16_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpL16depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_L16_DEPAY); +} diff --git a/gst/rtp/gstrtpL16depay.h b/gst/rtp/gstrtpL16depay.h new file mode 100755 index 0000000..125d4cd --- /dev/null +++ b/gst/rtp/gstrtpL16depay.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_L16_DEPAY_H__ +#define __GST_RTP_L16_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> +#include <gst/audio/audio.h> + +#include "gstrtpchannels.h" + +G_BEGIN_DECLS + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_RTP_L16_DEPAY \ + (gst_rtp_L16_depay_get_type()) +#define GST_RTP_L16_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_L16_DEPAY,GstRtpL16Depay)) +#define GST_RTP_L16_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_L16_DEPAY,GstRtpL16DepayClass)) +#define GST_IS_RTP_L16_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_L16_DEPAY)) +#define GST_IS_RTP_L16_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_L16_DEPAY)) + +typedef struct _GstRtpL16Depay GstRtpL16Depay; +typedef struct _GstRtpL16DepayClass GstRtpL16DepayClass; + +/* Definition of structure storing data for this element. */ +struct _GstRtpL16Depay +{ + GstRTPBaseDepayload depayload; + + GstAudioInfo info; + const GstRTPChannelOrder *order; +}; + +/* Standard definition defining a class for this element. */ +struct _GstRtpL16DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_L16_depay_get_type (void); + +gboolean gst_rtp_L16_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_L16_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpL16pay.c b/gst/rtp/gstrtpL16pay.c new file mode 100755 index 0000000..a8ed36f --- /dev/null +++ b/gst/rtp/gstrtpL16pay.c @@ -0,0 +1,263 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpL16pay + * @see_also: rtpL16depay + * + * Payload raw audio into RTP packets according to RFC 3551. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3551.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 -v audiotestsrc ! audioconvert ! rtpL16pay ! udpsink + * ]| This example pipeline will payload raw audio. Refer to + * the rtpL16depay example to depayload and play the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/audio/audio.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpL16pay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpL16pay_debug); +#define GST_CAT_DEFAULT (rtpL16pay_debug) + +static GstStaticPadTemplate gst_rtp_L16_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) S16BE, " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_L16_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) [ 96, 127 ], " + "clock-rate = (int) [ 1, MAX ], " + "encoding-name = (string) \"L16\", " + "channels = (int) [ 1, MAX ];" + "application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"L16\", " + "payload = (int) " GST_RTP_PAYLOAD_L16_STEREO_STRING ", " + "clock-rate = (int) 44100;" + "application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"L16\", " + "payload = (int) " GST_RTP_PAYLOAD_L16_MONO_STRING ", " + "clock-rate = (int) 44100") + ); + +static gboolean gst_rtp_L16_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstCaps *gst_rtp_L16_pay_getcaps (GstRTPBasePayload * rtppayload, + GstPad * pad, GstCaps * filter); +static GstFlowReturn +gst_rtp_L16_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer); + +#define gst_rtp_L16_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpL16Pay, gst_rtp_L16_pay, GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_L16_pay_class_init (GstRtpL16PayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstrtpbasepayload_class->set_caps = gst_rtp_L16_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_L16_pay_getcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_L16_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L16_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L16_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio payloader", "Codec/Payloader/Network/RTP", + "Payload-encode Raw audio into RTP packets (RFC 3551)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpL16pay_debug, "rtpL16pay", 0, + "L16 RTP Payloader"); +} + +static void +gst_rtp_L16_pay_init (GstRtpL16Pay * rtpL16pay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpL16pay); + + /* tell rtpbaseaudiopayload that this is a sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_L16_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpL16Pay *rtpL16pay; + gboolean res; + gchar *params; + GstAudioInfo *info; + const GstRTPChannelOrder *order; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (basepayload); + rtpL16pay = GST_RTP_L16_PAY (basepayload); + + info = &rtpL16pay->info; + gst_audio_info_init (info); + if (!gst_audio_info_from_caps (info, caps)) + goto invalid_caps; + + order = gst_rtp_channels_get_by_pos (info->channels, info->position); + rtpL16pay->order = order; + + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "L16", + info->rate); + params = g_strdup_printf ("%d", info->channels); + + if (!order && info->channels > 2) { + GST_ELEMENT_WARNING (rtpL16pay, STREAM, DECODE, + (NULL), ("Unknown channel order for %d channels", info->channels)); + } + + if (order && order->name) { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + info->channels, "channel-order", G_TYPE_STRING, order->name, NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + info->channels, NULL); + } + + g_free (params); + + /* octet-per-sample is 2 * channels for L16 */ + gst_rtp_base_audio_payload_set_sample_options (rtpbaseaudiopayload, + 2 * info->channels); + + return res; + + /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (rtpL16pay, "invalid caps"); + return FALSE; + } +} + +static GstCaps * +gst_rtp_L16_pay_getcaps (GstRTPBasePayload * rtppayload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + caps = gst_pad_get_pad_template_caps (pad); + + otherpadcaps = gst_pad_get_allowed_caps (rtppayload->srcpad); + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *structure; + gint channels; + gint pt; + gint rate; + + structure = gst_caps_get_structure (otherpadcaps, 0); + caps = gst_caps_make_writable (caps); + + if (gst_structure_get_int (structure, "channels", &channels)) { + gst_caps_set_simple (caps, "channels", G_TYPE_INT, channels, NULL); + } else if (gst_structure_get_int (structure, "payload", &pt)) { + if (pt == 10) + gst_caps_set_simple (caps, "channels", G_TYPE_INT, 2, NULL); + else if (pt == 11) + gst_caps_set_simple (caps, "channels", G_TYPE_INT, 1, NULL); + } + + if (gst_structure_get_int (structure, "clock-rate", &rate)) { + gst_caps_set_simple (caps, "rate", G_TYPE_INT, rate, NULL); + } else if (gst_structure_get_int (structure, "payload", &pt)) { + if (pt == 10 || pt == 11) + gst_caps_set_simple (caps, "rate", G_TYPE_INT, 44100, NULL); + } + + } + gst_caps_unref (otherpadcaps); + } + + if (filter) { + GstCaps *tcaps = caps; + + caps = gst_caps_intersect_full (filter, tcaps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tcaps); + } + + return caps; +} + +static GstFlowReturn +gst_rtp_L16_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpL16Pay *rtpL16pay; + + rtpL16pay = GST_RTP_L16_PAY (basepayload); + buffer = gst_buffer_make_writable (buffer); + + if (rtpL16pay->order && + !gst_audio_buffer_reorder_channels (buffer, rtpL16pay->info.finfo->format, + rtpL16pay->info.channels, rtpL16pay->info.position, + rtpL16pay->order->pos)) { + return GST_FLOW_ERROR; + } + + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->handle_buffer (basepayload, + buffer); +} + +gboolean +gst_rtp_L16_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpL16pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_L16_PAY); +} diff --git a/gst/rtp/gstrtpL16pay.h b/gst/rtp/gstrtpL16pay.h new file mode 100755 index 0000000..f4f3702 --- /dev/null +++ b/gst/rtp/gstrtpL16pay.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_L16_PAY_H__ +#define __GST_RTP_L16_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +#include "gstrtpchannels.h" + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_L16_PAY \ + (gst_rtp_L16_pay_get_type()) +#define GST_RTP_L16_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_L16_PAY,GstRtpL16Pay)) +#define GST_RTP_L16_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_L16_PAY,GstRtpL16PayClass)) +#define GST_IS_RTP_L16_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_L16_PAY)) +#define GST_IS_RTP_L16_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_L16_PAY)) + +typedef struct _GstRtpL16Pay GstRtpL16Pay; +typedef struct _GstRtpL16PayClass GstRtpL16PayClass; + +struct _GstRtpL16Pay +{ + GstRTPBaseAudioPayload payload; + + GstAudioInfo info; + const GstRTPChannelOrder *order; +}; + +struct _GstRtpL16PayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_L16_pay_get_type (void); + +gboolean gst_rtp_L16_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_L16_PAY_H__ */ diff --git a/gst/rtp/gstrtpL24depay.c b/gst/rtp/gstrtpL24depay.c new file mode 100755 index 0000000..7b5ef0c --- /dev/null +++ b/gst/rtp/gstrtpL24depay.c @@ -0,0 +1,266 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpL24depay + * @see_also: rtpL24pay + * + * Extract raw audio from RTP packets according to RFC 3190, section 4. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3190.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 udpsrc caps='application/x-rtp, media=(string)audio, clock-rate=(int)44100, encoding-name=(string)L24, encoding-params=(string)1, channels=(int)1, payload=(int)96' ! rtpL24depay ! pulsesink + * ]| This example pipeline will depayload an RTP raw audio stream. Refer to + * the rtpL24pay example to create the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gst/audio/audio.h> + +#include "gstrtpL24depay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpL24depay_debug); +#define GST_CAT_DEFAULT (rtpL24depay_debug) + +static GstStaticPadTemplate gst_rtp_L24_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) S24BE, " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_L24_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " "clock-rate = (int) [ 1, MAX ], " + "encoding-name = (string) \"L24\"") + ); + +#define gst_rtp_L24_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpL24Depay, gst_rtp_L24_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_L24_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_L24_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_L24_depay_class_init (GstRtpL24DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->set_caps = gst_rtp_L24_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_L24_depay_process; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L24_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L24_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts raw 24-bit audio from RTP packets", + "Zeeshan Ali <zak147@yahoo.com>," "Wim Taymans <wim.taymans@gmail.com>," + "David Holroyd <dave@badgers-in-foil.co.uk>"); + + GST_DEBUG_CATEGORY_INIT (rtpL24depay_debug, "rtpL24depay", 0, + "Raw Audio RTP Depayloader"); +} + +static void +gst_rtp_L24_depay_init (GstRtpL24Depay * rtpL24depay) +{ +} + +static gint +gst_rtp_L24_depay_parse_int (GstStructure * structure, const gchar * field, + gint def) +{ + const gchar *str; + gint res; + + if ((str = gst_structure_get_string (structure, field))) + return atoi (str); + + if (gst_structure_get_int (structure, field, &res)) + return res; + + return def; +} + +static gboolean +gst_rtp_L24_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpL24Depay *rtpL24depay; + gint clock_rate, payload; + gint channels; + GstCaps *srccaps; + gboolean res; + const gchar *channel_order; + const GstRTPChannelOrder *order; + GstAudioInfo *info; + + rtpL24depay = GST_RTP_L24_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + payload = 96; + gst_structure_get_int (structure, "payload", &payload); + /* no fixed mapping, we need clock-rate */ + channels = 0; + clock_rate = 0; + + /* caps can overwrite defaults */ + clock_rate = + gst_rtp_L24_depay_parse_int (structure, "clock-rate", clock_rate); + if (clock_rate == 0) + goto no_clockrate; + + channels = + gst_rtp_L24_depay_parse_int (structure, "encoding-params", channels); + if (channels == 0) { + channels = gst_rtp_L24_depay_parse_int (structure, "channels", channels); + if (channels == 0) { + /* channels defaults to 1 otherwise */ + channels = 1; + } + } + + depayload->clock_rate = clock_rate; + + info = &rtpL24depay->info; + gst_audio_info_init (info); + info->finfo = gst_audio_format_get_info (GST_AUDIO_FORMAT_S24BE); + info->rate = clock_rate; + info->channels = channels; + info->bpf = (info->finfo->width / 8) * channels; + + /* add channel positions */ + channel_order = gst_structure_get_string (structure, "channel-order"); + + order = gst_rtp_channels_get_by_order (channels, channel_order); + rtpL24depay->order = order; + if (order) { + memcpy (info->position, order->pos, + sizeof (GstAudioChannelPosition) * channels); + gst_audio_channel_positions_to_valid_order (info->position, info->channels); + } else { + GST_ELEMENT_WARNING (rtpL24depay, STREAM, DECODE, + (NULL), ("Unknown channel order '%s' for %d channels", + GST_STR_NULL (channel_order), channels)); + /* create default NONE layout */ + gst_rtp_channels_create_default (channels, info->position); + } + + srccaps = gst_audio_info_to_caps (info); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +no_clockrate: + { + GST_ERROR_OBJECT (depayload, "no clock-rate specified"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_L24_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpL24Depay *rtpL24depay; + GstBuffer *outbuf; + gint payload_len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + rtpL24depay = GST_RTP_L24_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len <= 0) + goto empty_packet; + + GST_DEBUG_OBJECT (rtpL24depay, "got payload of %d bytes", payload_len); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + + if (marker) { + /* mark talk spurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + outbuf = gst_buffer_make_writable (outbuf); + if (rtpL24depay->order && + !gst_audio_buffer_reorder_channels (outbuf, + rtpL24depay->info.finfo->format, rtpL24depay->info.channels, + rtpL24depay->info.position, rtpL24depay->order->pos)) { + goto reorder_failed; + } + + gst_rtp_buffer_unmap (&rtp); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpL24depay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +reorder_failed: + { + GST_ELEMENT_ERROR (rtpL24depay, STREAM, DECODE, + ("Channel reordering failed."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_L24_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpL24depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_L24_DEPAY); +} diff --git a/gst/rtp/gstrtpL24depay.h b/gst/rtp/gstrtpL24depay.h new file mode 100755 index 0000000..c4e00e6 --- /dev/null +++ b/gst/rtp/gstrtpL24depay.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_L24_DEPAY_H__ +#define __GST_RTP_L24_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> +#include <gst/audio/audio.h> + +#include "gstrtpchannels.h" + +G_BEGIN_DECLS + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_RTP_L24_DEPAY \ + (gst_rtp_L24_depay_get_type()) +#define GST_RTP_L24_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_L24_DEPAY,GstRtpL24Depay)) +#define GST_RTP_L24_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_L24_DEPAY,GstRtpL24DepayClass)) +#define GST_IS_RTP_L24_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_L24_DEPAY)) +#define GST_IS_RTP_L24_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_L24_DEPAY)) + +typedef struct _GstRtpL24Depay GstRtpL24Depay; +typedef struct _GstRtpL24DepayClass GstRtpL24DepayClass; + +/* Definition of structure storing data for this element. */ +struct _GstRtpL24Depay +{ + GstRTPBaseDepayload depayload; + + GstAudioInfo info; + const GstRTPChannelOrder *order; +}; + +/* Standard definition defining a class for this element. */ +struct _GstRtpL24DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_L24_depay_get_type (void); + +gboolean gst_rtp_L24_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_L24_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpL24pay.c b/gst/rtp/gstrtpL24pay.c new file mode 100755 index 0000000..1bb37cb --- /dev/null +++ b/gst/rtp/gstrtpL24pay.c @@ -0,0 +1,244 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpL24pay + * @see_also: rtpL24depay + * + * Payload raw 24-bit audio into RTP packets according to RFC 3190, section 4. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3190.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 -v audiotestsrc ! audioconvert ! rtpL24pay ! udpsink + * ]| This example pipeline will payload raw audio. Refer to + * the rtpL24depay example to depayload and play the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/audio/audio.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpL24pay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpL24pay_debug); +#define GST_CAT_DEFAULT (rtpL24pay_debug) + +static GstStaticPadTemplate gst_rtp_L24_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) S24BE, " + "layout = (string) interleaved, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_L24_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) [ 96, 127 ], " + "clock-rate = (int) [ 1, MAX ], " + "encoding-name = (string) \"L24\", " "channels = (int) [ 1, MAX ];") + ); + +static gboolean gst_rtp_L24_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstCaps *gst_rtp_L24_pay_getcaps (GstRTPBasePayload * rtppayload, + GstPad * pad, GstCaps * filter); +static GstFlowReturn +gst_rtp_L24_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer); + +#define gst_rtp_L24_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpL24Pay, gst_rtp_L24_pay, GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_L24_pay_class_init (GstRtpL24PayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstrtpbasepayload_class->set_caps = gst_rtp_L24_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_L24_pay_getcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_L24_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L24_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_L24_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio payloader", "Codec/Payloader/Network/RTP", + "Payload-encode Raw 24-bit audio into RTP packets (RFC 3190)", + "Wim Taymans <wim.taymans@gmail.com>," + "David Holroyd <dave@badgers-in-foil.co.uk>"); + + GST_DEBUG_CATEGORY_INIT (rtpL24pay_debug, "rtpL24pay", 0, + "L24 RTP Payloader"); +} + +static void +gst_rtp_L24_pay_init (GstRtpL24Pay * rtpL24pay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpL24pay); + + /* tell rtpbaseaudiopayload that this is a sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_L24_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpL24Pay *rtpL24pay; + gboolean res; + gchar *params; + GstAudioInfo *info; + const GstRTPChannelOrder *order; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (basepayload); + rtpL24pay = GST_RTP_L24_PAY (basepayload); + + info = &rtpL24pay->info; + gst_audio_info_init (info); + if (!gst_audio_info_from_caps (info, caps)) + goto invalid_caps; + + order = gst_rtp_channels_get_by_pos (info->channels, info->position); + rtpL24pay->order = order; + + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "L24", + info->rate); + params = g_strdup_printf ("%d", info->channels); + + if (!order && info->channels > 2) { + GST_ELEMENT_WARNING (rtpL24pay, STREAM, DECODE, + (NULL), ("Unknown channel order for %d channels", info->channels)); + } + + if (order && order->name) { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + info->channels, "channel-order", G_TYPE_STRING, order->name, NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + info->channels, NULL); + } + + g_free (params); + + /* octet-per-sample is 3 * channels for L24 */ + gst_rtp_base_audio_payload_set_sample_options (rtpbaseaudiopayload, + 3 * info->channels); + + return res; + + /* ERRORS */ +invalid_caps: + { + GST_DEBUG_OBJECT (rtpL24pay, "invalid caps"); + return FALSE; + } +} + +static GstCaps * +gst_rtp_L24_pay_getcaps (GstRTPBasePayload * rtppayload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + caps = gst_pad_get_pad_template_caps (pad); + + otherpadcaps = gst_pad_get_allowed_caps (rtppayload->srcpad); + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *structure; + gint channels; + gint rate; + + structure = gst_caps_get_structure (otherpadcaps, 0); + caps = gst_caps_make_writable (caps); + + if (gst_structure_get_int (structure, "channels", &channels)) { + gst_caps_set_simple (caps, "channels", G_TYPE_INT, channels, NULL); + } + + if (gst_structure_get_int (structure, "clock-rate", &rate)) { + gst_caps_set_simple (caps, "rate", G_TYPE_INT, rate, NULL); + } + + } + gst_caps_unref (otherpadcaps); + } + + if (filter) { + GstCaps *tcaps = caps; + + caps = gst_caps_intersect_full (filter, tcaps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tcaps); + } + + return caps; +} + +static GstFlowReturn +gst_rtp_L24_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpL24Pay *rtpL24pay; + + rtpL24pay = GST_RTP_L24_PAY (basepayload); + buffer = gst_buffer_make_writable (buffer); + + if (rtpL24pay->order && + !gst_audio_buffer_reorder_channels (buffer, rtpL24pay->info.finfo->format, + rtpL24pay->info.channels, rtpL24pay->info.position, + rtpL24pay->order->pos)) { + return GST_FLOW_ERROR; + } + + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->handle_buffer (basepayload, + buffer); +} + +gboolean +gst_rtp_L24_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpL24pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_L24_PAY); +} diff --git a/gst/rtp/gstrtpL24pay.h b/gst/rtp/gstrtpL24pay.h new file mode 100755 index 0000000..47395ad --- /dev/null +++ b/gst/rtp/gstrtpL24pay.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_L24_PAY_H__ +#define __GST_RTP_L24_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +#include "gstrtpchannels.h" + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_L24_PAY \ + (gst_rtp_L24_pay_get_type()) +#define GST_RTP_L24_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_L24_PAY,GstRtpL24Pay)) +#define GST_RTP_L24_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_L24_PAY,GstRtpL24PayClass)) +#define GST_IS_RTP_L24_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_L24_PAY)) +#define GST_IS_RTP_L24_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_L24_PAY)) + +typedef struct _GstRtpL24Pay GstRtpL24Pay; +typedef struct _GstRtpL24PayClass GstRtpL24PayClass; + +struct _GstRtpL24Pay +{ + GstRTPBaseAudioPayload payload; + + GstAudioInfo info; + const GstRTPChannelOrder *order; +}; + +struct _GstRtpL24PayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_L24_pay_get_type (void); + +gboolean gst_rtp_L24_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_L24_PAY_H__ */ diff --git a/gst/rtp/gstrtpac3depay.c b/gst/rtp/gstrtpac3depay.c new file mode 100755 index 0000000..e8a05c9 --- /dev/null +++ b/gst/rtp/gstrtpac3depay.c @@ -0,0 +1,182 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpac3depay + * @see_also: rtpac3pay + * + * Extract AC3 audio from RTP packets according to RFC 4184. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc4184.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 udpsrc caps='application/x-rtp, media=(string)audio, clock-rate=(int)44100, encoding-name=(string)AC3, payload=(int)96' ! rtpac3depay ! a52dec ! pulsesink + * ]| This example pipeline will depayload and decode an RTP AC3 stream. Refer to + * the rtpac3pay example to create the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpac3depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpac3depay_debug); +#define GST_CAT_DEFAULT (rtpac3depay_debug) + +static GstStaticPadTemplate gst_rtp_ac3_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/ac3") + ); + +static GstStaticPadTemplate gst_rtp_ac3_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) { 32000, 44100, 48000 }, " + "encoding-name = (string) \"AC3\"") + ); + +G_DEFINE_TYPE (GstRtpAC3Depay, gst_rtp_ac3_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_ac3_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_ac3_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_ac3_depay_class_init (GstRtpAC3DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ac3_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ac3_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP AC3 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts AC3 audio from RTP packets (RFC 4184)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->set_caps = gst_rtp_ac3_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_ac3_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpac3depay_debug, "rtpac3depay", 0, + "AC3 Audio RTP Depayloader"); +} + +static void +gst_rtp_ac3_depay_init (GstRtpAC3Depay * rtpac3depay) +{ + /* needed because of G_DEFINE_TYPE */ +} + +static gboolean +gst_rtp_ac3_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + gint clock_rate; + GstCaps *srccaps; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_empty_simple ("audio/ac3"); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static GstBuffer * +gst_rtp_ac3_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpAC3Depay *rtpac3depay; + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL, }; + guint8 *payload; + guint16 FT, NF; + + rtpac3depay = GST_RTP_AC3_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + if (gst_rtp_buffer_get_payload_len (&rtp) < 2) + goto empty_packet; + + payload = gst_rtp_buffer_get_payload (&rtp); + + /* strip off header + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | FT| NF | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + FT = payload[0] & 0x3; + NF = payload[1]; + + GST_DEBUG_OBJECT (rtpac3depay, "FT: %d, NF: %d", FT, NF); + + /* We don't bother with fragmented packets yet */ + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, 2, -1); + + gst_rtp_buffer_unmap (&rtp); + + if (outbuf) + GST_DEBUG_OBJECT (rtpac3depay, "pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpac3depay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_ac3_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpac3depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_AC3_DEPAY); +} diff --git a/gst/rtp/gstrtpac3depay.h b/gst/rtp/gstrtpac3depay.h new file mode 100755 index 0000000..294bb12 --- /dev/null +++ b/gst/rtp/gstrtpac3depay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_AC3_DEPAY_H__ +#define __GST_RTP_AC3_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_AC3_DEPAY \ + (gst_rtp_ac3_depay_get_type()) +#define GST_RTP_AC3_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_AC3_DEPAY,GstRtpAC3Depay)) +#define GST_RTP_AC3_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_AC3_DEPAY,GstRtpAC3DepayClass)) +#define GST_IS_RTP_AC3_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_AC3_DEPAY)) +#define GST_IS_RTP_AC3_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_AC3_DEPAY)) + +typedef struct _GstRtpAC3Depay GstRtpAC3Depay; +typedef struct _GstRtpAC3DepayClass GstRtpAC3DepayClass; + +struct _GstRtpAC3Depay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpAC3DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_ac3_depay_get_type (void); + +gboolean gst_rtp_ac3_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_AC3_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpac3pay.c b/gst/rtp/gstrtpac3pay.c new file mode 100755 index 0000000..e283afd --- /dev/null +++ b/gst/rtp/gstrtpac3pay.c @@ -0,0 +1,469 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpac3pay + * @see_also: rtpac3depay + * + * Payload AC3 audio into RTP packets according to RFC 4184. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc4184.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 -v audiotestsrc ! avenc_ac3 ! rtpac3pay ! udpsink + * ]| This example pipeline will encode and payload AC3 stream. Refer to + * the rtpac3depay example to depayload and decode the RTP stream. + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpac3pay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpac3pay_debug); +#define GST_CAT_DEFAULT (rtpac3pay_debug) + +static GstStaticPadTemplate gst_rtp_ac3_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/ac3; " "audio/x-ac3; ") + ); + +static GstStaticPadTemplate gst_rtp_ac3_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 32000, 44100, 48000 }, " + "encoding-name = (string) \"AC3\"") + ); + +static void gst_rtp_ac3_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_ac3_pay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_ac3_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static gboolean gst_rtp_ac3_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); +static GstFlowReturn gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay); +static GstFlowReturn gst_rtp_ac3_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); + +#define gst_rtp_ac3_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpAC3Pay, gst_rtp_ac3_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_ac3_pay_class_init (GstRtpAC3PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpac3pay_debug, "rtpac3pay", 0, + "AC3 Audio RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_ac3_pay_finalize; + + gstelement_class->change_state = gst_rtp_ac3_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ac3_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ac3_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP AC3 audio payloader", "Codec/Payloader/Network/RTP", + "Payload AC3 audio as RTP packets (RFC 4184)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_ac3_pay_setcaps; + gstrtpbasepayload_class->sink_event = gst_rtp_ac3_pay_sink_event; + gstrtpbasepayload_class->handle_buffer = gst_rtp_ac3_pay_handle_buffer; +} + +static void +gst_rtp_ac3_pay_init (GstRtpAC3Pay * rtpac3pay) +{ + rtpac3pay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_ac3_pay_finalize (GObject * object) +{ + GstRtpAC3Pay *rtpac3pay; + + rtpac3pay = GST_RTP_AC3_PAY (object); + + g_object_unref (rtpac3pay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_ac3_pay_reset (GstRtpAC3Pay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + +static gboolean +gst_rtp_ac3_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + gint rate; + GstStructure *structure; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "rate", &rate)) + rate = 90000; /* default */ + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, "AC3", rate); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +static gboolean +gst_rtp_ac3_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + gboolean res; + GstRtpAC3Pay *rtpac3pay; + + rtpac3pay = GST_RTP_AC3_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_ac3_pay_flush (rtpac3pay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + + res = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); + + return res; +} + +struct frmsize_s +{ + guint16 bit_rate; + guint16 frm_size[3]; +}; + +static const struct frmsize_s frmsizecod_tbl[] = { + {32, {64, 69, 96}}, + {32, {64, 70, 96}}, + {40, {80, 87, 120}}, + {40, {80, 88, 120}}, + {48, {96, 104, 144}}, + {48, {96, 105, 144}}, + {56, {112, 121, 168}}, + {56, {112, 122, 168}}, + {64, {128, 139, 192}}, + {64, {128, 140, 192}}, + {80, {160, 174, 240}}, + {80, {160, 175, 240}}, + {96, {192, 208, 288}}, + {96, {192, 209, 288}}, + {112, {224, 243, 336}}, + {112, {224, 244, 336}}, + {128, {256, 278, 384}}, + {128, {256, 279, 384}}, + {160, {320, 348, 480}}, + {160, {320, 349, 480}}, + {192, {384, 417, 576}}, + {192, {384, 418, 576}}, + {224, {448, 487, 672}}, + {224, {448, 488, 672}}, + {256, {512, 557, 768}}, + {256, {512, 558, 768}}, + {320, {640, 696, 960}}, + {320, {640, 697, 960}}, + {384, {768, 835, 1152}}, + {384, {768, 836, 1152}}, + {448, {896, 975, 1344}}, + {448, {896, 976, 1344}}, + {512, {1024, 1114, 1536}}, + {512, {1024, 1115, 1536}}, + {576, {1152, 1253, 1728}}, + {576, {1152, 1254, 1728}}, + {640, {1280, 1393, 1920}}, + {640, {1280, 1394, 1920}} +}; + +static GstFlowReturn +gst_rtp_ac3_pay_flush (GstRtpAC3Pay * rtpac3pay) +{ + guint avail, FT, NF, mtu; + GstBuffer *outbuf; + GstFlowReturn ret; + + /* the data available in the adapter is either smaller + * than the MTU or bigger. In the case it is smaller, the complete + * adapter contents can be put in one packet. In the case the + * adapter has more than one MTU, we need to split the AC3 data + * over multiple packets. */ + avail = gst_adapter_available (rtpac3pay->adapter); + + ret = GST_FLOW_OK; + + FT = 0; + /* number of frames */ + NF = rtpac3pay->NF; + + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpac3pay); + + GST_LOG_OBJECT (rtpac3pay, "flushing %u bytes", avail); + + while (avail > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + GstRTPBuffer rtp = { NULL, }; + + /* this will be the total length of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (2 + avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + if (FT == 0) { + /* check if it all fits */ + if (towrite < packet_len) { + guint maxlen; + + GST_LOG_OBJECT (rtpac3pay, "we need to fragment"); + /* check if we will be able to put at least 5/8th of the total + * frame in this first frame. */ + if ((avail * 5) / 8 >= (payload_len - 2)) + FT = 1; + else + FT = 2; + /* check how many fragments we will need */ + maxlen = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0); + NF = (avail + maxlen - 1) / maxlen; + } + } else if (FT != 3) { + /* remaining fragment */ + FT = 3; + } + + /* + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | FT| NF | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * FT: 0: one or more complete frames + * 1: initial 5/8 fragment + * 2: initial fragment not 5/8 + * 3: other fragment + * NF: amount of frames if FT = 0, else number of fragments. + */ + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + GST_LOG_OBJECT (rtpac3pay, "FT %u, NF %u", FT, NF); + payload = gst_rtp_buffer_get_payload (&rtp); + payload[0] = (FT & 3); + payload[1] = NF; + payload_len -= 2; + + gst_adapter_copy (rtpac3pay->adapter, &payload[2], 0, payload_len); + gst_adapter_flush (rtpac3pay->adapter, payload_len); + + avail -= payload_len; + if (avail == 0) + gst_rtp_buffer_set_marker (&rtp, TRUE); + gst_rtp_buffer_unmap (&rtp); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpac3pay->first_ts; + GST_BUFFER_DURATION (outbuf) = rtpac3pay->duration; + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpac3pay), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_rtp_ac3_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpAC3Pay *rtpac3pay; + GstFlowReturn ret; + gsize avail, left, NF; + GstMapInfo map; + guint8 *p; + guint packet_len; + GstClockTime duration, timestamp; + + rtpac3pay = GST_RTP_AC3_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpac3pay, "DISCONT"); + gst_rtp_ac3_pay_reset (rtpac3pay); + } + + /* count the amount of incomming packets */ + NF = 0; + left = map.size; + p = map.data; + while (TRUE) { + guint bsid, fscod, frmsizecod, frame_size; + + if (left < 6) + break; + + if (p[0] != 0x0b || p[1] != 0x77) + break; + + bsid = p[5] >> 3; + if (bsid > 8) + break; + + frmsizecod = p[4] & 0x3f; + fscod = p[4] >> 6; + + GST_DEBUG_OBJECT (rtpac3pay, "fscod %u, %u", fscod, frmsizecod); + + if (fscod >= 3 || frmsizecod >= 38) + break; + + frame_size = frmsizecod_tbl[frmsizecod].frm_size[fscod] * 2; + if (frame_size > left) + break; + + NF++; + GST_DEBUG_OBJECT (rtpac3pay, "found frame %" G_GSIZE_FORMAT " of size %u", + NF, frame_size); + + p += frame_size; + left -= frame_size; + } + gst_buffer_unmap (buffer, &map); + if (NF == 0) + goto no_frames; + + avail = gst_adapter_available (rtpac3pay->adapter); + + /* get packet length of previous data and this new data, + * payload length includes a 4 byte header */ + packet_len = gst_rtp_buffer_calc_packet_len (2 + avail + map.size, 0, 0); + + /* if this buffer is going to overflow the packet, flush what we + * have. */ + if (gst_rtp_base_payload_is_filled (basepayload, + packet_len, rtpac3pay->duration + duration)) { + ret = gst_rtp_ac3_pay_flush (rtpac3pay); + avail = 0; + } else { + ret = GST_FLOW_OK; + } + + if (avail == 0) { + GST_DEBUG_OBJECT (rtpac3pay, + "first packet, save timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + rtpac3pay->first_ts = timestamp; + rtpac3pay->duration = 0; + rtpac3pay->NF = 0; + } + + gst_adapter_push (rtpac3pay->adapter, buffer); + rtpac3pay->duration += duration; + rtpac3pay->NF += NF; + + return ret; + + /* ERRORS */ +no_frames: + { + GST_WARNING_OBJECT (rtpac3pay, "no valid AC3 frames found"); + return GST_FLOW_OK; + } +} + +static GstStateChangeReturn +gst_rtp_ac3_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpAC3Pay *rtpac3pay; + GstStateChangeReturn ret; + + rtpac3pay = GST_RTP_AC3_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_ac3_pay_reset (rtpac3pay); + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_ac3_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpac3pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_AC3_PAY); +} diff --git a/gst/rtp/gstrtpac3pay.h b/gst/rtp/gstrtpac3pay.h new file mode 100755 index 0000000..c131eac --- /dev/null +++ b/gst/rtp/gstrtpac3pay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_AC3_PAY_H__ +#define __GST_RTP_AC3_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_AC3_PAY \ + (gst_rtp_ac3_pay_get_type()) +#define GST_RTP_AC3_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_AC3_PAY,GstRtpAC3Pay)) +#define GST_RTP_AC3_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_AC3_PAY,GstRtpAC3PayClass)) +#define GST_IS_RTP_AC3_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_AC3_PAY)) +#define GST_IS_RTP_AC3_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_AC3_PAY)) + +typedef struct _GstRtpAC3Pay GstRtpAC3Pay; +typedef struct _GstRtpAC3PayClass GstRtpAC3PayClass; + +struct _GstRtpAC3Pay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + GstClockTime duration; + guint NF; +}; + +struct _GstRtpAC3PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_ac3_pay_get_type (void); + +gboolean gst_rtp_ac3_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_AC3_PAY_H__ */ diff --git a/gst/rtp/gstrtpamrdepay.c b/gst/rtp/gstrtpamrdepay.c new file mode 100755 index 0000000..4b53843 --- /dev/null +++ b/gst/rtp/gstrtpamrdepay.c @@ -0,0 +1,479 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpamrdepay + * @see_also: rtpamrpay + * + * Extract AMR audio from RTP packets according to RFC 3267. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3267.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 udpsrc caps='application/x-rtp, media=(string)audio, clock-rate=(int)8000, encoding-name=(string)AMR, encoding-params=(string)1, octet-align=(string)1, payload=(int)96' ! rtpamrdepay ! amrnbdec ! pulsesink + * ]| This example pipeline will depayload and decode an RTP AMR stream. Refer to + * the rtpamrpay example to create the RTP stream. + * </refsect2> + */ + +/* + * RFC 3267 - Real-Time Transport Protocol (RTP) Payload Format and File + * Storage Format for the Adaptive Multi-Rate (AMR) and Adaptive Multi-Rate + * Wideband (AMR-WB) Audio Codecs. + * + */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include "gstrtpamrdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpamrdepay_debug); +#define GST_CAT_DEFAULT (rtpamrdepay_debug) + +/* RtpAMRDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +/* input is an RTP packet + * + * params see RFC 3267, section 8.1 + */ +static GstStaticPadTemplate gst_rtp_amr_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"AMR\", " + /* This is the default, so the peer doesn't have to specify it + * "encoding-params = (string) \"1\", " */ + /* NOTE that all values must be strings in orde to be able to do SDP <-> + * GstCaps mapping. */ + "octet-align = (string) \"1\";" + /* following options are not needed for a decoder + * + "crc = (string) { \"0\", \"1\" }, " + "robust-sorting = (string) \"0\", " + "interleaving = (string) \"0\";" + "mode-set = (int) [ 0, 7 ], " + "mode-change-period = (int) [ 1, MAX ], " + "mode-change-neighbor = (boolean) { TRUE, FALSE }, " + "maxptime = (int) [ 20, MAX ], " + "ptime = (int) [ 20, MAX ]" + */ + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 16000, " "encoding-name = (string) \"AMR-WB\", " + /* This is the default, so the peer doesn't have to specify it + * "encoding-params = (string) \"1\", " */ + /* NOTE that all values must be strings in orde to be able to do SDP <-> + * GstCaps mapping. */ + "octet-align = (string) \"1\";" + /* following options are not needed for a decoder + * + "crc = (string) { \"0\", \"1\" }, " + "robust-sorting = (string) \"0\", " + "interleaving = (string) \"0\"" + "mode-set = (int) [ 0, 7 ], " + "mode-change-period = (int) [ 1, MAX ], " + "mode-change-neighbor = (boolean) { TRUE, FALSE }, " + "maxptime = (int) [ 20, MAX ], " + "ptime = (int) [ 20, MAX ]" + */ + ) + ); + +static GstStaticPadTemplate gst_rtp_amr_depay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR, " "channels = (int) 1," "rate = (int) 8000;" + "audio/AMR-WB, " "channels = (int) 1," "rate = (int) 16000") + ); + +static gboolean gst_rtp_amr_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_amr_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +#define gst_rtp_amr_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpAMRDepay, gst_rtp_amr_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_amr_depay_class_init (GstRtpAMRDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_amr_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_amr_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP AMR depayloader", "Codec/Depayloader/Network/RTP", + "Extracts AMR or AMR-WB audio from RTP packets (RFC 3267)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_amr_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_amr_depay_setcaps; + + GST_DEBUG_CATEGORY_INIT (rtpamrdepay_debug, "rtpamrdepay", 0, + "AMR/AMR-WB RTP Depayloader"); +} + +static void +gst_rtp_amr_depay_init (GstRtpAMRDepay * rtpamrdepay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtpamrdepay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); +} + +static gboolean +gst_rtp_amr_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstCaps *srccaps; + GstRtpAMRDepay *rtpamrdepay; + const gchar *params; + const gchar *str, *type; + gint clock_rate, need_clock_rate; + gboolean res; + + rtpamrdepay = GST_RTP_AMR_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + /* figure out the mode first and set the clock rates */ + if ((str = gst_structure_get_string (structure, "encoding-name"))) { + if (strcmp (str, "AMR") == 0) { + rtpamrdepay->mode = GST_RTP_AMR_DP_MODE_NB; + need_clock_rate = 8000; + type = "audio/AMR"; + } else if (strcmp (str, "AMR-WB") == 0) { + rtpamrdepay->mode = GST_RTP_AMR_DP_MODE_WB; + need_clock_rate = 16000; + type = "audio/AMR-WB"; + } else + goto invalid_mode; + } else + goto invalid_mode; + + if (!(str = gst_structure_get_string (structure, "octet-align"))) + rtpamrdepay->octet_align = FALSE; + else + rtpamrdepay->octet_align = (atoi (str) == 1); + + if (!(str = gst_structure_get_string (structure, "crc"))) + rtpamrdepay->crc = FALSE; + else + rtpamrdepay->crc = (atoi (str) == 1); + + if (rtpamrdepay->crc) { + /* crc mode implies octet aligned mode */ + rtpamrdepay->octet_align = TRUE; + } + + if (!(str = gst_structure_get_string (structure, "robust-sorting"))) + rtpamrdepay->robust_sorting = FALSE; + else + rtpamrdepay->robust_sorting = (atoi (str) == 1); + + if (rtpamrdepay->robust_sorting) { + /* robust_sorting mode implies octet aligned mode */ + rtpamrdepay->octet_align = TRUE; + } + + if (!(str = gst_structure_get_string (structure, "interleaving"))) + rtpamrdepay->interleaving = FALSE; + else + rtpamrdepay->interleaving = (atoi (str) == 1); + + if (rtpamrdepay->interleaving) { + /* interleaving mode implies octet aligned mode */ + rtpamrdepay->octet_align = TRUE; + } + + if (!(params = gst_structure_get_string (structure, "encoding-params"))) + rtpamrdepay->channels = 1; + else { + rtpamrdepay->channels = atoi (params); + } + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = need_clock_rate; + depayload->clock_rate = clock_rate; + + /* we require 1 channel, 8000 Hz, octet aligned, no CRC, + * no robust sorting, no interleaving for now */ + if (rtpamrdepay->channels != 1) + return FALSE; + if (clock_rate != need_clock_rate) + return FALSE; + if (rtpamrdepay->octet_align != TRUE) + return FALSE; + if (rtpamrdepay->robust_sorting != FALSE) + return FALSE; + if (rtpamrdepay->interleaving != FALSE) + return FALSE; + + srccaps = gst_caps_new_simple (type, + "channels", G_TYPE_INT, rtpamrdepay->channels, + "rate", G_TYPE_INT, clock_rate, NULL); + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +invalid_mode: + { + GST_ERROR_OBJECT (rtpamrdepay, "invalid encoding-name"); + return FALSE; + } +} + +/* -1 is invalid */ +static const gint nb_frame_size[16] = { + 12, 13, 15, 17, 19, 20, 26, 31, + 5, -1, -1, -1, -1, -1, -1, 0 +}; + +static const gint wb_frame_size[16] = { + 17, 23, 32, 36, 40, 46, 50, 58, + 60, 5, -1, -1, -1, -1, -1, 0 +}; + +static GstBuffer * +gst_rtp_amr_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpAMRDepay *rtpamrdepay; + const gint *frame_size; + GstBuffer *outbuf = NULL; + gint payload_len; + GstRTPBuffer rtp = { NULL }; + GstMapInfo map; + + rtpamrdepay = GST_RTP_AMR_DEPAY (depayload); + + /* setup frame size pointer */ + if (rtpamrdepay->mode == GST_RTP_AMR_DP_MODE_NB) + frame_size = nb_frame_size; + else + frame_size = wb_frame_size; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + /* when we get here, 1 channel, 8000/16000 Hz, octet aligned, no CRC, + * no robust sorting, no interleaving data is to be depayloaded */ + { + guint8 *payload, *p, *dp; + gint i, num_packets, num_nonempty_packets; + gint amr_len; + gint ILL, ILP; + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + /* need at least 2 bytes for the header */ + if (payload_len < 2) + goto too_small; + + payload = gst_rtp_buffer_get_payload (&rtp); + + /* depay CMR. The CMR is used by the sender to request + * a new encoding mode. + * + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * | CMR |R|R|R|R| + * +-+-+-+-+-+-+-+-+ + */ + /* CMR = (payload[0] & 0xf0) >> 4; */ + + /* strip CMR header now, pack FT and the data for the decoder */ + payload_len -= 1; + payload += 1; + + GST_DEBUG_OBJECT (rtpamrdepay, "payload len %d", payload_len); + + if (rtpamrdepay->interleaving) { + ILL = (payload[0] & 0xf0) >> 4; + ILP = (payload[0] & 0x0f); + + payload_len -= 1; + payload += 1; + + if (ILP > ILL) + goto wrong_interleaving; + } + + /* + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 + * +-+-+-+-+-+-+-+-+.. + * |F| FT |Q|P|P| more FT.. + * +-+-+-+-+-+-+-+-+.. + */ + /* count number of packets by counting the FTs. Also + * count number of amr data bytes and number of non-empty + * packets (this is also the number of CRCs if present). */ + amr_len = 0; + num_nonempty_packets = 0; + num_packets = 0; + for (i = 0; i < payload_len; i++) { + gint fr_size; + guint8 FT; + + FT = (payload[i] & 0x78) >> 3; + + fr_size = frame_size[FT]; + GST_DEBUG_OBJECT (rtpamrdepay, "frame size %d", fr_size); + if (fr_size == -1) + goto wrong_framesize; + + if (fr_size > 0) { + amr_len += fr_size; + num_nonempty_packets++; + } + num_packets++; + + if ((payload[i] & 0x80) == 0) + break; + } + + if (rtpamrdepay->crc) { + /* data len + CRC len + header bytes should be smaller than payload_len */ + if (num_packets + num_nonempty_packets + amr_len > payload_len) + goto wrong_length_1; + } else { + /* data len + header bytes should be smaller than payload_len */ + if (num_packets + amr_len > payload_len) + goto wrong_length_2; + } + + outbuf = gst_buffer_new_and_alloc (payload_len); + + /* point to destination */ + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + + /* point to first data packet */ + p = map.data; + dp = payload + num_packets; + if (rtpamrdepay->crc) { + /* skip CRC if present */ + dp += num_nonempty_packets; + } + + for (i = 0; i < num_packets; i++) { + gint fr_size; + + /* copy FT, clear F bit */ + *p++ = payload[i] & 0x7f; + + fr_size = frame_size[(payload[i] & 0x78) >> 3]; + if (fr_size > 0) { + /* copy data packet, FIXME, calc CRC here. */ + memcpy (p, dp, fr_size); + + p += fr_size; + dp += fr_size; + } + } + gst_buffer_unmap (outbuf, &map); + + /* we can set the duration because each packet is 20 milliseconds */ + GST_BUFFER_DURATION (outbuf) = num_packets * 20 * GST_MSECOND; + + if (gst_rtp_buffer_get_marker (&rtp)) { + /* marker bit marks a buffer after a talkspurt. */ + GST_DEBUG_OBJECT (depayload, "marker bit was set"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + GST_DEBUG_OBJECT (depayload, "pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + } + + gst_rtp_buffer_unmap (&rtp); + return outbuf; + + /* ERRORS */ +too_small: + { + GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE, + (NULL), ("AMR RTP payload too small (%d)", payload_len)); + goto bad_packet; + } +wrong_interleaving: + { + GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE, + (NULL), ("AMR RTP wrong interleaving")); + goto bad_packet; + } +wrong_framesize: + { + GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE, + (NULL), ("AMR RTP frame size == -1")); + goto bad_packet; + } +wrong_length_1: + { + GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE, + (NULL), ("AMR RTP wrong length 1")); + goto bad_packet; + } +wrong_length_2: + { + GST_ELEMENT_WARNING (rtpamrdepay, STREAM, DECODE, + (NULL), ("AMR RTP wrong length 2")); + goto bad_packet; + } +bad_packet: + { + /* no fatal error */ + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_amr_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpamrdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_AMR_DEPAY); +} diff --git a/gst/rtp/gstrtpamrdepay.h b/gst/rtp/gstrtpamrdepay.h new file mode 100755 index 0000000..0b80634 --- /dev/null +++ b/gst/rtp/gstrtpamrdepay.h @@ -0,0 +1,77 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_AMR_DEPAY_H__ +#define __GST_RTP_AMR_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_AMR_DEPAY \ + (gst_rtp_amr_depay_get_type()) +#define GST_RTP_AMR_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_AMR_DEPAY,GstRtpAMRDepay)) +#define GST_RTP_AMR_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_AMR_DEPAY,GstRtpAMRDepayClass)) +#define GST_IS_RTP_AMR_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_AMR_DEPAY)) +#define GST_IS_RTP_AMR_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_AMR_DEPAY)) + +typedef struct _GstRtpAMRDepay GstRtpAMRDepay; +typedef struct _GstRtpAMRDepayClass GstRtpAMRDepayClass; + +typedef enum { + GST_RTP_AMR_DP_MODE_INVALID = 0, + GST_RTP_AMR_DP_MODE_NB = 1, + GST_RTP_AMR_DP_MODE_WB = 2 +} GstRtpAMRDepayMode; + +struct _GstRtpAMRDepay +{ + GstRTPBaseDepayload depayload; + + GstRtpAMRDepayMode mode; + + gboolean octet_align; + guint8 mode_set; + gint mode_change_period; + gboolean mode_change_neighbor; + gint maxptime; + gboolean crc; + gboolean robust_sorting; + gboolean interleaving; + gint ptime; + gint channels; +}; + +struct _GstRtpAMRDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_amr_depay_get_type (void); + +gboolean gst_rtp_amr_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_AMR_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpamrpay.c b/gst/rtp/gstrtpamrpay.c new file mode 100755 index 0000000..ead9f94 --- /dev/null +++ b/gst/rtp/gstrtpamrpay.c @@ -0,0 +1,459 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpamrpay + * @see_also: rtpamrdepay + * + * Payload AMR audio into RTP packets according to RFC 3267. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc3267.txt + * + * <refsect2> + * <title>Example pipeline</title> + * |[ + * gst-launch-1.0 -v audiotestsrc ! amrnbenc ! rtpamrpay ! udpsink + * ]| This example pipeline will encode and payload an AMR stream. Refer to + * the rtpamrdepay example to depayload and decode the RTP stream. + * </refsect2> + */ + +/* references: + * + * RFC 3267 - Real-Time Transport Protocol (RTP) Payload Format and File + * Storage Format for the Adaptive Multi-Rate (AMR) and Adaptive + * Multi-Rate Wideband (AMR-WB) Audio Codecs. + * + * ETSI TS 126 201 V6.0.0 (2004-12) - Digital cellular telecommunications system (Phase 2+); + * Universal Mobile Telecommunications System (UMTS); + * AMR speech codec, wideband; + * Frame structure + * (3GPP TS 26.201 version 6.0.0 Release 6) + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpamrpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpamrpay_debug); +#define GST_CAT_DEFAULT (rtpamrpay_debug) + +static GstStaticPadTemplate gst_rtp_amr_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/AMR, channels=(int)1, rate=(int)8000; " + "audio/AMR-WB, channels=(int)1, rate=(int)16000") + ); + +static GstStaticPadTemplate gst_rtp_amr_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"AMR\", " + "encoding-params = (string) \"1\", " + "octet-align = (string) \"1\", " + "crc = (string) \"0\", " + "robust-sorting = (string) \"0\", " + "interleaving = (string) \"0\", " + "mode-set = (int) [ 0, 7 ], " + "mode-change-period = (int) [ 1, MAX ], " + "mode-change-neighbor = (string) { \"0\", \"1\" }, " + "maxptime = (int) [ 20, MAX ], " "ptime = (int) [ 20, MAX ];" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 16000, " + "encoding-name = (string) \"AMR-WB\", " + "encoding-params = (string) \"1\", " + "octet-align = (string) \"1\", " + "crc = (string) \"0\", " + "robust-sorting = (string) \"0\", " + "interleaving = (string) \"0\", " + "mode-set = (int) [ 0, 7 ], " + "mode-change-period = (int) [ 1, MAX ], " + "mode-change-neighbor = (string) { \"0\", \"1\" }, " + "maxptime = (int) [ 20, MAX ], " "ptime = (int) [ 20, MAX ]") + ); + +static gboolean gst_rtp_amr_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstFlowReturn gst_rtp_amr_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); + +static GstStateChangeReturn +gst_rtp_amr_pay_change_state (GstElement * element, GstStateChange transition); + +#define gst_rtp_amr_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpAMRPay, gst_rtp_amr_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_amr_pay_class_init (GstRtpAMRPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstelement_class->change_state = gst_rtp_amr_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_amr_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_amr_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP AMR payloader", + "Codec/Payloader/Network/RTP", + "Payload-encode AMR or AMR-WB audio into RTP packets (RFC 3267)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_amr_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_amr_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpamrpay_debug, "rtpamrpay", 0, + "AMR/AMR-WB RTP Payloader"); +} + +static void +gst_rtp_amr_pay_init (GstRtpAMRPay * rtpamrpay) +{ +} + +static void +gst_rtp_amr_pay_reset (GstRtpAMRPay * pay) +{ + pay->next_rtp_time = 0; + pay->first_ts = GST_CLOCK_TIME_NONE; + pay->first_rtp_time = 0; +} + +static gboolean +gst_rtp_amr_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpAMRPay *rtpamrpay; + gboolean res; + const GstStructure *s; + const gchar *str; + + rtpamrpay = GST_RTP_AMR_PAY (basepayload); + + /* figure out the mode Narrow or Wideband */ + s = gst_caps_get_structure (caps, 0); + if ((str = gst_structure_get_name (s))) { + if (strcmp (str, "audio/AMR") == 0) + rtpamrpay->mode = GST_RTP_AMR_P_MODE_NB; + else if (strcmp (str, "audio/AMR-WB") == 0) + rtpamrpay->mode = GST_RTP_AMR_P_MODE_WB; + else + goto wrong_type; + } else + goto wrong_type; + + if (rtpamrpay->mode == GST_RTP_AMR_P_MODE_NB) + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "AMR", 8000); + else + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "AMR-WB", + 16000); + + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, "1", "octet-align", G_TYPE_STRING, "1", + /* don't set the defaults + * + * "crc", G_TYPE_STRING, "0", + * "robust-sorting", G_TYPE_STRING, "0", + * "interleaving", G_TYPE_STRING, "0", + */ + NULL); + + return res; + + /* ERRORS */ +wrong_type: + { + GST_ERROR_OBJECT (rtpamrpay, "unsupported media type '%s'", + GST_STR_NULL (str)); + return FALSE; + } +} + +static void +gst_rtp_amr_pay_recalc_rtp_time (GstRtpAMRPay * rtpamrpay, + GstClockTime timestamp) +{ + /* re-sync rtp time */ + if (GST_CLOCK_TIME_IS_VALID (rtpamrpay->first_ts) && + GST_CLOCK_TIME_IS_VALID (timestamp) && timestamp >= rtpamrpay->first_ts) { + GstClockTime diff; + guint32 rtpdiff; + + /* interpolate to reproduce gap from start, rather than intermediate + * intervals to avoid roundup accumulation errors */ + diff = timestamp - rtpamrpay->first_ts; + rtpdiff = ((diff / GST_MSECOND) * 8) << + (rtpamrpay->mode == GST_RTP_AMR_P_MODE_WB); + rtpamrpay->next_rtp_time = rtpamrpay->first_rtp_time + rtpdiff; + GST_DEBUG_OBJECT (rtpamrpay, + "elapsed time %" GST_TIME_FORMAT ", rtp %" G_GUINT32_FORMAT ", " + "new offset %" G_GUINT32_FORMAT, GST_TIME_ARGS (diff), rtpdiff, + rtpamrpay->next_rtp_time); + } +} + +/* -1 is invalid */ +static const gint nb_frame_size[16] = { + 12, 13, 15, 17, 19, 20, 26, 31, + 5, -1, -1, -1, -1, -1, -1, 0 +}; + +static const gint wb_frame_size[16] = { + 17, 23, 32, 36, 40, 46, 50, 58, + 60, 5, -1, -1, -1, -1, -1, 0 +}; + +static GstFlowReturn +gst_rtp_amr_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpAMRPay *rtpamrpay; + const gint *frame_size; + GstFlowReturn ret; + guint payload_len; + GstMapInfo map; + GstBuffer *outbuf; + guint8 *payload, *ptr, *payload_amr; + GstClockTime timestamp, duration; + guint packet_len, mtu; + gint i, num_packets, num_nonempty_packets; + gint amr_len; + gboolean sid = FALSE; + GstRTPBuffer rtp = { NULL }; + + rtpamrpay = GST_RTP_AMR_PAY (basepayload); + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpamrpay); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + + /* setup frame size pointer */ + if (rtpamrpay->mode == GST_RTP_AMR_P_MODE_NB) + frame_size = nb_frame_size; + else + frame_size = wb_frame_size; + + GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", map.size); + + /* FIXME, only + * octet aligned, no interleaving, single channel, no CRC, + * no robust-sorting. To fix this you need to implement the downstream + * negotiation function. */ + + /* first count number of packets and total amr frame size */ + amr_len = num_packets = num_nonempty_packets = 0; + for (i = 0; i < map.size; i++) { + guint8 FT; + gint fr_size; + + FT = (map.data[i] & 0x78) >> 3; + + fr_size = frame_size[FT]; + GST_DEBUG_OBJECT (basepayload, "frame type %d, frame size %d", FT, fr_size); + /* FIXME, we don't handle this yet.. */ + if (fr_size <= 0) + goto wrong_size; + + if (fr_size == 5) + sid = TRUE; + + amr_len += fr_size; + num_nonempty_packets++; + num_packets++; + i += fr_size; + } + if (amr_len > map.size) + goto incomplete_frame; + + /* we need one extra byte for the CMR, the ToC is in the input + * data */ + payload_len = map.size + 1; + + /* get packet len to check against MTU */ + packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); + if (packet_len > mtu) + goto too_big; + + /* now alloc output buffer */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* copy timestamp */ + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + if (duration != GST_CLOCK_TIME_NONE) + GST_BUFFER_DURATION (outbuf) = duration; + else { + GST_BUFFER_DURATION (outbuf) = num_packets * 20 * GST_MSECOND; + } + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (basepayload, "discont, setting marker bit"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + gst_rtp_buffer_set_marker (&rtp, TRUE); + gst_rtp_amr_pay_recalc_rtp_time (rtpamrpay, timestamp); + } + + if (G_UNLIKELY (sid)) { + gst_rtp_amr_pay_recalc_rtp_time (rtpamrpay, timestamp); + } + + /* perfect rtptime */ + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rtpamrpay->first_ts))) { + rtpamrpay->first_ts = timestamp; + rtpamrpay->first_rtp_time = rtpamrpay->next_rtp_time; + } + GST_BUFFER_OFFSET (outbuf) = rtpamrpay->next_rtp_time; + rtpamrpay->next_rtp_time += + (num_packets * 160) << (rtpamrpay->mode == GST_RTP_AMR_P_MODE_WB); + + /* get payload, this is now writable */ + payload = gst_rtp_buffer_get_payload (&rtp); + + /* 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * | CMR |R|R|R|R| + * +-+-+-+-+-+-+-+-+ + */ + payload[0] = 0xF0; /* CMR, no specific mode requested */ + + /* this is where we copy the AMR data, after num_packets FTs and the + * CMR. */ + payload_amr = payload + num_packets + 1; + + /* copy data in payload, first we copy all the FTs then all + * the AMR data. The last FT has to have the F flag cleared. */ + ptr = map.data; + for (i = 1; i <= num_packets; i++) { + guint8 FT; + gint fr_size; + + /* 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |F| FT |Q|P|P| more FT... + * +-+-+-+-+-+-+-+-+ + */ + FT = (*ptr & 0x78) >> 3; + + fr_size = frame_size[FT]; + + if (i == num_packets) + /* last packet, clear F flag */ + payload[i] = *ptr & 0x7f; + else + /* set F flag */ + payload[i] = *ptr | 0x80; + + memcpy (payload_amr, &ptr[1], fr_size); + + /* all sizes are > 0 since we checked for that above */ + ptr += fr_size + 1; + payload_amr += fr_size; + } + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + gst_rtp_buffer_unmap (&rtp); + + ret = gst_rtp_base_payload_push (basepayload, outbuf); + + return ret; + + /* ERRORS */ +wrong_size: + { + GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, + (NULL), ("received AMR frame with size <= 0")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return GST_FLOW_ERROR; + } +incomplete_frame: + { + GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, + (NULL), ("received incomplete AMR frames")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return GST_FLOW_ERROR; + } +too_big: + { + GST_ELEMENT_ERROR (basepayload, STREAM, FORMAT, + (NULL), ("received too many AMR frames for MTU")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return GST_FLOW_ERROR; + } +} + +static GstStateChangeReturn +gst_rtp_amr_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + /* handle upwards state changes here */ + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + /* handle downwards state changes */ + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_amr_pay_reset (GST_RTP_AMR_PAY (element)); + break; + default: + break; + } + + return ret; +} + +gboolean +gst_rtp_amr_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpamrpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_AMR_PAY); +} diff --git a/gst/rtp/gstrtpamrpay.h b/gst/rtp/gstrtpamrpay.h new file mode 100755 index 0000000..a3df1ce --- /dev/null +++ b/gst/rtp/gstrtpamrpay.h @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_AMR_PAY_H__ +#define __GST_RTP_AMR_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_AMR_PAY \ + (gst_rtp_amr_pay_get_type()) +#define GST_RTP_AMR_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_AMR_PAY,GstRtpAMRPay)) +#define GST_RTP_AMR_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_AMR_PAY,GstRtpAMRPayClass)) +#define GST_IS_RTP_AMR_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_AMR_PAY)) +#define GST_IS_RTP_AMR_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_AMR_PAY)) + +typedef struct _GstRtpAMRPay GstRtpAMRPay; +typedef struct _GstRtpAMRPayClass GstRtpAMRPayClass; + +typedef enum { + GST_RTP_AMR_P_MODE_INVALID = 0, + GST_RTP_AMR_P_MODE_NB = 1, + GST_RTP_AMR_P_MODE_WB = 2 +} GstRtpAMRPayMode; + +struct _GstRtpAMRPay +{ + GstRTPBasePayload payload; + + GstRtpAMRPayMode mode; + GstClockTime first_ts; + guint32 first_rtp_time; + guint32 next_rtp_time; +}; + +struct _GstRtpAMRPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_amr_pay_get_type (void); + +gboolean gst_rtp_amr_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_AMR_PAY_H__ */ diff --git a/gst/rtp/gstrtpbvdepay.c b/gst/rtp/gstrtpbvdepay.c new file mode 100755 index 0000000..13efebb --- /dev/null +++ b/gst/rtp/gstrtpbvdepay.c @@ -0,0 +1,188 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpbvdepay + * @see_also: rtpbvpay + * + * Extract BroadcomVoice audio from RTP packets according to RFC 4298. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc4298.txt + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpbvdepay.h" + +static GstStaticPadTemplate gst_rtp_bv_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"BV16\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 16000, " "encoding-name = (string) \"BV32\"") + ); + +static GstStaticPadTemplate gst_rtp_bv_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-bv, " "mode = (int) { 16, 32 }") + ); + +static GstBuffer *gst_rtp_bv_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_bv_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_bv_depay_parent_class parent_class +G_DEFINE_TYPE (GstRTPBVDepay, gst_rtp_bv_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_bv_depay_class_init (GstRTPBVDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_bv_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_bv_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP BroadcomVoice depayloader", "Codec/Depayloader/Network/RTP", + "Extracts BroadcomVoice audio from RTP packets (RFC 4298)", + "Wim Taymans <wim.taymans@collabora.co.uk>"); + + gstrtpbasedepayload_class->process = gst_rtp_bv_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_bv_depay_setcaps; +} + +static void +gst_rtp_bv_depay_init (GstRTPBVDepay * rtpbvdepay) +{ + rtpbvdepay->mode = -1; +} + +static gboolean +gst_rtp_bv_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstRTPBVDepay *rtpbvdepay = GST_RTP_BV_DEPAY (depayload); + GstCaps *srccaps; + GstStructure *structure; + const gchar *mode_str = NULL; + gint mode, clock_rate, expected_rate; + gboolean ret; + + structure = gst_caps_get_structure (caps, 0); + + mode_str = gst_structure_get_string (structure, "encoding-name"); + if (!mode_str) + goto no_mode; + + if (!strcmp (mode_str, "BV16")) { + mode = 16; + expected_rate = 8000; + } else if (!strcmp (mode_str, "BV32")) { + mode = 32; + expected_rate = 16000; + } else + goto invalid_mode; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = expected_rate; + else if (clock_rate != expected_rate) + goto wrong_rate; + + depayload->clock_rate = clock_rate; + rtpbvdepay->mode = mode; + + srccaps = gst_caps_new_simple ("audio/x-bv", + "mode", G_TYPE_INT, rtpbvdepay->mode, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + + GST_DEBUG ("set caps on source: %" GST_PTR_FORMAT " (ret=%d)", srccaps, ret); + gst_caps_unref (srccaps); + + return ret; + + /* ERRORS */ +no_mode: + { + GST_ERROR_OBJECT (rtpbvdepay, "did not receive an encoding-name"); + return FALSE; + } +invalid_mode: + { + GST_ERROR_OBJECT (rtpbvdepay, + "invalid encoding-name, expected BV16 or BV32, got %s", mode_str); + return FALSE; + } +wrong_rate: + { + GST_ERROR_OBJECT (rtpbvdepay, "invalid clock-rate, expected %d, got %d", + expected_rate, clock_rate); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_bv_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf; + gboolean marker; + GstRTPBuffer rtp = { NULL, }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (marker && outbuf) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + return outbuf; +} + +gboolean +gst_rtp_bv_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpbvdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_BV_DEPAY); +} diff --git a/gst/rtp/gstrtpbvdepay.h b/gst/rtp/gstrtpbvdepay.h new file mode 100755 index 0000000..f130682 --- /dev/null +++ b/gst/rtp/gstrtpbvdepay.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_BV_DEPAY_H__ +#define __GST_RTP_BV_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPBVDepay GstRTPBVDepay; +typedef struct _GstRTPBVDepayClass GstRTPBVDepayClass; + +#define GST_TYPE_RTP_BV_DEPAY \ + (gst_rtp_bv_depay_get_type()) +#define GST_RTP_BV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_BV_DEPAY,GstRTPBVDepay)) +#define GST_RTP_BV_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_BV_DEPAY,GstRTPBVDepayClass)) +#define GST_IS_RTP_BV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_BV_DEPAY)) +#define GST_IS_RTP_BV_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_BV_DEPAY)) + +struct _GstRTPBVDepay +{ + GstRTPBaseDepayload depayload; + + gint mode; +}; + +struct _GstRTPBVDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_bv_depay_get_type (void); + +gboolean gst_rtp_bv_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_BV_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpbvpay.c b/gst/rtp/gstrtpbvpay.c new file mode 100755 index 0000000..15a7b7f --- /dev/null +++ b/gst/rtp/gstrtpbvpay.c @@ -0,0 +1,228 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpbvpay + * @see_also: rtpbvdepay + * + * Payload BroadcomVoice audio into RTP packets according to RFC 4298. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc4298.txt + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpbvpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpbvpay_debug); +#define GST_CAT_DEFAULT (rtpbvpay_debug) + +static GstStaticPadTemplate gst_rtp_bv_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-bv, " "mode = (int) {16, 32}") + ); + +static GstStaticPadTemplate gst_rtp_bv_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"BV16\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 16000, " "encoding-name = (string) \"BV32\"") + ); + + +static GstCaps *gst_rtp_bv_pay_sink_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static gboolean gst_rtp_bv_pay_sink_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); + +#define gst_rtp_bv_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPBVPay, gst_rtp_bv_pay, GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_bv_pay_class_init (GstRTPBVPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpbvpay_debug, "rtpbvpay", 0, + "BroadcomVoice audio RTP payloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_bv_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_bv_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP BV Payloader", + "Codec/Payloader/Network/RTP", + "Packetize BroadcomVoice audio streams into RTP packets (RFC 4298)", + "Wim Taymans <wim.taymans@collabora.co.uk>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_bv_pay_sink_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_bv_pay_sink_getcaps; +} + +static void +gst_rtp_bv_pay_init (GstRTPBVPay * rtpbvpay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpbvpay); + + rtpbvpay->mode = -1; + + /* tell rtpbaseaudiopayload that this is a frame based codec */ + gst_rtp_base_audio_payload_set_frame_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_bv_pay_sink_setcaps (GstRTPBasePayload * rtpbasepayload, GstCaps * caps) +{ + GstRTPBVPay *rtpbvpay; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + gint mode; + GstStructure *structure; + const char *payload_name; + + rtpbvpay = GST_RTP_BV_PAY (rtpbasepayload); + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpbasepayload); + + structure = gst_caps_get_structure (caps, 0); + + payload_name = gst_structure_get_name (structure); + if (g_ascii_strcasecmp ("audio/x-bv", payload_name)) + goto wrong_caps; + + if (!gst_structure_get_int (structure, "mode", &mode)) + goto no_mode; + + if (mode != 16 && mode != 32) + goto wrong_mode; + + if (mode == 16) { + gst_rtp_base_payload_set_options (rtpbasepayload, "audio", TRUE, "BV16", + 8000); + rtpbasepayload->clock_rate = 8000; + } else { + gst_rtp_base_payload_set_options (rtpbasepayload, "audio", TRUE, "BV32", + 16000); + rtpbasepayload->clock_rate = 16000; + } + + /* set options for this frame based audio codec */ + gst_rtp_base_audio_payload_set_frame_options (rtpbaseaudiopayload, + mode, mode == 16 ? 10 : 20); + + if (mode != rtpbvpay->mode && rtpbvpay->mode != -1) + goto mode_changed; + + rtpbvpay->mode = mode; + + return TRUE; + + /* ERRORS */ +wrong_caps: + { + GST_ERROR_OBJECT (rtpbvpay, "expected audio/x-bv, received %s", + payload_name); + return FALSE; + } +no_mode: + { + GST_ERROR_OBJECT (rtpbvpay, "did not receive a mode"); + return FALSE; + } +wrong_mode: + { + GST_ERROR_OBJECT (rtpbvpay, "mode must be 16 or 32, received %d", mode); + return FALSE; + } +mode_changed: + { + GST_ERROR_OBJECT (rtpbvpay, "Mode has changed from %d to %d! " + "Mode cannot change while streaming", rtpbvpay->mode, mode); + return FALSE; + } +} + +/* we return the padtemplate caps with the mode field fixated to a value if we + * can */ +static GstCaps * +gst_rtp_bv_pay_sink_getcaps (GstRTPBasePayload * rtppayload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + caps = gst_pad_get_pad_template_caps (pad); + + otherpadcaps = gst_pad_get_allowed_caps (rtppayload->srcpad); + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *structure; + const gchar *mode_str; + gint mode; + + structure = gst_caps_get_structure (otherpadcaps, 0); + + /* construct mode, if we can */ + mode_str = gst_structure_get_string (structure, "encoding-name"); + if (mode_str) { + if (!strcmp (mode_str, "BV16")) + mode = 16; + else if (!strcmp (mode_str, "BV32")) + mode = 32; + else + mode = -1; + + if (mode == 16 || mode == 32) { + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + gst_structure_set (structure, "mode", G_TYPE_INT, mode, NULL); + } + } + } + gst_caps_unref (otherpadcaps); + } + return caps; +} + +gboolean +gst_rtp_bv_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpbvpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_BV_PAY); +} diff --git a/gst/rtp/gstrtpbvpay.h b/gst/rtp/gstrtpbvpay.h new file mode 100755 index 0000000..09766cc --- /dev/null +++ b/gst/rtp/gstrtpbvpay.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_BV_PAY_H__ +#define __GST_RTP_BV_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_BV_PAY \ + (gst_rtp_bv_pay_get_type()) +#define GST_RTP_BV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_BV_PAY,GstRTPBVPay)) +#define GST_RTP_BV_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_BV_PAY,GstRTPBVPayClass)) +#define GST_IS_RTP_BV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_BV_PAY)) +#define GST_IS_RTP_BV_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_BV_PAY)) + +typedef struct _GstRTPBVPay GstRTPBVPay; +typedef struct _GstRTPBVPayClass GstRTPBVPayClass; + +struct _GstRTPBVPay +{ + GstRTPBaseAudioPayload audiopayload; + + gint mode; +}; + +struct _GstRTPBVPayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_bv_pay_get_type (void); + +gboolean gst_rtp_bv_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_BV_PAY_H__ */ diff --git a/gst/rtp/gstrtpceltdepay.c b/gst/rtp/gstrtpceltdepay.c new file mode 100755 index 0000000..5e15cc6 --- /dev/null +++ b/gst/rtp/gstrtpceltdepay.c @@ -0,0 +1,276 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpceltdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpceltdepay_debug); +#define GST_CAT_DEFAULT (rtpceltdepay_debug) + +/* RtpCELTDepay signals and args */ + +#define DEFAULT_FRAMESIZE 480 +#define DEFAULT_CHANNELS 1 +#define DEFAULT_CLOCKRATE 32000 + +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstStaticPadTemplate gst_rtp_celt_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [32000, 48000], " + "encoding-name = (string) \"CELT\"") + ); + +static GstStaticPadTemplate gst_rtp_celt_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-celt") + ); + +static GstBuffer *gst_rtp_celt_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_celt_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_celt_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpCELTDepay, gst_rtp_celt_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_celt_depay_class_init (GstRtpCELTDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpceltdepay_debug, "rtpceltdepay", 0, + "CELT RTP Depayloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_celt_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_celt_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP CELT depayloader", "Codec/Depayloader/Network/RTP", + "Extracts CELT audio from RTP packets", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_celt_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_celt_depay_setcaps; +} + +static void +gst_rtp_celt_depay_init (GstRtpCELTDepay * rtpceltdepay) +{ +} + +/* len 4 bytes LE, + * vendor string (len bytes), + * user_len 4 (0) bytes LE + */ +static const gchar gst_rtp_celt_comment[] = + "\045\0\0\0Depayloaded with GStreamer celtdepay\0\0\0\0"; + +static gboolean +gst_rtp_celt_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpCELTDepay *rtpceltdepay; + gint clock_rate, nb_channels = 0, frame_size = 0; + GstBuffer *buf; + GstMapInfo map; + guint8 *ptr; + const gchar *params; + GstCaps *srccaps; + gboolean res; + + rtpceltdepay = GST_RTP_CELT_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + goto no_clockrate; + depayload->clock_rate = clock_rate; + + if ((params = gst_structure_get_string (structure, "encoding-params"))) + nb_channels = atoi (params); + if (!nb_channels) + nb_channels = DEFAULT_CHANNELS; + + if ((params = gst_structure_get_string (structure, "frame-size"))) + frame_size = atoi (params); + if (!frame_size) + frame_size = DEFAULT_FRAMESIZE; + rtpceltdepay->frame_size = frame_size; + + GST_DEBUG_OBJECT (depayload, "clock-rate=%d channels=%d frame-size=%d", + clock_rate, nb_channels, frame_size); + + /* construct minimal header and comment packet for the decoder */ + buf = gst_buffer_new_and_alloc (60); + gst_buffer_map (buf, &map, GST_MAP_WRITE); + ptr = map.data; + memcpy (ptr, "CELT ", 8); + ptr += 8; + memcpy (ptr, "1.1.12", 7); + ptr += 20; + GST_WRITE_UINT32_LE (ptr, 0x80000006); /* version */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, 56); /* header_size */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, clock_rate); /* rate */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, nb_channels); /* channels */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, frame_size); /* frame-size */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, -1); /* overlap */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, -1); /* bytes_per_packet */ + ptr += 4; + GST_WRITE_UINT32_LE (ptr, 0); /* extra headers */ + gst_buffer_unmap (buf, &map); + + srccaps = gst_caps_new_empty_simple ("audio/x-celt"); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpceltdepay), buf); + + buf = gst_buffer_new_and_alloc (sizeof (gst_rtp_celt_comment)); + gst_buffer_fill (buf, 0, gst_rtp_celt_comment, sizeof (gst_rtp_celt_comment)); + + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpceltdepay), buf); + + return res; + + /* ERRORS */ +no_clockrate: + { + GST_ERROR_OBJECT (depayload, "no clock-rate specified"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_celt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf = NULL; + guint8 *payload; + guint offset, pos, payload_len, total_size, size; + guint8 s; + gint clock_rate = 0, frame_size = 0; + GstClockTime framesize_ns = 0, timestamp; + guint n = 0; + GstRtpCELTDepay *rtpceltdepay; + GstRTPBuffer rtp = { NULL, }; + + rtpceltdepay = GST_RTP_CELT_DEPAY (depayload); + clock_rate = depayload->clock_rate; + frame_size = rtpceltdepay->frame_size; + framesize_ns = gst_util_uint64_scale_int (frame_size, GST_SECOND, clock_rate); + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + GST_LOG_OBJECT (depayload, + "got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), gst_rtp_buffer_get_marker (&rtp), + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + GST_LOG_OBJECT (depayload, "got clock-rate=%d, frame_size=%d, " + "_ns=%" GST_TIME_FORMAT ", timestamp=%" GST_TIME_FORMAT, clock_rate, + frame_size, GST_TIME_ARGS (framesize_ns), GST_TIME_ARGS (timestamp)); + + payload = gst_rtp_buffer_get_payload (&rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + /* first count how many bytes are consumed by the size headers and make offset + * point to the first data byte */ + total_size = 0; + offset = 0; + while (total_size < payload_len) { + do { + s = payload[offset++]; + total_size += s + 1; + } while (s == 0xff); + } + + /* offset is now pointing to the payload */ + total_size = 0; + pos = 0; + while (total_size < payload_len) { + n++; + size = 0; + do { + s = payload[pos++]; + size += s; + total_size += s + 1; + } while (s == 0xff); + + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, offset, size); + offset += size; + + if (frame_size != -1 && clock_rate != -1) { + GST_BUFFER_TIMESTAMP (outbuf) = timestamp + framesize_ns * n; + GST_BUFFER_DURATION (outbuf) = framesize_ns; + } + GST_LOG_OBJECT (depayload, "push timestamp=%" + GST_TIME_FORMAT ", duration=%" GST_TIME_FORMAT, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), + GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); + + gst_rtp_base_depayload_push (depayload, outbuf); + } + gst_rtp_buffer_unmap (&rtp); + + return NULL; +} + +gboolean +gst_rtp_celt_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpceltdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_CELT_DEPAY); +} diff --git a/gst/rtp/gstrtpceltdepay.h b/gst/rtp/gstrtpceltdepay.h new file mode 100755 index 0000000..0905c68 --- /dev/null +++ b/gst/rtp/gstrtpceltdepay.h @@ -0,0 +1,54 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_CELT_DEPAY_H__ +#define __GST_RTP_CELT_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpCELTDepay GstRtpCELTDepay; +typedef struct _GstRtpCELTDepayClass GstRtpCELTDepayClass; + +#define GST_TYPE_RTP_CELT_DEPAY \ + (gst_rtp_celt_depay_get_type()) +#define GST_RTP_CELT_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_CELT_DEPAY,GstRtpCELTDepay)) +#define GST_RTP_CELT_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_CELT_DEPAY,GstRtpCELTDepayClass)) +#define GST_IS_RTP_CELT_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_CELT_DEPAY)) +#define GST_IS_RTP_CELT_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_CELT_DEPAY)) + +struct _GstRtpCELTDepay +{ + GstRTPBaseDepayload depayload; + gint frame_size; +}; + +struct _GstRtpCELTDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_celt_depay_get_type (void); + +gboolean gst_rtp_celt_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_CELT_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpceltpay.c b/gst/rtp/gstrtpceltpay.c new file mode 100755 index 0000000..3aabd9f --- /dev/null +++ b/gst/rtp/gstrtpceltpay.c @@ -0,0 +1,488 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpceltpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpceltpay_debug); +#define GST_CAT_DEFAULT (rtpceltpay_debug) + +static GstStaticPadTemplate gst_rtp_celt_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-celt, " + "rate = (int) [ 32000, 64000 ], " + "channels = (int) [1, 2], " "frame-size = (int) [ 64, 512 ]") + ); + +static GstStaticPadTemplate gst_rtp_celt_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [ 32000, 48000 ], " + "encoding-name = (string) \"CELT\"") + ); + +static void gst_rtp_celt_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_celt_pay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_celt_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstCaps *gst_rtp_celt_pay_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static GstFlowReturn gst_rtp_celt_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +#define gst_rtp_celt_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpCELTPay, gst_rtp_celt_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_celt_pay_class_init (GstRtpCELTPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpceltpay_debug, "rtpceltpay", 0, + "CELT RTP Payloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_celt_pay_finalize; + + gstelement_class->change_state = gst_rtp_celt_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_celt_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_celt_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP CELT payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes CELT audio into a RTP packet", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_celt_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_celt_pay_getcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_celt_pay_handle_buffer; +} + +static void +gst_rtp_celt_pay_init (GstRtpCELTPay * rtpceltpay) +{ + rtpceltpay->queue = g_queue_new (); +} + +static void +gst_rtp_celt_pay_finalize (GObject * object) +{ + GstRtpCELTPay *rtpceltpay; + + rtpceltpay = GST_RTP_CELT_PAY (object); + + g_queue_free (rtpceltpay->queue); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_celt_pay_clear_queued (GstRtpCELTPay * rtpceltpay) +{ + GstBuffer *buf; + + while ((buf = g_queue_pop_head (rtpceltpay->queue))) + gst_buffer_unref (buf); + + rtpceltpay->bytes = 0; + rtpceltpay->sbytes = 0; + rtpceltpay->qduration = 0; +} + +static void +gst_rtp_celt_pay_add_queued (GstRtpCELTPay * rtpceltpay, GstBuffer * buffer, + guint ssize, guint size, GstClockTime duration) +{ + g_queue_push_tail (rtpceltpay->queue, buffer); + rtpceltpay->sbytes += ssize; + rtpceltpay->bytes += size; + /* only add durations when we have a valid previous duration */ + if (rtpceltpay->qduration != -1) { + if (duration != -1) + /* only add valid durations */ + rtpceltpay->qduration += duration; + else + /* if we add a buffer without valid duration, our total queued duration + * becomes unknown */ + rtpceltpay->qduration = -1; + } +} + +static gboolean +gst_rtp_celt_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + /* don't configure yet, we wait for the ident packet */ + return TRUE; +} + + +static GstCaps * +gst_rtp_celt_pay_getcaps (GstRTPBasePayload * payload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + const gchar *params; + + caps = gst_pad_get_pad_template_caps (pad); + + otherpadcaps = gst_pad_get_allowed_caps (payload->srcpad); + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *ps; + GstStructure *s; + gint clock_rate = 0, frame_size = 0, channels = 1; + + caps = gst_caps_make_writable (caps); + + ps = gst_caps_get_structure (otherpadcaps, 0); + s = gst_caps_get_structure (caps, 0); + + if (gst_structure_get_int (ps, "clock-rate", &clock_rate)) { + gst_structure_fixate_field_nearest_int (s, "rate", clock_rate); + } + + if ((params = gst_structure_get_string (ps, "frame-size"))) + frame_size = atoi (params); + if (frame_size) + gst_structure_set (s, "frame-size", G_TYPE_INT, frame_size, NULL); + + if ((params = gst_structure_get_string (ps, "encoding-params"))) { + channels = atoi (params); + gst_structure_fixate_field_nearest_int (s, "channels", channels); + } + + GST_DEBUG_OBJECT (payload, "clock-rate=%d frame-size=%d channels=%d", + clock_rate, frame_size, channels); + } + gst_caps_unref (otherpadcaps); + } + + return caps; +} + +static gboolean +gst_rtp_celt_pay_parse_ident (GstRtpCELTPay * rtpceltpay, + const guint8 * data, guint size) +{ + guint32 version, header_size, rate, nb_channels, frame_size, overlap; + guint32 bytes_per_packet; + GstRTPBasePayload *payload; + gchar *cstr, *fsstr; + gboolean res; + + /* we need the header string (8), the version string (20), the version + * and the header length. */ + if (size < 36) + goto too_small; + + if (!g_str_has_prefix ((const gchar *) data, "CELT ")) + goto wrong_header; + + /* skip header and version string */ + data += 28; + + version = GST_READ_UINT32_LE (data); + GST_DEBUG_OBJECT (rtpceltpay, "version %08x", version); +#if 0 + if (version != 1) + goto wrong_version; +#endif + + data += 4; + /* ensure sizes */ + header_size = GST_READ_UINT32_LE (data); + if (header_size < 56) + goto header_too_small; + + if (size < header_size) + goto payload_too_small; + + data += 4; + rate = GST_READ_UINT32_LE (data); + data += 4; + nb_channels = GST_READ_UINT32_LE (data); + data += 4; + frame_size = GST_READ_UINT32_LE (data); + data += 4; + overlap = GST_READ_UINT32_LE (data); + data += 4; + bytes_per_packet = GST_READ_UINT32_LE (data); + + GST_DEBUG_OBJECT (rtpceltpay, "rate %d, nb_channels %d, frame_size %d", + rate, nb_channels, frame_size); + GST_DEBUG_OBJECT (rtpceltpay, "overlap %d, bytes_per_packet %d", + overlap, bytes_per_packet); + + payload = GST_RTP_BASE_PAYLOAD (rtpceltpay); + + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "CELT", rate); + cstr = g_strdup_printf ("%d", nb_channels); + fsstr = g_strdup_printf ("%d", frame_size); + res = gst_rtp_base_payload_set_outcaps (payload, "encoding-params", + G_TYPE_STRING, cstr, "frame-size", G_TYPE_STRING, fsstr, NULL); + g_free (cstr); + g_free (fsstr); + + return res; + + /* ERRORS */ +too_small: + { + GST_DEBUG_OBJECT (rtpceltpay, + "ident packet too small, need at least 32 bytes"); + return FALSE; + } +wrong_header: + { + GST_DEBUG_OBJECT (rtpceltpay, + "ident packet does not start with \"CELT \""); + return FALSE; + } +#if 0 +wrong_version: + { + GST_DEBUG_OBJECT (rtpceltpay, "can only handle version 1, have version %d", + version); + return FALSE; + } +#endif +header_too_small: + { + GST_DEBUG_OBJECT (rtpceltpay, + "header size too small, need at least 80 bytes, " "got only %d", + header_size); + return FALSE; + } +payload_too_small: + { + GST_DEBUG_OBJECT (rtpceltpay, + "payload too small, need at least %d bytes, got only %d", header_size, + size); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_celt_pay_flush_queued (GstRtpCELTPay * rtpceltpay) +{ + GstFlowReturn ret; + GstBuffer *buf, *outbuf; + guint8 *payload, *spayload; + guint payload_len; + GstClockTime duration; + GstRTPBuffer rtp = { NULL, }; + + payload_len = rtpceltpay->bytes + rtpceltpay->sbytes; + duration = rtpceltpay->qduration; + + GST_DEBUG_OBJECT (rtpceltpay, "flushing out %u, duration %" GST_TIME_FORMAT, + payload_len, GST_TIME_ARGS (rtpceltpay->qduration)); + + /* get a big enough packet for the sizes + payloads */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + GST_BUFFER_DURATION (outbuf) = duration; + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* point to the payload for size headers and data */ + spayload = gst_rtp_buffer_get_payload (&rtp); + payload = spayload + rtpceltpay->sbytes; + + while ((buf = g_queue_pop_head (rtpceltpay->queue))) { + guint size; + + /* copy first timestamp to output */ + if (GST_BUFFER_TIMESTAMP (outbuf) == -1) + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); + + /* write the size to the header */ + size = gst_buffer_get_size (buf); + while (size > 0xff) { + *spayload++ = 0xff; + size -= 0xff; + } + *spayload++ = size; + + /* copy payload */ + size = gst_buffer_get_size (buf); + gst_buffer_extract (buf, 0, payload, size); + payload += size; + + gst_buffer_unref (buf); + } + gst_rtp_buffer_unmap (&rtp); + + /* we consumed it all */ + rtpceltpay->bytes = 0; + rtpceltpay->sbytes = 0; + rtpceltpay->qduration = 0; + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpceltpay), outbuf); + + return ret; +} + +static GstFlowReturn +gst_rtp_celt_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstFlowReturn ret; + GstRtpCELTPay *rtpceltpay; + gsize payload_len; + GstMapInfo map; + GstClockTime duration, packet_dur; + guint i, ssize, packet_len; + + rtpceltpay = GST_RTP_CELT_PAY (basepayload); + + ret = GST_FLOW_OK; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + switch (rtpceltpay->packet) { + case 0: + /* ident packet. We need to parse the headers to construct the RTP + * properties. */ + if (!gst_rtp_celt_pay_parse_ident (rtpceltpay, map.data, map.size)) + goto parse_error; + + goto cleanup; + case 1: + /* comment packet, we ignore it */ + goto cleanup; + default: + /* other packets go in the payload */ + break; + } + gst_buffer_unmap (buffer, &map); + + duration = GST_BUFFER_DURATION (buffer); + + GST_LOG_OBJECT (rtpceltpay, + "got buffer of duration %" GST_TIME_FORMAT ", size %" G_GSIZE_FORMAT, + GST_TIME_ARGS (duration), map.size); + + /* calculate the size of the size field and the payload */ + ssize = 1; + for (i = map.size; i > 0xff; i -= 0xff) + ssize++; + + GST_DEBUG_OBJECT (rtpceltpay, "bytes for size %u", ssize); + + /* calculate what the new size and duration would be of the packet */ + payload_len = ssize + map.size + rtpceltpay->bytes + rtpceltpay->sbytes; + if (rtpceltpay->qduration != -1 && duration != -1) + packet_dur = rtpceltpay->qduration + duration; + else + packet_dur = 0; + + packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); + + if (gst_rtp_base_payload_is_filled (basepayload, packet_len, packet_dur)) { + /* size or duration would overflow the packet, flush the queued data */ + ret = gst_rtp_celt_pay_flush_queued (rtpceltpay); + } + + /* queue the packet */ + gst_rtp_celt_pay_add_queued (rtpceltpay, buffer, ssize, map.size, duration); + +done: + rtpceltpay->packet++; + + return ret; + + /* ERRORS */ +cleanup: + { + gst_buffer_unmap (buffer, &map); + goto done; + } +parse_error: + { + GST_ELEMENT_ERROR (rtpceltpay, STREAM, DECODE, (NULL), + ("Error parsing first identification packet.")); + gst_buffer_unmap (buffer, &map); + return GST_FLOW_ERROR; + } +} + +static GstStateChangeReturn +gst_rtp_celt_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpCELTPay *rtpceltpay; + GstStateChangeReturn ret; + + rtpceltpay = GST_RTP_CELT_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + rtpceltpay->packet = 0; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_celt_pay_clear_queued (rtpceltpay); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_celt_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpceltpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_CELT_PAY); +} diff --git a/gst/rtp/gstrtpceltpay.h b/gst/rtp/gstrtpceltpay.h new file mode 100755 index 0000000..dcdd0ec --- /dev/null +++ b/gst/rtp/gstrtpceltpay.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + + +#ifndef __GST_RTP_CELT_PAY_H__ +#define __GST_RTP_CELT_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpCELTPay GstRtpCELTPay; +typedef struct _GstRtpCELTPayClass GstRtpCELTPayClass; + +#define GST_TYPE_RTP_CELT_PAY \ + (gst_rtp_celt_pay_get_type()) +#define GST_RTP_CELT_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_CELT_PAY,GstRtpCELTPay)) +#define GST_RTP_CELT_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_CELT_PAY,GstRtpCELTPayClass)) +#define GST_IS_RTP_CELT_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_CELT_PAY)) +#define GST_IS_RTP_CELT_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_CELT_PAY)) + +struct _GstRtpCELTPay +{ + GstRTPBasePayload payload; + + guint64 packet; + + /* queue to hold packets */ + GQueue *queue; + guint sbytes; /* bytes queued for sizes */ + guint bytes; /* bytes queued for data */ + GstClockTime qduration; /* queued duration */ +}; + +struct _GstRtpCELTPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_celt_pay_get_type (void); + +gboolean gst_rtp_celt_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_CELT_PAY_H__ */ diff --git a/gst/rtp/gstrtpchannels.c b/gst/rtp/gstrtpchannels.c new file mode 100755 index 0000000..100aaa4 --- /dev/null +++ b/gst/rtp/gstrtpchannels.c @@ -0,0 +1,164 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <string.h> +#include <stdlib.h> + +#include "gstrtpchannels.h" + +/* + * RTP channel positions as discussed in RFC 3551 and also RFC 3555 + * + * We can't really represent the described channel positions in GStreamer but we + * implement a (very rough) approximation here. + */ + +static gboolean +check_channels (const GstRTPChannelOrder * order, + const GstAudioChannelPosition * pos) +{ + gint i, j; + gboolean res = TRUE; + + for (i = 0; i < order->channels; i++) { + for (j = 0; j < order->channels; j++) { + if (order->pos[j] == pos[i]) + break; + } + if (j == order->channels) + return FALSE; + } + return res; +} + +/** + * gst_rtp_channels_get_by_pos: + * @channels: the amount of channels + * @pos: a channel layout + * + * Return a description of the channel layout. + * + * Returns: a #GstRTPChannelOrder with the channel information or NULL when @pos + * is not a valid layout. + */ +const GstRTPChannelOrder * +gst_rtp_channels_get_by_pos (gint channels, const GstAudioChannelPosition * pos) +{ + gint i; + const GstRTPChannelOrder *res = NULL; + + g_return_val_if_fail (pos != NULL, NULL); + + for (i = 0; channel_orders[i].pos; i++) { + if (channel_orders[i].channels != channels) + continue; + + if (check_channels (&channel_orders[i], pos)) { + res = &channel_orders[i]; + break; + } + } + return res; +} + +/** + * gst_rtp_channels_create_default: + * @channels: the amount of channels + * @order: a channel order + * + * Get the channel order info the @order and @channels. + * + * Returns: a #GstRTPChannelOrder with the channel information or NULL when + * @order is not a know layout for @channels. + */ +const GstRTPChannelOrder * +gst_rtp_channels_get_by_order (gint channels, const gchar * order) +{ + gint i; + const GstRTPChannelOrder *res = NULL; + + for (i = 0; channel_orders[i].pos; i++) { + if (channel_orders[i].channels != channels) + continue; + + /* no name but channels match, continue */ + if (!channel_orders[i].name || !order) { + res = &channel_orders[i]; + break; + } + + /* compare names */ + if (g_ascii_strcasecmp (channel_orders[i].name, order)) { + res = &channel_orders[i]; + break; + } + } + return res; +} + +/** + * gst_rtp_channels_get_by_index: + * @channels: the amount of channels + * @idx: the channel index to retrieve + * + * Get the allowed channel order descriptions for @channels. @idx can be used to + * retrieve the desired index. + * + * Returns: a #GstRTPChannelOrder at @idx, NULL when there are no valid channel + * orders. + */ +const GstRTPChannelOrder * +gst_rtp_channels_get_by_index (gint channels, guint idx) +{ + gint i; + const GstRTPChannelOrder *res = NULL; + + for (i = 0; channel_orders[i].pos; i++) { + if (channel_orders[i].channels != channels) + continue; + + if (idx == 0) { + res = &channel_orders[i]; + break; + } + idx--; + } + return res; +} + + +/** + * gst_rtp_channels_create_default: + * @channels: the amount of channels + * + * Create a default none channel mapping for @channels. + * + * Returns: a #GstAudioChannelPosition with all the channel position info set to + * #GST_AUDIO_CHANNEL_POSITION_NONE. + */ +void +gst_rtp_channels_create_default (gint channels, GstAudioChannelPosition * posn) +{ + gint i; + + g_return_if_fail (channels > 0); + + for (i = 0; i < channels; i++) + posn[i] = GST_AUDIO_CHANNEL_POSITION_NONE; +} diff --git a/gst/rtp/gstrtpchannels.h b/gst/rtp/gstrtpchannels.h new file mode 100755 index 0000000..31727fb --- /dev/null +++ b/gst/rtp/gstrtpchannels.h @@ -0,0 +1,190 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include <string.h> +#include <stdlib.h> + +#include <gst/audio/audio.h> + +#ifndef __GST_RTP_CHANNELS_H__ +#define __GST_RTP_CHANNELS_H__ + +typedef struct +{ + const gchar *name; + gint channels; + const GstAudioChannelPosition *pos; +} GstRTPChannelOrder; + +static const GstAudioChannelPosition pos_4_1[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT +}; + +static const GstAudioChannelPosition pos_4_2[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1 +}; + +static const GstAudioChannelPosition pos_4_3[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1 +}; + +static const GstAudioChannelPosition pos_5_1[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER +}; + +static const GstAudioChannelPosition pos_6_1[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1 +}; + +static const GstAudioChannelPosition pos_6_2[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT +}; + +static const GstAudioChannelPosition pos_8_1[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT +}; + +static const GstAudioChannelPosition pos_8_2[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT +}; + +static const GstAudioChannelPosition pos_8_3[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_LFE1, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_REAR_LEFT, + GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT +}; + +static const GstAudioChannelPosition pos_def_1[] = { + GST_AUDIO_CHANNEL_POSITION_MONO +}; + +static const GstAudioChannelPosition pos_def_2[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT +}; + +static const GstAudioChannelPosition pos_def_3[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER +}; + +static const GstAudioChannelPosition pos_def_4[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE1 +}; + +static const GstAudioChannelPosition pos_def_5[] = { + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT +}; + +static const GstAudioChannelPosition pos_def_6[] = { + GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT, + GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER, + GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT, + GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT, + GST_AUDIO_CHANNEL_POSITION_LFE1 +}; + +static const GstRTPChannelOrder channel_orders[] = +{ + /* 4 channels */ + { "DV.LRLsRs", 4, pos_4_1 }, + { "DV.LRCS", 4, pos_4_2 }, + { "DV.LRCWo", 4, pos_4_3 }, + /* 5 channels */ + { "DV.LRLsRsC", 5, pos_5_1 }, + /* 6 channels */ + { "DV.LRLsRsCS", 6, pos_6_1 }, + { "DV.LmixRmixTWoQ1Q2", 6, pos_6_2 }, + /* 8 channels */ + { "DV.LRCWoLsRsLmixRmix", 8, pos_8_1 }, + { "DV.LRCWoLs1Rs1Ls2Rs2", 8, pos_8_2 }, + { "DV.LRCWoLsRsLcRc", 8, pos_8_3 }, + + /* default layouts */ + { NULL, 1, pos_def_1 }, + { NULL, 2, pos_def_2 }, + { NULL, 3, pos_def_3 }, + { NULL, 4, pos_def_4 }, + { NULL, 5, pos_def_5 }, + { NULL, 6, pos_def_6 }, + + /* terminator, invalid entry */ + { NULL, 0, NULL }, +}; + +const GstRTPChannelOrder * gst_rtp_channels_get_by_pos (gint channels, + const GstAudioChannelPosition *pos); +const GstRTPChannelOrder * gst_rtp_channels_get_by_order (gint channels, + const gchar *order); +const GstRTPChannelOrder * gst_rtp_channels_get_by_index (gint channels, guint idx); + +void gst_rtp_channels_create_default (gint channels, GstAudioChannelPosition *pos); + +#endif /* __GST_RTP_CHANNELS_H__ */ diff --git a/gst/rtp/gstrtpdvdepay.c b/gst/rtp/gstrtpdvdepay.c new file mode 100755 index 0000000..f13b696 --- /dev/null +++ b/gst/rtp/gstrtpdvdepay.c @@ -0,0 +1,415 @@ +/* Farsight + * Copyright (C) 2006 Marcel Moreaux <marcelm@spacelabs.nl> + * (C) 2008 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * RTP DV depayloader. + * + * Important note for NTSC-users: + * + * Because the author uses PAL video, and he does not have proper DV + * documentation (the DV format specification is not freely available), + * this code may very well contain PAL-specific assumptions. + */ + +#include <stdlib.h> +#include <string.h> +#include <gst/gst.h> + +#include "gstrtpdvdepay.h" + +GST_DEBUG_CATEGORY (rtpdvdepay_debug); +#define GST_CAT_DEFAULT (rtpdvdepay_debug) +/* Filter signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, +}; + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-dv") + ); + +static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) { \"video\", \"audio\" }," + "encoding-name = (string) \"DV\", " + "clock-rate = (int) 90000," + "encode = (string) { \"SD-VCR/525-60\", \"SD-VCR/625-50\", \"HD-VCR/1125-60\"," + "\"HD-VCR/1250-50\", \"SDL-VCR/525-60\", \"SDL-VCR/625-50\"," + "\"306M/525-60\", \"306M/625-50\", \"314M-25/525-60\"," + "\"314M-25/625-50\", \"314M-50/525-60\", \"314M-50/625-50\" }" + /* optional parameters can't go in the template + * "audio = (string) { \"bundled\", \"none\" }" + */ + ) + ); + +static GstStateChangeReturn +gst_rtp_dv_depay_change_state (GstElement * element, GstStateChange transition); + +static GstBuffer *gst_rtp_dv_depay_process (GstRTPBaseDepayload * base, + GstBuffer * in); +static gboolean gst_rtp_dv_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_dv_depay_parent_class parent_class +G_DEFINE_TYPE (GstRTPDVDepay, gst_rtp_dv_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + + +static void +gst_rtp_dv_depay_class_init (GstRTPDVDepayClass * klass) +{ + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class = + (GstRTPBaseDepayloadClass *) klass; + + GST_DEBUG_CATEGORY_INIT (rtpdvdepay_debug, "rtpdvdepay", 0, + "DV RTP Depayloader"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rtp_dv_depay_change_state); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_factory)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_factory)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP DV Depayloader", + "Codec/Depayloader/Network/RTP", + "Depayloads DV from RTP packets (RFC 3189)", + "Marcel Moreaux <marcelm@spacelabs.nl>, Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->process = + GST_DEBUG_FUNCPTR (gst_rtp_dv_depay_process); + gstrtpbasedepayload_class->set_caps = + GST_DEBUG_FUNCPTR (gst_rtp_dv_depay_setcaps); +} + +/* initialize the new element + * instantiate pads and add them to element + * set functions + * initialize structure + */ +static void +gst_rtp_dv_depay_init (GstRTPDVDepay * filter) +{ +} + +static gboolean +parse_encode (GstRTPDVDepay * rtpdvdepay, const gchar * encode) +{ + rtpdvdepay->width = 720; + if (!strcmp (encode, "314M-25/525-60")) { + rtpdvdepay->frame_size = 240000; + rtpdvdepay->height = 480; + rtpdvdepay->rate_num = 30000; + rtpdvdepay->rate_denom = 1001; + } else if (!strcmp (encode, "SD-VCR/525-60")) { + rtpdvdepay->frame_size = 120000; + rtpdvdepay->height = 480; + rtpdvdepay->rate_num = 30000; + rtpdvdepay->rate_denom = 1001; + } else if (!strcmp (encode, "314M-50/625-50")) { + rtpdvdepay->frame_size = 288000; + rtpdvdepay->height = 576; + rtpdvdepay->rate_num = 25; + rtpdvdepay->rate_denom = 1; + } else if (!strcmp (encode, "SD-VCR/625-50")) { + rtpdvdepay->frame_size = 144000; + rtpdvdepay->height = 576; + rtpdvdepay->rate_num = 25; + rtpdvdepay->rate_denom = 1; + } else if (!strcmp (encode, "314M-25/625-50")) { + rtpdvdepay->frame_size = 144000; + rtpdvdepay->height = 576; + rtpdvdepay->rate_num = 25; + rtpdvdepay->rate_denom = 1; + } else + rtpdvdepay->frame_size = -1; + + return rtpdvdepay->frame_size != -1; +} + +static gboolean +gst_rtp_dv_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRTPDVDepay *rtpdvdepay; + GstCaps *srccaps; + gint clock_rate; + gboolean systemstream, ret; + const gchar *encode, *media; + + rtpdvdepay = GST_RTP_DV_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + /* we really need the encode property to figure out the frame size, it's also + * required by the spec */ + if (!(encode = gst_structure_get_string (structure, "encode"))) + goto no_encode; + + /* figure out the size of one frame */ + if (!parse_encode (rtpdvdepay, encode)) + goto unknown_encode; + + /* check the media, this tells us that the stream has video or not */ + if (!(media = gst_structure_get_string (structure, "media"))) + goto no_media; + + systemstream = FALSE; + + if (!strcmp (media, "audio")) { + /* we need a demuxer for audio only */ + systemstream = TRUE; + } else if (!strcmp (media, "video")) { + const gchar *audio; + + /* check the optional audio field, if it's present and set to bundled, we + * are dealing with a system stream. */ + if ((audio = gst_structure_get_string (structure, "audio"))) { + if (!strcmp (audio, "bundled")) + systemstream = TRUE; + } + } + + /* allocate accumulator */ + rtpdvdepay->acc = gst_buffer_new_and_alloc (rtpdvdepay->frame_size); + + /* Initialize the new accumulator frame. + * If the previous frame exists, copy that into the accumulator frame. + * This way, missing packets in the stream won't show up badly. */ + gst_buffer_memset (rtpdvdepay->acc, 0, 0, rtpdvdepay->frame_size); + + srccaps = gst_caps_new_simple ("video/x-dv", + "systemstream", G_TYPE_BOOLEAN, systemstream, + "width", G_TYPE_INT, rtpdvdepay->width, + "height", G_TYPE_INT, rtpdvdepay->height, + "framerate", GST_TYPE_FRACTION, rtpdvdepay->rate_num, + rtpdvdepay->rate_denom, NULL); + ret = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return ret; + + /* ERRORS */ +no_encode: + { + GST_ERROR_OBJECT (rtpdvdepay, "required encode property not found in caps"); + return FALSE; + } +unknown_encode: + { + GST_ERROR_OBJECT (rtpdvdepay, "unknown encode property %s found", encode); + return FALSE; + } +no_media: + { + GST_ERROR_OBJECT (rtpdvdepay, "required media property not found in caps"); + return FALSE; + } +} + +/* A DV frame consists of a bunch of 80-byte DIF blocks. + * Each DIF block contains a 3-byte header telling where in the DV frame the + * DIF block should go. We use this information to calculate its position. + */ +static guint +calculate_difblock_location (guint8 * block) +{ + gint block_type, dif_sequence, dif_block; + guint location; + + block_type = block[0] >> 5; + dif_sequence = block[1] >> 4; + dif_block = block[2]; + + location = dif_sequence * 150; + + switch (block_type) { + case 0: /* Header block, no offset */ + break; + case 1: /* Subcode block */ + location += (1 + dif_block); + break; + case 2: /* VAUX block */ + location += (3 + dif_block); + break; + case 3: /* Audio block */ + location += (6 + dif_block * 16); + break; + case 4: /* Video block */ + location += (7 + (dif_block / 15) + dif_block); + break; + default: /* Something bogus */ + GST_DEBUG ("UNKNOWN BLOCK"); + location = -1; + break; + } + return location; +} + +/* Process one RTP packet. Accumulate RTP payload in the proper place in a DV + * frame, and return that frame if we detect a new frame, or NULL otherwise. + * We assume a DV frame is 144000 bytes. That should accomodate PAL as well as + * NTSC. + */ +static GstBuffer * +gst_rtp_dv_depay_process (GstRTPBaseDepayload * base, GstBuffer * in) +{ + GstBuffer *out = NULL; + guint8 *payload; + guint32 rtp_ts; + guint payload_len, location; + GstRTPDVDepay *dvdepay = GST_RTP_DV_DEPAY (base); + gboolean marker; + GstRTPBuffer rtp = { NULL, }; + + gst_rtp_buffer_map (in, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + /* Check if the received packet contains (the start of) a new frame, we do + * this by checking the RTP timestamp. */ + rtp_ts = gst_rtp_buffer_get_timestamp (&rtp); + + /* we cannot copy the packet yet if the marker is set, we will do that below + * after taking out the data */ + if (dvdepay->prev_ts != -1 && rtp_ts != dvdepay->prev_ts && !marker) { + /* the timestamp changed */ + GST_DEBUG_OBJECT (dvdepay, "new frame with ts %u, old ts %u", rtp_ts, + dvdepay->prev_ts); + + /* return copy of accumulator. */ + out = gst_buffer_copy (dvdepay->acc); + } + + /* Extract the payload */ + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + /* copy all DIF chunks in their place. */ + while (payload_len >= 80) { + guint offset; + + /* Calculate where in the frame the payload should go */ + location = calculate_difblock_location (payload); + + if (location < 6) { + /* part of a header, set the flag to mark that we have the header. */ + dvdepay->header_mask |= (1 << location); + GST_LOG_OBJECT (dvdepay, "got header at location %d, now %02x", location, + dvdepay->header_mask); + } else { + GST_LOG_OBJECT (dvdepay, "got block at location %d", location); + } + + if (location != -1) { + /* get the byte offset of the dif block */ + offset = location * 80; + + /* And copy it in, provided the location is sane. */ + if (offset <= dvdepay->frame_size - 80) + gst_buffer_fill (dvdepay->acc, offset, payload, 80); + } + + payload += 80; + payload_len -= 80; + } + gst_rtp_buffer_unmap (&rtp); + + if (marker) { + GST_DEBUG_OBJECT (dvdepay, "marker bit complete frame %u", rtp_ts); + /* only copy the frame when we have a complete header */ + if (dvdepay->header_mask == 0x3f) { + /* The marker marks the end of a frame that we need to push. The next frame + * will change the timestamp but we won't copy the accumulator again because + * we set the prev_ts to -1. */ + out = gst_buffer_copy (dvdepay->acc); + } else { + GST_WARNING_OBJECT (dvdepay, "waiting for frame headers %02x", + dvdepay->header_mask); + } + dvdepay->prev_ts = -1; + } else { + /* save last timestamp */ + dvdepay->prev_ts = rtp_ts; + } + return out; +} + +static void +gst_rtp_dv_depay_reset (GstRTPDVDepay * depay) +{ + if (depay->acc) + gst_buffer_unref (depay->acc); + depay->acc = NULL; + + depay->prev_ts = -1; + depay->header_mask = 0; +} + +static GstStateChangeReturn +gst_rtp_dv_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstRTPDVDepay *depay = GST_RTP_DV_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_dv_depay_reset (depay); + break; + default: + break; + } + + ret = GST_CALL_PARENT_WITH_DEFAULT (GST_ELEMENT_CLASS, change_state, + (element, transition), GST_STATE_CHANGE_FAILURE); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_dv_depay_reset (depay); + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_dv_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpdvdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_DV_DEPAY); +} diff --git a/gst/rtp/gstrtpdvdepay.h b/gst/rtp/gstrtpdvdepay.h new file mode 100755 index 0000000..1ce5b97 --- /dev/null +++ b/gst/rtp/gstrtpdvdepay.h @@ -0,0 +1,66 @@ +/* Farsight + * Copyright (C) 2006 Marcel Moreaux <marcelm@spacelabs.nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GSTRTPDVDEPAY_H__ +#define __GSTRTPDVDEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +/* #define's don't like whitespacey bits */ +#define GST_TYPE_RTP_DV_DEPAY (gst_rtp_dv_depay_get_type()) +#define GST_RTP_DV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DV_DEPAY,GstRTPDVDepay)) +#define GST_RTP_DV_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DV_DEPAY,GstRTPDVDepay)) +#define GST_IS_RTP_DV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DV_DEPAY)) +#define GST_IS_RTP_DV_DEPAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DV_DEPAY)) + +typedef struct _GstRTPDVDepay GstRTPDVDepay; +typedef struct _GstRTPDVDepayClass GstRTPDVDepayClass; + +struct _GstRTPDVDepay +{ + GstRTPBaseDepayload parent; + + GstBuffer *acc; + guint frame_size; + guint32 prev_ts; + guint8 header_mask; + + gint width, height; + gint rate_num, rate_denom; +}; + +struct _GstRTPDVDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_dv_depay_get_type (void); + +gboolean gst_rtp_dv_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GSTRTPDVDEPAY_H__ */ diff --git a/gst/rtp/gstrtpdvpay.c b/gst/rtp/gstrtpdvpay.c new file mode 100755 index 0000000..db75cf6 --- /dev/null +++ b/gst/rtp/gstrtpdvpay.c @@ -0,0 +1,395 @@ +/* Farsight + * Copyright (C) 2006 Marcel Moreaux <marcelm@spacelabs.nl> + * (C) 2008 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpdvpay.h" + +GST_DEBUG_CATEGORY (rtpdvpay_debug); +#define GST_CAT_DEFAULT (rtpdvpay_debug) + +#define DEFAULT_MODE GST_DV_PAY_MODE_VIDEO +enum +{ + PROP_0, + PROP_MODE +}; + +/* takes both system and non-system streams */ +static GstStaticPadTemplate gst_rtp_dv_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-dv") + ); + +static GstStaticPadTemplate gst_rtp_dv_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) { \"video\", \"audio\" } ," + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "encoding-name = (string) \"DV\", " + "clock-rate = (int) 90000," + "encode = (string) { \"SD-VCR/525-60\", \"SD-VCR/625-50\", \"HD-VCR/1125-60\"," + "\"HD-VCR/1250-50\", \"SDL-VCR/525-60\", \"SDL-VCR/625-50\"," + "\"306M/525-60\", \"306M/625-50\", \"314M-25/525-60\"," + "\"314M-25/625-50\", \"314M-50/525-60\", \"314M-50/625-50\" }" + /* optional parameters can't go in the template + * "audio = (string) { \"bundled\", \"none\" }" + */ + ) + ); + +static gboolean gst_rtp_dv_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_dv_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); + +#define GST_TYPE_DV_PAY_MODE (gst_dv_pay_mode_get_type()) +static GType +gst_dv_pay_mode_get_type (void) +{ + static GType dv_pay_mode_type = 0; + static const GEnumValue dv_pay_modes[] = { + {GST_DV_PAY_MODE_VIDEO, "Video only", "video"}, + {GST_DV_PAY_MODE_BUNDLED, "Video and Audio bundled", "bundled"}, + {GST_DV_PAY_MODE_AUDIO, "Audio only", "audio"}, + {0, NULL, NULL}, + }; + + if (!dv_pay_mode_type) { + dv_pay_mode_type = g_enum_register_static ("GstDVPayMode", dv_pay_modes); + } + return dv_pay_mode_type; +} + + +static void gst_dv_pay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_dv_pay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +#define gst_rtp_dv_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPDVPay, gst_rtp_dv_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_dv_pay_class_init (GstRTPDVPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpdvpay_debug, "rtpdvpay", 0, "DV RTP Payloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_dv_pay_set_property; + gobject_class->get_property = gst_dv_pay_get_property; + + g_object_class_install_property (gobject_class, PROP_MODE, + g_param_spec_enum ("mode", "Mode", + "The payload mode of payloading", + GST_TYPE_DV_PAY_MODE, DEFAULT_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_dv_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_dv_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP DV Payloader", + "Codec/Payloader/Network/RTP", + "Payloads DV into RTP packets (RFC 3189)", + "Marcel Moreaux <marcelm@spacelabs.nl>, Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_dv_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_dv_pay_handle_buffer; +} + +static void +gst_rtp_dv_pay_init (GstRTPDVPay * rtpdvpay) +{ +} + +static void +gst_dv_pay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRTPDVPay *rtpdvpay = GST_RTP_DV_PAY (object); + + switch (prop_id) { + case PROP_MODE: + rtpdvpay->mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_dv_pay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRTPDVPay *rtpdvpay = GST_RTP_DV_PAY (object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_enum (value, rtpdvpay->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_rtp_dv_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + /* We don't do anything here, but we could check if it's a system stream and if + * it's not, default to sending the video only. We will negotiate downstream + * caps when we get to see the first frame. */ + + return TRUE; +} + +static gboolean +gst_dv_pay_negotiate (GstRTPDVPay * rtpdvpay, guint8 * data, gsize size) +{ + const gchar *encode, *media; + gboolean audio_bundled, res; + + if ((data[3] & 0x80) == 0) { /* DSF flag */ + /* it's an NTSC format */ + if ((data[80 * 5 + 48 + 3] & 0x4) && (data[80 * 5 + 48] == 0x60)) { /* 4:2:2 sampling */ + /* NTSC 50Mbps */ + encode = "314M-25/525-60"; + } else { /* 4:1:1 sampling */ + /* NTSC 25Mbps */ + encode = "SD-VCR/525-60"; + } + } else { + /* it's a PAL format */ + if ((data[80 * 5 + 48 + 3] & 0x4) && (data[80 * 5 + 48] == 0x60)) { /* 4:2:2 sampling */ + /* PAL 50Mbps */ + encode = "314M-50/625-50"; + } else if ((data[5] & 0x07) == 0) { /* APT flag */ + /* PAL 25Mbps 4:2:0 */ + encode = "SD-VCR/625-50"; + } else + /* PAL 25Mbps 4:1:1 */ + encode = "314M-25/625-50"; + } + + media = "video"; + audio_bundled = FALSE; + + switch (rtpdvpay->mode) { + case GST_DV_PAY_MODE_AUDIO: + media = "audio"; + break; + case GST_DV_PAY_MODE_BUNDLED: + audio_bundled = TRUE; + break; + default: + break; + } + gst_rtp_base_payload_set_options (GST_RTP_BASE_PAYLOAD (rtpdvpay), media, + TRUE, "DV", 90000); + + if (audio_bundled) { + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpdvpay), + "encode", G_TYPE_STRING, encode, + "audio", G_TYPE_STRING, "bundled", NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpdvpay), + "encode", G_TYPE_STRING, encode, NULL); + } + return res; +} + +static gboolean +include_dif (GstRTPDVPay * rtpdvpay, guint8 * data) +{ + gint block_type; + gboolean res; + + block_type = data[0] >> 5; + + switch (block_type) { + case 0: /* Header block */ + case 1: /* Subcode block */ + case 2: /* VAUX block */ + /* always include these blocks */ + res = TRUE; + break; + case 3: /* Audio block */ + /* never include audio if we are doing video only */ + if (rtpdvpay->mode == GST_DV_PAY_MODE_VIDEO) + res = FALSE; + else + res = TRUE; + break; + case 4: /* Video block */ + /* never include video if we are doing audio only */ + if (rtpdvpay->mode == GST_DV_PAY_MODE_AUDIO) + res = FALSE; + else + res = TRUE; + break; + default: /* Something bogus, just ignore */ + res = FALSE; + break; + } + return res; +} + +/* Get a DV frame, chop it up in pieces, and push the pieces to the RTP layer. + */ +static GstFlowReturn +gst_rtp_dv_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRTPDVPay *rtpdvpay; + guint max_payload_size; + GstBuffer *outbuf; + GstFlowReturn ret = GST_FLOW_OK; + gint hdrlen; + gsize size; + GstMapInfo map; + guint8 *data; + guint8 *dest; + guint filled; + GstRTPBuffer rtp = { NULL, }; + + rtpdvpay = GST_RTP_DV_PAY (basepayload); + + hdrlen = gst_rtp_buffer_calc_header_len (0); + /* DV frames are made up from a bunch of DIF blocks. DIF blocks are 80 bytes + * each, and we should put an integral number of them in each RTP packet. + * Therefore, we round the available room down to the nearest multiple of 80. + * + * The available room is just the packet MTU, minus the RTP header length. */ + max_payload_size = ((GST_RTP_BASE_PAYLOAD_MTU (rtpdvpay) - hdrlen) / 80) * 80; + + /* The length of the buffer to transmit. */ + if (!gst_buffer_map (buffer, &map, GST_MAP_READ)) { + GST_ELEMENT_ERROR (rtpdvpay, CORE, FAILED, + (NULL), ("Failed to map buffer")); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } + data = map.data; + size = map.size; + + GST_DEBUG_OBJECT (rtpdvpay, + "DV RTP payloader got buffer of %" G_GSIZE_FORMAT + " bytes, splitting in %u byte " "payload fragments, at time %" + GST_TIME_FORMAT, size, max_payload_size, + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buffer))); + + if (!rtpdvpay->negotiated) { + gst_dv_pay_negotiate (rtpdvpay, data, size); + /* if we have not yet scanned the stream for its type, do so now */ + rtpdvpay->negotiated = TRUE; + } + + outbuf = NULL; + dest = NULL; + filled = 0; + + /* while we have a complete DIF chunks left */ + while (size >= 80) { + /* Allocate a new buffer, set the timestamp */ + if (outbuf == NULL) { + outbuf = gst_rtp_buffer_new_allocate (max_payload_size, 0, 0); + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buffer); + + if (!gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp)) { + gst_buffer_unref (outbuf); + GST_ELEMENT_ERROR (rtpdvpay, CORE, FAILED, + (NULL), ("Failed to map RTP buffer")); + ret = GST_FLOW_ERROR; + goto beach; + } + dest = gst_rtp_buffer_get_payload (&rtp); + filled = 0; + } + + /* inspect the DIF chunk, if we don't need to include it, skip to the next one. */ + if (include_dif (rtpdvpay, data)) { + /* copy data in packet */ + memcpy (dest, data, 80); + + dest += 80; + filled += 80; + } + + /* go to next dif chunk */ + size -= 80; + data += 80; + + /* push out the buffer if the next one would exceed the max packet size or + * when we are pushing the last packet */ + if (filled + 80 > max_payload_size || size < 80) { + if (size < 160) { + guint hlen; + + /* set marker */ + gst_rtp_buffer_set_marker (&rtp, TRUE); + + /* shrink buffer to last packet */ + hlen = gst_rtp_buffer_get_header_len (&rtp); + gst_rtp_buffer_set_packet_len (&rtp, hlen + filled); + } + + /* Push out the created piece, and check for errors. */ + gst_rtp_buffer_unmap (&rtp); + ret = gst_rtp_base_payload_push (basepayload, outbuf); + if (ret != GST_FLOW_OK) + break; + + outbuf = NULL; + } + } + +beach: + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return ret; +} + +gboolean +gst_rtp_dv_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpdvpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_DV_PAY); +} diff --git a/gst/rtp/gstrtpdvpay.h b/gst/rtp/gstrtpdvpay.h new file mode 100755 index 0000000..4c250a8 --- /dev/null +++ b/gst/rtp/gstrtpdvpay.h @@ -0,0 +1,69 @@ +/* Farsight + * Copyright (C) 2006 Marcel Moreaux <marcelm@spacelabs.nl> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GSTRTPDVPAY_H__ +#define __GSTRTPDVPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPDVPay GstRTPDVPay; +typedef struct _GstRTPDVPayClass GstRTPDVPayClass; + +#define GST_TYPE_RTP_DV_PAY \ + (gst_rtp_dv_pay_get_type()) +#define GST_RTP_DV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_DV_PAY,GstRTPDVPay)) +#define GST_RTP_DV_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_DV_PAY,GstRTPDVPay)) +#define GST_IS_RTP_DV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_DV_PAY)) +#define GST_IS_RTP_DV_PAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_DV_PAY)) + +typedef enum +{ + GST_DV_PAY_MODE_VIDEO, + GST_DV_PAY_MODE_BUNDLED, + GST_DV_PAY_MODE_AUDIO +} GstDVPayMode; + +struct _GstRTPDVPay +{ + GstRTPBasePayload payload; + + gboolean negotiated; + GstDVPayMode mode; +}; + +struct _GstRTPDVPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_dv_pay_get_type (void); + +gboolean gst_rtp_dv_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GSTRTPDVPAY_H__ */ diff --git a/gst/rtp/gstrtpg722depay.c b/gst/rtp/gstrtpg722depay.c new file mode 100755 index 0000000..c77fb95 --- /dev/null +++ b/gst/rtp/gstrtpg722depay.c @@ -0,0 +1,262 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gst/audio/audio.h> + +#include "gstrtpg722depay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg722depay_debug); +#define GST_CAT_DEFAULT (rtpg722depay_debug) + +static GstStaticPadTemplate gst_rtp_g722_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G722, " + "rate = (int) [ 1, MAX ], " "channels = (int) [ 1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_g722_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " "clock-rate = (int) 8000, " + /* "channels = (int) [1, MAX]" */ + /* "channel-order = (string) ANY" */ + "encoding-name = (string) \"G722\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_G722_STRING ", " + "clock-rate = (int) [ 1, MAX ]" + /* "channels = (int) [1, MAX]" */ + /* "emphasis = (string) ANY" */ + /* "channel-order = (string) ANY" */ + ) + ); + +#define gst_rtp_g722_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG722Depay, gst_rtp_g722_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_g722_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_g722_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_g722_depay_class_init (GstRtpG722DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpg722depay_debug, "rtpg722depay", 0, + "G722 RTP Depayloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g722_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g722_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts G722 audio from RTP packets", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->set_caps = gst_rtp_g722_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_g722_depay_process; +} + +static void +gst_rtp_g722_depay_init (GstRtpG722Depay * rtpg722depay) +{ +} + +static gint +gst_rtp_g722_depay_parse_int (GstStructure * structure, const gchar * field, + gint def) +{ + const gchar *str; + gint res; + + if ((str = gst_structure_get_string (structure, field))) + return atoi (str); + + if (gst_structure_get_int (structure, field, &res)) + return res; + + return def; +} + +static gboolean +gst_rtp_g722_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpG722Depay *rtpg722depay; + gint clock_rate, payload, samplerate; + gint channels; + GstCaps *srccaps; + gboolean res; +#if 0 + const gchar *channel_order; + const GstRTPChannelOrder *order; +#endif + + rtpg722depay = GST_RTP_G722_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + payload = 96; + gst_structure_get_int (structure, "payload", &payload); + switch (payload) { + case GST_RTP_PAYLOAD_G722: + channels = 1; + clock_rate = 8000; + samplerate = 16000; + break; + default: + /* no fixed mapping, we need clock-rate */ + channels = 0; + clock_rate = 0; + samplerate = 0; + break; + } + + /* caps can overwrite defaults */ + clock_rate = + gst_rtp_g722_depay_parse_int (structure, "clock-rate", clock_rate); + if (clock_rate == 0) + goto no_clockrate; + + if (clock_rate == 8000) + samplerate = 16000; + + if (samplerate == 0) + samplerate = clock_rate; + + channels = + gst_rtp_g722_depay_parse_int (structure, "encoding-params", channels); + if (channels == 0) { + channels = gst_rtp_g722_depay_parse_int (structure, "channels", channels); + if (channels == 0) { + /* channels defaults to 1 otherwise */ + channels = 1; + } + } + + depayload->clock_rate = clock_rate; + rtpg722depay->rate = samplerate; + rtpg722depay->channels = channels; + + srccaps = gst_caps_new_simple ("audio/G722", + "rate", G_TYPE_INT, samplerate, "channels", G_TYPE_INT, channels, NULL); + + /* FIXME: Do something with the channel order */ +#if 0 + /* add channel positions */ + channel_order = gst_structure_get_string (structure, "channel-order"); + + order = gst_rtp_channels_get_by_order (channels, channel_order); + if (order) { + gst_audio_set_channel_positions (gst_caps_get_structure (srccaps, 0), + order->pos); + } else { + GstAudioChannelPosition *pos; + + GST_ELEMENT_WARNING (rtpg722depay, STREAM, DECODE, + (NULL), ("Unknown channel order '%s' for %d channels", + GST_STR_NULL (channel_order), channels)); + /* create default NONE layout */ + pos = gst_rtp_channels_create_default (channels); + gst_audio_set_channel_positions (gst_caps_get_structure (srccaps, 0), pos); + g_free (pos); + } +#endif + + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +no_clockrate: + { + GST_ERROR_OBJECT (depayload, "no clock-rate specified"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_g722_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpG722Depay *rtpg722depay; + GstBuffer *outbuf; + gint payload_len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + rtpg722depay = GST_RTP_G722_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len <= 0) + goto empty_packet; + + GST_DEBUG_OBJECT (rtpg722depay, "got payload of %d bytes", payload_len); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (marker && outbuf) { + /* mark talk spurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpg722depay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_g722_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg722depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G722_DEPAY); +} diff --git a/gst/rtp/gstrtpg722depay.h b/gst/rtp/gstrtpg722depay.h new file mode 100755 index 0000000..8b6ffa0 --- /dev/null +++ b/gst/rtp/gstrtpg722depay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G722_DEPAY_H__ +#define __GST_RTP_G722_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +/* Standard macros for defining types for this element. */ +#define GST_TYPE_RTP_G722_DEPAY \ + (gst_rtp_g722_depay_get_type()) +#define GST_RTP_G722_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G722_DEPAY,GstRtpG722Depay)) +#define GST_RTP_G722_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G722_DEPAY,GstRtpG722DepayClass)) +#define GST_IS_RTP_G722_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G722_DEPAY)) +#define GST_IS_RTP_G722_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G722_DEPAY)) + +typedef struct _GstRtpG722Depay GstRtpG722Depay; +typedef struct _GstRtpG722DepayClass GstRtpG722DepayClass; + +/* Definition of structure storing data for this element. */ +struct _GstRtpG722Depay +{ + GstRTPBaseDepayload depayload; + + guint rate; + guint channels; +}; + +/* Standard definition defining a class for this element. */ +struct _GstRtpG722DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_g722_depay_get_type (void); + +gboolean gst_rtp_g722_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G722_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpg722pay.c b/gst/rtp/gstrtpg722pay.c new file mode 100755 index 0000000..e9e625a --- /dev/null +++ b/gst/rtp/gstrtpg722pay.c @@ -0,0 +1,217 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/audio/audio.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpg722pay.h" +#include "gstrtpchannels.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg722pay_debug); +#define GST_CAT_DEFAULT (rtpg722pay_debug) + +static GstStaticPadTemplate gst_rtp_g722_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G722, " "rate = (int) 16000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_g722_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"G722\", " + "payload = (int) " GST_RTP_PAYLOAD_G722_STRING ", " + "clock-rate = (int) 8000") + ); + +static gboolean gst_rtp_g722_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstCaps *gst_rtp_g722_pay_getcaps (GstRTPBasePayload * rtppayload, + GstPad * pad, GstCaps * filter); + +#define gst_rtp_g722_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG722Pay, gst_rtp_g722_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_g722_pay_class_init (GstRtpG722PayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpg722pay_debug, "rtpg722pay", 0, + "G722 RTP Payloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g722_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g722_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP audio payloader", "Codec/Payloader/Network/RTP", + "Payload-encode Raw audio into RTP packets (RFC 3551)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_g722_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_g722_pay_getcaps; +} + +static void +gst_rtp_g722_pay_init (GstRtpG722Pay * rtpg722pay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpg722pay); + + /* tell rtpbaseaudiopayload that this is a sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_g722_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpG722Pay *rtpg722pay; + GstStructure *structure; + gint rate, channels, clock_rate; + gboolean res; + gchar *params; +#if 0 + GstAudioChannelPosition *pos; + const GstRTPChannelOrder *order; +#endif + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (basepayload); + rtpg722pay = GST_RTP_G722_PAY (basepayload); + + structure = gst_caps_get_structure (caps, 0); + + /* first parse input caps */ + if (!gst_structure_get_int (structure, "rate", &rate)) + goto no_rate; + + if (!gst_structure_get_int (structure, "channels", &channels)) + goto no_channels; + + /* FIXME: Do something with the channel positions */ +#if 0 + /* get the channel order */ + pos = gst_audio_get_channel_positions (structure); + if (pos) + order = gst_rtp_channels_get_by_pos (channels, pos); + else + order = NULL; +#endif + + /* Clock rate is always 8000 Hz for G722 according to + * RFC 3551 although the sampling rate is 16000 Hz */ + clock_rate = 8000; + + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "G722", + clock_rate); + params = g_strdup_printf ("%d", channels); + +#if 0 + if (!order && channels > 2) { + GST_ELEMENT_WARNING (rtpg722pay, STREAM, DECODE, + (NULL), ("Unknown channel order for %d channels", channels)); + } + + if (order && order->name) { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + channels, "channel-order", G_TYPE_STRING, order->name, NULL); + } else { +#endif + res = gst_rtp_base_payload_set_outcaps (basepayload, + "encoding-params", G_TYPE_STRING, params, "channels", G_TYPE_INT, + channels, NULL); +#if 0 + } +#endif + + g_free (params); +#if 0 + g_free (pos); +#endif + + rtpg722pay->rate = rate; + rtpg722pay->channels = channels; + + /* bits-per-sample is 4 * channels for G722, but as the RTP clock runs at + * half speed (8 instead of 16 khz), pretend it's 8 bits per sample + * channels. */ + gst_rtp_base_audio_payload_set_samplebits_options (rtpbaseaudiopayload, + 8 * rtpg722pay->channels); + + return res; + + /* ERRORS */ +no_rate: + { + GST_DEBUG_OBJECT (rtpg722pay, "no rate given"); + return FALSE; + } +no_channels: + { + GST_DEBUG_OBJECT (rtpg722pay, "no channels given"); + return FALSE; + } +} + +static GstCaps * +gst_rtp_g722_pay_getcaps (GstRTPBasePayload * rtppayload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + otherpadcaps = gst_pad_get_allowed_caps (rtppayload->srcpad); + caps = gst_pad_get_pad_template_caps (pad); + + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + caps = gst_caps_make_writable (caps); + gst_caps_set_simple (caps, "channels", G_TYPE_INT, 1, NULL); + gst_caps_set_simple (caps, "rate", G_TYPE_INT, 16000, NULL); + } + gst_caps_unref (otherpadcaps); + } + return caps; +} + +gboolean +gst_rtp_g722_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg722pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G722_PAY); +} diff --git a/gst/rtp/gstrtpg722pay.h b/gst/rtp/gstrtpg722pay.h new file mode 100755 index 0000000..f238286 --- /dev/null +++ b/gst/rtp/gstrtpg722pay.h @@ -0,0 +1,61 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G722_PAY_H__ +#define __GST_RTP_G722_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_G722_PAY \ + (gst_rtp_g722_pay_get_type()) +#define GST_RTP_G722_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G722_PAY,GstRtpG722Pay)) +#define GST_RTP_G722_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G722_PAY,GstRtpG722PayClass)) +#define GST_IS_RTP_G722_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G722_PAY)) +#define GST_IS_RTP_G722_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G722_PAY)) + +typedef struct _GstRtpG722Pay GstRtpG722Pay; +typedef struct _GstRtpG722PayClass GstRtpG722PayClass; + +struct _GstRtpG722Pay +{ + GstRTPBaseAudioPayload payload; + + gint rate; + gint channels; +}; + +struct _GstRtpG722PayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_g722_pay_get_type (void); + +gboolean gst_rtp_g722_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G722_PAY_H__ */ diff --git a/gst/rtp/gstrtpg723depay.c b/gst/rtp/gstrtpg723depay.c new file mode 100755 index 0000000..fb726f2 --- /dev/null +++ b/gst/rtp/gstrtpg723depay.c @@ -0,0 +1,228 @@ +/* GStreamer + * + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include "gstrtpg723depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg723depay_debug); +#define GST_CAT_DEFAULT (rtpg723depay_debug) + + +/* references: + * + * RFC 3551 (4.5.3) + */ + +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +/* input is an RTP packet + * + */ +static GstStaticPadTemplate gst_rtp_g723_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"G723\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_G723_STRING ", " + "clock-rate = (int) 8000") + ); + +static GstStaticPadTemplate gst_rtp_g723_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G723, " "channels = (int) 1," "rate = (int) 8000") + ); + +static gboolean gst_rtp_g723_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_g723_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +#define gst_rtp_g723_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG723Depay, gst_rtp_g723_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_g723_depay_class_init (GstRtpG723DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpg723depay_debug, "rtpg723depay", 0, + "G.723 RTP Depayloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g723_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g723_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.723 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts G.723 audio from RTP packets (RFC 3551)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_g723_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_g723_depay_setcaps; +} + +static void +gst_rtp_g723_depay_init (GstRtpG723Depay * rtpg723depay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtpg723depay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); +} + +static gboolean +gst_rtp_g723_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstCaps *srccaps; + GstRtpG723Depay *rtpg723depay; + const gchar *params; + gint clock_rate, channels; + gboolean ret; + + rtpg723depay = GST_RTP_G723_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!(params = gst_structure_get_string (structure, "encoding-params"))) + channels = 1; + else { + channels = atoi (params); + } + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; + + if (channels != 1) + goto wrong_channels; + + if (clock_rate != 8000) + goto wrong_clock_rate; + + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("audio/G723", + "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, clock_rate, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; + + /* ERRORS */ +wrong_channels: + { + GST_DEBUG_OBJECT (rtpg723depay, "expected 1 channel, got %d", channels); + return FALSE; + } +wrong_clock_rate: + { + GST_DEBUG_OBJECT (rtpg723depay, "expected 8000 clock-rate, got %d", + clock_rate); + return FALSE; + } +} + + +static GstBuffer * +gst_rtp_g723_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpG723Depay *rtpg723depay; + GstBuffer *outbuf = NULL; + gint payload_len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + rtpg723depay = GST_RTP_G723_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + /* At least 4 bytes */ + if (payload_len < 4) + goto too_small; + + GST_LOG_OBJECT (rtpg723depay, "payload len %d", payload_len); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (marker) { + /* marker bit starts talkspurt */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + GST_LOG_OBJECT (depayload, "pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + + return outbuf; + + /* ERRORS */ +too_small: + { + GST_ELEMENT_WARNING (rtpg723depay, STREAM, DECODE, + (NULL), ("G723 RTP payload too small (%d)", payload_len)); + goto bad_packet; + } +bad_packet: + { + /* no fatal error */ + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_g723_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg723depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G723_DEPAY); +} diff --git a/gst/rtp/gstrtpg723depay.h b/gst/rtp/gstrtpg723depay.h new file mode 100755 index 0000000..dd942b3 --- /dev/null +++ b/gst/rtp/gstrtpg723depay.h @@ -0,0 +1,59 @@ +/* GStreamer + * + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G723_DEPAY_H__ +#define __GST_RTP_G723_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_G723_DEPAY \ + (gst_rtp_g723_depay_get_type()) +#define GST_RTP_G723_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G723_DEPAY,GstRtpG723Depay)) +#define GST_RTP_G723_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G723_DEPAY,GstRtpG723DepayClass)) +#define GST_IS_RTP_G723_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G723_DEPAY)) +#define GST_IS_RTP_G723_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G723_DEPAY)) + +typedef struct _GstRtpG723Depay GstRtpG723Depay; +typedef struct _GstRtpG723DepayClass GstRtpG723DepayClass; + +struct _GstRtpG723Depay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpG723DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_g723_depay_get_type (void); + +gboolean gst_rtp_g723_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G723_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpg723pay.c b/gst/rtp/gstrtpg723pay.c new file mode 100755 index 0000000..4a10b3b --- /dev/null +++ b/gst/rtp/gstrtpg723pay.c @@ -0,0 +1,317 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation + * Copyright (C) <2007> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> +#include <gst/base/gstadapter.h> + +#include "gstrtpg723pay.h" + +#define GST_RTP_PAYLOAD_G723 4 +#define GST_RTP_PAYLOAD_G723_STRING "4" + +#define G723_FRAME_DURATION (30 * GST_MSECOND) + +static gboolean gst_rtp_g723_pay_set_caps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buf); + +static GstStaticPadTemplate gst_rtp_g723_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G723, " /* according to RFC 3551 */ + "channels = (int) 1, " "rate = (int) 8000") + ); + +static GstStaticPadTemplate gst_rtp_g723_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_G723_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"G723\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"G723\"") + ); + +static void gst_rtp_g723_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_g723_pay_change_state (GstElement * element, + GstStateChange transition); + +#define gst_rtp_g723_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPG723Pay, gst_rtp_g723_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_g723_pay_class_init (GstRTPG723PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *payload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + payload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_g723_pay_finalize; + + gstelement_class->change_state = gst_rtp_g723_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g723_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g723_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.723 payloader", "Codec/Payloader/Network/RTP", + "Packetize G.723 audio into RTP packets", + "Wim Taymans <wim.taymans@gmail.com>"); + + payload_class->set_caps = gst_rtp_g723_pay_set_caps; + payload_class->handle_buffer = gst_rtp_g723_pay_handle_buffer; +} + +static void +gst_rtp_g723_pay_init (GstRTPG723Pay * pay) +{ + GstRTPBasePayload *payload = GST_RTP_BASE_PAYLOAD (pay); + + pay->adapter = gst_adapter_new (); + + payload->pt = GST_RTP_PAYLOAD_G723; + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "G723", 8000); +} + +static void +gst_rtp_g723_pay_finalize (GObject * object) +{ + GstRTPG723Pay *pay; + + pay = GST_RTP_G723_PAY (object); + + g_object_unref (pay->adapter); + pay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static gboolean +gst_rtp_g723_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + GstStructure *structure; + gint pt; + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "payload", &pt)) + pt = GST_RTP_PAYLOAD_G723; + + payload->pt = pt; + payload->dynamic = pt != GST_RTP_PAYLOAD_G723; + + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +static GstFlowReturn +gst_rtp_g723_pay_flush (GstRTPG723Pay * pay) +{ + GstBuffer *outbuf; + GstFlowReturn ret; + guint8 *payload; + guint avail; + GstRTPBuffer rtp = { NULL }; + + avail = gst_adapter_available (pay->adapter); + + outbuf = gst_rtp_buffer_new_allocate (avail, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + GST_BUFFER_TIMESTAMP (outbuf) = pay->timestamp; + GST_BUFFER_DURATION (outbuf) = pay->duration; + + /* copy G723 data as payload */ + gst_adapter_copy (pay->adapter, payload, 0, avail); + + /* flush bytes from adapter */ + gst_adapter_flush (pay->adapter, avail); + pay->timestamp = GST_CLOCK_TIME_NONE; + pay->duration = 0; + + /* set discont and marker */ + if (pay->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + gst_rtp_buffer_set_marker (&rtp, TRUE); + pay->discont = FALSE; + } + gst_rtp_buffer_unmap (&rtp); + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (pay), outbuf); + + return ret; +} + +/* 00 high-rate speech (6.3 kb/s) 24 + * 01 low-rate speech (5.3 kb/s) 20 + * 10 SID frame 4 + * 11 reserved 0 */ +static const guint size_tab[4] = { + 24, 20, 4, 0 +}; + +static GstFlowReturn +gst_rtp_g723_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstMapInfo map; + guint8 HDR; + GstRTPG723Pay *pay; + GstClockTime packet_dur, timestamp; + guint payload_len, packet_len; + + pay = GST_RTP_G723_PAY (payload); + + gst_buffer_map (buf, &map, GST_MAP_READ); + timestamp = GST_BUFFER_TIMESTAMP (buf); + + if (GST_BUFFER_IS_DISCONT (buf)) { + /* flush everything on discont */ + gst_adapter_clear (pay->adapter); + pay->timestamp = GST_CLOCK_TIME_NONE; + pay->duration = 0; + pay->discont = TRUE; + } + + /* should be one of these sizes */ + if (map.size != 4 && map.size != 20 && map.size != 24) + goto invalid_size; + + /* check size by looking at the header bits */ + HDR = map.data[0] & 0x3; + if (size_tab[HDR] != map.size) + goto wrong_size; + + /* calculate packet size and duration */ + payload_len = gst_adapter_available (pay->adapter) + map.size; + packet_dur = pay->duration + G723_FRAME_DURATION; + packet_len = gst_rtp_buffer_calc_packet_len (payload_len, 0, 0); + + if (gst_rtp_base_payload_is_filled (payload, packet_len, packet_dur)) { + /* size or duration would overflow the packet, flush the queued data */ + ret = gst_rtp_g723_pay_flush (pay); + } + + /* update timestamp, we keep the timestamp for the first packet in the adapter + * but are able to calculate it from next packets. */ + if (timestamp != GST_CLOCK_TIME_NONE && pay->timestamp == GST_CLOCK_TIME_NONE) { + if (timestamp > pay->duration) + pay->timestamp = timestamp - pay->duration; + else + pay->timestamp = 0; + } + gst_buffer_unmap (buf, &map); + + /* add packet to the queue */ + gst_adapter_push (pay->adapter, buf); + pay->duration = packet_dur; + + /* check if we can flush now */ + if (pay->duration >= payload->min_ptime) { + ret = gst_rtp_g723_pay_flush (pay); + } + + return ret; + + /* WARNINGS */ +invalid_size: + { + GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE, + ("Invalid input buffer size"), + ("Input size should be 4, 20 or 24, got %" G_GSIZE_FORMAT, map.size)); + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + return GST_FLOW_OK; + } +wrong_size: + { + GST_ELEMENT_WARNING (pay, STREAM, WRONG_TYPE, + ("Wrong input buffer size"), + ("Expected input buffer size %u but got %" G_GSIZE_FORMAT, + size_tab[HDR], map.size)); + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + return GST_FLOW_OK; + } +} + +static GstStateChangeReturn +gst_rtp_g723_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstRTPG723Pay *pay; + + pay = GST_RTP_G723_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (pay->adapter); + pay->timestamp = GST_CLOCK_TIME_NONE; + pay->duration = 0; + pay->discont = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_adapter_clear (pay->adapter); + break; + default: + break; + } + + return ret; +} + +/*Plugin init functions*/ +gboolean +gst_rtp_g723_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg723pay", GST_RANK_SECONDARY, + gst_rtp_g723_pay_get_type ()); +} diff --git a/gst/rtp/gstrtpg723pay.h b/gst/rtp/gstrtpg723pay.h new file mode 100755 index 0000000..3780741 --- /dev/null +++ b/gst/rtp/gstrtpg723pay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation + * Copyright (C) <2007> Collabora Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G723_PAY_H__ +#define __GST_RTP_G723_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_G723_PAY \ + (gst_rtp_g723_pay_get_type()) +#define GST_RTP_G723_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G723_PAY,GstRTPG723Pay)) +#define GST_RTP_G723_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G723_PAY,GstRTPG723PayClass)) +#define GST_IS_RTP_G723_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G723_PAY)) +#define GST_IS_RTP_G723_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G723_PAY)) + +typedef struct _GstRTPG723Pay GstRTPG723Pay; +typedef struct _GstRTPG723PayClass GstRTPG723PayClass; + +struct _GstRTPG723Pay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime duration; + GstClockTime timestamp; + gboolean discont; +}; + +struct _GstRTPG723PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_g723_pay_get_type (void); + +gboolean gst_rtp_g723_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G723_PAY_H__ */ diff --git a/gst/rtp/gstrtpg726depay.c b/gst/rtp/gstrtpg726depay.c new file mode 100755 index 0000000..a6175eb --- /dev/null +++ b/gst/rtp/gstrtpg726depay.c @@ -0,0 +1,395 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) 2005 Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) 2005 Zeeshan Ali <zeenix@gmail.com> + * Copyright (C) 2008 Axis Communications <dev-gstreamer@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpg726depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg726depay_debug); +#define GST_CAT_DEFAULT (rtpg726depay_debug) + +#define DEFAULT_BIT_RATE 32000 +#define DEFAULT_BLOCK_ALIGN 4 +#define SAMPLE_RATE 8000 +#define LAYOUT_G726 "g726" + +/* RtpG726Depay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_FORCE_AAL2 TRUE + +enum +{ + PROP_0, + PROP_FORCE_AAL2 +}; + +static GstStaticPadTemplate gst_rtp_g726_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) { \"G726\", \"G726-16\", \"G726-24\", \"G726-32\", \"G726-40\", " + "\"AAL2-G726-16\", \"AAL2-G726-24\", \"AAL2-G726-32\", \"AAL2-G726-40\" }, " + "clock-rate = (int) 8000;") + ); + +static GstStaticPadTemplate gst_rtp_g726_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-adpcm, " + "channels = (int) 1, " + "rate = (int) 8000, " + "bitrate = (int) { 16000, 24000, 32000, 40000 }, " + "block_align = (int) { 2, 3, 4, 5 }, " "layout = (string) \"g726\"") + ); + +static void gst_rtp_g726_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_g726_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static GstBuffer *gst_rtp_g726_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_g726_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_g726_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG726Depay, gst_rtp_g726_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_g726_depay_class_init (GstRtpG726DepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpg726depay_debug, "rtpg726depay", 0, + "G.726 RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->set_property = gst_rtp_g726_depay_set_property; + gobject_class->get_property = gst_rtp_g726_depay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FORCE_AAL2, + g_param_spec_boolean ("force-aal2", "Force AAL2", + "Force AAL2 decoding for compatibility with bad payloaders", + DEFAULT_FORCE_AAL2, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g726_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g726_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.726 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts G.726 audio from RTP packets", + "Axis Communications <dev-gstreamer@axis.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_g726_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_g726_depay_setcaps; +} + +static void +gst_rtp_g726_depay_init (GstRtpG726Depay * rtpG726depay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtpG726depay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); + + rtpG726depay->force_aal2 = DEFAULT_FORCE_AAL2; +} + +static gboolean +gst_rtp_g726_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure; + gboolean ret; + gint clock_rate; + const gchar *encoding_name; + GstRtpG726Depay *depay; + + depay = GST_RTP_G726_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; /* default */ + depayload->clock_rate = clock_rate; + + depay->aal2 = FALSE; + encoding_name = gst_structure_get_string (structure, "encoding-name"); + if (encoding_name == NULL || g_ascii_strcasecmp (encoding_name, "G726") == 0) { + depay->bitrate = DEFAULT_BIT_RATE; + depay->block_align = DEFAULT_BLOCK_ALIGN; + } else { + if (g_str_has_prefix (encoding_name, "AAL2-")) { + depay->aal2 = TRUE; + encoding_name += 5; + } + if (g_ascii_strcasecmp (encoding_name, "G726-16") == 0) { + depay->bitrate = 16000; + depay->block_align = 2; + } else if (g_ascii_strcasecmp (encoding_name, "G726-24") == 0) { + depay->bitrate = 24000; + depay->block_align = 3; + } else if (g_ascii_strcasecmp (encoding_name, "G726-32") == 0) { + depay->bitrate = 32000; + depay->block_align = 4; + } else if (g_ascii_strcasecmp (encoding_name, "G726-40") == 0) { + depay->bitrate = 40000; + depay->block_align = 5; + } else + goto unknown_encoding; + } + + GST_DEBUG ("RTP G.726 depayloader, bitrate set to %d\n", depay->bitrate); + + srccaps = gst_caps_new_simple ("audio/x-adpcm", + "channels", G_TYPE_INT, 1, + "rate", G_TYPE_INT, clock_rate, + "bitrate", G_TYPE_INT, depay->bitrate, + "block_align", G_TYPE_INT, depay->block_align, + "layout", G_TYPE_STRING, LAYOUT_G726, NULL); + + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; + + /* ERRORS */ +unknown_encoding: + { + GST_WARNING ("Could not determine bitrate from encoding-name (%s)", + encoding_name); + return FALSE; + } +} + + +static GstBuffer * +gst_rtp_g726_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpG726Depay *depay; + GstBuffer *outbuf = NULL; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + depay = GST_RTP_G726_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READWRITE, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + if (depay->aal2 || depay->force_aal2) { + /* AAL2, we can just copy the bytes */ + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + if (!outbuf) + goto bad_len; + } else { + guint8 *in, *out, tmp; + guint len; + GstMapInfo map; + + in = gst_rtp_buffer_get_payload (&rtp); + len = gst_rtp_buffer_get_payload_len (&rtp); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + if (!outbuf) + goto bad_len; + outbuf = gst_buffer_make_writable (outbuf); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + out = map.data; + + /* we need to reshuffle the bytes, input is always of the form + * A B C D ... with the number of bits depending on the bitrate. */ + switch (depay->bitrate) { + case 16000: + { + /* 0 + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+- + * |D D|C C|B B|A A| ... + * |0 1|0 1|0 1|0 1| + * +-+-+-+-+-+-+-+-+- + */ + while (len > 0) { + tmp = *in++; + *out++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x30) >> 2) | ((tmp & 0x0c) << 2) | ((tmp & 0x03) << 6); + len--; + } + break; + } + case 24000: + { + /* 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |C C|B B B|A A A|F|E E E|D D D|C|H H H|G G G|F F| ... + * |1 2|0 1 2|0 1 2|2|0 1 2|0 1 2|0|0 1 2|0 1 2|0 1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 2) { + tmp = *in++; + *out++ = ((tmp & 0xe0) >> 5) | + ((tmp & 0x1c) << 1) | ((tmp & 0x03) << 6); + tmp = *in++; + *out++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x70) >> 3) | ((tmp & 0x0e) << 4) | ((tmp & 0x01) << 7); + tmp = *in++; + *out++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x38) >> 1) | ((tmp & 0x07) << 5); + len -= 3; + } + break; + } + case 32000: + { + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B B|A A A A|D D D D|C C C C| ... + * |0 1 2 3|0 1 2 3|0 1 2 3|0 1 2 3| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 0) { + tmp = *in++; + *out++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + len--; + } + break; + } + case 40000: + { + /* 0 1 2 3 4 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B|A A A A A|D|C C C C C|B B|E E E E|D D D D|G G|F F F F F|E|H H H H H|G G G| + * |2 3 4|0 1 2 3 4|4|0 1 2 3 4|0 1|1 2 3 4|0 1 2 3|3 4|0 1 2 3 4|0|0 1 2 3 4|0 1 2| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (len > 4) { + tmp = *in++; + *out++ = ((tmp & 0xf8) >> 3) | ((tmp & 0x07) << 5); + tmp = *in++; + *out++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x3e) << 1) | ((tmp & 0x01) << 7); + tmp = *in++; + *out++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + tmp = *in++; + *out++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x7c) >> 1) | ((tmp & 0x03) << 6); + tmp = *in++; + *out++ = ((tmp & 0xe0) >> 5) | ((tmp & 0x1f) << 3); + len -= 5; + } + break; + } + } + gst_buffer_unmap (outbuf, &map); + } + + gst_rtp_buffer_unmap (&rtp); + + if (marker) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + return outbuf; + +bad_len: + { + gst_rtp_buffer_unmap (&rtp); + return NULL; + } + +} + +static void +gst_rtp_g726_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpG726Depay *rtpg726depay; + + rtpg726depay = GST_RTP_G726_DEPAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + rtpg726depay->force_aal2 = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_g726_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpG726Depay *rtpg726depay; + + rtpg726depay = GST_RTP_G726_DEPAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + g_value_set_boolean (value, rtpg726depay->force_aal2); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_g726_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg726depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G726_DEPAY); +} diff --git a/gst/rtp/gstrtpg726depay.h b/gst/rtp/gstrtpg726depay.h new file mode 100755 index 0000000..c395a37 --- /dev/null +++ b/gst/rtp/gstrtpg726depay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) 2005 Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) 2008 Axis Communications AB <dev-gstreamer@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_G726_DEPAY_H__ +#define __GST_RTP_G726_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpG726Depay GstRtpG726Depay; +typedef struct _GstRtpG726DepayClass GstRtpG726DepayClass; + +#define GST_TYPE_RTP_G726_DEPAY \ + (gst_rtp_g726_depay_get_type()) +#define GST_RTP_G726_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G726_DEPAY,GstRtpG726Depay)) +#define GST_RTP_G726_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G726_DEPAY,GstRtpG726DepayClass)) +#define GST_IS_RTP_G726_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G726_DEPAY)) +#define GST_IS_RTP_G726_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G726_DEPAY)) + +struct _GstRtpG726Depay +{ + GstRTPBaseDepayload depayload; + + gboolean aal2; + gboolean force_aal2; + gint bitrate; + guint block_align; +}; + +struct _GstRtpG726DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_g726_depay_get_type (void); + +gboolean gst_rtp_g726_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_G726_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpg726pay.c b/gst/rtp/gstrtpg726pay.c new file mode 100755 index 0000000..7c7e9a8 --- /dev/null +++ b/gst/rtp/gstrtpg726pay.c @@ -0,0 +1,422 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) 2005 Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) 2005 Nokia Corporation <kai.vehmanen@nokia.com> + * Copyright (C) 2007,2008 Axis Communications <dev-gstreamer@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpg726pay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg726pay_debug); +#define GST_CAT_DEFAULT (rtpg726pay_debug) + +#define DEFAULT_FORCE_AAL2 TRUE + +enum +{ + PROP_0, + PROP_FORCE_AAL2 +}; + +static GstStaticPadTemplate gst_rtp_g726_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-adpcm, " + "channels = (int) 1, " + "rate = (int) 8000, " + "bitrate = (int) { 16000, 24000, 32000, 40000 }, " + "layout = (string) \"g726\"") + ); + +static GstStaticPadTemplate gst_rtp_g726_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) { \"G726-16\", \"G726-24\", \"G726-32\", \"G726-40\", " + " \"AAL2-G726-16\", \"AAL2-G726-24\", \"AAL2-G726-32\", \"AAL2-G726-40\" } ") + ); + +static void gst_rtp_g726_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_g726_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static gboolean gst_rtp_g726_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_g726_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +#define gst_rtp_g726_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG726Pay, gst_rtp_g726_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_g726_pay_class_init (GstRtpG726PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_g726_pay_set_property; + gobject_class->get_property = gst_rtp_g726_pay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FORCE_AAL2, + g_param_spec_boolean ("force-aal2", "Force AAL2", + "Force AAL2 encoding for compatibility with bad depayloaders", + DEFAULT_FORCE_AAL2, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g726_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g726_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.726 payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes G.726 audio into a RTP packet", + "Axis Communications <dev-gstreamer@axis.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_g726_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_g726_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpg726pay_debug, "rtpg726pay", 0, + "G.726 RTP Payloader"); +} + +static void +gst_rtp_g726_pay_init (GstRtpG726Pay * rtpg726pay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpg726pay); + + GST_RTP_BASE_PAYLOAD (rtpg726pay)->clock_rate = 8000; + + rtpg726pay->force_aal2 = DEFAULT_FORCE_AAL2; + + /* sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_g726_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gchar *encoding_name; + GstStructure *structure; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + GstRtpG726Pay *pay; + GstCaps *peercaps; + gboolean res; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (payload); + pay = GST_RTP_G726_PAY (payload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "bitrate", &pay->bitrate)) + pay->bitrate = 32000; + + GST_DEBUG_OBJECT (payload, "using bitrate %d", pay->bitrate); + + pay->aal2 = FALSE; + + /* first see what we can do with the bitrate */ + switch (pay->bitrate) { + case 16000: + encoding_name = g_strdup ("G726-16"); + gst_rtp_base_audio_payload_set_samplebits_options (rtpbaseaudiopayload, + 2); + break; + case 24000: + encoding_name = g_strdup ("G726-24"); + gst_rtp_base_audio_payload_set_samplebits_options (rtpbaseaudiopayload, + 3); + break; + case 32000: + encoding_name = g_strdup ("G726-32"); + gst_rtp_base_audio_payload_set_samplebits_options (rtpbaseaudiopayload, + 4); + break; + case 40000: + encoding_name = g_strdup ("G726-40"); + gst_rtp_base_audio_payload_set_samplebits_options (rtpbaseaudiopayload, + 5); + break; + default: + goto invalid_bitrate; + } + + GST_DEBUG_OBJECT (payload, "selected base encoding %s", encoding_name); + + /* now see if we need to produce AAL2 or not */ + peercaps = gst_pad_peer_query_caps (payload->srcpad, NULL); + if (peercaps) { + GstCaps *filter, *intersect; + gchar *capsstr; + + GST_DEBUG_OBJECT (payload, "have peercaps %" GST_PTR_FORMAT, peercaps); + + capsstr = g_strdup_printf ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) %s; " + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) AAL2-%s", encoding_name, encoding_name); + filter = gst_caps_from_string (capsstr); + g_free (capsstr); + g_free (encoding_name); + + /* intersect to filter */ + intersect = gst_caps_intersect (peercaps, filter); + gst_caps_unref (peercaps); + gst_caps_unref (filter); + + GST_DEBUG_OBJECT (payload, "intersected to %" GST_PTR_FORMAT, intersect); + + if (!intersect) + goto no_format; + if (gst_caps_is_empty (intersect)) { + gst_caps_unref (intersect); + goto no_format; + } + + structure = gst_caps_get_structure (intersect, 0); + + /* now see what encoding name we settled on, we need to dup because the + * string goes away when we unref the intersection below. */ + encoding_name = + g_strdup (gst_structure_get_string (structure, "encoding-name")); + + /* if we managed to negotiate to AAL2, we definatly are going to do AAL2 + * encoding. Else we only encode AAL2 when explicitly set by the + * property. */ + if (g_str_has_prefix (encoding_name, "AAL2-")) + pay->aal2 = TRUE; + else + pay->aal2 = pay->force_aal2; + + GST_DEBUG_OBJECT (payload, "final encoding %s, AAL2 %d", encoding_name, + pay->aal2); + + gst_caps_unref (intersect); + } else { + /* downstream can do anything but we prefer the better supported non-AAL2 */ + pay->aal2 = pay->force_aal2; + GST_DEBUG_OBJECT (payload, "no peer caps, AAL2 %d", pay->aal2); + } + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, encoding_name, + 8000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + g_free (encoding_name); + + return res; + + /* ERRORS */ +invalid_bitrate: + { + GST_ERROR_OBJECT (payload, "invalid bitrate %d specified", pay->bitrate); + return FALSE; + } +no_format: + { + GST_ERROR_OBJECT (payload, "could not negotiate format"); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_g726_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + GstFlowReturn res; + GstRtpG726Pay *pay; + + pay = GST_RTP_G726_PAY (payload); + + if (!pay->aal2) { + GstMapInfo map; + guint8 *data, tmp; + gsize size; + + /* for non AAL2, we need to reshuffle the bytes, we can do this in-place + * when the buffer is writable. */ + buffer = gst_buffer_make_writable (buffer); + + gst_buffer_map (buffer, &map, GST_MAP_READWRITE); + data = map.data; + size = map.size; + + GST_LOG_OBJECT (pay, "packing %" G_GSIZE_FORMAT " bytes of data", map.size); + + /* we need to reshuffle the bytes, output is of the form: + * A B C D .. with the number of bits depending on the bitrate. */ + switch (pay->bitrate) { + case 16000: + { + /* 0 + * 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+- + * |D D|C C|B B|A A| ... + * |0 1|0 1|0 1|0 1| + * +-+-+-+-+-+-+-+-+- + */ + while (size > 0) { + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x30) >> 2) | ((tmp & 0x0c) << 2) | ((tmp & 0x03) << 6); + size--; + } + break; + } + case 24000: + { + /* 0 1 2 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |C C|B B B|A A A|F|E E E|D D D|C|H H H|G G G|F F| ... + * |1 2|0 1 2|0 1 2|2|0 1 2|0 1 2|0|0 1 2|0 1 2|0 1| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (size > 2) { + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x38) >> 1) | ((tmp & 0x07) << 5); + tmp = *data; + *data++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x70) >> 3) | ((tmp & 0x0e) << 4) | ((tmp & 0x01) << 7); + tmp = *data; + *data++ = ((tmp & 0xe0) >> 5) | + ((tmp & 0x1c) >> 2) | ((tmp & 0x03) << 6); + size -= 3; + } + break; + } + case 32000: + { + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B B|A A A A|D D D D|C C C C| ... + * |0 1 2 3|0 1 2 3|0 1 2 3|0 1 2 3| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (size > 0) { + tmp = *data; + *data++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + size--; + } + break; + } + case 40000: + { + /* 0 1 2 3 4 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + * |B B B|A A A A A|D|C C C C C|B B|E E E E|D D D D|G G|F F F F F|E|H H H H H|G G G| + * |2 3 4|0 1 2 3 4|4|0 1 2 3 4|0 1|1 2 3 4|0 1 2 3|3 4|0 1 2 3 4|0|0 1 2 3 4|0 1 2| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- + */ + while (size > 4) { + tmp = *data; + *data++ = ((tmp & 0xe0) >> 5) | ((tmp & 0x1f) << 3); + tmp = *data; + *data++ = ((tmp & 0x80) >> 7) | + ((tmp & 0x7c) >> 2) | ((tmp & 0x03) << 6); + tmp = *data; + *data++ = ((tmp & 0xf0) >> 4) | ((tmp & 0x0f) << 4); + tmp = *data; + *data++ = ((tmp & 0xc0) >> 6) | + ((tmp & 0x3e) << 2) | ((tmp & 0x01) << 7); + tmp = *data; + *data++ = ((tmp & 0xf8) >> 3) | ((tmp & 0x07) << 5); + size -= 5; + } + break; + } + } + gst_buffer_unmap (buffer, &map); + } + + res = + GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->handle_buffer (payload, + buffer); + + return res; +} + +static void +gst_rtp_g726_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpG726Pay *rtpg726pay; + + rtpg726pay = GST_RTP_G726_PAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + rtpg726pay->force_aal2 = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_g726_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpG726Pay *rtpg726pay; + + rtpg726pay = GST_RTP_G726_PAY (object); + + switch (prop_id) { + case PROP_FORCE_AAL2: + g_value_set_boolean (value, rtpg726pay->force_aal2); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_g726_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg726pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G726_PAY); +} diff --git a/gst/rtp/gstrtpg726pay.h b/gst/rtp/gstrtpg726pay.h new file mode 100755 index 0000000..5b16966 --- /dev/null +++ b/gst/rtp/gstrtpg726pay.h @@ -0,0 +1,55 @@ +/* GStreamer + * Copyright (C) 2005 Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) 2007,2008 Axis Communications <dev-gstreamer@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_G726_PAY_H__ +#define __GST_RTP_G726_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS typedef struct _GstRtpG726Pay GstRtpG726Pay; +typedef struct _GstRtpG726PayClass GstRtpG726PayClass; + +#define GST_TYPE_RTP_G726_PAY \ + (gst_rtp_g726_pay_get_type()) +#define GST_RTP_G726_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G726_PAY,GstRtpG726Pay)) +#define GST_RTP_G726_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G726_PAY,GstRtpG726PayClass)) +#define GST_IS_RTP_G726_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G726_PAY)) +#define GST_IS_RTP_G726_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G726_PAY)) + +struct _GstRtpG726Pay +{ + GstRTPBaseAudioPayload audiopayload; + + gboolean aal2; + gboolean force_aal2; + gint bitrate; +}; + +struct _GstRtpG726PayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_g726_pay_get_type (void); + +gboolean gst_rtp_g726_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_G726_PAY_H__ */ diff --git a/gst/rtp/gstrtpg729depay.c b/gst/rtp/gstrtpg729depay.c new file mode 100755 index 0000000..9910add --- /dev/null +++ b/gst/rtp/gstrtpg729depay.c @@ -0,0 +1,226 @@ +/* GStreamer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include "gstrtpg729depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg729depay_debug); +#define GST_CAT_DEFAULT (rtpg729depay_debug) + + +/* references: + * + * RFC 3551 (4.5.6) + */ + +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +/* input is an RTP packet + * + */ +static GstStaticPadTemplate gst_rtp_g729_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"G729\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_G729_STRING ", " + "clock-rate = (int) 8000") + ); + +static GstStaticPadTemplate gst_rtp_g729_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G729, " "channels = (int) 1," "rate = (int) 8000") + ); + +static gboolean gst_rtp_g729_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_g729_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +#define gst_rtp_g729_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpG729Depay, gst_rtp_g729_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_g729_depay_class_init (GstRtpG729DepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpg729depay_debug, "rtpg729depay", 0, + "G.729 RTP Depayloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g729_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g729_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.729 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts G.729 audio from RTP packets (RFC 3551)", + "Laurent Glayal <spglegle@yahoo.fr>"); + + gstrtpbasedepayload_class->process = gst_rtp_g729_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_g729_depay_setcaps; +} + +static void +gst_rtp_g729_depay_init (GstRtpG729Depay * rtpg729depay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtpg729depay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); +} + +static gboolean +gst_rtp_g729_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstCaps *srccaps; + GstRtpG729Depay *rtpg729depay; + const gchar *params; + gint clock_rate, channels; + gboolean ret; + + rtpg729depay = GST_RTP_G729_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!(params = gst_structure_get_string (structure, "encoding-params"))) + channels = 1; + else { + channels = atoi (params); + } + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; + + if (channels != 1) + goto wrong_channels; + + if (clock_rate != 8000) + goto wrong_clock_rate; + + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("audio/G729", + "channels", G_TYPE_INT, channels, "rate", G_TYPE_INT, clock_rate, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; + + /* ERRORS */ +wrong_channels: + { + GST_DEBUG_OBJECT (rtpg729depay, "expected 1 channel, got %d", channels); + return FALSE; + } +wrong_clock_rate: + { + GST_DEBUG_OBJECT (rtpg729depay, "expected 8000 clock-rate, got %d", + clock_rate); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_g729_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpG729Depay *rtpg729depay; + GstBuffer *outbuf = NULL; + gint payload_len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + rtpg729depay = GST_RTP_G729_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + /* At least 2 bytes (CNG from G729 Annex B) */ + if (payload_len < 2) { + GST_ELEMENT_WARNING (rtpg729depay, STREAM, DECODE, + (NULL), ("G729 RTP payload too small (%d)", payload_len)); + goto bad_packet; + } + + GST_LOG_OBJECT (rtpg729depay, "payload len %d", payload_len); + + if ((payload_len % 10) == 2) { + GST_LOG_OBJECT (rtpg729depay, "G729 payload contains CNG frame"); + } + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + + gst_rtp_buffer_unmap (&rtp); + + if (marker) { + /* marker bit starts talkspurt */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + GST_LOG_OBJECT (depayload, "pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + + return outbuf; + + /* ERRORS */ +bad_packet: + { + /* no fatal error */ + return NULL; + } +} + +gboolean +gst_rtp_g729_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg729depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G729_DEPAY); +} diff --git a/gst/rtp/gstrtpg729depay.h b/gst/rtp/gstrtpg729depay.h new file mode 100755 index 0000000..a23562e --- /dev/null +++ b/gst/rtp/gstrtpg729depay.h @@ -0,0 +1,61 @@ +/* GStreamer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G729_DEPAY_H__ +#define __GST_RTP_G729_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_G729_DEPAY \ + (gst_rtp_g729_depay_get_type()) + +#define GST_RTP_G729_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G729_DEPAY,GstRtpG729Depay)) + +#define GST_RTP_G729_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G729_DEPAY,GstRtpG729DepayClass)) + +#define GST_IS_RTP_G729_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G729_DEPAY)) + +#define GST_IS_RTP_G729_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G729_DEPAY)) + +typedef struct _GstRtpG729Depay GstRtpG729Depay; +typedef struct _GstRtpG729DepayClass GstRtpG729DepayClass; + +struct _GstRtpG729Depay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpG729DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_g729_depay_get_type (void); + +gboolean gst_rtp_g729_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G729_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpg729pay.c b/gst/rtp/gstrtpg729pay.c new file mode 100755 index 0000000..306e175 --- /dev/null +++ b/gst/rtp/gstrtpg729pay.c @@ -0,0 +1,418 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation + * Copyright (C) <2007> Collabora Ltd + * @author: Olivier Crete <olivier.crete@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/* + * This payloader assumes that the data will ALWAYS come as zero or more + * 10 bytes frame of audio followed by 0 or 1 2 byte frame of silence. + * Any other buffer format won't work + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> +#include <gst/base/gstadapter.h> + +#include "gstrtpg729pay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpg729pay_debug); +#define GST_CAT_DEFAULT (rtpg729pay_debug) + +#define G729_FRAME_SIZE 10 +#define G729B_CN_FRAME_SIZE 2 +#define G729_FRAME_DURATION (10 * GST_MSECOND) +#define G729_FRAME_DURATION_MS (10) + +static gboolean +gst_rtp_g729_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps); +static GstFlowReturn +gst_rtp_g729_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf); + +static GstStateChangeReturn +gst_rtp_g729_pay_change_state (GstElement * element, GstStateChange transition); + +static GstStaticPadTemplate gst_rtp_g729_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/G729, " /* according to RFC 3555 */ + "channels = (int) 1, " "rate = (int) 8000") + ); + +static GstStaticPadTemplate gst_rtp_g729_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_G729_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"G729\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"G729\"") + ); + +#define gst_rtp_g729_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPG729Pay, gst_rtp_g729_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_g729_pay_finalize (GObject * object) +{ + GstRTPG729Pay *pay = GST_RTP_G729_PAY (object); + + g_object_unref (pay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_g729_pay_class_init (GstRTPG729PayClass * klass) +{ + GObjectClass *gobject_class = (GObjectClass *) klass; + GstElementClass *gstelement_class = (GstElementClass *) klass; + GstRTPBasePayloadClass *payload_class = GST_RTP_BASE_PAYLOAD_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (rtpg729pay_debug, "rtpg729pay", 0, + "G.729 RTP Payloader"); + + gobject_class->finalize = gst_rtp_g729_pay_finalize; + + gstelement_class->change_state = gst_rtp_g729_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g729_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_g729_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP G.729 payloader", "Codec/Payloader/Network/RTP", + "Packetize G.729 audio into RTP packets", + "Olivier Crete <olivier.crete@collabora.co.uk>"); + + payload_class->set_caps = gst_rtp_g729_pay_set_caps; + payload_class->handle_buffer = gst_rtp_g729_pay_handle_buffer; +} + +static void +gst_rtp_g729_pay_init (GstRTPG729Pay * pay) +{ + GstRTPBasePayload *payload = GST_RTP_BASE_PAYLOAD (pay); + + payload->pt = GST_RTP_PAYLOAD_G729; + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "G729", 8000); + + pay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_g729_pay_reset (GstRTPG729Pay * pay) +{ + gst_adapter_clear (pay->adapter); + pay->discont = FALSE; + pay->next_rtp_time = 0; + pay->first_ts = GST_CLOCK_TIME_NONE; + pay->first_rtp_time = 0; +} + +static gboolean +gst_rtp_g729_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + GstStructure *structure; + gint pt; + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "payload", &pt)) + pt = GST_RTP_PAYLOAD_G729; + + payload->pt = pt; + payload->dynamic = pt != GST_RTP_PAYLOAD_G729; + + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +static GstFlowReturn +gst_rtp_g729_pay_push (GstRTPG729Pay * rtpg729pay, + const guint8 * data, guint payload_len) +{ + GstRTPBasePayload *basepayload; + GstClockTime duration; + guint frames; + GstBuffer *outbuf; + guint8 *payload; + GstFlowReturn ret; + GstRTPBuffer rtp = { NULL }; + + basepayload = GST_RTP_BASE_PAYLOAD (rtpg729pay); + + GST_DEBUG_OBJECT (rtpg729pay, "Pushing %d bytes ts %" GST_TIME_FORMAT, + payload_len, GST_TIME_ARGS (rtpg729pay->next_ts)); + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_READWRITE, &rtp); + + /* copy payload */ + payload = gst_rtp_buffer_get_payload (&rtp); + memcpy (payload, data, payload_len); + + /* set metadata */ + frames = + (payload_len / G729_FRAME_SIZE) + ((payload_len % G729_FRAME_SIZE) >> 1); + duration = frames * G729_FRAME_DURATION; + GST_BUFFER_TIMESTAMP (outbuf) = rtpg729pay->next_ts; + GST_BUFFER_DURATION (outbuf) = duration; + GST_BUFFER_OFFSET (outbuf) = rtpg729pay->next_rtp_time; + rtpg729pay->next_ts += duration; + rtpg729pay->next_rtp_time += frames * 80; + + if (G_UNLIKELY (rtpg729pay->discont)) { + GST_DEBUG_OBJECT (basepayload, "discont, setting marker bit"); + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + gst_rtp_buffer_set_marker (&rtp, TRUE); + rtpg729pay->discont = FALSE; + } + gst_rtp_buffer_unmap (&rtp); + + ret = gst_rtp_base_payload_push (basepayload, outbuf); + + return ret; +} + +static GstFlowReturn +gst_rtp_g729_pay_push_and_free (GstRTPG729Pay * rtpg729pay, + guint8 * data, guint payload_len) +{ + GstFlowReturn ret; + + ret = gst_rtp_g729_pay_push (rtpg729pay, data, payload_len); + g_free (data); + return ret; +} + +static void +gst_rtp_g729_pay_recalc_rtp_time (GstRTPG729Pay * rtpg729pay, GstClockTime time) +{ + if (GST_CLOCK_TIME_IS_VALID (rtpg729pay->first_ts) + && GST_CLOCK_TIME_IS_VALID (time) && time >= rtpg729pay->first_ts) { + GstClockTime diff; + guint32 rtpdiff; + + diff = time - rtpg729pay->first_ts; + rtpdiff = (diff / GST_MSECOND) * 8; + rtpg729pay->next_rtp_time = rtpg729pay->first_rtp_time + rtpdiff; + GST_DEBUG_OBJECT (rtpg729pay, + "elapsed time %" GST_TIME_FORMAT ", rtp %" G_GUINT32_FORMAT ", " + "new offset %" G_GUINT32_FORMAT, GST_TIME_ARGS (diff), rtpdiff, + rtpg729pay->next_rtp_time); + } +} + +static GstFlowReturn +gst_rtp_g729_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buf) +{ + GstFlowReturn ret = GST_FLOW_OK; + GstRTPG729Pay *rtpg729pay = GST_RTP_G729_PAY (payload); + GstAdapter *adapter = NULL; + guint payload_len; + guint available; + guint maxptime_octets = G_MAXUINT; + guint minptime_octets = 0; + guint min_payload_len; + guint max_payload_len; + gsize size; + GstClockTime timestamp; + + size = gst_buffer_get_size (buf); + + if (size % G729_FRAME_SIZE != 0 && + size % G729_FRAME_SIZE != G729B_CN_FRAME_SIZE) + goto invalid_size; + + /* max number of bytes based on given ptime, has to be multiple of + * frame_duration */ + if (payload->max_ptime != -1) { + guint ptime_ms = payload->max_ptime / GST_MSECOND; + + maxptime_octets = G729_FRAME_SIZE * + (int) (ptime_ms / G729_FRAME_DURATION_MS); + + if (maxptime_octets < G729_FRAME_SIZE) { + GST_WARNING_OBJECT (payload, "Given ptime %" G_GINT64_FORMAT + " is smaller than minimum %d ns, overwriting to minimum", + payload->max_ptime, G729_FRAME_DURATION_MS); + maxptime_octets = G729_FRAME_SIZE; + } + } + + max_payload_len = MIN ( + /* MTU max */ + (int) (gst_rtp_buffer_calc_payload_len (GST_RTP_BASE_PAYLOAD_MTU + (payload), 0, 0) / G729_FRAME_SIZE) + * G729_FRAME_SIZE, + /* ptime max */ + maxptime_octets); + + /* min number of bytes based on a given ptime, has to be a multiple + of frame duration */ + { + guint64 min_ptime = payload->min_ptime; + + min_ptime = min_ptime / GST_MSECOND; + minptime_octets = G729_FRAME_SIZE * + (int) (min_ptime / G729_FRAME_DURATION_MS); + } + + min_payload_len = MAX (minptime_octets, G729_FRAME_SIZE); + + if (min_payload_len > max_payload_len) { + min_payload_len = max_payload_len; + } + + /* If the ptime is specified in the caps, tried to adhere to it exactly */ + if (payload->ptime) { + guint64 ptime = payload->ptime / GST_MSECOND; + guint ptime_in_bytes = G729_FRAME_SIZE * + (guint) (ptime / G729_FRAME_DURATION_MS); + + /* clip to computed min and max lengths */ + ptime_in_bytes = MAX (min_payload_len, ptime_in_bytes); + ptime_in_bytes = MIN (max_payload_len, ptime_in_bytes); + + min_payload_len = max_payload_len = ptime_in_bytes; + } + + GST_LOG_OBJECT (payload, + "Calculated min_payload_len %u and max_payload_len %u", + min_payload_len, max_payload_len); + + adapter = rtpg729pay->adapter; + available = gst_adapter_available (adapter); + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + /* resync rtp time on discont or a discontinuous cn packet */ + if (GST_BUFFER_IS_DISCONT (buf)) { + /* flush remainder */ + if (available > 0) { + gst_rtp_g729_pay_push_and_free (rtpg729pay, + gst_adapter_take (adapter, available), available); + available = 0; + } + rtpg729pay->discont = TRUE; + gst_rtp_g729_pay_recalc_rtp_time (rtpg729pay, timestamp); + } + + if (size < G729_FRAME_SIZE) + gst_rtp_g729_pay_recalc_rtp_time (rtpg729pay, timestamp); + + if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (rtpg729pay->first_ts))) { + rtpg729pay->first_ts = timestamp; + rtpg729pay->first_rtp_time = rtpg729pay->next_rtp_time; + } + + /* let's reset the base timestamp when the adapter is empty */ + if (available == 0) + rtpg729pay->next_ts = timestamp; + + if (available == 0 && size >= min_payload_len && size <= max_payload_len) { + GstMapInfo map; + + gst_buffer_map (buf, &map, GST_MAP_READ); + ret = gst_rtp_g729_pay_push (rtpg729pay, map.data, map.size); + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + return ret; + } + + gst_adapter_push (adapter, buf); + available = gst_adapter_available (adapter); + + /* as long as we have full frames */ + /* this loop will push all available buffers till the last frame */ + while (available >= min_payload_len || + available % G729_FRAME_SIZE == G729B_CN_FRAME_SIZE) { + /* We send as much as we can */ + if (available <= max_payload_len) { + payload_len = available; + } else { + payload_len = MIN (max_payload_len, + (available / G729_FRAME_SIZE) * G729_FRAME_SIZE); + } + + ret = gst_rtp_g729_pay_push_and_free (rtpg729pay, + gst_adapter_take (adapter, payload_len), payload_len); + available -= payload_len; + } + + return ret; + + /* ERRORS */ +invalid_size: + { + GST_ELEMENT_ERROR (payload, STREAM, WRONG_TYPE, + ("Invalid input buffer size"), + ("Invalid buffer size, should be a multiple of" + " G729_FRAME_SIZE(10) with an optional G729B_CN_FRAME_SIZE(2)" + " added to it, but it is %" G_GSIZE_FORMAT, size)); + gst_buffer_unref (buf); + return GST_FLOW_ERROR; + } +} + +static GstStateChangeReturn +gst_rtp_g729_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; + + /* handle upwards state changes here */ + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + /* handle downwards state changes */ + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_g729_pay_reset (GST_RTP_G729_PAY (element)); + break; + default: + break; + } + + return ret; +} + +gboolean +gst_rtp_g729_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpg729pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_G729_PAY); +} diff --git a/gst/rtp/gstrtpg729pay.h b/gst/rtp/gstrtpg729pay.h new file mode 100755 index 0000000..1b92460 --- /dev/null +++ b/gst/rtp/gstrtpg729pay.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation + * Copyright (C) <2007> Collabora Ltd + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_G729_PAY_H__ +#define __GST_RTP_G729_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_G729_PAY \ + (gst_rtp_g729_pay_get_type()) +#define GST_RTP_G729_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_G729_PAY,GstRTPG729Pay)) +#define GST_RTP_G729_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_G729_PAY,GstRTPG729PayClass)) +#define GST_IS_RTP_G729_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_G729_PAY)) +#define GST_IS_RTP_G729_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_G729_PAY)) + +typedef struct _GstRTPG729Pay GstRTPG729Pay; +typedef struct _GstRTPG729PayClass GstRTPG729PayClass; + +struct _GstRTPG729Pay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime next_ts; + guint32 next_rtp_time; + GstClockTime first_ts; + guint32 first_rtp_time; + gboolean discont; +}; + +struct _GstRTPG729PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_g729_pay_get_type (void); + +gboolean gst_rtp_g729_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_G729_PAY_H__ */ diff --git a/gst/rtp/gstrtpgsmdepay.c b/gst/rtp/gstrtpgsmdepay.c new file mode 100755 index 0000000..96b30cf --- /dev/null +++ b/gst/rtp/gstrtpgsmdepay.c @@ -0,0 +1,151 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Zeeshan Ali <zeenix@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpgsmdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpgsmdepay_debug); +#define GST_CAT_DEFAULT (rtpgsmdepay_debug) + +/* RTPGSMDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +static GstStaticPadTemplate gst_rtp_gsm_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-gsm, " "rate = (int) 8000, " "channels = 1") + ); + +static GstStaticPadTemplate gst_rtp_gsm_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"GSM\";" + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_GSM_STRING ", " + "clock-rate = (int) 8000") + ); + +static GstBuffer *gst_rtp_gsm_depay_process (GstRTPBaseDepayload * _depayload, + GstBuffer * buf); +static gboolean gst_rtp_gsm_depay_setcaps (GstRTPBaseDepayload * _depayload, + GstCaps * caps); + +#define gst_rtp_gsm_depay_parent_class parent_class +G_DEFINE_TYPE (GstRTPGSMDepay, gst_rtp_gsm_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_gsm_depay_class_init (GstRTPGSMDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbase_depayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbase_depayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gsm_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gsm_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP GSM depayloader", "Codec/Depayloader/Network/RTP", + "Extracts GSM audio from RTP packets", "Zeeshan Ali <zeenix@gmail.com>"); + + gstrtpbase_depayload_class->process = gst_rtp_gsm_depay_process; + gstrtpbase_depayload_class->set_caps = gst_rtp_gsm_depay_setcaps; + + GST_DEBUG_CATEGORY_INIT (rtpgsmdepay_debug, "rtpgsmdepay", 0, + "GSM Audio RTP Depayloader"); +} + +static void +gst_rtp_gsm_depay_init (GstRTPGSMDepay * rtpgsmdepay) +{ +} + +static gboolean +gst_rtp_gsm_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + gboolean ret; + GstStructure *structure; + gint clock_rate; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("audio/x-gsm", + "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, clock_rate, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; +} + +static GstBuffer * +gst_rtp_gsm_depay_process (GstRTPBaseDepayload * _depayload, GstBuffer * buf) +{ + GstBuffer *outbuf = NULL; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + + gst_rtp_buffer_unmap (&rtp); + + if (marker && outbuf) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + return outbuf; +} + +gboolean +gst_rtp_gsm_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgsmdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_GSM_DEPAY); +} diff --git a/gst/rtp/gstrtpgsmdepay.h b/gst/rtp/gstrtpgsmdepay.h new file mode 100755 index 0000000..e428aa0 --- /dev/null +++ b/gst/rtp/gstrtpgsmdepay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_GSM_DEPAY_H__ +#define __GST_RTP_GSM_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPGSMDepay GstRTPGSMDepay; +typedef struct _GstRTPGSMDepayClass GstRTPGSMDepayClass; + +#define GST_TYPE_RTP_GSM_DEPAY \ + (gst_rtp_gsm_depay_get_type()) +#define GST_RTP_GSM_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GSM_DEPAY,GstRTPGSMDepay)) +#define GST_RTP_GSM_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GSM_DEPAY,GstRTPGSMDepayClass)) +#define GST_IS_RTP_GSM_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GSM_DEPAY)) +#define GST_IS_RTP_GSM_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GSM_DEPAY)) + +struct _GstRTPGSMDepay +{ + GstRTPBaseDepayload _depayload; +}; + +struct _GstRTPGSMDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_gsm_depay_get_type (void); + +gboolean gst_rtp_gsm_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GSM_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpgsmpay.c b/gst/rtp/gstrtpgsmpay.c new file mode 100755 index 0000000..b6964c5 --- /dev/null +++ b/gst/rtp/gstrtpgsmpay.c @@ -0,0 +1,191 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Zeeshan Ali <zeenix@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpgsmpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpgsmpay_debug); +#define GST_CAT_DEFAULT (rtpgsmpay_debug) + +static GstStaticPadTemplate gst_rtp_gsm_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-gsm, " "rate = (int) 8000, " "channels = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_gsm_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_GSM_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"GSM\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"GSM\"") + ); + +static gboolean gst_rtp_gsm_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_gsm_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); + +#define gst_rtp_gsm_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPGSMPay, gst_rtp_gsm_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_gsm_pay_class_init (GstRTPGSMPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpgsmpay_debug, "rtpgsmpay", 0, + "GSM Audio RTP Payloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gsm_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gsm_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP GSM payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes GSM audio into a RTP packet", + "Zeeshan Ali <zeenix@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_gsm_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_gsm_pay_handle_buffer; +} + +static void +gst_rtp_gsm_pay_init (GstRTPGSMPay * rtpgsmpay) +{ + GST_RTP_BASE_PAYLOAD (rtpgsmpay)->clock_rate = 8000; + GST_RTP_BASE_PAYLOAD_PT (rtpgsmpay) = GST_RTP_PAYLOAD_GSM; +} + +static gboolean +gst_rtp_gsm_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + const char *stname; + GstStructure *structure; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + stname = gst_structure_get_name (structure); + + if (strcmp ("audio/x-gsm", stname)) + goto invalid_type; + + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "GSM", 8000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; + + /* ERRORS */ +invalid_type: + { + GST_WARNING_OBJECT (payload, "invalid media type received"); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_gsm_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRTPGSMPay *rtpgsmpay; + guint payload_len; + GstBuffer *outbuf; + GstMapInfo map; + guint8 *payload; + GstClockTime timestamp, duration; + GstFlowReturn ret; + GstRTPBuffer rtp = { NULL }; + + rtpgsmpay = GST_RTP_GSM_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + + /* FIXME, only one GSM frame per RTP packet for now */ + payload_len = map.size; + + /* FIXME, just error out for now */ + if (payload_len > GST_RTP_BASE_PAYLOAD_MTU (rtpgsmpay)) + goto too_big; + + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + /* copy timestamp and duration */ + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + GST_BUFFER_DURATION (outbuf) = duration; + + /* get payload */ + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* copy data in payload */ + payload = gst_rtp_buffer_get_payload (&rtp); + memcpy (payload, map.data, map.size); + + gst_rtp_buffer_unmap (&rtp); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + GST_DEBUG ("gst_rtp_gsm_pay_chain: pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + + ret = gst_rtp_base_payload_push (basepayload, outbuf); + + return ret; + + /* ERRORS */ +too_big: + { + GST_ELEMENT_ERROR (rtpgsmpay, STREAM, ENCODE, (NULL), + ("payload_len %u > mtu %u", payload_len, + GST_RTP_BASE_PAYLOAD_MTU (rtpgsmpay))); + gst_buffer_unmap (buffer, &map); + return GST_FLOW_ERROR; + } +} + +gboolean +gst_rtp_gsm_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgsmpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_GSM_PAY); +} diff --git a/gst/rtp/gstrtpgsmpay.h b/gst/rtp/gstrtpgsmpay.h new file mode 100755 index 0000000..b6437f5 --- /dev/null +++ b/gst/rtp/gstrtpgsmpay.h @@ -0,0 +1,59 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_RTP_GSM_PAY_H__ +#define __GST_RTP_GSM_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPGSMPay GstRTPGSMPay; +typedef struct _GstRTPGSMPayClass GstRTPGSMPayClass; + +#define GST_TYPE_RTP_GSM_PAY \ + (gst_rtp_gsm_pay_get_type()) +#define GST_RTP_GSM_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GSM_PAY,GstRTPGSMPay)) +#define GST_RTP_GSM_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GSM_PAY,GstRTPGSMPayClass)) +#define GST_IS_RTP_GSM_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GSM_PAY)) +#define GST_IS_RTP_GSM_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GSM_PAY)) + +struct _GstRTPGSMPay +{ + GstRTPBasePayload payload; +}; + +struct _GstRTPGSMPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_gsm_pay_get_type (void); + +gboolean gst_rtp_gsm_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GSM_PAY_H__ */ diff --git a/gst/rtp/gstrtpgstdepay.c b/gst/rtp/gstrtpgstdepay.c new file mode 100755 index 0000000..621aa47 --- /dev/null +++ b/gst/rtp/gstrtpgstdepay.c @@ -0,0 +1,625 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include "gstrtpgstdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpgstdepay_debug); +#define GST_CAT_DEFAULT (rtpgstdepay_debug) + +static GstStaticPadTemplate gst_rtp_gst_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_rtp_gst_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"application\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"X-GST\"") + ); + +#define gst_rtp_gst_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpGSTDepay, gst_rtp_gst_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_gst_depay_finalize (GObject * object); + +static gboolean gst_rtp_gst_depay_handle_event (GstRTPBaseDepayload * depay, + GstEvent * event); +static GstStateChangeReturn gst_rtp_gst_depay_change_state (GstElement * + element, GstStateChange transition); + +static void gst_rtp_gst_depay_reset (GstRtpGSTDepay * rtpgstdepay, gboolean + full); +static gboolean gst_rtp_gst_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_gst_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_gst_depay_class_init (GstRtpGSTDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpgstdepay_debug, "rtpgstdepay", 0, + "Gstreamer RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_gst_depay_finalize; + + gstelement_class->change_state = gst_rtp_gst_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gst_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gst_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "GStreamer depayloader", "Codec/Depayloader/Network", + "Extracts GStreamer buffers from RTP packets", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->handle_event = gst_rtp_gst_depay_handle_event; + gstrtpbasedepayload_class->set_caps = gst_rtp_gst_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_gst_depay_process; +} + +static void +gst_rtp_gst_depay_init (GstRtpGSTDepay * rtpgstdepay) +{ + rtpgstdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_gst_depay_finalize (GObject * object) +{ + GstRtpGSTDepay *rtpgstdepay; + + rtpgstdepay = GST_RTP_GST_DEPAY (object); + + gst_rtp_gst_depay_reset (rtpgstdepay, TRUE); + g_object_unref (rtpgstdepay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +store_cache (GstRtpGSTDepay * rtpgstdepay, guint CV, GstCaps * caps) +{ + if (rtpgstdepay->CV_cache[CV]) + gst_caps_unref (rtpgstdepay->CV_cache[CV]); + rtpgstdepay->CV_cache[CV] = caps; +} + +static void +gst_rtp_gst_depay_reset (GstRtpGSTDepay * rtpgstdepay, gboolean full) +{ + guint i; + + gst_adapter_clear (rtpgstdepay->adapter); + if (full) { + rtpgstdepay->current_CV = 0; + for (i = 0; i < 8; i++) + store_cache (rtpgstdepay, i, NULL); + g_free (rtpgstdepay->stream_id); + rtpgstdepay->stream_id = NULL; + if (rtpgstdepay->tags) + gst_tag_list_unref (rtpgstdepay->tags); + rtpgstdepay->tags = NULL; + } +} + +static gboolean +gst_rtp_gst_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstRtpGSTDepay *rtpgstdepay; + GstStructure *structure; + gint clock_rate; + gboolean res; + const gchar *capsenc; + + rtpgstdepay = GST_RTP_GST_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + capsenc = gst_structure_get_string (structure, "caps"); + if (capsenc) { + GstCaps *outcaps; + gsize out_len; + gchar *capsstr; + const gchar *capsver; + guint CV; + + /* decode caps */ + capsstr = (gchar *) g_base64_decode (capsenc, &out_len); + outcaps = gst_caps_from_string (capsstr); + g_free (capsstr); + + /* parse version */ + capsver = gst_structure_get_string (structure, "capsversion"); + if (capsver) { + CV = atoi (capsver); + } else { + /* no version, assume 0 */ + CV = 0; + } + /* store in cache */ + rtpgstdepay->current_CV = CV; + gst_caps_ref (outcaps); + store_cache (rtpgstdepay, CV, outcaps); + + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + } else { + GST_WARNING_OBJECT (depayload, "no caps given"); + rtpgstdepay->current_CV = -1; + res = TRUE; + } + + return res; +} + +static gboolean +read_length (GstRtpGSTDepay * rtpgstdepay, guint8 * data, guint size, + guint * length, guint * skip) +{ + guint b, len, offset; + + /* start reading the length, we need this to skip to the data later */ + len = offset = 0; + do { + if (offset >= size) + return FALSE; + b = data[offset++]; + len = (len << 7) | (b & 0x7f); + } while (b & 0x80); + + /* check remaining buffer size */ + if (size - offset < len) + return FALSE; + + *length = len; + *skip = offset; + + return TRUE; +} + +static GstCaps * +read_caps (GstRtpGSTDepay * rtpgstdepay, GstBuffer * buf, guint * skip) +{ + guint offset, length; + GstCaps *caps; + GstMapInfo map; + + gst_buffer_map (buf, &map, GST_MAP_READ); + + GST_DEBUG_OBJECT (rtpgstdepay, "buffer size %" G_GSIZE_FORMAT, map.size); + + if (!read_length (rtpgstdepay, map.data, map.size, &length, &offset)) + goto too_small; + + if (length == 0 || map.data[offset + length - 1] != '\0') + goto invalid_buffer; + + GST_DEBUG_OBJECT (rtpgstdepay, "parsing caps %s", &map.data[offset]); + + /* parse and store in cache */ + caps = gst_caps_from_string ((gchar *) & map.data[offset]); + gst_buffer_unmap (buf, &map); + + *skip = length + offset; + + return caps; + +too_small: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Buffer too small."), (NULL)); + gst_buffer_unmap (buf, &map); + return NULL; + } +invalid_buffer: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("caps string not 0-terminated."), (NULL)); + gst_buffer_unmap (buf, &map); + return NULL; + } +} + +static GstEvent * +read_event (GstRtpGSTDepay * rtpgstdepay, guint type, + GstBuffer * buf, guint * skip) +{ + guint offset, length; + GstStructure *s; + GstEvent *event; + GstEventType etype; + gchar *end; + GstMapInfo map; + + gst_buffer_map (buf, &map, GST_MAP_READ); + + GST_DEBUG_OBJECT (rtpgstdepay, "buffer size %" G_GSIZE_FORMAT, map.size); + + if (!read_length (rtpgstdepay, map.data, map.size, &length, &offset)) + goto too_small; + + if (length == 0) + goto invalid_buffer; + if (map.data[offset + length - 1] != '\0') + goto invalid_buffer; + /* backward compat, old payloader did not put 0-byte at the end */ + if (map.data[offset + length - 1] != ';') + goto invalid_buffer; + + GST_DEBUG_OBJECT (rtpgstdepay, "parsing event %s", &map.data[offset]); + + /* parse */ + s = gst_structure_from_string ((gchar *) & map.data[offset], &end); + gst_buffer_unmap (buf, &map); + + if (s == NULL) + goto parse_failed; + + switch (type) { + case 1: + etype = GST_EVENT_TAG; + break; + case 2: + etype = GST_EVENT_CUSTOM_DOWNSTREAM; + break; + case 3: + etype = GST_EVENT_CUSTOM_BOTH; + break; + case 4: + etype = GST_EVENT_STREAM_START; + break; + default: + goto unknown_event; + } + event = gst_event_new_custom (etype, s); + + *skip = length + offset; + + return event; + +too_small: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Buffer too small."), (NULL)); + gst_buffer_unmap (buf, &map); + return NULL; + } +invalid_buffer: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("event string not 0-terminated."), (NULL)); + gst_buffer_unmap (buf, &map); + return NULL; + } +parse_failed: + { + GST_WARNING_OBJECT (rtpgstdepay, "could not parse event"); + return NULL; + } +unknown_event: + { + GST_DEBUG_OBJECT (rtpgstdepay, "unknown event type"); + gst_structure_free (s); + return NULL; + } +} + +static void +store_event (GstRtpGSTDepay * rtpgstdepay, GstEvent * event) +{ + gboolean do_push = FALSE; + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_TAG: + { + GstTagList *old, *tags; + + gst_event_parse_tag (event, &tags); + + old = rtpgstdepay->tags; + if (!old || !gst_tag_list_is_equal (old, tags)) { + do_push = TRUE; + if (old) + gst_tag_list_unref (old); + rtpgstdepay->tags = gst_tag_list_ref (tags); + } + break; + } + case GST_EVENT_CUSTOM_DOWNSTREAM: + case GST_EVENT_CUSTOM_BOTH: + /* always push custom events */ + do_push = TRUE; + break; + case GST_EVENT_STREAM_START: + { + gchar *old; + const gchar *stream_id = NULL; + + gst_event_parse_stream_start (event, &stream_id); + + old = rtpgstdepay->stream_id; + if (!old || g_strcmp0 (old, stream_id)) { + do_push = TRUE; + g_free (old); + rtpgstdepay->stream_id = g_strdup (stream_id); + } + break; + } + default: + /* unknown event, don't push */ + break; + } + if (do_push) + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD (rtpgstdepay)->srcpad, event); + else + gst_event_unref (event); +} + +static GstBuffer * +gst_rtp_gst_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpGSTDepay *rtpgstdepay; + GstBuffer *subbuf, *outbuf = NULL; + gint payload_len; + guint8 *payload; + guint CV, frag_offset, avail, offset; + GstRTPBuffer rtp = { NULL }; + + rtpgstdepay = GST_RTP_GST_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len <= 8) + goto empty_packet; + + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_WARNING_OBJECT (rtpgstdepay, "DISCONT, clear adapter"); + gst_adapter_clear (rtpgstdepay->adapter); + } + + payload = gst_rtp_buffer_get_payload (&rtp); + + /* strip off header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|0|0|0| ETYPE | MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = + (payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) | payload[7]; + + avail = gst_adapter_available (rtpgstdepay->adapter); + if (avail != frag_offset) + goto wrong_frag; + + /* subbuffer skipping the 8 header bytes */ + subbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, 8, -1); + gst_adapter_push (rtpgstdepay->adapter, subbuf); + + offset = 0; + if (gst_rtp_buffer_get_marker (&rtp)) { + guint avail; + GstCaps *outcaps; + + /* take the buffer */ + avail = gst_adapter_available (rtpgstdepay->adapter); + outbuf = gst_adapter_take_buffer (rtpgstdepay->adapter, avail); + + CV = (payload[0] >> 4) & 0x7; + + if (payload[0] & 0x80) { + guint size; + + /* C bit, we have inline caps */ + outcaps = read_caps (rtpgstdepay, outbuf, &size); + if (outcaps == NULL) + goto no_caps; + + GST_DEBUG_OBJECT (rtpgstdepay, + "inline caps %u, length %u, %" GST_PTR_FORMAT, CV, size, outcaps); + + store_cache (rtpgstdepay, CV, outcaps); + + /* skip caps */ + offset += size; + avail -= size; + } + if (payload[1]) { + guint size; + GstEvent *event; + + /* we have an event */ + event = read_event (rtpgstdepay, payload[1], outbuf, &size); + if (event == NULL) + goto no_event; + + GST_DEBUG_OBJECT (rtpgstdepay, + "inline event, length %u, %" GST_PTR_FORMAT, size, event); + + store_event (rtpgstdepay, event); + + /* no buffer after event */ + avail = 0; + } + + if (avail) { + if (offset != 0) { + GstBuffer *temp; + + GST_DEBUG_OBJECT (rtpgstdepay, "sub buffer: offset %u, size %u", offset, + avail); + + temp = + gst_buffer_copy_region (outbuf, GST_BUFFER_COPY_ALL, offset, avail); + + gst_buffer_unref (outbuf); + outbuf = temp; + } + + /* see what caps we need */ + if (CV != rtpgstdepay->current_CV) { + /* we need to switch caps, check if we have the caps */ + if ((outcaps = rtpgstdepay->CV_cache[CV]) == NULL) + goto missing_caps; + + GST_DEBUG_OBJECT (rtpgstdepay, + "need caps switch from %u to %u, %" GST_PTR_FORMAT, + rtpgstdepay->current_CV, CV, outcaps); + + /* and set caps */ + if (gst_pad_set_caps (depayload->srcpad, outcaps)) + rtpgstdepay->current_CV = CV; + } + + if (payload[0] & 0x8) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + } else { + gst_buffer_unref (outbuf); + outbuf = NULL; + } + } + gst_rtp_buffer_unmap (&rtp); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +wrong_frag: + { + gst_adapter_clear (rtpgstdepay->adapter); + gst_rtp_buffer_unmap (&rtp); + GST_LOG_OBJECT (rtpgstdepay, "wrong fragment, skipping"); + return NULL; + } +no_caps: + { + GST_WARNING_OBJECT (rtpgstdepay, "failed to parse caps"); + gst_buffer_unref (outbuf); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +no_event: + { + GST_WARNING_OBJECT (rtpgstdepay, "failed to parse event"); + gst_buffer_unref (outbuf); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +missing_caps: + { + GST_ELEMENT_WARNING (rtpgstdepay, STREAM, DECODE, + ("Missing caps %u.", CV), (NULL)); + gst_buffer_unref (outbuf); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static gboolean +gst_rtp_gst_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event) +{ + GstRtpGSTDepay *rtpgstdepay; + + rtpgstdepay = GST_RTP_GST_DEPAY (depay); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_gst_depay_reset (rtpgstdepay, FALSE); + break; + default: + break; + } + + return + GST_RTP_BASE_DEPAYLOAD_CLASS (parent_class)->handle_event (depay, event); +} + + +static GstStateChangeReturn +gst_rtp_gst_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpGSTDepay *rtpgstdepay; + GstStateChangeReturn ret; + + rtpgstdepay = GST_RTP_GST_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_gst_depay_reset (rtpgstdepay, TRUE); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_gst_depay_reset (rtpgstdepay, TRUE); + break; + default: + break; + } + return ret; +} + + +gboolean +gst_rtp_gst_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgstdepay", + GST_RANK_MARGINAL, GST_TYPE_RTP_GST_DEPAY); +} diff --git a/gst/rtp/gstrtpgstdepay.h b/gst/rtp/gstrtpgstdepay.h new file mode 100755 index 0000000..4b8f2c6 --- /dev/null +++ b/gst/rtp/gstrtpgstdepay.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_GST_DEPAY_H__ +#define __GST_RTP_GST_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_GST_DEPAY \ + (gst_rtp_gst_depay_get_type()) +#define GST_RTP_GST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GST_DEPAY,GstRtpGSTDepay)) +#define GST_RTP_GST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GST_DEPAY,GstRtpGSTDepayClass)) +#define GST_IS_RTP_GST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GST_DEPAY)) +#define GST_IS_RTP_GST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GST_DEPAY)) + +typedef struct _GstRtpGSTDepay GstRtpGSTDepay; +typedef struct _GstRtpGSTDepayClass GstRtpGSTDepayClass; + +struct _GstRtpGSTDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + guint current_CV; + GstCaps *CV_cache[8]; + + GstTagList *tags; + gchar *stream_id; +}; + +struct _GstRtpGSTDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_gst_depay_get_type (void); + +gboolean gst_rtp_gst_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GST_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpgstpay.c b/gst/rtp/gstrtpgstpay.c new file mode 100755 index 0000000..6167008 --- /dev/null +++ b/gst/rtp/gstrtpgstpay.c @@ -0,0 +1,661 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpgstpay.h" + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_pay_debug + +/* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|0|0|0| ETYPE | MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * C: caps inlined flag + * When C set, first part of payload contains caps definition. Caps definition + * starts with variable-length length prefix and then a string of that length. + * the length is encoded in big endian 7 bit chunks, the top 1 bit of a byte + * is the continuation marker and the 7 next bits the data. A continuation + * marker of 1 means that the next byte contains more data. + * + * CV: caps version, 0 = caps from SDP, 1 - 7 inlined caps + * D: delta unit buffer + * ETYPE: type of event. Payload contains the event, prefixed with a + * variable length field. + * 0 = NO event + * 1 = GST_EVENT_TAG + * 2 = GST_EVENT_CUSTOM_DOWNSTREAM + * 3 = GST_EVENT_CUSTOM_BOTH + * 4 = GST_EVENT_STREAM_START + */ + +static GstStaticPadTemplate gst_rtp_gst_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_rtp_gst_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"application\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"X-GST\"") + ); + +enum +{ + PROP_0, + PROP_CONFIG_INTERVAL +}; + +#define DEFAULT_CONFIG_INTERVAL 0 + +static void gst_rtp_gst_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_gst_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_gst_pay_finalize (GObject * obj); +static GstStateChangeReturn gst_rtp_gst_pay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_gst_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_gst_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); +static gboolean gst_rtp_gst_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); + +#define gst_rtp_gst_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpGSTPay, gst_rtp_gst_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_gst_pay_class_init (GstRtpGSTPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_gst_pay_set_property; + gobject_class->get_property = gst_rtp_gst_pay_get_property; + gobject_class->finalize = gst_rtp_gst_pay_finalize; + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_CONFIG_INTERVAL, + g_param_spec_uint ("config-interval", + "Caps/Tags Send Interval", + "Interval for sending caps and TAG events in seconds (0 = disabled)", + 0, 3600, DEFAULT_CONFIG_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gstelement_class->change_state = gst_rtp_gst_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gst_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_gst_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP GStreamer payloader", "Codec/Payloader/Network/RTP", + "Payload GStreamer buffers as RTP packets", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_gst_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_gst_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_gst_pay_sink_event; + + GST_DEBUG_CATEGORY_INIT (gst_rtp_pay_debug, "rtpgstpay", 0, + "rtpgstpay element"); +} + +static void +gst_rtp_gst_pay_init (GstRtpGSTPay * rtpgstpay) +{ + rtpgstpay->adapter = gst_adapter_new (); + rtpgstpay->pending_buffers = NULL; + gst_rtp_base_payload_set_options (GST_RTP_BASE_PAYLOAD (rtpgstpay), + "application", TRUE, "X-GST", 90000); + rtpgstpay->last_config = GST_CLOCK_TIME_NONE; + rtpgstpay->taglist = NULL; + rtpgstpay->config_interval = DEFAULT_CONFIG_INTERVAL; +} + +static void +gst_rtp_gst_pay_reset (GstRtpGSTPay * rtpgstpay, gboolean full) +{ + rtpgstpay->last_config = GST_CLOCK_TIME_NONE; + gst_adapter_clear (rtpgstpay->adapter); + rtpgstpay->flags &= 0x70; + rtpgstpay->etype = 0; + if (rtpgstpay->pending_buffers) + g_list_free_full (rtpgstpay->pending_buffers, + (GDestroyNotify) gst_buffer_list_unref); + rtpgstpay->pending_buffers = NULL; + if (full) { + if (rtpgstpay->taglist) + gst_tag_list_unref (rtpgstpay->taglist); + rtpgstpay->taglist = NULL; + if (rtpgstpay->stream_id) + g_free (rtpgstpay->stream_id); + rtpgstpay->stream_id = NULL; + rtpgstpay->current_CV = 0; + rtpgstpay->next_CV = 0; + } +} + +static void +gst_rtp_gst_pay_finalize (GObject * obj) +{ + GstRtpGSTPay *rtpgstpay; + + rtpgstpay = GST_RTP_GST_PAY (obj); + + gst_rtp_gst_pay_reset (rtpgstpay, TRUE); + + g_object_unref (rtpgstpay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (obj); +} + +static void +gst_rtp_gst_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpGSTPay *rtpgstpay; + + rtpgstpay = GST_RTP_GST_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + rtpgstpay->config_interval = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_gst_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpGSTPay *rtpgstpay; + + rtpgstpay = GST_RTP_GST_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + g_value_set_uint (value, rtpgstpay->config_interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_rtp_gst_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpGSTPay *rtpgstpay; + GstStateChangeReturn ret; + + rtpgstpay = GST_RTP_GST_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_gst_pay_reset (rtpgstpay, TRUE); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_gst_pay_reset (rtpgstpay, TRUE); + break; + default: + break; + } + return ret; +} + +#define RTP_HEADER_LEN 12 + +static gboolean +gst_rtp_gst_pay_create_from_adapter (GstRtpGSTPay * rtpgstpay, + GstClockTime timestamp) +{ + guint avail, mtu; + guint frag_offset; + GstBufferList *list; + + avail = gst_adapter_available (rtpgstpay->adapter); + if (avail == 0) + return FALSE; + + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpgstpay); + + list = gst_buffer_list_new_sized ((avail / (mtu - (RTP_HEADER_LEN + 8))) + 1); + frag_offset = 0; + + while (avail) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + GstBuffer *paybuf; + + + /* this will be the total lenght of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (8 + avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer to hold the header */ + outbuf = gst_rtp_buffer_new_allocate (8, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + GST_DEBUG_OBJECT (rtpgstpay, "new packet len %u, frag %u", packet_len, + frag_offset); + + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| CV |D|0|0|0| ETYPE | MBZ | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + payload[0] = rtpgstpay->flags; + payload[1] = rtpgstpay->etype; + payload[2] = payload[3] = 0; + payload[4] = frag_offset >> 24; + payload[5] = frag_offset >> 16; + payload[6] = frag_offset >> 8; + payload[7] = frag_offset & 0xff; + + payload += 8; + payload_len -= 8; + + frag_offset += payload_len; + avail -= payload_len; + + if (avail == 0) + gst_rtp_buffer_set_marker (&rtp, TRUE); + + gst_rtp_buffer_unmap (&rtp); + + /* create a new buf to hold the payload */ + GST_DEBUG_OBJECT (rtpgstpay, "take %u bytes from adapter", payload_len); + paybuf = gst_adapter_take_buffer_fast (rtpgstpay->adapter, payload_len); + + /* create a new group to hold the rtp header and the payload */ + gst_buffer_append (outbuf, paybuf); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + /* and add to list */ + gst_buffer_list_insert (list, -1, outbuf); + } + + rtpgstpay->flags &= 0x70; + rtpgstpay->etype = 0; + rtpgstpay->pending_buffers = g_list_append (rtpgstpay->pending_buffers, list); + + return TRUE; +} + +static GstFlowReturn +gst_rtp_gst_pay_flush (GstRtpGSTPay * rtpgstpay, GstClockTime timestamp) +{ + GstFlowReturn ret = GST_FLOW_OK; + GList *iter; + + gst_rtp_gst_pay_create_from_adapter (rtpgstpay, timestamp); + + iter = rtpgstpay->pending_buffers; + while (iter) { + GstBufferList *list = iter->data; + + rtpgstpay->pending_buffers = iter = + g_list_delete_link (rtpgstpay->pending_buffers, iter); + + /* push the whole buffer list at once */ + ret = gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpgstpay), + list); + if (ret != GST_FLOW_OK) + break; + } + + return ret; +} + +static GstBuffer * +make_data_buffer (GstRtpGSTPay * rtpgstpay, gchar * data, guint size) +{ + guint plen; + guint8 *ptr; + GstBuffer *outbuf; + GstMapInfo map; + + /* calculate length */ + plen = 1; + while (size >> (7 * plen)) + plen++; + + outbuf = gst_buffer_new_allocate (NULL, plen + size, NULL); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + ptr = map.data; + + /* write length */ + while (plen) { + plen--; + *ptr++ = ((plen > 0) ? 0x80 : 0) | ((size >> (7 * plen)) & 0x7f); + } + /* copy data */ + memcpy (ptr, data, size); + gst_buffer_unmap (outbuf, &map); + + return outbuf; +} + +static void +gst_rtp_gst_pay_send_caps (GstRtpGSTPay * rtpgstpay, guint8 cv, GstCaps * caps) +{ + gchar *capsstr; + guint capslen; + GstBuffer *outbuf; + + if (rtpgstpay->flags & (1 << 7)) + return; + + capsstr = gst_caps_to_string (caps); + capslen = strlen (capsstr); + /* for 0 byte */ + capslen++; + + GST_DEBUG_OBJECT (rtpgstpay, "sending caps=%s", capsstr); + + /* make a data buffer of it */ + outbuf = make_data_buffer (rtpgstpay, capsstr, capslen); + g_free (capsstr); + + /* store in adapter, we don't flush yet, buffer might follow */ + rtpgstpay->flags = (1 << 7) | (cv << 4); + gst_adapter_push (rtpgstpay->adapter, outbuf); +} + +static gboolean +gst_rtp_gst_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpGSTPay *rtpgstpay; + gboolean res; + gchar *capsstr, *capsenc, *capsver; + guint capslen; + + rtpgstpay = GST_RTP_GST_PAY (payload); + + capsstr = gst_caps_to_string (caps); + capslen = strlen (capsstr); + + /* encode without 0 byte */ + capsenc = g_base64_encode ((guchar *) capsstr, capslen); + GST_DEBUG_OBJECT (payload, "caps=%s, caps(base64)=%s", capsstr, capsenc); + g_free (capsstr); + + /* Send the new caps */ + rtpgstpay->current_CV = rtpgstpay->next_CV; + rtpgstpay->next_CV = (rtpgstpay->next_CV + 1) & 0x7; + gst_rtp_gst_pay_send_caps (rtpgstpay, rtpgstpay->current_CV, caps); + + /* make caps for SDP */ + capsver = g_strdup_printf ("%d", rtpgstpay->current_CV); + res = + gst_rtp_base_payload_set_outcaps (payload, "caps", G_TYPE_STRING, capsenc, + "capsversion", G_TYPE_STRING, capsver, NULL); + g_free (capsenc); + g_free (capsver); + + return res; +} + +static void +gst_rtp_gst_pay_send_event (GstRtpGSTPay * rtpgstpay, guint etype, + GstEvent * event) +{ + const GstStructure *s; + gchar *estr; + guint elen; + GstBuffer *outbuf; + + /* Create the standalone caps packet if an inlined caps was pending */ + gst_rtp_gst_pay_create_from_adapter (rtpgstpay, GST_CLOCK_TIME_NONE); + + s = gst_event_get_structure (event); + + estr = gst_structure_to_string (s); + elen = strlen (estr); + /* for 0 byte */ + elen++; + outbuf = make_data_buffer (rtpgstpay, estr, elen); + GST_DEBUG_OBJECT (rtpgstpay, "sending event=%s", estr); + g_free (estr); + + rtpgstpay->etype = etype; + gst_adapter_push (rtpgstpay->adapter, outbuf); + /* Create the event packet now to avoid conflict with data/caps packets */ + gst_rtp_gst_pay_create_from_adapter (rtpgstpay, GST_CLOCK_TIME_NONE); +} + +static gboolean +gst_rtp_gst_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + gboolean ret; + GstRtpGSTPay *rtpgstpay; + guint etype = 0; + + rtpgstpay = GST_RTP_GST_PAY (payload); + + ret = + GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, + gst_event_ref (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_gst_pay_reset (rtpgstpay, FALSE); + break; + case GST_EVENT_TAG:{ + GstTagList *tags; + + gst_event_parse_tag (event, &tags); + + if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_STREAM) { + GstTagList *old; + + GST_DEBUG_OBJECT (rtpgstpay, "storing stream tags %" GST_PTR_FORMAT, + tags); + if ((old = rtpgstpay->taglist)) + gst_tag_list_unref (old); + rtpgstpay->taglist = gst_tag_list_ref (tags); + } + etype = 1; + break; + } + case GST_EVENT_CUSTOM_DOWNSTREAM: + etype = 2; + break; + case GST_EVENT_CUSTOM_BOTH: + etype = 3; + break; + case GST_EVENT_STREAM_START:{ + const gchar *stream_id = NULL; + + if (rtpgstpay->taglist) + gst_tag_list_unref (rtpgstpay->taglist); + rtpgstpay->taglist = NULL; + + gst_event_parse_stream_start (event, &stream_id); + if (stream_id) { + if (rtpgstpay->stream_id) + g_free (rtpgstpay->stream_id); + rtpgstpay->stream_id = g_strdup (stream_id); + } + etype = 4; + break; + } + default: + GST_LOG_OBJECT (rtpgstpay, "no event for %s", + GST_EVENT_TYPE_NAME (event)); + break; + } + if (etype) { + GST_DEBUG_OBJECT (rtpgstpay, "make event type %d for %s", + etype, GST_EVENT_TYPE_NAME (event)); + gst_rtp_gst_pay_send_event (rtpgstpay, etype, event); + /* Do not send stream-start right away since caps/new-segment were not yet + sent, so our data would be considered invalid */ + if (etype != 4) { + /* flush the adapter immediately */ + gst_rtp_gst_pay_flush (rtpgstpay, GST_CLOCK_TIME_NONE); + } + } + + gst_event_unref (event); + + return ret; +} + +static void +gst_rtp_gst_pay_send_config (GstRtpGSTPay * rtpgstpay, GstClockTime timestamp) +{ + GstPad *pad = GST_RTP_BASE_PAYLOAD_SINKPAD (rtpgstpay); + GstCaps *caps = NULL; + GstEvent *tag = NULL; + GstEvent *stream_start = NULL; + + GST_DEBUG_OBJECT (rtpgstpay, "time to send config"); + /* Send tags */ + if (rtpgstpay->taglist && !gst_tag_list_is_empty (rtpgstpay->taglist)) + tag = gst_event_new_tag (gst_tag_list_ref (rtpgstpay->taglist)); + if (tag) { + /* Send start-stream to clear tags */ + if (rtpgstpay->stream_id) + stream_start = gst_event_new_stream_start (rtpgstpay->stream_id); + if (stream_start) { + gst_rtp_gst_pay_send_event (rtpgstpay, 4, stream_start); + gst_event_unref (stream_start); + } + gst_rtp_gst_pay_send_event (rtpgstpay, 1, tag); + gst_event_unref (tag); + } + /* send caps */ + caps = gst_pad_get_current_caps (pad); + if (caps) { + gst_rtp_gst_pay_send_caps (rtpgstpay, rtpgstpay->current_CV, caps); + gst_caps_unref (caps); + } + rtpgstpay->last_config = timestamp; +} + +static GstFlowReturn +gst_rtp_gst_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstFlowReturn ret; + GstRtpGSTPay *rtpgstpay; + GstClockTime timestamp; + + rtpgstpay = GST_RTP_GST_PAY (basepayload); + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + /* check if we need to send the caps and taglist now */ + if (rtpgstpay->config_interval > 0) { + GST_DEBUG_OBJECT (rtpgstpay, + "timestamp %" GST_TIME_FORMAT ", last config %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpgstpay->last_config)); + + if (timestamp != GST_CLOCK_TIME_NONE && + rtpgstpay->last_config != GST_CLOCK_TIME_NONE) { + guint64 diff; + + /* calculate diff between last SPS/PPS in milliseconds */ + if (timestamp > rtpgstpay->last_config) + diff = timestamp - rtpgstpay->last_config; + else + diff = 0; + + GST_DEBUG_OBJECT (rtpgstpay, + "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); + + /* bigger than interval, queue SPS/PPS */ + if (GST_TIME_AS_SECONDS (diff) >= rtpgstpay->config_interval) + gst_rtp_gst_pay_send_config (rtpgstpay, timestamp); + } else { + gst_rtp_gst_pay_send_config (rtpgstpay, timestamp); + } + } + + /* caps always from SDP for now */ + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) + rtpgstpay->flags |= (1 << 3); + + gst_adapter_push (rtpgstpay->adapter, buffer); + ret = gst_rtp_gst_pay_flush (rtpgstpay, timestamp); + + return ret; +} + +gboolean +gst_rtp_gst_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpgstpay", + GST_RANK_NONE, GST_TYPE_RTP_GST_PAY); +} diff --git a/gst/rtp/gstrtpgstpay.h b/gst/rtp/gstrtpgstpay.h new file mode 100755 index 0000000..f3272b2 --- /dev/null +++ b/gst/rtp/gstrtpgstpay.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_GST_PAY_H__ +#define __GST_RTP_GST_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_GST_PAY \ + (gst_rtp_gst_pay_get_type()) +#define GST_RTP_GST_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_GST_PAY,GstRtpGSTPay)) +#define GST_RTP_GST_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_GST_PAY,GstRtpGSTPayClass)) +#define GST_IS_RTP_GST_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_GST_PAY)) +#define GST_IS_RTP_GST_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_GST_PAY)) + +typedef struct _GstRtpGSTPay GstRtpGSTPay; +typedef struct _GstRtpGSTPayClass GstRtpGSTPayClass; + +struct _GstRtpGSTPay +{ + GstRTPBasePayload payload; + + GList *pending_buffers; /* GstBufferList */ + GstAdapter *adapter; + guint8 flags; + guint8 etype; + + guint8 current_CV; /* CV field of incoming caps*/ + guint8 next_CV; + + gchar *stream_id; + GstTagList *taglist; + guint config_interval; + GstClockTime last_config; +}; + +struct _GstRtpGSTPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_gst_pay_get_type (void); + +gboolean gst_rtp_gst_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_GST_PAY_H__ */ diff --git a/gst/rtp/gstrtph263depay.c b/gst/rtp/gstrtph263depay.c new file mode 100755 index 0000000..a63b0b1 --- /dev/null +++ b/gst/rtp/gstrtph263depay.c @@ -0,0 +1,430 @@ +/* GStreamer + * + * Copyright 2007 Nokia Corporation + * Copyright 2007 Collabora Ltd, + * @author: Philippe Kalaf <philippe.kalaf@collabora.co.uk> + * + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * <2007> Edward Hervey <bilboed@bilboed.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtph263depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtph263depay_debug); +#define GST_CAT_DEFAULT (rtph263depay_debug) + +#define GST_RFC2190A_HEADER_LEN 4 +#define GST_RFC2190B_HEADER_LEN 8 +#define GST_RFC2190C_HEADER_LEN 12 + +static GstStaticPadTemplate gst_rtp_h263_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h263, " + "variant = (string) \"itu\", " "h263version = (string) \"h263\"") + ); + +static GstStaticPadTemplate gst_rtp_h263_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_H263_STRING ", " + "clock-rate = (int) 90000; " + /* optional SDP attribute: + * "a-framesize = (string) \"1234-1234\", " + */ + "application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263\"" + /* optional SDP attribute: + * "a-framesize = (string) \"1234-1234\", " + */ + ) + ); + +#define gst_rtp_h263_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH263Depay, gst_rtp_h263_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_h263_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_h263_depay_change_state (GstElement * + element, GstStateChange transition); + +static GstBuffer *gst_rtp_h263_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +gboolean gst_rtp_h263_depay_setcaps (GstRTPBaseDepayload * filter, + GstCaps * caps); + +static void +gst_rtp_h263_depay_class_init (GstRtpH263DepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtph263depay_debug, "rtph263depay", 0, + "H263 Video RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_h263_depay_finalize; + + gstelement_class->change_state = gst_rtp_h263_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP H263 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts H263 video from RTP packets (RFC 2190)", + "Philippe Kalaf <philippe.kalaf@collabora.co.uk>, " + "Edward Hervey <bilboed@bilboed.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_h263_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_h263_depay_setcaps; +} + +static void +gst_rtp_h263_depay_init (GstRtpH263Depay * rtph263depay) +{ + rtph263depay->adapter = gst_adapter_new (); + + rtph263depay->offset = 0; + rtph263depay->leftover = 0; +} + +static void +gst_rtp_h263_depay_finalize (GObject * object) +{ + GstRtpH263Depay *rtph263depay; + + rtph263depay = GST_RTP_H263_DEPAY (object); + + g_object_unref (rtph263depay->adapter); + rtph263depay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_h263_parse_framesize (GstRTPBaseDepayload * filter, + const gchar * media_attr, GstCaps * srccaps) +{ + gchar *dimension, *endptr; + gint width, height; + GstStructure *d; + + width = g_ascii_strtoull (media_attr, &endptr, 10); + if (width <= 0) { + GST_ERROR_OBJECT (filter, + "Framesize media attribute width out of valid range"); + return FALSE; + } else if (*endptr != '-') { + GST_ERROR_OBJECT (filter, + "Framesize media attribute has invalid dimension separator"); + return FALSE; + } + + dimension = endptr + 1; + height = g_ascii_strtoull (dimension, &endptr, 10); + if (height <= 0) { + GST_ERROR_OBJECT (filter, + "Framesize media attribute height out of valid range"); + return FALSE; + } else if (*endptr != '\0') { + GST_ERROR_OBJECT (filter, + "Framesize media attribute unexpectedly has trailing characters"); + return FALSE; + } + + d = gst_caps_get_structure (srccaps, 0); + gst_structure_set (d, "width", G_TYPE_INT, width, "height", G_TYPE_INT, + height, NULL); + + return TRUE; +} + +gboolean +gst_rtp_h263_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint clock_rate; + const gchar *framesize; + + srccaps = gst_caps_new_simple ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263", NULL); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + filter->clock_rate = clock_rate; + + framesize = gst_structure_get_string (structure, "a-framesize"); + if (framesize != NULL) { + if (!gst_rtp_h263_parse_framesize (filter, framesize, srccaps)) { + return FALSE; + } + } + + gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (filter), srccaps); + gst_caps_unref (srccaps); + + return TRUE; +} + +static GstBuffer * +gst_rtp_h263_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpH263Depay *rtph263depay; + GstBuffer *outbuf; + gint payload_len; + guint8 *payload; + guint header_len; + guint SBIT, EBIT; + gboolean F, P, M; + gboolean I; + GstRTPBuffer rtp = { NULL }; + + rtph263depay = GST_RTP_H263_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_LOG_OBJECT (depayload, "Discont buffer, flushing adapter"); + gst_adapter_clear (rtph263depay->adapter); + rtph263depay->offset = 0; + rtph263depay->leftover = 0; + rtph263depay->start = FALSE; + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + M = gst_rtp_buffer_get_marker (&rtp); + + /* Let's see what mode we are using */ + F = (payload[0] & 0x80) == 0x80; + P = (payload[0] & 0x40) == 0x40; + + /* Bit shifting */ + SBIT = (payload[0] & 0x38) >> 3; + EBIT = (payload[0] & 0x07); + + /* Figure out header length and I-flag */ + if (F == 0) { + /* F == 0 and P == 0 or 1 + * mode A */ + header_len = GST_RFC2190A_HEADER_LEN; + GST_LOG ("Mode A"); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F|P|SBIT |EBIT | SRC |I|U|S|A|R |DBQ| TRB | TR | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + I = (payload[1] & 0x10) == 0x10; + } else { + if (P == 0) { + /* F == 1 and P == 0 + * mode B */ + header_len = GST_RFC2190B_HEADER_LEN; + GST_LOG ("Mode B"); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + I = (payload[4] & 0x80) == 0x80; + } else { + /* F == 1 and P == 1 + * mode C */ + header_len = GST_RFC2190C_HEADER_LEN; + GST_LOG ("Mode C"); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F|P|SBIT |EBIT | SRC | QUANT | GOBN | MBA |R | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |I|U|S|A| HMV1 | VMV1 | HMV2 | VMV2 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RR |DBQ| TRB | TR | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + I = (payload[4] & 0x80) == 0x80; + } + } + + GST_LOG ("F/P/M/I : %d/%d/%d/%d", F, P, M, I); + GST_LOG ("SBIT : %d , EBIT : %d", SBIT, EBIT); + GST_LOG ("payload_len : %d, header_len : %d , leftover : 0x%x", + payload_len, header_len, rtph263depay->leftover); + + /* skip header */ + payload += header_len; + payload_len -= header_len; + + if (!rtph263depay->start) { + /* do not skip this fragment if it is a Mode A with picture start code */ + if (!F && payload_len > 4 && (GST_READ_UINT32_BE (payload) >> 10 == 0x20)) { + GST_DEBUG ("Mode A with PSC => frame start"); + rtph263depay->start = TRUE; + if ((! !(payload[4] & 0x02)) != I) { + GST_DEBUG ("Wrong Picture Coding Type Flag in rtp header"); + I = !I; + } + rtph263depay->psc_I = I; + } else { + GST_DEBUG ("no frame start yet, skipping payload"); + goto skip; + } + } + + /* only trust I info from Mode A starting packet + * from buggy payloaders or hw */ + I = rtph263depay->psc_I; + + if (SBIT) { + /* take the leftover and merge it at the beginning, FIXME make the buffer + * data writable. */ + GST_LOG ("payload[0] : 0x%x", payload[0]); + payload[0] &= 0xFF >> SBIT; + GST_LOG ("payload[0] : 0x%x", payload[0]); + payload[0] |= rtph263depay->leftover; + GST_LOG ("payload[0] : 0x%x", payload[0]); + rtph263depay->leftover = 0; + rtph263depay->offset = 0; + } + + if (!EBIT) { + GstBuffer *tmp; + + /* Take the entire buffer */ + tmp = gst_rtp_buffer_get_payload_subbuffer (&rtp, header_len, payload_len); + gst_adapter_push (rtph263depay->adapter, tmp); + } else { + GstBuffer *tmp; + + /* Take the entire buffer except for the last byte */ + tmp = gst_rtp_buffer_get_payload_subbuffer (&rtp, header_len, + payload_len - 1); + gst_adapter_push (rtph263depay->adapter, tmp); + + /* Put the last byte into the leftover */ + GST_DEBUG ("payload[payload_len - 1] : 0x%x", payload[payload_len - 1]); + GST_DEBUG ("mask : 0x%x", 0xFF << EBIT); + rtph263depay->leftover = (payload[payload_len - 1] >> EBIT) << EBIT; + rtph263depay->offset = 1; + GST_DEBUG ("leftover : 0x%x", rtph263depay->leftover); + } + +skip: + if (M) { + if (rtph263depay->start) { + /* frame is completed */ + guint avail; + + if (rtph263depay->offset) { + /* push in the leftover */ + GstBuffer *buf = gst_buffer_new_and_alloc (1); + + GST_DEBUG ("Pushing leftover in adapter"); + gst_buffer_fill (buf, 0, &rtph263depay->leftover, 1); + gst_adapter_push (rtph263depay->adapter, buf); + } + + avail = gst_adapter_available (rtph263depay->adapter); + outbuf = gst_adapter_take_buffer (rtph263depay->adapter, avail); + + if (I) + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + + GST_DEBUG ("Pushing out a buffer of %d bytes", avail); + + gst_rtp_base_depayload_push (depayload, outbuf); + rtph263depay->offset = 0; + rtph263depay->leftover = 0; + rtph263depay->start = FALSE; + } else { + rtph263depay->start = TRUE; + } + } + gst_rtp_buffer_unmap (&rtp); + + return NULL; +} + +static GstStateChangeReturn +gst_rtp_h263_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpH263Depay *rtph263depay; + GstStateChangeReturn ret; + + rtph263depay = GST_RTP_H263_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtph263depay->adapter); + rtph263depay->start = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_h263_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtph263depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H263_DEPAY); +} diff --git a/gst/rtp/gstrtph263depay.h b/gst/rtp/gstrtph263depay.h new file mode 100755 index 0000000..2d9ca55 --- /dev/null +++ b/gst/rtp/gstrtph263depay.h @@ -0,0 +1,66 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_H263_DEPAY_H__ +#define __GST_RTP_H263_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_H263_DEPAY \ + (gst_rtp_h263_depay_get_type()) +#define GST_RTP_H263_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H263_DEPAY,GstRtpH263Depay)) +#define GST_RTP_H263_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H263_DEPAY,GstRtpH263DepayClass)) +#define GST_IS_RTP_H263_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H263_DEPAY)) +#define GST_IS_RTP_H263_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H263_DEPAY)) + +typedef struct _GstRtpH263Depay GstRtpH263Depay; +typedef struct _GstRtpH263DepayClass GstRtpH263DepayClass; + +struct _GstRtpH263Depay +{ + GstRTPBaseDepayload depayload; + + guint8 offset; /* offset to apply to next payload */ + guint8 leftover; /* leftover from previous payload (if offset != 0) */ + gboolean psc_I; /* Picture-Coding-Type == I from Picture Start Code packet */ + GstAdapter *adapter; + gboolean start; +}; + +struct _GstRtpH263DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_h263_depay_get_type (void); + +gboolean gst_rtp_h263_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_H263_DEPAY_H__ */ + diff --git a/gst/rtp/gstrtph263pay.c b/gst/rtp/gstrtph263pay.c new file mode 100755 index 0000000..0b7d24e --- /dev/null +++ b/gst/rtp/gstrtph263pay.c @@ -0,0 +1,1838 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * Copyright (C) <2008> Dejan Sakelsak <dejan.sakelsak@marand.si> + * Copyright (C) <2009> Janin Kolenc <janin.kolenc@marand.si> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <math.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtph263pay.h" + +typedef enum +{ + GST_H263_FRAME_TYPE_I = 0, + GST_H263_FRAME_TYPE_P = 1, + GST_H263_FRAME_TYPE_PB = 2 +} GstRtpH263PayFrameType; + +typedef enum +{ + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_RES1 = 0, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_SQCIF = 1, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_QCIF = 2, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_CIF = 3, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_4CIF = 4, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_16CIF = 5, + GST_RTP_H263_PAYLOAD_PICTURE_FORMAT_RES2 = 6, + GST_H263_PAYLOAD_PICTURE_FORMAT_PLUS = 7 +} GstRtpH263PayPictureFormat; + +static const guint format_props[8][2] = { {254, 254}, +{6, 8}, +{9, 11}, +{18, 22}, +{18, 88}, +{18, 352}, +{254, 254}, +{255, 255} +}; + +/* + * I-frame MCBPC table: code, mask, nbits, cb, cr, mb type -> 10 = undefined (because we have guint) + */ +#define MCBPC_I_LEN 9 +#define MCBPC_I_WID 6 +static const guint32 mcbpc_I[9][6] = { + {0x8000, 0x8000, 1, 0, 0, 3}, + {0x2000, 0xe000, 3, 0, 1, 3}, + {0x4000, 0xe000, 3, 1, 0, 3}, + {0x6000, 0xe000, 3, 1, 1, 3}, + {0x1000, 0xf000, 4, 0, 0, 4}, + {0x0400, 0xfc00, 6, 0, 1, 4}, + {0x0800, 0xfc00, 6, 1, 0, 4}, + {0x0c00, 0xfc00, 6, 1, 1, 4}, + {0x0080, 0xff80, 9, 10, 10, 10} +}; + +/* + * P-frame MCBPC table: code, mask, nbits, cb, cr, mb type -> 10 = undefined (because we have guint) + */ +#define MCBPC_P_LEN 21 +#define MCBPC_P_WID 6 +static const guint16 mcbpc_P[21][6] = { + {0x8000, 0x8000, 1, 0, 0, 0}, + {0x3000, 0xf000, 4, 0, 1, 0}, + {0x2000, 0xf000, 4, 1, 0, 0}, + {0x1400, 0xfc00, 6, 1, 1, 0}, + {0x6000, 0xe000, 3, 0, 0, 1}, + {0x0e00, 0xfe00, 7, 0, 1, 1}, + {0x0c00, 0xfe00, 7, 1, 0, 1}, + {0x0280, 0xff80, 9, 1, 1, 1}, + {0x4000, 0xe000, 3, 0, 0, 2}, + {0x0a00, 0xfe00, 7, 0, 1, 2}, + {0x0800, 0xfe00, 7, 1, 0, 2}, + {0x0500, 0xff00, 8, 1, 1, 2}, + {0x1800, 0xf800, 5, 0, 0, 3}, + {0x0400, 0xff00, 8, 0, 1, 3}, + {0x0300, 0xff00, 8, 1, 0, 3}, + {0x0600, 0xfe00, 7, 1, 1, 3}, + {0x1000, 0xfc00, 6, 0, 0, 4}, + {0x0200, 0xff80, 9, 0, 1, 4}, + {0x0180, 0xff80, 9, 1, 0, 4}, + {0x0100, 0xff80, 9, 1, 1, 4}, + {0x0080, 0xff80, 9, 10, 10, 10} +}; + +/* + * I-frame CBPY (code, mask, nbits, Y0, Y1, Y2, Y3) + */ +#define CBPY_LEN 16 +#define CBPY_WID 7 +static const guint8 cbpy_I[16][7] = { + {0x30, 0xf0, 4, 0, 0, 0, 0}, + {0x28, 0xf8, 5, 0, 0, 0, 1}, + {0x20, 0xf8, 5, 0, 0, 1, 0}, + {0x90, 0xf0, 4, 0, 0, 1, 1}, + {0x18, 0xf8, 5, 0, 1, 0, 0}, + {0x70, 0xf0, 4, 0, 1, 0, 1}, + {0x08, 0xfc, 6, 0, 1, 1, 0}, + {0xb0, 0xf0, 4, 0, 1, 1, 1}, + {0x10, 0xf8, 5, 1, 0, 0, 0}, + {0x0c, 0xfc, 6, 1, 0, 0, 1}, + {0x50, 0xf0, 4, 1, 0, 1, 0}, + {0xa0, 0xf0, 4, 1, 0, 1, 1}, + {0x40, 0xf0, 4, 1, 1, 0, 0}, + {0x80, 0xf0, 4, 1, 1, 0, 1}, + {0x60, 0xf0, 4, 1, 1, 1, 0}, + {0xc0, 0xc0, 2, 1, 1, 1, 1} +}; + +/* + * P-frame CBPY (code, mask, nbits, Y0, Y1, Y2, Y3) + */ +static const guint8 cbpy_P[16][7] = { + {0x30, 0xf0, 4, 1, 1, 1, 1}, + {0x28, 0xf8, 5, 1, 1, 1, 0}, + {0x20, 0xf8, 5, 1, 1, 0, 1}, + {0x90, 0xf0, 4, 1, 1, 0, 0}, + {0x18, 0xf8, 5, 1, 0, 1, 1}, + {0x70, 0xf0, 4, 1, 0, 1, 0}, + {0x08, 0xfc, 6, 1, 0, 0, 1}, + {0xb0, 0xf0, 4, 1, 0, 0, 0}, + {0x10, 0xf8, 5, 0, 1, 1, 1}, + {0x0c, 0xfc, 6, 0, 1, 1, 0}, + {0x50, 0xf0, 4, 0, 1, 0, 1}, + {0xa0, 0xf0, 4, 0, 1, 0, 0}, + {0x40, 0xf0, 4, 0, 0, 1, 1}, + {0x80, 0xf0, 4, 0, 0, 1, 0}, + {0x60, 0xf0, 4, 0, 0, 0, 1}, + {0xc0, 0xc0, 2, 0, 0, 0, 0} +}; + +/* + * Block TCOEF table (code, mask, nbits, LAST, RUN, LEVEL) + */ +#define TCOEF_LEN 103 +#define TCOEF_WID 6 +static const guint16 tcoef[103][6] = { + {0x8000, 0xc000, 3, 0, 0, 1}, + {0xf000, 0xf000, 5, 0, 0, 2}, + {0x5400, 0xfc00, 7, 0, 0, 3}, + {0x2e00, 0xfe00, 8, 0, 0, 4}, + {0x1f00, 0xff00, 9, 0, 0, 5}, + {0x1280, 0xff80, 10, 0, 0, 6}, + {0x1200, 0xff80, 10, 0, 0, 7}, + {0x0840, 0xffc0, 11, 0, 0, 8}, + {0x0800, 0xffc0, 11, 0, 0, 9}, + {0x00e0, 0xffe0, 12, 0, 0, 10}, //10 + {0x00c0, 0xffe0, 12, 0, 0, 11}, + {0x0400, 0xffe0, 12, 0, 0, 12}, + {0xc000, 0xe000, 4, 0, 1, 1}, + {0x5000, 0xfc00, 7, 0, 1, 2}, + {0x1e00, 0xff00, 9, 0, 1, 3}, + {0x03c0, 0xffc0, 11, 0, 1, 4}, + {0x0420, 0xffe0, 12, 0, 1, 5}, + {0x0500, 0xfff0, 13, 0, 1, 6}, + {0xe000, 0xf000, 5, 0, 2, 1}, + {0x1d00, 0xff00, 9, 0, 2, 2}, //20 + {0x0380, 0xffc0, 11, 0, 2, 3}, + {0x0510, 0xfff0, 13, 0, 2, 4}, + {0x6800, 0xf800, 6, 0, 3, 1}, + {0x1180, 0xff80, 10, 0, 3, 2}, + {0x0340, 0xffc0, 11, 0, 3, 3}, + {0x6000, 0xf800, 6, 0, 4, 1}, + {0x1100, 0xff80, 10, 0, 4, 2}, + {0x0520, 0xfff0, 13, 0, 4, 3}, + {0x5800, 0xf800, 6, 0, 5, 1}, + {0x0300, 0xffc0, 11, 0, 5, 2}, // 30 + {0x0530, 0xfff0, 13, 0, 5, 3}, + {0x4c00, 0xfc00, 7, 0, 6, 1}, + {0x02c0, 0xffc0, 11, 0, 6, 2}, + {0x0540, 0xfff0, 13, 0, 6, 3}, + {0x4800, 0xfc00, 7, 0, 7, 1}, + {0x0280, 0xffc0, 11, 0, 7, 2}, + {0x4400, 0xfc00, 7, 0, 8, 1}, + {0x0240, 0xffc0, 11, 0, 8, 2}, + {0x4000, 0xfc00, 7, 0, 9, 1}, + {0x0200, 0xffc0, 11, 0, 9, 2}, // 40 + {0x2c00, 0xfe00, 8, 0, 10, 1}, + {0x0550, 0xfff0, 13, 0, 10, 2}, + {0x2a00, 0xfe00, 8, 0, 11, 1}, + {0x2800, 0xfe00, 8, 0, 12, 1}, + {0x1c00, 0xff00, 9, 0, 13, 1}, + {0x1b00, 0xff00, 9, 0, 14, 1}, + {0x1080, 0xff80, 10, 0, 15, 1}, + {0x1000, 0xff80, 10, 0, 16, 1}, + {0x0f80, 0xff80, 10, 0, 17, 1}, + {0x0f00, 0xff80, 10, 0, 18, 1}, // 50 + {0x0e80, 0xff80, 10, 0, 19, 1}, + {0x0e00, 0xff80, 10, 0, 20, 1}, + {0x0d80, 0xff80, 10, 0, 21, 1}, + {0x0d00, 0xff80, 10, 0, 22, 1}, + {0x0440, 0xffe0, 12, 0, 23, 1}, + {0x0460, 0xffe0, 12, 0, 24, 1}, + {0x0560, 0xfff0, 13, 0, 25, 1}, + {0x0570, 0xfff0, 13, 0, 26, 1}, + {0x7000, 0xf000, 5, 1, 0, 1}, + {0x0c80, 0xff80, 10, 1, 0, 2}, // 60 + {0x00a0, 0xffe0, 12, 1, 0, 3}, + {0x3c00, 0xfc00, 7, 1, 1, 1}, + {0x0080, 0xffe0, 12, 1, 1, 2}, + {0x3800, 0xfc00, 7, 1, 2, 1}, + {0x3400, 0xfc00, 7, 1, 3, 1}, + {0x3000, 0xfc00, 7, 1, 4, 1}, + {0x2600, 0xfe00, 8, 1, 5, 1}, + {0x2400, 0xfe00, 8, 1, 6, 1}, + {0x2200, 0xfe00, 8, 1, 7, 1}, + {0x2000, 0xfe00, 8, 1, 8, 1}, // 70 + {0x1a00, 0xff00, 9, 1, 9, 1}, + {0x1900, 0xff00, 9, 1, 10, 1}, + {0x1800, 0xff00, 9, 1, 11, 1}, + {0x1700, 0xff00, 9, 1, 12, 1}, + {0x1600, 0xff00, 9, 1, 13, 1}, + {0x1500, 0xff00, 9, 1, 14, 1}, + {0x1400, 0xff00, 9, 1, 15, 1}, + {0x1300, 0xff00, 9, 1, 16, 1}, + {0x0c00, 0xff80, 10, 1, 17, 1}, + {0x0b80, 0xff80, 10, 1, 18, 1}, // 80 + {0x0b00, 0xff80, 10, 1, 19, 1}, + {0x0a80, 0xff80, 10, 1, 20, 1}, + {0x0a00, 0xff80, 10, 1, 21, 1}, + {0x0980, 0xff80, 10, 1, 22, 1}, + {0x0900, 0xff80, 10, 1, 23, 1}, + {0x0880, 0xff80, 10, 1, 24, 1}, + {0x01c0, 0xffc0, 11, 1, 25, 1}, + {0x0180, 0xffc0, 11, 1, 26, 1}, + {0x0140, 0xffc0, 11, 1, 27, 1}, + {0x0100, 0xffc0, 11, 1, 28, 1}, // 90 + {0x0480, 0xffe0, 12, 1, 29, 1}, + {0x04a0, 0xffe0, 12, 1, 30, 1}, + {0x04c0, 0xffe0, 12, 1, 31, 1}, + {0x04e0, 0xffe0, 12, 1, 32, 1}, + {0x0580, 0xfff0, 13, 1, 33, 1}, + {0x0590, 0xfff0, 13, 1, 34, 1}, + {0x05a0, 0xfff0, 13, 1, 35, 1}, + {0x05b0, 0xfff0, 13, 1, 36, 1}, + {0x05c0, 0xfff0, 13, 1, 37, 1}, + {0x05d0, 0xfff0, 13, 1, 38, 1}, // 100 + {0x05e0, 0xfff0, 13, 1, 39, 1}, + {0x05f0, 0xfff0, 13, 1, 40, 1}, + {0x0600, 0xfe00, 7, 0, 0xffff, 0xffff} +}; + +/* + * Motion vector code table (Code, mask, nbits, vector (halfpixel, two's complement), diff (halfpixel, two's complement)) + */ +#define MVD_LEN 64 +#define MVD_WID 5 +static const guint16 mvd[64][5] = { + {0x0028, 0xfff8, 13, 0x0060, 0x0020}, + {0x0038, 0xfff8, 13, 0x0061, 0x0021}, + {0x0050, 0xfff0, 12, 0x0062, 0x0022}, + {0x0070, 0xfff0, 12, 0x0063, 0x0023}, + {0x0090, 0xfff0, 12, 0x0064, 0x0024}, + {0x00b0, 0xfff0, 12, 0x0065, 0x0025}, + {0x00d0, 0xfff0, 12, 0x0066, 0x0026}, + {0x00f0, 0xfff0, 12, 0x0067, 0x0027}, + {0x0120, 0xffe0, 11, 0x0068, 0x0028}, + {0x0160, 0xffe0, 11, 0x0069, 0x0029}, + {0x01a0, 0xffe0, 11, 0x006a, 0x002a}, + {0x01e0, 0xffe0, 11, 0x006b, 0x002b}, + {0x0220, 0xffe0, 11, 0x006c, 0x002c}, + {0x0260, 0xffe0, 11, 0x006d, 0x002d}, + {0x02a0, 0xffe0, 11, 0x006e, 0x002e}, + {0x02e0, 0xffe0, 11, 0x006f, 0x002f}, + {0x0320, 0xffe0, 11, 0x0070, 0x0030}, + {0x0360, 0xffe0, 11, 0x0071, 0x0031}, + {0x03a0, 0xffe0, 11, 0x0072, 0x0032}, + {0x03e0, 0xffe0, 11, 0x0073, 0x0033}, + {0x0420, 0xffe0, 11, 0x0074, 0x0034}, + {0x0460, 0xffe0, 11, 0x0075, 0x0035}, + {0x04c0, 0xffc0, 10, 0x0076, 0x0036}, + {0x0540, 0xffc0, 10, 0x0077, 0x0037}, + {0x05c0, 0xffc0, 10, 0x0078, 0x0038}, + {0x0700, 0xff00, 8, 0x0079, 0x0039}, + {0x0900, 0xff00, 8, 0x007a, 0x003a}, + {0x0b00, 0xff00, 8, 0x007b, 0x003b}, + {0x0e00, 0xfe00, 7, 0x007c, 0x003c}, + {0x1800, 0xf800, 5, 0x007d, 0x003d}, + {0x3000, 0xf000, 4, 0x007e, 0x003e}, + {0x6000, 0xe000, 3, 0x007f, 0x003f}, + {0x8000, 0x8000, 1, 0x0000, 0x0000}, + {0x4000, 0xe000, 3, 0x0001, 0x0041}, + {0x2000, 0xf000, 4, 0x0002, 0x0042}, + {0x1000, 0xf800, 5, 0x0003, 0x0043}, + {0x0c00, 0xfe00, 7, 0x0004, 0x0044}, + {0x0a00, 0xff00, 8, 0x0005, 0x0045}, + {0x0800, 0xff00, 8, 0x0006, 0x0046}, + {0x0600, 0xff00, 8, 0x0007, 0x0047}, + {0x0580, 0xffc0, 10, 0x0008, 0x0048}, + {0x0500, 0xffc0, 10, 0x0009, 0x0049}, + {0x0480, 0xffc0, 10, 0x000a, 0x004a}, + {0x0440, 0xffe0, 11, 0x000b, 0x004b}, + {0x0400, 0xffe0, 11, 0x000c, 0x004c}, + {0x03c0, 0xffe0, 11, 0x000d, 0x004d}, + {0x0380, 0xffe0, 11, 0x000e, 0x004e}, + {0x0340, 0xffe0, 11, 0x000f, 0x004f}, + {0x0300, 0xffe0, 11, 0x0010, 0x0050}, + {0x02c0, 0xffe0, 11, 0x0011, 0x0051}, + {0x0280, 0xffe0, 11, 0x0012, 0x0052}, + {0x0240, 0xffe0, 11, 0x0013, 0x0053}, + {0x0200, 0xffe0, 11, 0x0014, 0x0054}, + {0x01c0, 0xffe0, 11, 0x0015, 0x0055}, + {0x0180, 0xffe0, 11, 0x0016, 0x0056}, + {0x0140, 0xffe0, 11, 0x0017, 0x0057}, + {0x0100, 0xffe0, 11, 0x0018, 0x0058}, + {0x00e0, 0xfff0, 12, 0x0019, 0x0059}, + {0x00c0, 0xfff0, 12, 0x001a, 0x005a}, + {0x00a0, 0xfff0, 12, 0x001b, 0x005b}, + {0x0080, 0xfff0, 12, 0x001c, 0x005c}, + {0x0060, 0xfff0, 12, 0x001d, 0x005d}, + {0x0040, 0xfff0, 12, 0x001e, 0x005e}, + {0x0030, 0xfff8, 13, 0x001f, 0x005f} +}; + +GST_DEBUG_CATEGORY_STATIC (rtph263pay_debug); +#define GST_CAT_DEFAULT (rtph263pay_debug) + +#define GST_RTP_HEADER_LEN 12 + +enum +{ + PROP_0, + PROP_MODE_A_ONLY +}; + +static GstStaticPadTemplate gst_rtp_h263_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h263, " + "variant = (string) \"itu\", " "h263version = (string) \"h263\"") + ); + +static GstStaticPadTemplate gst_rtp_h263_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_H263_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263\"; " + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263\"") + ); + +static void gst_rtp_h263_pay_finalize (GObject * object); + +static gboolean gst_rtp_h263_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static void gst_rtp_h263_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_h263_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static GstFlowReturn gst_rtp_h263_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +static void gst_rtp_h263_pay_boundry_init (GstRtpH263PayBoundry * boundry, + guint8 * start, guint8 * end, guint8 sbit, guint8 ebit); +static GstRtpH263PayGob *gst_rtp_h263_pay_gob_new (GstRtpH263PayBoundry * + boundry, guint gobn); +static GstRtpH263PayMB *gst_rtp_h263_pay_mb_new (GstRtpH263PayBoundry * boundry, + guint mba); +static GstRtpH263PayPackage *gst_rtp_h263_pay_package_new_empty (); +static GstRtpH263PayPackage *gst_rtp_h263_pay_package_new (guint8 * start, + guint8 * end, guint length, guint8 sbit, guint8 ebit, GstBuffer * outbuf, + gboolean marker); + +static void gst_rtp_h263_pay_mb_destroy (GstRtpH263PayMB * mb); +static void gst_rtp_h263_pay_gob_destroy (GstRtpH263PayGob * gob, guint ind); +static void gst_rtp_h263_pay_context_destroy (GstRtpH263PayContext * context, + guint ind); +static void gst_rtp_h263_pay_package_destroy (GstRtpH263PayPackage * pack); + +#define gst_rtp_h263_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH263Pay, gst_rtp_h263_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_h263_pay_class_init (GstRtpH263PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_h263_pay_finalize; + + gstrtpbasepayload_class->set_caps = gst_rtp_h263_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_h263_pay_handle_buffer; + gobject_class->set_property = gst_rtp_h263_pay_set_property; + gobject_class->get_property = gst_rtp_h263_pay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_MODE_A_ONLY, g_param_spec_boolean ("modea-only", + "Fragment packets in mode A Only", + "Disable packetization modes B and C", DEFAULT_MODE_A, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP H263 packet payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes H263 video in RTP packets (RFC 2190)", + "Neil Stratford <neils@vipadia.com>" + "Dejan Sakelsak <dejan.sakelsak@marand.si>"); + + GST_DEBUG_CATEGORY_INIT (rtph263pay_debug, "rtph263pay", 0, + "H263 RTP Payloader"); +} + +static void +gst_rtp_h263_pay_init (GstRtpH263Pay * rtph263pay) +{ + rtph263pay->adapter = gst_adapter_new (); + + rtph263pay->prop_payload_mode = DEFAULT_MODE_A; +} + +static void +gst_rtp_h263_pay_finalize (GObject * object) +{ + GstRtpH263Pay *rtph263pay; + + rtph263pay = GST_RTP_H263_PAY (object); + + g_object_unref (rtph263pay->adapter); + rtph263pay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_h263_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstStructure *s = gst_caps_get_structure (caps, 0); + gint width, height; + gchar *framesize = NULL; + gboolean res; + + if (gst_structure_has_field (s, "width") && + gst_structure_has_field (s, "height")) { + if (!gst_structure_get_int (s, "width", &width) || width <= 0) { + goto invalid_dimension; + } + + if (!gst_structure_get_int (s, "height", &height) || height <= 0) { + goto invalid_dimension; + } + + framesize = g_strdup_printf ("%d-%d", width, height); + } + + payload->pt = GST_RTP_PAYLOAD_H263; + gst_rtp_base_payload_set_options (payload, "video", TRUE, "H263", 90000); + + if (framesize != NULL) { + res = gst_rtp_base_payload_set_outcaps (payload, + "a-framesize", G_TYPE_STRING, framesize, NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + } + g_free (framesize); + + return res; + + /* ERRORS */ +invalid_dimension: + { + GST_ERROR_OBJECT (payload, "Invalid width/height from caps"); + return FALSE; + } +} + +static void +gst_rtp_h263_pay_context_destroy (GstRtpH263PayContext * context, guint ind) +{ + if (!context) + return; + + if (context->gobs) { + guint i; + + for (i = 0; i < format_props[ind][0]; i++) { + if (context->gobs[i]) { + gst_rtp_h263_pay_gob_destroy (context->gobs[i], ind); + } + } + + g_free (context->gobs); + } + + g_free (context); +} + +static void +gst_rtp_h263_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpH263Pay *rtph263pay; + + rtph263pay = GST_RTP_H263_PAY (object); + + switch (prop_id) { + case PROP_MODE_A_ONLY: + rtph263pay->prop_payload_mode = g_value_get_boolean (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_h263_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpH263Pay *rtph263pay; + + rtph263pay = GST_RTP_H263_PAY (object); + + switch (prop_id) { + case PROP_MODE_A_ONLY: + g_value_set_boolean (value, rtph263pay->prop_payload_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstRtpH263PayPackage * +gst_rtp_h263_pay_package_new_empty (void) +{ + return (GstRtpH263PayPackage *) g_malloc0 (sizeof (GstRtpH263PayPackage)); +} + +static GstRtpH263PayPackage * +gst_rtp_h263_pay_package_new (guint8 * start, guint8 * end, guint length, + guint8 sbit, guint8 ebit, GstBuffer * outbuf, gboolean marker) +{ + + GstRtpH263PayPackage *package; + + package = gst_rtp_h263_pay_package_new_empty (); + + package->payload_start = start; + package->payload_end = end; + package->payload_len = length; + package->sbit = sbit; + package->ebit = ebit; + package->outbuf = outbuf; + package->marker = marker; + + return package; +} + +static void +gst_rtp_h263_pay_package_destroy (GstRtpH263PayPackage * pack) +{ + if (pack) + g_free (pack); +} + +static void +gst_rtp_h263_pay_boundry_init (GstRtpH263PayBoundry * boundry, + guint8 * start, guint8 * end, guint8 sbit, guint8 ebit) +{ + boundry->start = start; + boundry->end = end; + boundry->sbit = sbit; + boundry->ebit = ebit; +} + +static void +gst_rtp_h263_pay_splat_header_A (guint8 * header, + GstRtpH263PayPackage * package, GstRtpH263PayPic * piclayer) +{ + + GstRtpH263PayAHeader *a_header; + + a_header = (GstRtpH263PayAHeader *) header; + + a_header->f = 0; + a_header->p = 0; + a_header->sbit = package->sbit; + a_header->ebit = package->ebit; + a_header->src = GST_H263_PICTURELAYER_PLSRC (piclayer); + a_header->i = GST_H263_PICTURELAYER_PLTYPE (piclayer); + a_header->u = GST_H263_PICTURELAYER_PLUMV (piclayer); + a_header->s = GST_H263_PICTURELAYER_PLSAC (piclayer); + a_header->a = GST_H263_PICTURELAYER_PLAP (piclayer); + a_header->r1 = 0; + a_header->r2 = 0; + a_header->dbq = 0; + a_header->trb = 0; + a_header->tr = 0; + +} + +static void +gst_rtp_h263_pay_splat_header_B (guint8 * header, + GstRtpH263PayPackage * package, GstRtpH263PayPic * piclayer) +{ + + GstRtpH263PayBHeader *b_header; + + b_header = (GstRtpH263PayBHeader *) header; + + b_header->f = 1; + b_header->p = 0; + b_header->sbit = package->sbit; + b_header->ebit = package->ebit; + b_header->src = GST_H263_PICTURELAYER_PLSRC (piclayer); + b_header->quant = package->quant; + b_header->gobn = package->gobn; + b_header->mba1 = package->mba >> 6; + b_header->mba2 = package->mba & 0x003f; + b_header->r = 0; + b_header->i = GST_H263_PICTURELAYER_PLTYPE (piclayer); + b_header->u = GST_H263_PICTURELAYER_PLUMV (piclayer); + b_header->s = GST_H263_PICTURELAYER_PLSAC (piclayer); + b_header->a = GST_H263_PICTURELAYER_PLAP (piclayer); + + b_header->hmv11 = 0; + b_header->hmv12 = 0; + b_header->vmv11 = 0; + b_header->vmv12 = 0; + b_header->hmv21 = 0; + b_header->hmv22 = 0; + b_header->vmv21 = 0; + + if (package->nmvd > 0) { + //mvd[0] + b_header->hmv11 = (package->mvd[0] & 0x7f) >> 3; + b_header->hmv12 = (package->mvd[0] & 0x07); + //mvd[1] + b_header->vmv11 = (package->mvd[1] & 0x07f) >> 2; + b_header->vmv12 = (package->mvd[1] & 0x03); + + if (package->nmvd == 8) { + //mvd[4] + b_header->hmv21 = (package->mvd[4] & 0x7f) >> 1; + b_header->hmv22 = (package->mvd[4] & 0x01); + //mvd[5] + b_header->vmv21 = (package->mvd[5] & 0x7f); + } + } + +} + +static gboolean +gst_rtp_h263_pay_gobfinder (GstRtpH263Pay * rtph263pay, + GstRtpH263PayBoundry * boundry) +{ + guint8 *current; + guint range; + guint i; + + current = boundry->end + 1; + range = (rtph263pay->data - current) + rtph263pay->available_data; + + + GST_DEBUG ("Searching for next GOB, data:%p, len:%u, payload_len:%p," + " current:%p, range:%u", rtph263pay->data, rtph263pay->available_data, + boundry->end + 1, current, range); + + /* If we are past the end, stop */ + if (current >= rtph263pay->data + rtph263pay->available_data) + return FALSE; + + for (i = 3; i < range - 3; i++) { + if ((current[i] == 0x0) && + (current[i + 1] == 0x0) && (current[i + 2] >> 7 == 0x1)) { + GST_LOG ("GOB end found at: %p start: %p len: %u", current + i - 1, + boundry->end + 1, (guint) (current + i - boundry->end + 2)); + gst_rtp_h263_pay_boundry_init (boundry, boundry->end + 1, + current + i - 1, 0, 0); + + return TRUE; + } + } + + GST_DEBUG ("Couldn't find any new GBSC in this frame, range:%u", range); + + gst_rtp_h263_pay_boundry_init (boundry, boundry->end + 1, + (guint8 *) (rtph263pay->data + rtph263pay->available_data - 1), 0, 0); + + return TRUE; +} + +static GstRtpH263PayGob * +gst_rtp_h263_pay_gob_new (GstRtpH263PayBoundry * boundry, guint gobn) +{ + GstRtpH263PayGob *gob; + + gob = (GstRtpH263PayGob *) g_malloc0 (sizeof (GstRtpH263PayGob)); + + gob->start = boundry->start; + gob->end = boundry->end; + gob->length = boundry->end - boundry->start + 1; + gob->ebit = boundry->ebit; + gob->sbit = boundry->sbit; + gob->gobn = gobn; + gob->quant = 0; + gob->macroblocks = NULL; + gob->nmacroblocs = 0; + + return gob; +} + +static void +gst_rtp_h263_pay_gob_destroy (GstRtpH263PayGob * gob, guint ind) +{ + + if (!gob) + return; + + if (gob->macroblocks) { + + guint i; + + for (i = 0; i < gob->nmacroblocs; i++) { + gst_rtp_h263_pay_mb_destroy (gob->macroblocks[i]); + } + + g_free (gob->macroblocks); + } + + g_free (gob); +} + +/* + * decode MCBPC for I frames and return index in table or -1 if not found + */ +static gint +gst_rtp_h263_pay_decode_mcbpc_I (guint32 value) +{ + + gint i; + guint16 code; + + code = value >> 16; + + GST_DEBUG ("value:0x%08x, code:0x%04x", value, code); + + for (i = 0; i < MCBPC_I_LEN; i++) { + if ((code & mcbpc_I[i][1]) == mcbpc_I[i][0]) { + return i; + } + } + + GST_WARNING ("Couldn't find code, returning -1"); + + return -1; +} + +/* + * decode MCBPC for P frames and return index in table or -1 if not found + */ +static gint +gst_rtp_h263_pay_decode_mcbpc_P (guint32 value) +{ + + gint i; + guint16 code; + + code = value >> 16; + + GST_DEBUG ("value:0x%08x, code:0x%04x", value, code); + + for (i = 0; i < MCBPC_P_LEN; i++) { + if ((code & mcbpc_P[i][1]) == mcbpc_P[i][0]) { + return i; + } + } + + GST_WARNING ("Couldn't find code, returning -1"); + + return -1; +} + +/* + * decode CBPY and return index in table or -1 if not found + */ +static gint +gst_rtp_h263_pay_decode_cbpy (guint32 value, const guint8 cbpy_table[16][7]) +{ + + gint i; + guint8 code; + + code = value >> 24; + + GST_DEBUG ("value:0x%08x, code:0x%04x", value, code); + + for (i = 0; i < CBPY_LEN; i++) { + if ((code & cbpy_table[i][1]) == cbpy_table[i][0]) { + return i; + } + } + + GST_WARNING ("Couldn't find code, returning -1"); + + return -1; +} + +/* + * decode MVD and return index in table or -1 if not found + */ +static gint +gst_rtp_h263_pay_decode_mvd (guint32 value) +{ + + gint i; + guint16 code; + + code = value >> 16; + + GST_DEBUG ("value:0x%08x, code:0x%04x", value, code); + + for (i = 0; i < MVD_LEN; i++) { + if ((code & mvd[i][1]) == mvd[i][0]) { + return i; + } + } + + GST_WARNING ("Couldn't find code, returning -1"); + + return -1; +} + +/* + * decode TCOEF and return index in table or -1 if not found + */ +static gint +gst_rtp_h263_pay_decode_tcoef (guint32 value) +{ + + gint i; + guint16 code; + + code = value >> 16; + + GST_DEBUG ("value:0x%08x, code:0x%04x", value, code); + + for (i = 0; i < TCOEF_LEN; i++) { + if ((code & tcoef[i][1]) == tcoef[i][0]) { + GST_LOG ("tcoef is %d", i); + return i; + } + } + + GST_WARNING ("Couldn't find code, returning -1"); + + return -1; +} + +/* + * the 32-bit register is like a window that we move right for "move_bits" to get the next v "data" h263 field + * "rest_bits" tells how many bits in the "data" byte address are still not used + */ + +static gint +gst_rtp_h263_pay_move_window_right (GstRtpH263PayContext * context, guint n, + guint rest_bits, guint8 ** orig_data, guint8 ** data_end) +{ + + GST_DEBUG ("Moving window: 0x%08x from: %p for %d bits, rest_bits: %d", + context->window, context->win_end, n, rest_bits); + + if (n == 0) + return rest_bits; + + while (n != 0 || context->win_end == ((*data_end) + 1)) { + //guint8 a = *data; + if (rest_bits == 0) { + if (n > 8) { + context->window = (context->window << 8) | *context->win_end; + n -= 8; + } else { + context->window = + (context->window << n) | (*context->win_end >> (8 - n)); + rest_bits = 8 - n; + if (rest_bits == 0) + context->win_end++; + break; + } + } else { + if (n > rest_bits) { + context->window = + (context->window << rest_bits) | (*context-> + win_end & (((guint) pow (2.0, (double) rest_bits)) - 1)); + n -= rest_bits; + rest_bits = 0; + } else { + context->window = + (context->window << n) | ((*context->win_end & (((guint) pow (2.0, + (double) rest_bits)) - 1)) >> (rest_bits - n)); + rest_bits -= n; + if (rest_bits == 0) + context->win_end++; + break; + } + } + + context->win_end++; + } + + *orig_data = context->win_end - 4; + + GST_DEBUG + ("Window moved to %p with value: 0x%08x and orig_data: %p rest_bits: %d", + context->win_end, context->window, *orig_data, rest_bits); + return rest_bits; +} + +/* + * Find the start of the next MB (end of the current MB) + * returns the number of excess bits and stores the end of the MB in end + * data must be placed on first MB byte + */ +static GstRtpH263PayMB * +gst_rtp_h263_pay_B_mbfinder (GstRtpH263PayContext * context, + GstRtpH263PayGob * gob, GstRtpH263PayMB * macroblock, guint mba) +{ + + guint mb_type_index; + guint cbpy_type_index; + guint tcoef_type_index; + GstRtpH263PayMB *mac; + GstRtpH263PayBoundry boundry; + + + gst_rtp_h263_pay_boundry_init (&boundry, macroblock->end, + macroblock->end, 8 - macroblock->ebit, macroblock->ebit); + mac = gst_rtp_h263_pay_mb_new (&boundry, mba); + + + if (mac->sbit == 8) { + mac->start++; +// mac->end++; + mac->sbit = 0; + } + + GST_LOG ("current_pos:%p, end:%p, rest_bits:%d, window:%x", + mac->start, mac->end, macroblock->ebit, context->window); + + GST_LOG ("Current pos after COD: %p", mac->end); + + if (context->piclayer->ptype_pictype == 0) { + //We have an I frame + gint i; + guint last; + guint ind; + + //Step 2 decode MCBPC I + mb_type_index = gst_rtp_h263_pay_decode_mcbpc_I (context->window); + + GST_DEBUG ("MCBPC index: %d", mb_type_index); + if (mb_type_index == -1) { + GST_ERROR ("MB index shouldn't be -1 in window: %08x", context->window); + goto beach; + } + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, mcbpc_I[mb_type_index][2], + mac->ebit, &mac->end, &gob->end); + + mac->mb_type = mcbpc_I[mb_type_index][5]; + + if (mb_type_index == 8) { + GST_LOG ("Stuffing skipping rest of MB header"); + return mac; + } + //Step 3 decode CBPY I + cbpy_type_index = gst_rtp_h263_pay_decode_cbpy (context->window, cbpy_I); + + GST_DEBUG ("CBPY index: %d", cbpy_type_index); + if (cbpy_type_index == -1) { + GST_ERROR ("CBPY index shouldn't be -1 in window: %08x", context->window); + goto beach; + } + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, cbpy_I[cbpy_type_index][2], + mac->ebit, &mac->end, &gob->end); + + //Step 4 decode rest of MB + //MB type 1 and 4 have DQUANT - we store it for packaging purposes + if (mcbpc_I[mb_type_index][5] == 4) { + GST_DEBUG ("Shifting DQUANT"); + + mac->quant = (context->window >> 30); + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 2, mac->ebit, &mac->end, + &gob->end); + } + //Step 5 go trough the blocks - decode DC and TCOEF + last = 0; + for (i = 0; i < N_BLOCKS; i++) { + + GST_DEBUG ("Decoding INTRADC and TCOEF, i:%d", i); + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 8, mac->ebit, &mac->end, + &gob->end); + + if (i > 3) { + ind = mcbpc_I[mb_type_index][i - 1]; + } else { + ind = cbpy_I[cbpy_type_index][i + 3]; + } + + if (ind == 1) { + while (last == 0) { + tcoef_type_index = gst_rtp_h263_pay_decode_tcoef (context->window); + + GST_DEBUG ("TCOEF index: %d", tcoef_type_index); + if (tcoef_type_index == -1) { + GST_ERROR ("TCOEF index shouldn't be -1 in window: %08x", + context->window); + goto beach; + } + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, + tcoef[tcoef_type_index][2], mac->ebit, &mac->end, &gob->end); + + last = tcoef[tcoef_type_index][3]; + if (tcoef_type_index == 102) { + if ((context->window & 0x80000000) == 0x80000000) + last = 1; + else + last = 0; + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 15, + mac->ebit, &mac->end, &gob->end); + } + } + last = 0; + } + } + + } else { + //We have a P frame + guint i; + guint last; + guint ind; + + //Step 1 check COD + GST_DEBUG ("Checking for COD"); + if ((context->window & 0x80000000) == 0x80000000) { + //The MB is not coded + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 1, mac->ebit, &mac->end, + &gob->end); + GST_DEBUG ("COOOOOOOOOOOD = 1"); + + return mac; + } else { + //The MB is coded + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 1, mac->ebit, &mac->end, + &gob->end); + } + + //Step 2 decode MCBPC P + mb_type_index = gst_rtp_h263_pay_decode_mcbpc_P (context->window); + + GST_DEBUG ("MCBPC index: %d", mb_type_index); + if (mb_type_index == -1) { + GST_ERROR ("MB index shouldn't be -1 in window: %08x", context->window); + goto beach; + } + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, mcbpc_P[mb_type_index][2], + mac->ebit, &mac->end, &gob->end); + + mac->mb_type = mcbpc_P[mb_type_index][5]; + + if (mb_type_index == 20) { + GST_LOG ("Stuffing skipping rest of MB header"); + return mac; + } + //Step 3 decode CBPY P + cbpy_type_index = gst_rtp_h263_pay_decode_cbpy (context->window, cbpy_P); + + GST_DEBUG ("CBPY index: %d", cbpy_type_index); + if (cbpy_type_index == -1) { + GST_ERROR ("CBPY index shouldn't be -1 in window: %08x", context->window); + goto beach; + } + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, cbpy_P[cbpy_type_index][2], + mac->ebit, &mac->end, &gob->end); + + //MB type 1 and 4 have DQUANT - we add it to MB object and jump over + if (mcbpc_P[mb_type_index][5] == 4 || mcbpc_P[mb_type_index][5] == 1) { + GST_DEBUG ("Shifting DQUANT"); + + mac->quant = context->window >> 30; + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 2, mac->ebit, &mac->end, + &gob->end); + } + //MB types < 3 have MVD1-4 + if (mcbpc_P[mb_type_index][5] < 3) { + + guint nmvd; + gint j; + + nmvd = 2; + if (mcbpc_P[mb_type_index][5] == 2) + nmvd = 8; + + for (j = 0; j < nmvd; j++) { + guint mvd_type; + + mvd_type = gst_rtp_h263_pay_decode_mvd (context->window); + + if (mvd_type == -1) { + GST_ERROR ("MVD1-4 index shouldn't be -1 in window: %08x", + context->window); + goto beach; + } + //set the MB mvd values + mac->mvd[j] = mvd[mvd_type][3]; + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, mvd[mvd_type][2], + mac->ebit, &mac->end, &gob->end); + } + + + } + //Step 5 go trough the blocks - decode DC and TCOEF + last = 0; + for (i = 0; i < N_BLOCKS; i++) { + + //if MB type 3 or 4 then INTRADC coef are present in blocks + if (mcbpc_P[mb_type_index][5] > 2) { + GST_DEBUG ("INTRADC coef: %d", i); + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 8, mac->ebit, + &mac->end, &gob->end); + } else { + GST_DEBUG ("INTRADC coef is not present"); + } + + //check if the block has TCOEF + if (i > 3) { + ind = mcbpc_P[mb_type_index][i - 1]; + } else { + if (mcbpc_P[mb_type_index][5] > 2) { + ind = cbpy_I[cbpy_type_index][i + 3]; + } else { + ind = cbpy_P[cbpy_type_index][i + 3]; + } + } + + if (ind == 1) { + while (last == 0) { + tcoef_type_index = gst_rtp_h263_pay_decode_tcoef (context->window); + + GST_DEBUG ("TCOEF index: %d", tcoef_type_index); + if (tcoef_type_index == -1) { + GST_ERROR ("TCOEF index shouldn't be -1 in window: %08x", + context->window); + goto beach; + } + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, + tcoef[tcoef_type_index][2], mac->ebit, &mac->end, &gob->end); + + last = tcoef[tcoef_type_index][3]; + if (tcoef_type_index == 102) { + if ((context->window & 0x80000000) == 0x80000000) + last = 1; + else + last = 0; + + mac->ebit = + gst_rtp_h263_pay_move_window_right (context, 15, + mac->ebit, &mac->end, &gob->end); + } + } + last = 0; + } + } + } + + mac->length = mac->end - mac->start + 1; + + return mac; + +beach: + gst_rtp_h263_pay_mb_destroy (mac); + return NULL; +} + +static GstRtpH263PayMB * +gst_rtp_h263_pay_mb_new (GstRtpH263PayBoundry * boundry, guint mba) +{ + GstRtpH263PayMB *mb; + gint i; + + mb = (GstRtpH263PayMB *) g_malloc0 (sizeof (GstRtpH263PayMB)); + + mb->start = boundry->start; + mb->end = boundry->end; + mb->length = boundry->end - boundry->start + 1; + mb->sbit = boundry->sbit; + mb->ebit = boundry->ebit; + mb->mba = mba; + + for (i = 0; i < 5; i++) + mb->mvd[i] = 0; + + return mb; +} + +static void +gst_rtp_h263_pay_mb_destroy (GstRtpH263PayMB * mb) +{ + if (!mb) + return; + + g_free (mb); +} + +static GstFlowReturn +gst_rtp_h263_pay_push (GstRtpH263Pay * rtph263pay, + GstRtpH263PayContext * context, GstRtpH263PayPackage * package) +{ + + /* + * Splat the payload header values + */ + guint8 *header; + guint8 *payload; + GstFlowReturn ret; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (package->outbuf, GST_MAP_WRITE, &rtp); + + header = gst_rtp_buffer_get_payload (&rtp); + payload = header + package->mode; + + switch (package->mode) { + case GST_RTP_H263_PAYLOAD_HEADER_MODE_A: + GST_LOG ("Pushing A packet"); + gst_rtp_h263_pay_splat_header_A (header, package, context->piclayer); + break; + case GST_RTP_H263_PAYLOAD_HEADER_MODE_B: + GST_LOG ("Pushing B packet"); + gst_rtp_h263_pay_splat_header_B (header, package, context->piclayer); + break; + case GST_RTP_H263_PAYLOAD_HEADER_MODE_C: + //gst_rtp_h263_pay_splat_header_C(header, package, context->piclayer); + //break; + default: + return GST_FLOW_ERROR; + } + + + /* + * Copy the payload data in the buffer + */ + GST_DEBUG ("Copying memory"); + memcpy (payload, (guint8 *) package->payload_start, package->payload_len); + + /* + * timestamp the buffer + */ + GST_BUFFER_TIMESTAMP (package->outbuf) = rtph263pay->first_ts; + + gst_rtp_buffer_set_marker (&rtp, package->marker); + if (package->marker) + GST_DEBUG ("Marker set!"); + + gst_rtp_buffer_unmap (&rtp); + + ret = + gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtph263pay), + package->outbuf); + GST_DEBUG ("Package pushed, returning"); + + gst_rtp_h263_pay_package_destroy (package); + + return ret; +} + +static GstFlowReturn +gst_rtp_h263_pay_A_fragment_push (GstRtpH263Pay * rtph263pay, + GstRtpH263PayContext * context, guint first, guint last) +{ + + GstRtpH263PayPackage *pack; + + pack = gst_rtp_h263_pay_package_new_empty (); + + pack->payload_start = context->gobs[first]->start; + pack->sbit = context->gobs[first]->sbit; + pack->ebit = context->gobs[last]->ebit; + pack->payload_len = + (context->gobs[last]->end - context->gobs[first]->start) + 1; + pack->marker = FALSE; + + if (last == context->no_gobs - 1) { + pack->marker = TRUE; + } + + pack->gobn = context->gobs[first]->gobn; + pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_A; + pack->outbuf = + gst_rtp_buffer_new_allocate (pack->payload_len + pack->mode, 0, 0); + + GST_DEBUG ("Sending len:%d data to push function", pack->payload_len); + + return gst_rtp_h263_pay_push (rtph263pay, context, pack); +} + +static GstFlowReturn +gst_rtp_h263_pay_B_fragment_push (GstRtpH263Pay * rtph263pay, + GstRtpH263PayContext * context, GstRtpH263PayGob * gob, guint first, + guint last, GstRtpH263PayBoundry * boundry) +{ + + GstRtpH263PayPackage *pack; + guint mv; + + pack = gst_rtp_h263_pay_package_new_empty (); + + pack->payload_start = gob->macroblocks[first]->start; + pack->sbit = gob->macroblocks[first]->sbit; + if (first == 0) { + pack->payload_start = boundry->start; + pack->sbit = boundry->sbit; + pack->quant = gob->quant; + } else { + pack->quant = gob->macroblocks[first]->quant; + } + pack->payload_end = gob->macroblocks[last]->end; + + pack->ebit = gob->macroblocks[last]->ebit; + pack->mba = gob->macroblocks[first]->mba; + pack->gobn = gob->gobn; + pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_B; + pack->nmvd = 0; + + if (gob->macroblocks[first]->mb_type < 3) { + if (gob->macroblocks[first]->mb_type == 2) + pack->nmvd = 8; + else if (gob->macroblocks[first]->mb_type < 2) + pack->nmvd = 2; + + for (mv = 0; mv < pack->nmvd; mv++) + pack->mvd[mv] = gob->macroblocks[first]->mvd[mv]; + } + + pack->marker = FALSE; + if (last == gob->nmacroblocs - 1) { + pack->ebit = 0; + } + + if ((format_props[context->piclayer->ptype_srcformat][0] - 1 == gob->gobn) + && (last == gob->nmacroblocs - 1)) { + pack->marker = TRUE; + } + + pack->payload_len = pack->payload_end - pack->payload_start + 1; + pack->outbuf = + gst_rtp_buffer_new_allocate (pack->payload_len + pack->mode, 0, 0); + + return gst_rtp_h263_pay_push (rtph263pay, context, pack); +} + + +static gboolean +gst_rtp_h263_pay_mode_B_fragment (GstRtpH263Pay * rtph263pay, + GstRtpH263PayContext * context, GstRtpH263PayGob * gob) +{ + + + /*---------- MODE B MODE FRAGMENTATION ----------*/ + GstRtpH263PayMB *mac = NULL; + guint max_payload_size; + GstRtpH263PayBoundry boundry; + guint mb; + guint8 ebit; + + guint first = 0; + guint payload_len; + + max_payload_size = + context->mtu - GST_RTP_H263_PAYLOAD_HEADER_MODE_B - GST_RTP_HEADER_LEN; + + gst_rtp_h263_pay_boundry_init (&boundry, gob->start, gob->start, gob->sbit, + 0); + + gob->macroblocks = + (GstRtpH263PayMB **) g_malloc0 (sizeof (GstRtpH263PayMB *) * + format_props[context->piclayer->ptype_srcformat][1]); + + GST_LOG ("GOB isn't PB frame, applying mode B"); + + //initializing window + context->win_end = boundry.end; + if (gst_rtp_h263_pay_move_window_right (context, 32, boundry.ebit, + &boundry.end, &gob->end) != 0) { + GST_ERROR + ("The rest of the bits should be 0, exiting, because something bad happend"); + gst_adapter_flush (rtph263pay->adapter, rtph263pay->available_data); + goto decode_error; + } + //The first GOB of a frame "has no" actual header - PICTURE header is his header + if (gob->gobn == 0) { + guint shift; + GST_LOG ("Initial GOB"); + shift = 43; + + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, shift, boundry.ebit, + &boundry.end, &gob->end); + + //We need PQUANT for mode B packages - so we store it + gob->quant = context->window >> 27; + + //if PCM == 1, then PSBI is present - header has 51 bits + //shift for PQUANT (5) and PCM (1) = 6 bits + shift = 6; + if (context->cpm == 1) + shift += 2; + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, shift, boundry.ebit, + &boundry.end, &gob->end); + + GST_DEBUG ("window: 0x%08x", context->window); + + //Shifting the PEI and PSPARE fields + while ((context->window & 0x80000000) == 0x80000000) { + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, 9, + boundry.ebit, &boundry.end, &gob->end); + GST_LOG ("window: 0x%x", context->window); + } + + //shift the last PEI field + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, 1, boundry.ebit, + &boundry.end, &gob->end); + + } else { + //skipping GOBs 24 header bits + 5 GQUANT + guint shift = 24; + + GST_LOG ("INTER GOB"); + + //if CPM == 1, there are 2 more bits in the header - GSBI header is 31 bits long + if (context->cpm == 1) + shift += 2; + + GST_LOG ("window: 0x%x", context->window); + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, shift, + boundry.ebit, &boundry.end, &gob->end); + + //We need GQUANT for mode B packages - so we store it + gob->quant = context->window >> 27; + + shift = 5; + boundry.ebit = + gst_rtp_h263_pay_move_window_right (context, shift, + boundry.ebit, &boundry.end, &gob->end); + + GST_LOG ("window: 0x%x", context->window); + } + + GST_DEBUG ("GQUANT IS: %08x", gob->quant); + + // We are on MB layer + + mac = gst_rtp_h263_pay_mb_new (&boundry, 0); + for (mb = 0; mb < format_props[context->piclayer->ptype_srcformat][1]; mb++) { + + GST_DEBUG ("================ START MB %d =================", mb); + + //Find next macroblock boundaries + ebit = mac->ebit; + if (!(mac = gst_rtp_h263_pay_B_mbfinder (context, gob, mac, mb))) { + + GST_DEBUG ("Error decoding MB - sbit: %d", 8 - ebit); + GST_ERROR ("Error decoding in GOB"); + + goto decode_error; + } + //If mb_type == stuffing, don't increment the mb address + if (mac->mb_type == 10) { + mb--; + continue; + } else { + gob->nmacroblocs++; + } + + gob->macroblocks[mb] = mac; + + if (mac->end >= gob->end) { + GST_LOG ("No more MBs in this GOB"); + if (!mac->ebit) { + mac->end--; + } + gob->end = mac->end; + break; + } + GST_DEBUG ("Found MB: mba: %d start: %p end: %p len: %d sbit: %d ebit: %d", + mac->mba, mac->start, mac->end, mac->length, mac->sbit, mac->ebit); + GST_DEBUG ("================ END MB %d =================", mb); + } + + mb = 0; + first = 0; + payload_len = boundry.end - boundry.start + 1; + GST_DEBUG ("------------------------- NEW PACKAGE ----------------------"); + while (mb < gob->nmacroblocs) { + if (payload_len + gob->macroblocks[mb]->length < max_payload_size) { + + //FIXME: payload_len is not the real length -> ignoring sbit/ebit + payload_len += gob->macroblocks[mb]->length; + mb++; + + } else { + //FIXME: we should include the last few bits of the GOB in the package - do we do that now? + //GST_DEBUG ("Pushing GOBS %d to %d because payload size is %d", first, + // first == mb - 1, payload_len); + + if (gst_rtp_h263_pay_B_fragment_push (rtph263pay, context, gob, first, + mb - 1, &boundry)) { + GST_ERROR ("Oooops, there was an error sending"); + goto decode_error; + } + + payload_len = 0; + first = mb; + GST_DEBUG + ("------------------------- END PACKAGE ----------------------"); + GST_DEBUG + ("------------------------- NEW PACKAGE ----------------------"); + } + } + + /* Push rest */ + GST_DEBUG ("Remainder first: %d, MB: %d", first, mb); + if (payload_len != 0) { + if (gst_rtp_h263_pay_B_fragment_push (rtph263pay, context, gob, first, + mb - 1, &boundry)) { + GST_ERROR ("Oooops, there was an error sending!"); + goto decode_error; + } + } + + /*---------- END OF MODE B FRAGMENTATION ----------*/ + + gst_rtp_h263_pay_mb_destroy (mac); + return TRUE; + +decode_error: + gst_rtp_h263_pay_mb_destroy (mac); + return FALSE; +} + +static GstFlowReturn +gst_rtp_h263_send_entire_frame (GstRtpH263Pay * rtph263pay, + GstRtpH263PayContext * context) +{ + GstRtpH263PayPackage *pack; + pack = + gst_rtp_h263_pay_package_new (rtph263pay->data, + rtph263pay->data + rtph263pay->available_data, + rtph263pay->available_data, 0, 0, NULL, TRUE); + pack->mode = GST_RTP_H263_PAYLOAD_HEADER_MODE_A; + + GST_DEBUG ("Available data: %d", rtph263pay->available_data); + + pack->outbuf = + gst_rtp_buffer_new_allocate (pack->payload_len + + GST_RTP_H263_PAYLOAD_HEADER_MODE_A, 0, 0); + + return gst_rtp_h263_pay_push (rtph263pay, context, pack); +} + +static GstFlowReturn +gst_rtp_h263_pay_flush (GstRtpH263Pay * rtph263pay) +{ + + /* + * FIXME: GSTUF bits are ignored right now, + * - not using EBIT/SBIT payload header fields in mode A fragmentation - ffmpeg doesn't need them, but others? + */ + + GstFlowReturn ret; + GstRtpH263PayContext *context; + gint i; + + ret = 0; + context = (GstRtpH263PayContext *) g_malloc0 (sizeof (GstRtpH263PayContext)); + context->mtu = + rtph263pay->payload.mtu - (MTU_SECURITY_OFFSET + GST_RTP_HEADER_LEN + + GST_RTP_H263_PAYLOAD_HEADER_MODE_C); + + GST_DEBUG ("MTU: %d", context->mtu); + rtph263pay->available_data = gst_adapter_available (rtph263pay->adapter); + if (rtph263pay->available_data == 0) { + ret = GST_FLOW_OK; + goto end; + } + + /* Get a pointer to all the data for the frame */ + rtph263pay->data = + (guint8 *) gst_adapter_map (rtph263pay->adapter, + rtph263pay->available_data); + + /* Picture header */ + context->piclayer = (GstRtpH263PayPic *) rtph263pay->data; + + if (context->piclayer->ptype_pictype == 0) + GST_DEBUG ("We got an I-frame"); + else + GST_DEBUG ("We got a P-frame"); + + context->cpm = rtph263pay->data[6] >> 7; + + GST_DEBUG ("CPM: %d", context->cpm); + + GST_DEBUG ("Payload length is: %d", rtph263pay->available_data); + + /* + * - MODE A - If normal, I and P frames, -> mode A + * - GOB layer fragmentation + * - MODE B - If normal, I and P frames, but GOBs > mtu + * - MB layer fragmentation + * - MODE C - For P frames with PB option, but GOBs > mtu + * - MB layer fragmentation + */ + if (rtph263pay->available_data + GST_RTP_H263_PAYLOAD_HEADER_MODE_A + + GST_RTP_HEADER_LEN < context->mtu) { + ret = gst_rtp_h263_send_entire_frame (rtph263pay, context); + } else { + + /*---------- FRAGMENTING THE FRAME BECAUSE TOO LARGE TO FIT IN MTU ----------*/ + GstRtpH263PayBoundry bound; + gint first; + guint payload_len; + gboolean forcea = FALSE; + + GST_DEBUG ("Frame too large for MTU"); + /* + * Let's go trough all the data and fragment it untill end is reached + */ + + gst_rtp_h263_pay_boundry_init (&bound, NULL, rtph263pay->data - 1, 0, 0); + context->gobs = + (GstRtpH263PayGob **) g_malloc0 (format_props[context->piclayer-> + ptype_srcformat][0] * sizeof (GstRtpH263PayGob *)); + + + for (i = 0; i < format_props[context->piclayer->ptype_srcformat][0]; i++) { + GST_DEBUG ("Searching for gob %d", i); + if (!gst_rtp_h263_pay_gobfinder (rtph263pay, &bound)) { + if (i <= 1) { + GST_WARNING + ("No GOB's were found in data stream! Please enable RTP mode in encoder. Forcing mode A for now."); + ret = gst_rtp_h263_send_entire_frame (rtph263pay, context); + goto end; + } else { + /* try to send fragments corresponding to found GOBs */ + forcea = TRUE; + break; + } + } + + context->gobs[i] = gst_rtp_h263_pay_gob_new (&bound, i); + //FIXME - encoders may generate an EOS gob that has to be processed + GST_DEBUG + ("Gob values are: gobn: %d, start: %p len: %d ebit: %d sbit: %d", i, + context->gobs[i]->start, context->gobs[i]->length, + context->gobs[i]->ebit, context->gobs[i]->sbit); + } + /* NOTE some places may still assume this to be the max possible */ + context->no_gobs = i; + GST_DEBUG ("Found %d GOBS of maximum %d", + context->no_gobs, format_props[context->piclayer->ptype_srcformat][0]); + + // Make packages smaller than MTU + // A mode + // - if ( GOB > MTU) -> B mode || C mode + // Push packages + + first = 0; + payload_len = 0; + i = 0; + while (i < context->no_gobs) { + + if (context->gobs[i]->length >= context->mtu) { + if (payload_len == 0) { + + GST_DEBUG ("GOB len > MTU"); + if (rtph263pay->prop_payload_mode || forcea) { + payload_len = context->gobs[i]->length; + goto force_a; + } + if (!context->piclayer->ptype_pbmode) { + GST_DEBUG ("MODE B on GOB %d needed", i); + if (!gst_rtp_h263_pay_mode_B_fragment (rtph263pay, context, + context->gobs[i])) { + GST_ERROR ("There was an error fragmenting in mode B"); + ret = GST_FLOW_ERROR; + goto end; + } + } else { + //IMPLEMENT C mode + GST_ERROR ("MODE C on GOB %d needed, but not supported yet", i); + /*if(!gst_rtp_h263_pay_mode_C_fragment(rtph263pay, context, context->gobs[i])) { + ret = GST_FLOW_OK; + GST_ERROR("There was an error fragmenting in mode C"); + goto decode_error; + } */ + goto decode_error; + } + decode_error: + i++; + first = i; + continue; + + } else { + goto payload_a_push; + } + } + + if (payload_len + context->gobs[i]->length < context->mtu) { + GST_DEBUG ("GOB %d fills mtu", i); + payload_len += context->gobs[i]->length; + i++; + if (i == context->no_gobs) { + GST_DEBUG ("LAST GOB %d", i); + goto payload_a_push; + } + + } else { + payload_a_push: + GST_DEBUG ("Pushing GOBS %d to %d because payload size is %d", first, + first == i ? i : i - 1, payload_len); + gst_rtp_h263_pay_A_fragment_push (rtph263pay, context, first, + first == i ? i : i - 1); + payload_len = 0; + first = i; + } + continue; + + force_a: + GST_DEBUG ("Pushing GOBS %d to %d because payload size is %d", first, i, + payload_len); + gst_rtp_h263_pay_A_fragment_push (rtph263pay, context, first, i); + payload_len = 0; + i++; + first = i; + } + + + }/*---------- END OF FRAGMENTATION ----------*/ + + /* Flush the input buffer data */ + +end: + gst_rtp_h263_pay_context_destroy (context, + context->piclayer->ptype_srcformat); + gst_adapter_unmap (rtph263pay->adapter); + gst_adapter_flush (rtph263pay->adapter, rtph263pay->available_data); + + return ret; +} + +static GstFlowReturn +gst_rtp_h263_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + + GstRtpH263Pay *rtph263pay; + GstFlowReturn ret; + + GST_DEBUG ("-------------------- NEW FRAME ---------------"); + rtph263pay = GST_RTP_H263_PAY (payload); + + rtph263pay->first_ts = GST_BUFFER_TIMESTAMP (buffer); + + /* we always encode and flush a full picture */ + gst_adapter_push (rtph263pay->adapter, buffer); + ret = gst_rtp_h263_pay_flush (rtph263pay); + GST_DEBUG ("-------------------- END FRAME ---------------"); + + return ret; +} + +gboolean +gst_rtp_h263_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtph263pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H263_PAY); +} diff --git a/gst/rtp/gstrtph263pay.h b/gst/rtp/gstrtph263pay.h new file mode 100755 index 0000000..d41c3ed --- /dev/null +++ b/gst/rtp/gstrtph263pay.h @@ -0,0 +1,414 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + * Author: Dejan Sakelsak sahel@kiberpipa.org + */ + +#ifndef __GST_RTP_H263_PAY_H__ +#define __GST_RTP_H263_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS +#define GST_TYPE_RTP_H263_PAY \ + (gst_rtp_h263_pay_get_type()) +#define GST_RTP_H263_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H263_PAY,GstRtpH263Pay)) +#define GST_RTP_H263_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H263_PAY,GstRtpH263PayClass)) +#define GST_IS_RTP_H263_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H263_PAY)) +#define GST_IS_RTP_H263_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H263_PAY)) +// blocks per macroblock +#define N_BLOCKS 6 +#define DEFAULT_MODE_A FALSE +#define MTU_SECURITY_OFFSET 50 + typedef enum _GstRtpH263PayHeaderMode +{ + GST_RTP_H263_PAYLOAD_HEADER_MODE_A = 4, + GST_RTP_H263_PAYLOAD_HEADER_MODE_B = 8, + GST_RTP_H263_PAYLOAD_HEADER_MODE_C = 12 +} GstRtpH263PayHeaderMode; + +typedef struct _GstRtpH263PayContext GstRtpH263PayContext; +typedef struct _GstRtpH263PayPic GstRtpH263PayPic; +typedef struct _GstRtpH263PayClass GstRtpH263PayClass; +typedef struct _GstRtpH263Pay GstRtpH263Pay; +typedef struct _GstRtpH263PayBoundry GstRtpH263PayBoundry; +typedef struct _GstRtpH263PayMB GstRtpH263PayMB; +typedef struct _GstRtpH263PayGob GstRtpH263PayGob; +typedef struct _GstRtpH263PayPackage GstRtpH263PayPackage; + +//typedef enum _GstRtpH263PayHeaderMode GstRtpH263PayHeaderMode; + +struct _GstRtpH263Pay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + gboolean prop_payload_mode; + guint8 *data; + guint available_data; + +}; + +struct _GstRtpH263PayContext +{ + GstRtpH263PayPic *piclayer; + + guint mtu; + guint window; + guint8 *win_end; + guint8 cpm; + + guint no_gobs; + GstRtpH263PayGob **gobs; + +}; + +struct _GstRtpH263PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +typedef struct _GstRtpH263PayAHeader +{ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + unsigned int ebit:3; /* End position */ + unsigned int sbit:3; /* Start position */ + unsigned int p:1; /* PB-frames mode */ + unsigned int f:1; /* flag bit */ + + unsigned int r1:1; /* Reserved */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int i:1; /* Picture coding type */ + unsigned int src:3; /* Source format */ + + unsigned int trb:3; /* Temporal ref for B frame */ + unsigned int dbq:2; /* Differential Quantisation parameter */ + unsigned int r2:3; /* Reserved */ +#elif G_BYTE_ORDER == G_BIG_ENDIAN + unsigned int f:1; /* flag bit */ + unsigned int p:1; /* PB-frames mode */ + unsigned int sbit:3; /* Start position */ + unsigned int ebit:3; /* End position */ + + unsigned int src:3; /* Source format */ + unsigned int i:1; /* Picture coding type */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int r1:1; /* Reserved */ + + unsigned int r2:3; /* Reserved */ + unsigned int dbq:2; /* Differential Quantisation parameter */ + unsigned int trb:3; /* Temporal ref for B frame */ +#else +#error "G_BYTE_ORDER should be big or little endian." +#endif + unsigned int tr:8; /* Temporal ref for P frame */ +} GstRtpH263PayAHeader; + +typedef struct _GstRtpH263PayBHeader +{ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + unsigned int ebit:3; /* End position */ + unsigned int sbit:3; /* Start position */ + unsigned int p:1; /* PB-frames mode */ + unsigned int f:1; /* flag bit */ + + unsigned int quant:5; /* Quantization value for first MB */ + unsigned int src:3; /* Source format */ + + unsigned int mba1:3; /* Address of first MB starting count from 0 - part1 */ + unsigned int gobn:5; /* GOB number in effect at start of packet */ + + unsigned int r:2; /* Reserved */ + unsigned int mba2:6; /* Address of first MB starting count from 0 - part2 */ + + unsigned int hmv11:4; /* horizontal motion vector predictor for MB 1 - part 1 */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int i:1; /* Picture coding type */ + + unsigned int vmv11:5; /* vertical motion vector predictor for MB 1 - part 1 */ + unsigned int hmv12:3; /* horizontal motion vector predictor for MB 1 - part 2 */ + + unsigned int hmv21:6; /* horizontal motion vector predictor for MB 3 - part 1 */ + unsigned int vmv12:2; /* vertical motion vector predictor for MB 1 - part 2 */ + + unsigned int vmv21:7; /* vertical motion vector predictor for MB 3 */ + unsigned int hmv22:1; /* horizontal motion vector predictor for MB 3 - part 2 */ + +#elif G_BYTE_ORDER == G_BIG_ENDIAN + unsigned int f:1; /* flag bit */ + unsigned int p:1; /* PB-frames mode */ + unsigned int sbit:3; /* Start position */ + unsigned int ebit:3; /* End position */ + + unsigned int src:3; /* Source format */ + unsigned int quant:5; /* Quantization value for first MB */ + + unsigned int gobn:5; /* GOB number in effect at start of packet */ + unsigned int mba1:3; /* Address of first MB starting count from 0 - part1 */ + + unsigned int mba2:6; /* Address of first MB starting count from 0 - part2 */ + unsigned int r:2; /* Reserved */ + + unsigned int i:1; /* Picture coding type */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int hmv11:4; /* horizontal motion vector predictor for MB 1 - part 1 */ + + unsigned int hmv12:3; /* horizontal motion vector predictor for MB 1 - part 2 */ + unsigned int vmv11:5; /* vertical motion vector predictor for MB 1 - part 1 */ + + unsigned int vmv12:2; /* vertical motion vector predictor for MB 1 - part 2 */ + unsigned int hmv21:6; /* horizontal motion vector predictor for MB 3 - part 1 */ + + unsigned int hmv22:1; /* horizontal motion vector predictor for MB 3 - part 2 */ + unsigned int vmv21:7; /* vertical motion vector predictor for MB 3 */ +#else +#error "G_BYTE_ORDER should be big or little endian." +#endif +} GstRtpH263PayBHeader; + +typedef struct _GstRtpH263PayCHeader +{ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + unsigned int ebit:3; /* End position */ + unsigned int sbit:3; /* Start position */ + unsigned int p:1; /* PB-frames mode */ + unsigned int f:1; /* flag bit */ + + unsigned int quant:5; /* Quantization value for first MB */ + unsigned int src:3; /* Source format */ + + unsigned int mba1:3; /* Address of first MB starting count from 0 - part1 */ + unsigned int gobn:5; /* GOB number in effect at start of packet */ + + unsigned int r:2; /* Reserved */ + unsigned int mba2:6; /* Address of first MB starting count from 0 - part2 */ + + unsigned int hmv11:4; /* horizontal motion vector predictor for MB 1 - part 1 */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int i:1; /* Picture coding type */ + + unsigned int vmv11:5; /* vertical motion vector predictor for MB 1 - part 1 */ + unsigned int hmv12:3; /* horizontal motion vector predictor for MB 1 - part 2 */ + + unsigned int hmv21:6; /* horizontal motion vector predictor for MB 3 - part 1 */ + unsigned int vmv12:2; /* vertical motion vector predictor for MB 1 - part 2 */ + + unsigned int vmv21:7; /* vertical motion vector predictor for MB 3 */ + unsigned int hmv22:1; /* horizontal motion vector predictor for MB 3 - part 2 */ + + unsigned int rr1:8; /* reserved */ + + unsigned int rr2:8; /* reserved */ + + unsigned int trb:3; /* Temporal Reference for the B */ + unsigned int dbq:2; /* Differential quantization parameter */ + unsigned int rr3:3; /* reserved */ + + unsigned int tr:8; /* Temporal Reference for the P frame */ + +#elif G_BYTE_ORDER == G_BIG_ENDIAN + unsigned int f:1; /* flag bit */ + unsigned int p:1; /* PB-frames mode */ + unsigned int sbit:3; /* Start position */ + unsigned int ebit:3; /* End position */ + + unsigned int src:3; /* Source format */ + unsigned int quant:5; /* Quantization value for first MB */ + + unsigned int gobn:5; /* GOB number in effect at start of packet */ + unsigned int mba1:3; /* Address of first MB starting count from 0 - part1 */ + + unsigned int mba2:6; /* Address of first MB starting count from 0 - part2 */ + unsigned int r:2; /* Reserved */ + + unsigned int i:1; /* Picture coding type */ + unsigned int u:1; /* Unrestricted motion vector */ + unsigned int s:1; /* syntax based arithmetic coding */ + unsigned int a:1; /* Advanced Prediction */ + unsigned int hmv11:4; /* horizontal motion vector predictor for MB 1 - part 1 */ + + unsigned int hmv12:3; /* horizontal motion vector predictor for MB 1 - part 2 */ + unsigned int vmv11:5; /* vertical motion vector predictor for MB 1 - part 1 */ + + unsigned int vmv12:2; /* vertical motion vector predictor for MB 1 - part 2 */ + unsigned int hmv21:6; /* horizontal motion vector predictor for MB 3 - part 1 */ + + unsigned int hmv22:1; /* horizontal motion vector predictor for MB 3 - part 2 */ + unsigned int vmv21:7; /* vertical motion vector predictor for MB 3 */ + unsigned int rr1:8; /* reserved */ + unsigned int rr2:8; /* reserved */ + + unsigned int rr3:3; /* reserved */ + unsigned int dbq:2; /* Differential quantization parameter */ + unsigned int trb:3; /* Temporal Reference for the B */ + + unsigned int tr:8; /* Temporal Reference for the P frame */ +#else +#error "G_BYTE_ORDER should be big or little endian." +#endif +} GstRtpH263PayCHeader; + +struct _GstRtpH263PayPic +{ +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + unsigned int psc1:16; + + unsigned int tr1:2; + unsigned int psc2:6; + + unsigned int ptype_263:1; + unsigned int ptype_start:1; + unsigned int tr2:6; + + unsigned int ptype_umvmode:1; + unsigned int ptype_pictype:1; + unsigned int ptype_srcformat:3; + unsigned int ptype_freeze:1; + unsigned int ptype_camera:1; + unsigned int ptype_split:1; + + unsigned int pquant:5; + unsigned int ptype_pbmode:1; + unsigned int ptype_apmode:1; + unsigned int ptype_sacmode:1; + +#elif G_BYTE_ORDER == G_BIG_ENDIAN + unsigned int psc1:16; + + unsigned int psc2:6; + unsigned int tr1:2; + + unsigned int tr2:6; + unsigned int ptype_start:2; + + unsigned int ptype_split:1; + unsigned int ptype_camera:1; + unsigned int ptype_freeze:1; + unsigned int ptype_srcformat:3; + unsigned int ptype_pictype:1; + unsigned int ptype_umvmode:1; + + unsigned int ptype_sacmode:1; + unsigned int ptype_apmode:1; + unsigned int ptype_pbmode:1; + unsigned int pquant:5; + +#else +#error "G_BYTE_ORDER should be big or little endian." +#endif +}; + +struct _GstRtpH263PayBoundry +{ + + guint8 *start; + guint8 *end; + guint8 sbit; + guint8 ebit; + +}; + +struct _GstRtpH263PayMB +{ + guint8 *start; + guint8 *end; + guint8 sbit; + guint8 ebit; + guint length; + + guint8 mb_type; + guint quant; + + guint mba; + guint8 mvd[10]; +}; + +struct _GstRtpH263PayGob +{ + guint8 *start; + guint8 *end; + guint length; + guint8 sbit; + guint8 ebit; + + guint gobn; + guint quant; + + GstRtpH263PayMB **macroblocks; + guint nmacroblocs; +}; + +struct _GstRtpH263PayPackage +{ + guint8 *payload_start; + guint8 *payload_end; + guint payload_len; + guint8 sbit; + guint8 ebit; + GstBuffer *outbuf; + gboolean marker; + + GstRtpH263PayHeaderMode mode; + + /* + * mode B,C data + */ + + guint16 mba; + guint nmvd; + guint8 mvd[10]; + guint gobn; + guint quant; +}; + +#define GST_H263_PICTURELAYER_PLSRC(buf) (((GstRtpH263PayPic *)(buf))->ptype_srcformat) +#define GST_H263_PICTURELAYER_PLTYPE(buf) (((GstRtpH263PayPic *)(buf))->ptype_pictype) +#define GST_H263_PICTURELAYER_PLUMV(buf) (((GstRtpH263PayPic *)(buf))->ptype_umvmode) +#define GST_H263_PICTURELAYER_PLSAC(buf) (((GstRtpH263PayPic *)(buf))->ptype_sacmode) +#define GST_H263_PICTURELAYER_PLAP(buf) (((GstRtpH263PayPic *)(buf))->ptype_apmode) + +/* + * TODO: PB frame relevant tables + */ + +#define GST_RTP_H263_PAY_END(start, len) (((guint8 *)start) + ((guint)len)) +#define GST_RTP_H263_PAY_GOBN(gob) (((((guint8 *) gob)[2] >> 2) & 0x1f) + +GType gst_rtp_h263_pay_get_type (void); + +gboolean gst_rtp_h263_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_H263_PAY_H__ */ diff --git a/gst/rtp/gstrtph263pdepay.c b/gst/rtp/gstrtph263pdepay.c new file mode 100755 index 0000000..e788e2f --- /dev/null +++ b/gst/rtp/gstrtph263pdepay.c @@ -0,0 +1,395 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtph263pdepay.h" + +static GstStaticPadTemplate gst_rtp_h263p_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h263, " "variant = (string) \"itu\" ") + ); + +static GstStaticPadTemplate gst_rtp_h263p_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) [1, MAX], " + "encoding-name = (string) \"H263-1998\"; " + /* optional params */ + /* NOTE all optional SDP params must be strings in the caps */ + /* + "sqcif = (string) [1, 32], " + "qcif = (string) [1, 32], " + "cif = (string) [1, 32], " + "cif4 = (string) [1, 32], " + "cif16 = (string) [1, 32], " + "custom = (string) ANY, " + "f = (string) {0, 1}," + "i = (string) {0, 1}," + "j = (string) {0, 1}," + "t = (string) {0, 1}," + "k = (string) {1, 2, 3, 4}," + "n = (string) {1, 2, 3, 4}," + "p = (string) ANY," + "par = (string) ANY, " + "cpcf = (string) ANY, " + "bpp = (string) [0, 65536], " + "hrd = (string) {0, 1}; " + */ + "application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) [1, MAX], " + "encoding-name = (string) \"H263-2000\" " + /* optional params */ + /* NOTE all optional SDP params must be strings in the caps */ + /* + "profile = (string) [0, 10], " + "level = (string) {10, 20, 30, 40, 45, 50, 60, 70}, " + "interlace = (string) {0, 1};" + */ + ) + ); + +#define gst_rtp_h263p_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH263PDepay, gst_rtp_h263p_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_h263p_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_h263p_depay_change_state (GstElement * + element, GstStateChange transition); + +static GstBuffer *gst_rtp_h263p_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +gboolean gst_rtp_h263p_depay_setcaps (GstRTPBaseDepayload * filter, + GstCaps * caps); + +static void +gst_rtp_h263p_depay_class_init (GstRtpH263PDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_h263p_depay_finalize; + + gstelement_class->change_state = gst_rtp_h263p_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263p_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263p_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP H263 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts H263/+/++ video from RTP packets (RFC 4629)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_h263p_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_h263p_depay_setcaps; +} + +static void +gst_rtp_h263p_depay_init (GstRtpH263PDepay * rtph263pdepay) +{ + rtph263pdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_h263p_depay_finalize (GObject * object) +{ + GstRtpH263PDepay *rtph263pdepay; + + rtph263pdepay = GST_RTP_H263P_DEPAY (object); + + g_object_unref (rtph263pdepay->adapter); + rtph263pdepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +gboolean +gst_rtp_h263p_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps) +{ + GstCaps *srccaps = NULL; + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint clock_rate; + const gchar *encoding_name = NULL; + gboolean res; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + filter->clock_rate = clock_rate; + + encoding_name = gst_structure_get_string (structure, "encoding-name"); + if (encoding_name == NULL) + goto no_encoding_name; + + if (g_ascii_strcasecmp (encoding_name, "H263-2000") == 0) { + /* always h263++ */ + srccaps = gst_caps_new_simple ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263pp", NULL); + } else if (g_ascii_strcasecmp (encoding_name, "H263-1998") == 0) { + /* this can be H263 or H263+ depending on defined appendixes in the optional + * SDP params */ + const gchar *F, *I, *J, *T, *K, *N, *P; + gboolean is_h263p = FALSE; + + F = gst_structure_get_string (structure, "f"); + if (F) + if (g_ascii_strcasecmp (F, "1") == 0) + is_h263p = TRUE; + I = gst_structure_get_string (structure, "i"); + if (I) + if (g_ascii_strcasecmp (I, "1") == 0) + is_h263p = TRUE; + J = gst_structure_get_string (structure, "j"); + if (J) + if (g_ascii_strcasecmp (J, "1") == 0) + is_h263p = TRUE; + T = gst_structure_get_string (structure, "t"); + if (T) + if (g_ascii_strcasecmp (T, "1") == 0) + is_h263p = TRUE; + K = gst_structure_get_string (structure, "k"); + if (K) + is_h263p = TRUE; + N = gst_structure_get_string (structure, "n"); + if (N) + is_h263p = TRUE; + P = gst_structure_get_string (structure, "p"); + if (P) + is_h263p = TRUE; + + if (is_h263p) { + srccaps = gst_caps_new_simple ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263p", NULL); + } else { + srccaps = gst_caps_new_simple ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263", NULL); + } + } + if (!srccaps) + goto no_caps; + + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (filter), srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +no_encoding_name: + { + GST_ERROR_OBJECT (filter, "no encoding-name"); + return FALSE; + } +no_caps: + { + GST_ERROR_OBJECT (filter, "invalid encoding-name"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_h263p_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpH263PDepay *rtph263pdepay; + GstBuffer *outbuf; + gint payload_len; + guint8 *payload; + gboolean P, V, M; + guint header_len; + guint8 PLEN, PEBIT; + GstRTPBuffer rtp = { NULL }; + + rtph263pdepay = GST_RTP_H263P_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_LOG_OBJECT (depayload, "DISCONT, flushing adapter"); + gst_adapter_clear (rtph263pdepay->adapter); + rtph263pdepay->wait_start = TRUE; + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + header_len = 2; + + if (payload_len < header_len) + goto too_small; + + payload = gst_rtp_buffer_get_payload (&rtp); + + M = gst_rtp_buffer_get_marker (&rtp); + + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RR |P|V| PLEN |PEBIT| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + P = (payload[0] & 0x04) == 0x04; + V = (payload[0] & 0x02) == 0x02; + PLEN = ((payload[0] & 0x1) << 5) | (payload[1] >> 3); + PEBIT = payload[1] & 0x7; + + GST_LOG_OBJECT (depayload, "P %d, V %d, PLEN %d, PEBIT %d", P, V, PLEN, + PEBIT); + + if (V) { + header_len++; + } + if (PLEN) { + header_len += PLEN; + } + + if ((!P && payload_len < header_len) || (P && payload_len < header_len - 2)) + goto too_small; + + if (P) { + /* FIXME, have to make the packet writable hear. Better to reset these + * bytes when we copy the packet below */ + rtph263pdepay->wait_start = FALSE; + header_len -= 2; + payload[header_len] = 0; + payload[header_len + 1] = 0; + } + + if (rtph263pdepay->wait_start) + goto waiting_start; + + if (payload_len < header_len) + goto too_small; + + /* FIXME do not ignore the VRC header (See RFC 2429 section 4.2) */ + /* FIXME actually use the RTP picture header when it is lost in the network */ + /* for now strip off header */ + payload += header_len; + payload_len -= header_len; + + if (M) { + /* frame is completed: append to previous, push it out */ + guint len, padlen; + guint avail; + GstMapInfo map; + + GST_LOG_OBJECT (depayload, "Frame complete"); + + avail = gst_adapter_available (rtph263pdepay->adapter); + len = avail + payload_len; + padlen = (len % 4) + 4; + + outbuf = gst_buffer_new_and_alloc (len + padlen); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + memset (map.data + len, 0, padlen); + + /* prepend previous data */ + if (avail > 0) { + gst_adapter_copy (rtph263pdepay->adapter, map.data, 0, avail); + gst_adapter_flush (rtph263pdepay->adapter, avail); + } + memcpy (map.data + avail, payload, payload_len); + gst_buffer_unmap (outbuf, &map); + gst_rtp_buffer_unmap (&rtp); + + return outbuf; + + } else { + /* frame not completed: store in adapter */ + outbuf = gst_buffer_new_and_alloc (payload_len); + + GST_LOG_OBJECT (depayload, "Frame incomplete, storing %d", payload_len); + gst_buffer_fill (outbuf, 0, payload, payload_len); + + gst_adapter_push (rtph263pdepay->adapter, outbuf); + gst_rtp_buffer_unmap (&rtp); + } + return NULL; + +too_small: + { + GST_ELEMENT_WARNING (rtph263pdepay, STREAM, DECODE, + ("Packet payload was too small"), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +waiting_start: + { + GST_DEBUG_OBJECT (rtph263pdepay, "waiting for picture start"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_h263p_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpH263PDepay *rtph263pdepay; + GstStateChangeReturn ret; + + rtph263pdepay = GST_RTP_H263P_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtph263pdepay->adapter); + rtph263pdepay->wait_start = TRUE; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_h263p_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtph263pdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H263P_DEPAY); +} diff --git a/gst/rtp/gstrtph263pdepay.h b/gst/rtp/gstrtph263pdepay.h new file mode 100755 index 0000000..bbdb2b0 --- /dev/null +++ b/gst/rtp/gstrtph263pdepay.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_H263P_DEPAY_H__ +#define __GST_RTP_H263P_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_H263P_DEPAY \ + (gst_rtp_h263p_depay_get_type()) +#define GST_RTP_H263P_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H263P_DEPAY,GstRtpH263PDepay)) +#define GST_RTP_H263P_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H263P_DEPAY,GstRtpH263PDepayClass)) +#define GST_IS_RTP_H263P_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H263P_DEPAY)) +#define GST_IS_RTP_H263P_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H263P_DEPAY)) + +typedef struct _GstRtpH263PDepay GstRtpH263PDepay; +typedef struct _GstRtpH263PDepayClass GstRtpH263PDepayClass; + +struct _GstRtpH263PDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + gboolean wait_start; +}; + +struct _GstRtpH263PDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_h263p_depay_get_type (void); + +gboolean gst_rtp_h263p_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_H263P_DEPAY_H__ */ diff --git a/gst/rtp/gstrtph263ppay.c b/gst/rtp/gstrtph263ppay.c new file mode 100755 index 0000000..54adb20 --- /dev/null +++ b/gst/rtp/gstrtph263ppay.c @@ -0,0 +1,777 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtph263ppay.h" + +#define DEFAULT_FRAGMENTATION_MODE GST_FRAGMENTATION_MODE_NORMAL + +enum +{ + PROP_0, + PROP_FRAGMENTATION_MODE +}; + +#define GST_TYPE_FRAGMENTATION_MODE (gst_fragmentation_mode_get_type()) +static GType +gst_fragmentation_mode_get_type (void) +{ + static GType fragmentation_mode_type = 0; + static const GEnumValue fragmentation_mode[] = { + {GST_FRAGMENTATION_MODE_NORMAL, "Normal", "normal"}, + {GST_FRAGMENTATION_MODE_SYNC, "Fragment at sync points", "sync"}, + {0, NULL, NULL}, + }; + + if (!fragmentation_mode_type) { + fragmentation_mode_type = + g_enum_register_static ("GstFragmentationMode", fragmentation_mode); + } + return fragmentation_mode_type; +} + + +GST_DEBUG_CATEGORY_STATIC (rtph263ppay_debug); +#define GST_CAT_DEFAULT rtph263ppay_debug + +static GstStaticPadTemplate gst_rtp_h263p_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h263, variant = (string) itu") + ); + +/* + * We also return these in getcaps() as required by the SDP caps + * + * width = (int) [16, 4096] + * height = (int) [16, 4096] + * "annex-f = (boolean) {true, false}," + * "annex-i = (boolean) {true, false}," + * "annex-j = (boolean) {true, false}," + * "annex-l = (boolean) {true, false}," + * "annex-t = (boolean) {true, false}," + * "annex-v = (boolean) {true, false}") + */ + + +static GstStaticPadTemplate gst_rtp_h263p_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263-1998\"; " + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H263-2000\"") + ); + +static void gst_rtp_h263p_pay_finalize (GObject * object); + +static void gst_rtp_h263p_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_h263p_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_rtp_h263p_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstCaps *gst_rtp_h263p_pay_sink_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static GstFlowReturn gst_rtp_h263p_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +#define gst_rtp_h263p_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH263PPay, gst_rtp_h263p_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_h263p_pay_class_init (GstRtpH263PPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_h263p_pay_finalize; + gobject_class->set_property = gst_rtp_h263p_pay_set_property; + gobject_class->get_property = gst_rtp_h263p_pay_get_property; + + gstrtpbasepayload_class->set_caps = gst_rtp_h263p_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_h263p_pay_sink_getcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_h263p_pay_handle_buffer; + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_FRAGMENTATION_MODE, g_param_spec_enum ("fragmentation-mode", + "Fragmentation Mode", + "Packet Fragmentation Mode", GST_TYPE_FRAGMENTATION_MODE, + DEFAULT_FRAGMENTATION_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263p_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h263p_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP H263 payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes H263/+/++ video in RTP packets (RFC 4629)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtph263ppay_debug, "rtph263ppay", + 0, "rtph263ppay (RFC 4629)"); +} + +static void +gst_rtp_h263p_pay_init (GstRtpH263PPay * rtph263ppay) +{ + rtph263ppay->adapter = gst_adapter_new (); + + rtph263ppay->fragmentation_mode = DEFAULT_FRAGMENTATION_MODE; +} + +static void +gst_rtp_h263p_pay_finalize (GObject * object) +{ + GstRtpH263PPay *rtph263ppay; + + rtph263ppay = GST_RTP_H263P_PAY (object); + + g_object_unref (rtph263ppay->adapter); + rtph263ppay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_h263p_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + GstCaps *peercaps; + gchar *encoding_name = NULL; + + g_return_val_if_fail (gst_caps_is_fixed (caps), FALSE); + + peercaps = + gst_pad_peer_query_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload), NULL); + if (peercaps) { + GstCaps *tcaps = + gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload)); + GstCaps *intersect = gst_caps_intersect (peercaps, tcaps); + gst_caps_unref (tcaps); + + gst_caps_unref (peercaps); + if (!gst_caps_is_empty (intersect)) { + GstStructure *s = gst_caps_get_structure (intersect, 0); + encoding_name = g_strdup (gst_structure_get_string (s, "encoding-name")); + } + gst_caps_unref (intersect); + } + + if (!encoding_name) + encoding_name = g_strdup ("H263-1998"); + + gst_rtp_base_payload_set_options (payload, "video", TRUE, + (gchar *) encoding_name, 90000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + g_free (encoding_name); + + return res; +} + +static GstCaps * +caps_append (GstCaps * caps, GstStructure * in_s, guint x, guint y, guint mpi) +{ + GstStructure *s; + + if (!in_s) + return caps; + + if (mpi < 1 || mpi > 32) + return caps; + + s = gst_structure_copy (in_s); + + gst_structure_set (s, + "width", GST_TYPE_INT_RANGE, 1, x, + "height", GST_TYPE_INT_RANGE, 1, y, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001 * mpi, NULL); + + caps = gst_caps_merge_structure (caps, s); + + return caps; +} + + +static GstCaps * +gst_rtp_h263p_pay_sink_getcaps (GstRTPBasePayload * payload, GstPad * pad, + GstCaps * filter) +{ + GstRtpH263PPay *rtph263ppay; + GstCaps *caps = NULL, *templ; + GstCaps *peercaps = NULL; + GstCaps *intersect = NULL; + guint i; + + rtph263ppay = GST_RTP_H263P_PAY (payload); + + peercaps = + gst_pad_peer_query_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload), NULL); + + /* if we're just outputting to udpsink or fakesink or so, we should also + * accept any input compatible with our sink template caps */ + if (!peercaps || gst_caps_is_any (peercaps)) { + if (peercaps) + gst_caps_unref (peercaps); + return + gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SINKPAD (payload)); + } + + /* We basically need to differentiate two use-cases here: One where there's + * a capsfilter after the payloader with caps created from an SDP; in this + * case the filter caps are fixed and we want to signal to an encoder what + * we want it to produce. The second case is simply payloader ! depayloader + * where we are dealing with the depayloader's template caps. In this case + * we should accept any input compatible with our sink template caps. */ + if (!gst_caps_is_fixed (peercaps)) + return + gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SINKPAD (payload)); + + templ = gst_pad_get_pad_template_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload)); + intersect = gst_caps_intersect (peercaps, templ); + gst_caps_unref (peercaps); + gst_caps_unref (templ); + + if (gst_caps_is_empty (intersect)) + return intersect; + + caps = gst_caps_new_empty (); + for (i = 0; i < gst_caps_get_size (intersect); i++) { + GstStructure *s = gst_caps_get_structure (intersect, i); + const gchar *encoding_name = gst_structure_get_string (s, "encoding-name"); + + if (!strcmp (encoding_name, "H263-2000")) { + const gchar *profile_str = gst_structure_get_string (s, "profile"); + const gchar *level_str = gst_structure_get_string (s, "level"); + int profile = 0; + int level = 0; + + if (profile_str && level_str) { + gboolean i = FALSE, j = FALSE, l = FALSE, t = FALSE, f = FALSE, + v = FALSE; + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + NULL); + + profile = atoi (profile_str); + level = atoi (level_str); + + /* These profiles are defined in the H.263 Annex X */ + switch (profile) { + case 0: + /* The Baseline Profile (Profile 0) */ + break; + case 1: + /* H.320 Coding Efficiency Version 2 Backward-Compatibility Profile + * (Profile 1) + * Baseline + Annexes I, J, L.4 and T + */ + i = j = l = t = TRUE; + break; + case 2: + /* Version 1 Backward-Compatibility Profile (Profile 2) + * Baseline + Annex F + */ + i = j = l = t = f = TRUE; + break; + case 3: + /* Version 2 Interactive and Streaming Wireless Profile + * Baseline + Annexes I, J, T + */ + i = j = t = TRUE; + break; + case 4: + /* Version 3 Interactive and Streaming Wireless Profile (Profile 4) + * Baseline + Annexes I, J, T, V, W.6.3.8, + */ + /* Missing W.6.3.8 */ + i = j = t = v = TRUE; + break; + case 5: + /* Conversational High Compression Profile (Profile 5) + * Baseline + Annexes F, I, J, L.4, T, D, U + */ + /* Missing D, U */ + f = i = j = l = t = TRUE; + break; + case 6: + /* Conversational Internet Profile (Profile 6) + * Baseline + Annexes F, I, J, L.4, T, D, U and + * K with arbitratry slice ordering + */ + /* Missing D, U, K with arbitratry slice ordering */ + f = i = j = l = t = TRUE; + break; + case 7: + /* Conversational Interlace Profile (Profile 7) + * Baseline + Annexes F, I, J, L.4, T, D, U, W.6.3.11 + */ + /* Missing D, U, W.6.3.11 */ + f = i = j = l = t = TRUE; + break; + case 8: + /* High Latency Profile (Profile 8) + * Baseline + Annexes F, I, J, L.4, T, D, U, P.5, O.1.1 and + * K with arbitratry slice ordering + */ + /* Missing D, U, P.5, O.1.1 */ + f = i = j = l = t = TRUE; + break; + } + + + if (f || i || j || t || l || v) { + GValue list = { 0 }; + GValue vstr = { 0 }; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&vstr, G_TYPE_STRING); + + g_value_set_static_string (&vstr, "h263"); + gst_value_list_append_value (&list, &vstr); + g_value_set_static_string (&vstr, "h263p"); + gst_value_list_append_value (&list, &vstr); + + if (l || v) { + g_value_set_static_string (&vstr, "h263pp"); + gst_value_list_append_value (&list, &vstr); + } + g_value_unset (&vstr); + + gst_structure_set_value (new_s, "h263version", &list); + g_value_unset (&list); + } else { + gst_structure_set (new_s, "h263version", G_TYPE_STRING, "h263", NULL); + } + + + if (!f) + gst_structure_set (new_s, "annex-f", G_TYPE_BOOLEAN, FALSE, NULL); + if (!i) + gst_structure_set (new_s, "annex-i", G_TYPE_BOOLEAN, FALSE, NULL); + if (!j) + gst_structure_set (new_s, "annex-j", G_TYPE_BOOLEAN, FALSE, NULL); + if (!t) + gst_structure_set (new_s, "annex-t", G_TYPE_BOOLEAN, FALSE, NULL); + if (!l) + gst_structure_set (new_s, "annex-l", G_TYPE_BOOLEAN, FALSE, NULL); + if (!v) + gst_structure_set (new_s, "annex-v", G_TYPE_BOOLEAN, FALSE, NULL); + + + if (level <= 10 || level == 45) { + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 176, + "height", GST_TYPE_INT_RANGE, 1, 144, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 2002, NULL); + caps = gst_caps_merge_structure (caps, new_s); + } else if (level <= 20) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 2002, NULL); + caps = gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 176, + "height", GST_TYPE_INT_RANGE, 1, 144, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001, NULL); + caps = gst_caps_merge_structure (caps, s_copy); + } else if (level <= 40) { + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 30000, 1001, NULL); + caps = gst_caps_merge_structure (caps, new_s); + } else if (level <= 50) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + caps = gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 352, + "height", GST_TYPE_INT_RANGE, 1, 240, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + caps = gst_caps_merge_structure (caps, s_copy); + } else if (level <= 60) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 288, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + caps = gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 240, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + caps = gst_caps_merge_structure (caps, s_copy); + } else if (level <= 70) { + GstStructure *s_copy = gst_structure_copy (new_s); + + gst_structure_set (new_s, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 576, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 50, 1, NULL); + caps = gst_caps_merge_structure (caps, new_s); + + gst_structure_set (s_copy, + "width", GST_TYPE_INT_RANGE, 1, 720, + "height", GST_TYPE_INT_RANGE, 1, 480, + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, 60000, 1001, NULL); + caps = gst_caps_merge_structure (caps, s_copy); + } else { + caps = gst_caps_merge_structure (caps, new_s); + } + + } else { + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + "h263version", G_TYPE_STRING, "h263", + NULL); + + GST_DEBUG_OBJECT (rtph263ppay, "No profile or level specified" + " for H263-2000, defaulting to baseline H263"); + + caps = gst_caps_merge_structure (caps, new_s); + } + } else { + gboolean f = FALSE, i = FALSE, j = FALSE, t = FALSE; + /* FIXME: ffmpeg support the Appendix K too, how do we express it ? + * guint k; + */ + const gchar *str; + GstStructure *new_s = gst_structure_new ("video/x-h263", + "variant", G_TYPE_STRING, "itu", + NULL); + gboolean added = FALSE; + + str = gst_structure_get_string (s, "f"); + if (str && !strcmp (str, "1")) + f = TRUE; + + str = gst_structure_get_string (s, "i"); + if (str && !strcmp (str, "1")) + i = TRUE; + + str = gst_structure_get_string (s, "j"); + if (str && !strcmp (str, "1")) + j = TRUE; + + str = gst_structure_get_string (s, "t"); + if (str && !strcmp (str, "1")) + t = TRUE; + + if (f || i || j || t) { + GValue list = { 0 }; + GValue vstr = { 0 }; + + g_value_init (&list, GST_TYPE_LIST); + g_value_init (&vstr, G_TYPE_STRING); + + g_value_set_static_string (&vstr, "h263"); + gst_value_list_append_value (&list, &vstr); + g_value_set_static_string (&vstr, "h263p"); + gst_value_list_append_value (&list, &vstr); + g_value_unset (&vstr); + + gst_structure_set_value (new_s, "h263version", &list); + g_value_unset (&list); + } else { + gst_structure_set (new_s, "h263version", G_TYPE_STRING, "h263", NULL); + } + + if (!f) + gst_structure_set (new_s, "annex-f", G_TYPE_BOOLEAN, FALSE, NULL); + if (!i) + gst_structure_set (new_s, "annex-i", G_TYPE_BOOLEAN, FALSE, NULL); + if (!j) + gst_structure_set (new_s, "annex-j", G_TYPE_BOOLEAN, FALSE, NULL); + if (!t) + gst_structure_set (new_s, "annex-t", G_TYPE_BOOLEAN, FALSE, NULL); + + + str = gst_structure_get_string (s, "custom"); + if (str) { + unsigned int xmax, ymax, mpi; + if (sscanf (str, "%u,%u,%u", &xmax, &ymax, &mpi) == 3) { + if (xmax % 4 && ymax % 4 && mpi >= 1 && mpi <= 32) { + caps = caps_append (caps, new_s, xmax, ymax, mpi); + added = TRUE; + } else { + GST_WARNING_OBJECT (rtph263ppay, "Invalid custom framesize/MPI" + " %u x %u at %u, ignoring", xmax, ymax, mpi); + } + } else { + GST_WARNING_OBJECT (rtph263ppay, "Invalid custom framesize/MPI: %s," + " ignoring", str); + } + } + + str = gst_structure_get_string (s, "16cif"); + if (str) { + int mpi = atoi (str); + caps = caps_append (caps, new_s, 1408, 1152, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "4cif"); + if (str) { + int mpi = atoi (str); + caps = caps_append (caps, new_s, 704, 576, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "cif"); + if (str) { + int mpi = atoi (str); + caps = caps_append (caps, new_s, 352, 288, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "qcif"); + if (str) { + int mpi = atoi (str); + caps = caps_append (caps, new_s, 176, 144, mpi); + added = TRUE; + } + + str = gst_structure_get_string (s, "sqcif"); + if (str) { + int mpi = atoi (str); + caps = caps_append (caps, new_s, 128, 96, mpi); + added = TRUE; + } + + if (added) + gst_structure_free (new_s); + else + caps = gst_caps_merge_structure (caps, new_s); + } + } + + gst_caps_unref (intersect); + + return caps; +} + + +static void +gst_rtp_h263p_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpH263PPay *rtph263ppay; + + rtph263ppay = GST_RTP_H263P_PAY (object); + + switch (prop_id) { + case PROP_FRAGMENTATION_MODE: + rtph263ppay->fragmentation_mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_h263p_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpH263PPay *rtph263ppay; + + rtph263ppay = GST_RTP_H263P_PAY (object); + + switch (prop_id) { + case PROP_FRAGMENTATION_MODE: + g_value_set_enum (value, rtph263ppay->fragmentation_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstFlowReturn +gst_rtp_h263p_pay_flush (GstRtpH263PPay * rtph263ppay) +{ + guint avail; + GstBuffer *outbuf; + GstFlowReturn ret; + gboolean fragmented; + + avail = gst_adapter_available (rtph263ppay->adapter); + if (avail == 0) + return GST_FLOW_OK; + + fragmented = FALSE; + /* This algorithm assumes the H263/+/++ encoder sends complete frames in each + * buffer */ + /* With Fragmentation Mode at GST_FRAGMENTATION_MODE_NORMAL: + * This algorithm implements the Follow-on packets method for packetization. + * This assumes low packet loss network. + * With Fragmentation Mode at GST_FRAGMENTATION_MODE_SYNC: + * This algorithm separates large frames at synchronisation points (Segments) + * (See RFC 4629 section 6). It would be interesting to have a property such as network + * quality to select between both packetization methods */ + /* TODO Add VRC supprt (See RFC 4629 section 5.2) */ + + while (avail > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + gint header_len; + guint next_gop = 0; + gboolean found_gob = FALSE; + GstRTPBuffer rtp = { NULL }; + + if (rtph263ppay->fragmentation_mode == GST_FRAGMENTATION_MODE_SYNC) { + /* start after 1st gop possible */ + guint parsed_len = 3; + const guint8 *parse_data = NULL; + + parse_data = gst_adapter_map (rtph263ppay->adapter, avail); + + /* Check if we have a gob or eos , eossbs */ + /* FIXME EOS and EOSSBS packets should never contain any gobs and vice-versa */ + if (avail >= 3 && *parse_data == 0 && *(parse_data + 1) == 0 + && *(parse_data + 2) >= 0x80) { + GST_DEBUG_OBJECT (rtph263ppay, " Found GOB header"); + found_gob = TRUE; + } + /* Find next and cut the packet accordingly */ + /* TODO we should get as many gobs as possible until MTU is reached, this + * code seems to just get one GOB per packet */ + while (parsed_len + 2 < avail) { + if (parse_data[parsed_len] == 0 && parse_data[parsed_len + 1] == 0 + && parse_data[parsed_len + 2] >= 0x80) { + next_gop = parsed_len; + GST_DEBUG_OBJECT (rtph263ppay, " Next GOB Detected at : %d", + next_gop); + break; + } + parsed_len++; + } + gst_adapter_unmap (rtph263ppay->adapter); + } + + /* for picture start frames (non-fragmented), we need to remove the first + * two 0x00 bytes and set P=1 */ + header_len = (fragmented && !found_gob) ? 2 : 0; + + towrite = MIN (avail, gst_rtp_buffer_calc_payload_len + (GST_RTP_BASE_PAYLOAD_MTU (rtph263ppay) - header_len, 0, 0)); + + if (next_gop > 0) + towrite = MIN (next_gop, towrite); + + payload_len = header_len + towrite; + + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + /* last fragment gets the marker bit set */ + gst_rtp_buffer_set_marker (&rtp, avail > towrite ? 0 : 1); + + payload = gst_rtp_buffer_get_payload (&rtp); + + gst_adapter_copy (rtph263ppay->adapter, &payload[header_len], 0, towrite); + + /* 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | RR |P|V| PLEN |PEBIT| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + /* if fragmented or gop header , write p bit =1 */ + payload[0] = (fragmented && !found_gob) ? 0x00 : 0x04; + payload[1] = 0; + + GST_BUFFER_TIMESTAMP (outbuf) = rtph263ppay->first_timestamp; + GST_BUFFER_DURATION (outbuf) = rtph263ppay->first_duration; + gst_rtp_buffer_unmap (&rtp); + + gst_adapter_flush (rtph263ppay->adapter, towrite); + + ret = + gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtph263ppay), outbuf); + + avail -= towrite; + fragmented = TRUE; + } + + return ret; +} + +static GstFlowReturn +gst_rtp_h263p_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer) +{ + GstRtpH263PPay *rtph263ppay; + GstFlowReturn ret; + + rtph263ppay = GST_RTP_H263P_PAY (payload); + + rtph263ppay->first_timestamp = GST_BUFFER_TIMESTAMP (buffer); + rtph263ppay->first_duration = GST_BUFFER_DURATION (buffer); + + /* we always encode and flush a full picture */ + gst_adapter_push (rtph263ppay->adapter, buffer); + ret = gst_rtp_h263p_pay_flush (rtph263ppay); + + return ret; +} + +gboolean +gst_rtp_h263p_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtph263ppay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H263P_PAY); +} diff --git a/gst/rtp/gstrtph263ppay.h b/gst/rtp/gstrtph263ppay.h new file mode 100755 index 0000000..23ec8b8 --- /dev/null +++ b/gst/rtp/gstrtph263ppay.h @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_H263P_PAY_H__ +#define __GST_RTP_H263P_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_H263P_PAY \ + (gst_rtp_h263p_pay_get_type()) +#define GST_RTP_H263P_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H263P_PAY,GstRtpH263PPay)) +#define GST_RTP_H263P_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H263P_PAY,GstRtpH263PPayClass)) +#define GST_IS_RTP_H263P_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H263P_PAY)) +#define GST_IS_RTP_H263P_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H263P_PAY)) + +typedef struct _GstRtpH263PPay GstRtpH263PPay; +typedef struct _GstRtpH263PPayClass GstRtpH263PPayClass; + +typedef enum +{ + GST_FRAGMENTATION_MODE_NORMAL = 0, + GST_FRAGMENTATION_MODE_SYNC = 1 +} GstFragmentationMode; + +struct _GstRtpH263PPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_timestamp; + GstClockTime first_duration; + GstFragmentationMode fragmentation_mode; +}; + +struct _GstRtpH263PPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_h263p_pay_get_type (void); + +gboolean gst_rtp_h263p_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_H263P_PAY_H__ */ diff --git a/gst/rtp/gstrtph264depay.c b/gst/rtp/gstrtph264depay.c new file mode 100755 index 0000000..56b6410 --- /dev/null +++ b/gst/rtp/gstrtph264depay.c @@ -0,0 +1,1174 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <string.h> + +#include <gst/base/gstbitreader.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtph264depay.h" + +GST_DEBUG_CATEGORY_STATIC (rtph264depay_debug); +#define GST_CAT_DEFAULT (rtph264depay_debug) + +/* This is what we'll default to when downstream hasn't + * expressed a restriction or preference via caps */ +#define DEFAULT_BYTE_STREAM TRUE +#define DEFAULT_ACCESS_UNIT FALSE + +/* 3 zero bytes syncword */ +static const guint8 sync_bytes[] = { 0, 0, 0, 1 }; + +static GstStaticPadTemplate gst_rtp_h264_depay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "stream-format = (string) avc, alignment = (string) au; " + "video/x-h264, " + "stream-format = (string) byte-stream, alignment = (string) { nal, au }") + ); + +static GstStaticPadTemplate gst_rtp_h264_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H264\"") + /** optional parameters **/ + /* "profile-level-id = (string) ANY, " */ + /* "max-mbps = (string) ANY, " */ + /* "max-fs = (string) ANY, " */ + /* "max-cpb = (string) ANY, " */ + /* "max-dpb = (string) ANY, " */ + /* "max-br = (string) ANY, " */ + /* "redundant-pic-cap = (string) { \"0\", \"1\" }, " */ + /* "sprop-parameter-sets = (string) ANY, " */ + /* "parameter-add = (string) { \"0\", \"1\" }, " */ + /* "packetization-mode = (string) { \"0\", \"1\", \"2\" }, " */ + /* "sprop-interleaving-depth = (string) ANY, " */ + /* "sprop-deint-buf-req = (string) ANY, " */ + /* "deint-buf-cap = (string) ANY, " */ + /* "sprop-init-buf-time = (string) ANY, " */ + /* "sprop-max-don-diff = (string) ANY, " */ + /* "max-rcmd-nalu-size = (string) ANY " */ + ); + +#define gst_rtp_h264_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH264Depay, gst_rtp_h264_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_h264_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_h264_depay_change_state (GstElement * + element, GstStateChange transition); + +static GstBuffer *gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * filter, + GstCaps * caps); +static gboolean gst_rtp_h264_depay_handle_event (GstRTPBaseDepayload * depay, + GstEvent * event); + +static void +gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_h264_depay_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h264_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h264_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP H264 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts H264 video from RTP packets (RFC 3984)", + "Wim Taymans <wim.taymans@gmail.com>"); + gstelement_class->change_state = gst_rtp_h264_depay_change_state; + + gstrtpbasedepayload_class->process = gst_rtp_h264_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_h264_depay_setcaps; + gstrtpbasedepayload_class->handle_event = gst_rtp_h264_depay_handle_event; +} + +static void +gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay) +{ + rtph264depay->adapter = gst_adapter_new (); + rtph264depay->picture_adapter = gst_adapter_new (); + rtph264depay->byte_stream = DEFAULT_BYTE_STREAM; + rtph264depay->merge = DEFAULT_ACCESS_UNIT; + rtph264depay->sps = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gst_buffer_unref); + rtph264depay->pps = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gst_buffer_unref); +} + +static void +gst_rtp_h264_depay_reset (GstRtpH264Depay * rtph264depay) +{ + gst_adapter_clear (rtph264depay->adapter); + rtph264depay->wait_start = TRUE; + gst_adapter_clear (rtph264depay->picture_adapter); + rtph264depay->picture_start = FALSE; + rtph264depay->last_keyframe = FALSE; + rtph264depay->last_ts = 0; + rtph264depay->current_fu_type = 0; + rtph264depay->new_codec_data = FALSE; + g_ptr_array_set_size (rtph264depay->sps, 0); + g_ptr_array_set_size (rtph264depay->pps, 0); +} + +static void +gst_rtp_h264_depay_finalize (GObject * object) +{ + GstRtpH264Depay *rtph264depay; + + rtph264depay = GST_RTP_H264_DEPAY (object); + + if (rtph264depay->codec_data) + gst_buffer_unref (rtph264depay->codec_data); + + g_object_unref (rtph264depay->adapter); + g_object_unref (rtph264depay->picture_adapter); + + g_ptr_array_free (rtph264depay->sps, TRUE); + g_ptr_array_free (rtph264depay->pps, TRUE); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_h264_depay_negotiate (GstRtpH264Depay * rtph264depay) +{ + GstCaps *caps; + gint byte_stream = -1; + gint merge = -1; + + caps = + gst_pad_get_allowed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (rtph264depay)); + + GST_DEBUG_OBJECT (rtph264depay, "allowed caps: %" GST_PTR_FORMAT, caps); + + if (caps) { + if (gst_caps_get_size (caps) > 0) { + GstStructure *s = gst_caps_get_structure (caps, 0); + const gchar *str = NULL; + + if ((str = gst_structure_get_string (s, "stream-format"))) { + if (strcmp (str, "avc") == 0) { + byte_stream = FALSE; + } else if (strcmp (str, "byte-stream") == 0) { + byte_stream = TRUE; + } else { + GST_DEBUG_OBJECT (rtph264depay, "unknown stream-format: %s", str); + } + } + + if ((str = gst_structure_get_string (s, "alignment"))) { + if (strcmp (str, "au") == 0) { + merge = TRUE; + } else if (strcmp (str, "nal") == 0) { + merge = FALSE; + } else { + GST_DEBUG_OBJECT (rtph264depay, "unknown alignment: %s", str); + } + } + } + gst_caps_unref (caps); + } + + if (byte_stream != -1) { + GST_DEBUG_OBJECT (rtph264depay, "downstream requires byte-stream %d", + byte_stream); + rtph264depay->byte_stream = byte_stream; + } else { + GST_DEBUG_OBJECT (rtph264depay, "defaulting to byte-stream %d", + DEFAULT_BYTE_STREAM); + rtph264depay->byte_stream = DEFAULT_BYTE_STREAM; + } + if (merge != -1) { + GST_DEBUG_OBJECT (rtph264depay, "downstream requires merge %d", merge); + rtph264depay->merge = merge; + } else { + GST_DEBUG_OBJECT (rtph264depay, "defaulting to merge %d", + DEFAULT_ACCESS_UNIT); + rtph264depay->merge = DEFAULT_ACCESS_UNIT; + } +} + +/* Stolen from bad/gst/mpegtsdemux/payloader_parsers.c */ +/* variable length Exp-Golomb parsing according to H.264 spec 9.1*/ +static gboolean +read_golomb (GstBitReader * br, guint32 * value) +{ + guint8 b, leading_zeros = -1; + *value = 1; + + for (b = 0; !b; leading_zeros++) { + if (!gst_bit_reader_get_bits_uint8 (br, &b, 1)) + return FALSE; + *value *= 2; + } + + *value = (*value >> 1) - 1; + if (leading_zeros > 0) { + guint32 tmp = 0; + if (!gst_bit_reader_get_bits_uint32 (br, &tmp, leading_zeros)) + return FALSE; + *value += tmp; + } + + return TRUE; +} + +static gboolean +parse_sps (GstMapInfo * map, guint32 * sps_id) +{ + GstBitReader br = GST_BIT_READER_INIT (map->data + 4, + map->size - 4); + + if (map->size < 5) + return FALSE; + + if (!read_golomb (&br, sps_id)) + return FALSE; + + return TRUE; +} + +static gboolean +parse_pps (GstMapInfo * map, guint32 * sps_id, guint32 * pps_id) +{ + GstBitReader br = GST_BIT_READER_INIT (map->data + 1, + map->size - 1); + + if (map->size < 2) + return FALSE; + + if (!read_golomb (&br, pps_id)) + return FALSE; + if (!read_golomb (&br, sps_id)) + return FALSE; + + return TRUE; +} + + +static gboolean +gst_rtp_h264_set_src_caps (GstRtpH264Depay * rtph264depay) +{ + gboolean res; + GstCaps *srccaps; + guchar level = 0; + guchar profile_compat = G_MAXUINT8; + + if (!rtph264depay->byte_stream && + (!rtph264depay->new_codec_data || + rtph264depay->sps->len == 0 || rtph264depay->pps->len == 0)) + return TRUE; + + srccaps = gst_caps_new_simple ("video/x-h264", + "stream-format", G_TYPE_STRING, + rtph264depay->byte_stream ? "byte-stream" : "avc", + "alignment", G_TYPE_STRING, rtph264depay->merge ? "au" : "nal", NULL); + + if (!rtph264depay->byte_stream) { + GstBuffer *codec_data; + GstMapInfo map; + GstMapInfo nalmap; + guint8 *data; + guint len; + guint new_size; + guint i; + + /* start with 7 bytes header */ + len = 7; + /* count sps & pps */ + for (i = 0; i < rtph264depay->sps->len; i++) + len += 2 + gst_buffer_get_size (g_ptr_array_index (rtph264depay->sps, i)); + for (i = 0; i < rtph264depay->pps->len; i++) + len += 2 + gst_buffer_get_size (g_ptr_array_index (rtph264depay->pps, i)); + + codec_data = gst_buffer_new_and_alloc (len); + g_debug ("alloc_len: %u", len); + gst_buffer_map (codec_data, &map, GST_MAP_READWRITE); + data = map.data; + + /* 8 bits version == 1 */ + *data++ = 1; + + /* According to: ISO/IEC 14496-15:2004(E) section 5.2.4.1 + * The level is the max level of all SPSes + * A profile compat bit can only be set if all SPSes include that bit + */ + for (i = 0; i < rtph264depay->sps->len; i++) { + gst_buffer_map (g_ptr_array_index (rtph264depay->sps, i), &nalmap, + GST_MAP_READ); + profile_compat &= nalmap.data[2]; + level = MAX (level, nalmap.data[3]); + gst_buffer_unmap (g_ptr_array_index (rtph264depay->sps, i), &nalmap); + } + + /* Assume all SPSes use the same profile, so extract from the first SPS */ + gst_buffer_map (g_ptr_array_index (rtph264depay->sps, 0), &nalmap, + GST_MAP_READ); + *data++ = nalmap.data[1]; + gst_buffer_unmap (g_ptr_array_index (rtph264depay->sps, 0), &nalmap); + *data++ = profile_compat; + *data++ = level; + + /* 6 bits reserved | 2 bits lengthSizeMinusOn */ + *data++ = 0xff; + /* 3 bits reserved | 5 bits numOfSequenceParameterSets */ + *data++ = 0xe0 | (rtph264depay->sps->len & 0x1f); + + /* copy all SPS */ + for (i = 0; i < rtph264depay->sps->len; i++) { + gst_buffer_map (g_ptr_array_index (rtph264depay->sps, i), &nalmap, + GST_MAP_READ); + + GST_DEBUG_OBJECT (rtph264depay, "copy SPS %d of length %u", i, + (guint) nalmap.size); + GST_WRITE_UINT16_BE (data, nalmap.size); + data += 2; + memcpy (data, nalmap.data, nalmap.size); + data += nalmap.size; + gst_buffer_unmap (g_ptr_array_index (rtph264depay->sps, i), &nalmap); + } + + /* 8 bits numOfPictureParameterSets */ + *data++ = rtph264depay->pps->len; + /* copy all PPS */ + for (i = 0; i < rtph264depay->pps->len; i++) { + gst_buffer_map (g_ptr_array_index (rtph264depay->pps, i), &nalmap, + GST_MAP_READ); + + GST_DEBUG_OBJECT (rtph264depay, "copy PPS %d of length %u", i, + (guint) nalmap.size); + GST_WRITE_UINT16_BE (data, nalmap.size); + data += 2; + memcpy (data, nalmap.data, nalmap.size); + data += nalmap.size; + gst_buffer_unmap (g_ptr_array_index (rtph264depay->pps, i), &nalmap); + } + + new_size = data - map.data; + gst_buffer_unmap (codec_data, &map); + gst_buffer_set_size (codec_data, new_size); + + + gst_caps_set_simple (srccaps, + "codec_data", GST_TYPE_BUFFER, codec_data, NULL); + gst_buffer_unref (codec_data); + } + + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (rtph264depay), + srccaps); + gst_caps_unref (srccaps); + + if (res) + rtph264depay->new_codec_data = FALSE; + + return res; +} + +gboolean +gst_rtp_h264_add_sps_pps (GstElement * rtph264, GPtrArray * sps_array, + GPtrArray * pps_array, GstBuffer * nal) +{ + GstMapInfo map; + guchar type; + guint i; + + gst_buffer_map (nal, &map, GST_MAP_READ); + + type = map.data[0] & 0x1f; + + if (type == 7) { + guint32 sps_id; + + if (!parse_sps (&map, &sps_id)) { + GST_WARNING_OBJECT (rtph264, "Invalid SPS," + " can't parse seq_parameter_set_id"); + goto drop; + } + + for (i = 0; i < sps_array->len; i++) { + GstBuffer *sps = g_ptr_array_index (sps_array, i); + GstMapInfo spsmap; + guint32 tmp_sps_id; + + gst_buffer_map (sps, &spsmap, GST_MAP_READ); + parse_sps (&spsmap, &tmp_sps_id); + + if (sps_id == tmp_sps_id) { + if (map.size == spsmap.size && + memcmp (map.data, spsmap.data, spsmap.size) == 0) { + GST_LOG_OBJECT (rtph264, "Unchanged SPS %u, not updating", sps_id); + gst_buffer_unmap (sps, &spsmap); + goto drop; + } else { + gst_buffer_unmap (sps, &spsmap); + g_ptr_array_remove_index_fast (sps_array, i); + g_ptr_array_add (sps_array, nal); + GST_LOG_OBJECT (rtph264, "Modified SPS %u, replacing", sps_id); + goto done; + } + } + gst_buffer_unmap (sps, &spsmap); + } + GST_LOG_OBJECT (rtph264, "Adding new SPS %u", sps_id); + g_ptr_array_add (sps_array, nal); + } else if (type == 8) { + guint32 sps_id; + guint32 pps_id; + + if (!parse_pps (&map, &sps_id, &pps_id)) { + GST_WARNING_OBJECT (rtph264, "Invalid PPS," + " can't parse seq_parameter_set_id or pic_parameter_set_id"); + goto drop; + } + + for (i = 0; i < pps_array->len; i++) { + GstBuffer *pps = g_ptr_array_index (pps_array, i); + GstMapInfo ppsmap; + guint32 tmp_sps_id; + guint32 tmp_pps_id; + + + gst_buffer_map (pps, &ppsmap, GST_MAP_READ); + parse_pps (&ppsmap, &tmp_sps_id, &tmp_pps_id); + + if (sps_id == tmp_sps_id && pps_id == tmp_pps_id) { + if (map.size == ppsmap.size && + memcmp (map.data, ppsmap.data, ppsmap.size) == 0) { + GST_LOG_OBJECT (rtph264, "Unchanged PPS %u:%u, not updating", sps_id, + pps_id); + gst_buffer_unmap (pps, &ppsmap); + goto drop; + } else { + gst_buffer_unmap (pps, &ppsmap); + g_ptr_array_remove_index_fast (pps_array, i); + g_ptr_array_add (pps_array, nal); + GST_LOG_OBJECT (rtph264, "Modified PPS %u:%u, replacing", + sps_id, pps_id); + goto done; + } + } + gst_buffer_unmap (pps, &ppsmap); + } + GST_LOG_OBJECT (rtph264, "Adding new PPS %u:%i", sps_id, pps_id); + g_ptr_array_add (pps_array, nal); + } else { + goto drop; + } + +done: + gst_buffer_unmap (nal, &map); + + return TRUE; + +drop: + gst_buffer_unmap (nal, &map); + gst_buffer_unref (nal); + + return FALSE; +} + + +static void +gst_rtp_h264_depay_add_sps_pps (GstRtpH264Depay * rtph264depay, GstBuffer * nal) +{ + if (gst_rtp_h264_add_sps_pps (GST_ELEMENT (rtph264depay), + rtph264depay->sps, rtph264depay->pps, nal)) + rtph264depay->new_codec_data = TRUE; +} + +static gboolean +gst_rtp_h264_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + gint clock_rate; + GstStructure *structure = gst_caps_get_structure (caps, 0); + GstRtpH264Depay *rtph264depay; + const gchar *ps; + GstBuffer *codec_data; + GstMapInfo map; + guint8 *ptr; + + rtph264depay = GST_RTP_H264_DEPAY (depayload); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + /* Base64 encoded, comma separated config NALs */ + ps = gst_structure_get_string (structure, "sprop-parameter-sets"); + + /* negotiate with downstream w.r.t. output format and alignment */ + gst_rtp_h264_depay_negotiate (rtph264depay); + + if (rtph264depay->byte_stream && ps != NULL) { + /* for bytestream we only need the parameter sets but we don't error out + * when they are not there, we assume they are in the stream. */ + gchar **params; + guint len, total; + gint i; + + params = g_strsplit (ps, ",", 0); + + /* count total number of bytes in base64. Also include the sync bytes in + * front of the params. */ + len = 0; + for (i = 0; params[i]; i++) { + len += strlen (params[i]); + len += sizeof (sync_bytes); + } + /* we seriously overshoot the length, but it's fine. */ + codec_data = gst_buffer_new_and_alloc (len); + + gst_buffer_map (codec_data, &map, GST_MAP_WRITE); + ptr = map.data; + total = 0; + for (i = 0; params[i]; i++) { + guint save = 0; + gint state = 0; + + GST_DEBUG_OBJECT (depayload, "decoding param %d (%s)", i, params[i]); + memcpy (ptr, sync_bytes, sizeof (sync_bytes)); + ptr += sizeof (sync_bytes); + len = + g_base64_decode_step (params[i], strlen (params[i]), ptr, &state, + &save); + GST_DEBUG_OBJECT (depayload, "decoded %d bytes", len); + total += len + sizeof (sync_bytes); + ptr += len; + } + gst_buffer_unmap (codec_data, &map); + gst_buffer_resize (codec_data, 0, total); + g_strfreev (params); + + /* keep the codec_data, we need to send it as the first buffer. We cannot + * push it in the adapter because the adapter might be flushed on discont. + */ + if (rtph264depay->codec_data) + gst_buffer_unref (rtph264depay->codec_data); + rtph264depay->codec_data = codec_data; + } else if (!rtph264depay->byte_stream) { + gchar **params; + gint i; + + if (ps == NULL) + goto incomplete_caps; + + params = g_strsplit (ps, ",", 0); + + GST_DEBUG_OBJECT (depayload, "we have %d params", g_strv_length (params)); + + /* start with 7 bytes header */ + for (i = 0; params[i]; i++) { + GstBuffer *nal; + GstMapInfo nalmap; + gsize nal_len; + guint save = 0; + gint state = 0; + + nal_len = strlen (params[i]); + nal = gst_buffer_new_and_alloc (nal_len); + gst_buffer_map (nal, &nalmap, GST_MAP_READWRITE); + + nal_len = + g_base64_decode_step (params[i], nal_len, nalmap.data, &state, &save); + + GST_DEBUG_OBJECT (depayload, "adding param %d as %s", i, + ((nalmap.data[0] & 0x1f) == 7) ? "SPS" : "PPS"); + + gst_buffer_unmap (nal, &nalmap); + gst_buffer_set_size (nal, nal_len); + + gst_rtp_h264_depay_add_sps_pps (rtph264depay, nal); + } + g_strfreev (params); + + if (rtph264depay->sps->len == 0 || rtph264depay->pps->len == 0) + goto incomplete_caps; + } + + return gst_rtp_h264_set_src_caps (rtph264depay); + + /* ERRORS */ +incomplete_caps: + { + GST_DEBUG_OBJECT (depayload, "we have incomplete caps," + " doing setcaps later"); + return TRUE; + } +} + +static GstBuffer * +gst_rtp_h264_complete_au (GstRtpH264Depay * rtph264depay, + GstClockTime * out_timestamp, gboolean * out_keyframe) +{ + guint outsize; + GstBuffer *outbuf; + + /* we had a picture in the adapter and we completed it */ + GST_DEBUG_OBJECT (rtph264depay, "taking completed AU"); + outsize = gst_adapter_available (rtph264depay->picture_adapter); + outbuf = gst_adapter_take_buffer (rtph264depay->picture_adapter, outsize); + + *out_timestamp = rtph264depay->last_ts; + *out_keyframe = rtph264depay->last_keyframe; + + rtph264depay->last_keyframe = FALSE; + rtph264depay->picture_start = FALSE; + + return outbuf; +} + +/* SPS/PPS/IDR considered key, all others DELTA; + * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */ +#define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) + +static GstBuffer * +gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal, + GstClockTime in_timestamp, gboolean marker) +{ + GstRTPBaseDepayload *depayload = GST_RTP_BASE_DEPAYLOAD (rtph264depay); + gint nal_type; + GstMapInfo map; + GstBuffer *outbuf = NULL; + GstClockTime out_timestamp; + gboolean keyframe, out_keyframe; + + gst_buffer_map (nal, &map, GST_MAP_READ); + if (G_UNLIKELY (map.size < 5)) + goto short_nal; + + nal_type = map.data[4] & 0x1f; + GST_DEBUG_OBJECT (rtph264depay, "handle NAL type %d", nal_type); + + keyframe = NAL_TYPE_IS_KEY (nal_type); + + out_keyframe = keyframe; + out_timestamp = in_timestamp; + + if (!rtph264depay->byte_stream) { + if (nal_type == 7 || nal_type == 8) { + gst_rtp_h264_depay_add_sps_pps (rtph264depay, + gst_buffer_copy_region (nal, GST_BUFFER_COPY_ALL, + 4, gst_buffer_get_size (nal) - 4)); + gst_buffer_unmap (nal, &map); + gst_buffer_unref (nal); + return NULL; + } else if (rtph264depay->sps->len == 0 || rtph264depay->pps->len == 0) { + /* Down push down any buffer in non-bytestream mode if the SPS/PPS haven't + * go through yet + */ + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "all-headers", G_TYPE_BOOLEAN, TRUE, NULL))); + gst_buffer_unmap (nal, &map); + gst_buffer_unref (nal); + return NULL; + } + + if (rtph264depay->new_codec_data && + rtph264depay->sps->len > 0 && rtph264depay->pps->len > 0) + gst_rtp_h264_set_src_caps (rtph264depay); + } + + + if (rtph264depay->merge) { + gboolean start = FALSE, complete = FALSE; + + /* marker bit isn't mandatory so in the following code we try to guess + * an AU boundary by detecting a new picture start */ + if (!marker) { + /* consider a coded slices (IDR or not) to start a picture, + * (so ending the previous one) if first_mb_in_slice == 0 + * (non-0 is part of previous one) */ + /* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4, + * but in practice it works in sane cases, needs not much parsing, + * and also works with broken frame_num in NAL (where spec-wise would fail) */ + /* FIXME: this code isn't correct for interlaced content as AUs should be + * constructed with pairs of fields and the guess here will just push out + * AUs with a single field in it */ + if (nal_type == 1 || nal_type == 2 || nal_type == 5) { + /* we have a picture start */ + start = TRUE; + if (map.data[5] & 0x80) { + /* first_mb_in_slice == 0 completes a picture */ + complete = TRUE; + } + } else if (nal_type >= 6 && nal_type <= 9) { + /* SEI, SPS, PPS, AU terminate picture */ + complete = TRUE; + } + GST_DEBUG_OBJECT (depayload, "start %d, complete %d", start, complete); + + if (complete && rtph264depay->picture_start) + outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp, + &out_keyframe); + } + /* add to adapter */ + gst_buffer_unmap (nal, &map); + + GST_DEBUG_OBJECT (depayload, "adding NAL to picture adapter"); + gst_adapter_push (rtph264depay->picture_adapter, nal); + rtph264depay->last_ts = in_timestamp; + rtph264depay->last_keyframe |= keyframe; + rtph264depay->picture_start |= start; + + if (marker) + outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp, + &out_keyframe); + } else { + /* no merge, output is input nal */ + GST_DEBUG_OBJECT (depayload, "using NAL as output"); + outbuf = nal; + gst_buffer_unmap (nal, &map); + } + + if (outbuf) { + /* prepend codec_data */ + if (rtph264depay->codec_data) { + GST_DEBUG_OBJECT (depayload, "prepending codec_data"); + outbuf = gst_buffer_append (rtph264depay->codec_data, outbuf); + rtph264depay->codec_data = NULL; + out_keyframe = TRUE; + } + outbuf = gst_buffer_make_writable (outbuf); + + GST_BUFFER_TIMESTAMP (outbuf) = out_timestamp; + + if (out_keyframe) + GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + else + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + } + + return outbuf; + + /* ERRORS */ +short_nal: + { + GST_WARNING_OBJECT (depayload, "dropping short NAL"); + gst_buffer_unmap (nal, &map); + gst_buffer_unref (nal); + return NULL; + } +} + +static GstBuffer * +gst_rtp_h264_push_fragmentation_unit (GstRtpH264Depay * rtph264depay, + gboolean send) +{ + guint outsize; + GstMapInfo map; + GstBuffer *outbuf; + + outsize = gst_adapter_available (rtph264depay->adapter); + outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + GST_DEBUG_OBJECT (rtph264depay, "output %d bytes", outsize); + + if (rtph264depay->byte_stream) { + memcpy (map.data, sync_bytes, sizeof (sync_bytes)); + } else { + outsize -= 4; + map.data[0] = (outsize >> 24); + map.data[1] = (outsize >> 16); + map.data[2] = (outsize >> 8); + map.data[3] = (outsize); + } + gst_buffer_unmap (outbuf, &map); + + rtph264depay->current_fu_type = 0; + + outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, + rtph264depay->fu_timestamp, rtph264depay->fu_marker); + + if (send && outbuf) { + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtph264depay), outbuf); + outbuf = NULL; + } + return outbuf; +} + +static GstBuffer * +gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpH264Depay *rtph264depay; + GstBuffer *outbuf = NULL; + guint8 nal_unit_type; + GstRTPBuffer rtp = { NULL }; + + rtph264depay = GST_RTP_H264_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + gst_adapter_clear (rtph264depay->adapter); + rtph264depay->wait_start = TRUE; + rtph264depay->current_fu_type = 0; + } + + { + gint payload_len; + guint8 *payload; + guint header_len; + guint8 nal_ref_idc; + GstMapInfo map; + guint outsize, nalu_size; + GstClockTime timestamp; + gboolean marker; + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG_OBJECT (rtph264depay, "receiving %d bytes", payload_len); + + if (payload_len == 0) + goto empty_packet; + + /* +---------------+ + * |0|1|2|3|4|5|6|7| + * +-+-+-+-+-+-+-+-+ + * |F|NRI| Type | + * +---------------+ + * + * F must be 0. + */ + nal_ref_idc = (payload[0] & 0x60) >> 5; + nal_unit_type = payload[0] & 0x1f; + + /* at least one byte header with type */ + header_len = 1; + + GST_DEBUG_OBJECT (rtph264depay, "NRI %d, Type %d", nal_ref_idc, + nal_unit_type); + + /* If FU unit was being processed, but the current nal is of a different + * type. Assume that the remote payloader is buggy (didn't set the end bit + * when the FU ended) and send out what we gathered thusfar */ + if (G_UNLIKELY (rtph264depay->current_fu_type != 0 && + nal_unit_type != rtph264depay->current_fu_type)) + gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE); + + switch (nal_unit_type) { + case 0: + case 30: + case 31: + /* undefined */ + goto undefined_type; + case 25: + /* STAP-B Single-time aggregation packet 5.7.1 */ + /* 2 byte extra header for DON */ + header_len += 2; + /* fallthrough */ + case 24: + { + /* strip headers */ + payload += header_len; + payload_len -= header_len; + + rtph264depay->wait_start = FALSE; + + + /* STAP-A Single-time aggregation packet 5.7.1 */ + while (payload_len > 2) { + /* 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | NALU Size | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + nalu_size = (payload[0] << 8) | payload[1]; + + /* dont include nalu_size */ + if (nalu_size > (payload_len - 2)) + nalu_size = payload_len - 2; + + outsize = nalu_size + sizeof (sync_bytes); + outbuf = gst_buffer_new_and_alloc (outsize); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + if (rtph264depay->byte_stream) { + memcpy (map.data, sync_bytes, sizeof (sync_bytes)); + } else { + map.data[0] = map.data[1] = 0; + map.data[2] = payload[0]; + map.data[3] = payload[1]; + } + + /* strip NALU size */ + payload += 2; + payload_len -= 2; + + memcpy (map.data + sizeof (sync_bytes), payload, nalu_size); + gst_buffer_unmap (outbuf, &map); + + outbuf = + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); + if (outbuf) + gst_adapter_push (rtph264depay->adapter, outbuf); + + payload += nalu_size; + payload_len -= nalu_size; + } + + outsize = gst_adapter_available (rtph264depay->adapter); + if (outsize > 0) { + outbuf = gst_adapter_take_buffer (rtph264depay->adapter, outsize); + outbuf = + gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); + } + break; + } + case 26: + /* MTAP16 Multi-time aggregation packet 5.7.2 */ + // header_len = 5; + /* fallthrough, not implemented */ + case 27: + /* MTAP24 Multi-time aggregation packet 5.7.2 */ + // header_len = 6; + goto not_implemented; + break; + case 28: + case 29: + { + /* FU-A Fragmentation unit 5.8 */ + /* FU-B Fragmentation unit 5.8 */ + gboolean S, E; + + /* +---------------+ + * |0|1|2|3|4|5|6|7| + * +-+-+-+-+-+-+-+-+ + * |S|E|R| Type | + * +---------------+ + * + * R is reserved and always 0 + */ + S = (payload[1] & 0x80) == 0x80; + E = (payload[1] & 0x40) == 0x40; + + GST_DEBUG_OBJECT (rtph264depay, "S %d, E %d", S, E); + + if (rtph264depay->wait_start && !S) + goto waiting_start; + + if (S) { + /* NAL unit starts here */ + guint8 nal_header; + + /* If a new FU unit started, while still processing an older one. + * Assume that the remote payloader is buggy (doesn't set the end + * bit) and send out what we've gathered thusfar */ + if (G_UNLIKELY (rtph264depay->current_fu_type != 0)) + gst_rtp_h264_push_fragmentation_unit (rtph264depay, TRUE); + + rtph264depay->current_fu_type = nal_unit_type; + rtph264depay->fu_timestamp = timestamp; + + rtph264depay->wait_start = FALSE; + + /* reconstruct NAL header */ + nal_header = (payload[0] & 0xe0) | (payload[1] & 0x1f); + + /* strip type header, keep FU header, we'll reuse it to reconstruct + * the NAL header. */ + payload += 1; + payload_len -= 1; + + nalu_size = payload_len; + outsize = nalu_size + sizeof (sync_bytes); + outbuf = gst_buffer_new_and_alloc (outsize); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + memcpy (map.data + sizeof (sync_bytes), payload, nalu_size); + map.data[sizeof (sync_bytes)] = nal_header; + gst_buffer_unmap (outbuf, &map); + + GST_DEBUG_OBJECT (rtph264depay, "queueing %d bytes", outsize); + + /* and assemble in the adapter */ + gst_adapter_push (rtph264depay->adapter, outbuf); + } else { + /* strip off FU indicator and FU header bytes */ + payload += 2; + payload_len -= 2; + + outsize = payload_len; + outbuf = gst_buffer_new_and_alloc (outsize); + gst_buffer_fill (outbuf, 0, payload, outsize); + + GST_DEBUG_OBJECT (rtph264depay, "queueing %d bytes", outsize); + + /* and assemble in the adapter */ + gst_adapter_push (rtph264depay->adapter, outbuf); + } + + outbuf = NULL; + rtph264depay->fu_marker = marker; + + /* if NAL unit ends, flush the adapter */ + if (E) + outbuf = gst_rtp_h264_push_fragmentation_unit (rtph264depay, FALSE); + break; + } + default: + { + rtph264depay->wait_start = FALSE; + + /* 1-23 NAL unit Single NAL unit packet per H.264 5.6 */ + /* the entire payload is the output buffer */ + nalu_size = payload_len; + outsize = nalu_size + sizeof (sync_bytes); + outbuf = gst_buffer_new_and_alloc (outsize); + + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + if (rtph264depay->byte_stream) { + memcpy (map.data, sync_bytes, sizeof (sync_bytes)); + } else { + map.data[0] = map.data[1] = 0; + map.data[2] = nalu_size >> 8; + map.data[3] = nalu_size & 0xff; + } + memcpy (map.data + sizeof (sync_bytes), payload, nalu_size); + gst_buffer_unmap (outbuf, &map); + + outbuf = gst_rtp_h264_depay_handle_nal (rtph264depay, outbuf, timestamp, + marker); + break; + } + } + gst_rtp_buffer_unmap (&rtp); + } + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_DEBUG_OBJECT (rtph264depay, "empty packet"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +undefined_type: + { + GST_ELEMENT_WARNING (rtph264depay, STREAM, DECODE, + (NULL), ("Undefined packet type")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +waiting_start: + { + GST_DEBUG_OBJECT (rtph264depay, "waiting for start"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +not_implemented: + { + GST_ELEMENT_ERROR (rtph264depay, STREAM, FORMAT, + (NULL), ("NAL unit type %d not supported yet", nal_unit_type)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static gboolean +gst_rtp_h264_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event) +{ + GstRtpH264Depay *rtph264depay; + + rtph264depay = GST_RTP_H264_DEPAY (depay); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_h264_depay_reset (rtph264depay); + break; + default: + break; + } + + return + GST_RTP_BASE_DEPAYLOAD_CLASS (parent_class)->handle_event (depay, event); +} + +static GstStateChangeReturn +gst_rtp_h264_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpH264Depay *rtph264depay; + GstStateChangeReturn ret; + + rtph264depay = GST_RTP_H264_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_h264_depay_reset (rtph264depay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_h264_depay_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (rtph264depay_debug, "rtph264depay", 0, + "H264 Video RTP Depayloader"); + + return gst_element_register (plugin, "rtph264depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H264_DEPAY); +} diff --git a/gst/rtp/gstrtph264depay.h b/gst/rtp/gstrtph264depay.h new file mode 100755 index 0000000..4895236 --- /dev/null +++ b/gst/rtp/gstrtph264depay.h @@ -0,0 +1,85 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_H264_DEPAY_H__ +#define __GST_RTP_H264_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_H264_DEPAY \ + (gst_rtp_h264_depay_get_type()) +#define GST_RTP_H264_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H264_DEPAY,GstRtpH264Depay)) +#define GST_RTP_H264_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H264_DEPAY,GstRtpH264DepayClass)) +#define GST_IS_RTP_H264_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H264_DEPAY)) +#define GST_IS_RTP_H264_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H264_DEPAY)) + +typedef struct _GstRtpH264Depay GstRtpH264Depay; +typedef struct _GstRtpH264DepayClass GstRtpH264DepayClass; + +struct _GstRtpH264Depay +{ + GstRTPBaseDepayload depayload; + + gboolean byte_stream; + + GstBuffer *codec_data; + GstAdapter *adapter; + gboolean wait_start; + + /* nal merging */ + gboolean merge; + GstAdapter *picture_adapter; + gboolean picture_start; + GstClockTime last_ts; + gboolean last_keyframe; + + /* Work around broken payloaders wrt. FU-A & FU-B */ + guint8 current_fu_type; + GstClockTime fu_timestamp; + gboolean fu_marker; + + /* misc */ + GPtrArray *sps; + GPtrArray *pps; + gboolean new_codec_data; +}; + +struct _GstRtpH264DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_h264_depay_get_type (void); + +gboolean gst_rtp_h264_depay_plugin_init (GstPlugin * plugin); + +gboolean gst_rtp_h264_add_sps_pps (GstElement * rtph264, GPtrArray * sps, + GPtrArray * pps, GstBuffer * nal); + +G_END_DECLS + +#endif /* __GST_RTP_H264_DEPAY_H__ */ diff --git a/gst/rtp/gstrtph264pay.c b/gst/rtp/gstrtph264pay.c new file mode 100755 index 0000000..665814a --- /dev/null +++ b/gst/rtp/gstrtph264pay.c @@ -0,0 +1,1405 @@ +/* ex: set tabstop=2 shiftwidth=2 expandtab: */ +/* GStreamer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include <gst/pbutils/pbutils.h> + +/* Included to not duplicate gst_rtp_h264_add_sps_pps () */ +#include "gstrtph264depay.h" + +#include "gstrtph264pay.h" + + +#define IDR_TYPE_ID 5 +#define SPS_TYPE_ID 7 +#define PPS_TYPE_ID 8 + +GST_DEBUG_CATEGORY_STATIC (rtph264pay_debug); +#define GST_CAT_DEFAULT (rtph264pay_debug) + +/* references: + * + * RFC 3984 + */ + +static GstStaticPadTemplate gst_rtp_h264_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "stream-format = (string) avc, alignment = (string) au;" + "video/x-h264, " + "stream-format = (string) byte-stream, alignment = (string) { nal, au }") + ); + +static GstStaticPadTemplate gst_rtp_h264_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"H264\"") + ); + +#define DEFAULT_SPROP_PARAMETER_SETS NULL +#define DEFAULT_CONFIG_INTERVAL 0 + +enum +{ + PROP_0, + PROP_SPROP_PARAMETER_SETS, + PROP_CONFIG_INTERVAL +}; + +#define IS_ACCESS_UNIT(x) (((x) > 0x00) && ((x) < 0x06)) + +static void gst_rtp_h264_pay_finalize (GObject * object); + +static void gst_rtp_h264_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_h264_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstCaps *gst_rtp_h264_pay_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static gboolean gst_rtp_h264_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstFlowReturn gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); +static gboolean gst_rtp_h264_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); +static GstStateChangeReturn gst_rtp_h264_pay_change_state (GstElement * + element, GstStateChange transition); + +#define gst_rtp_h264_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpH264Pay, gst_rtp_h264_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_h264_pay_class_init (GstRtpH264PayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_h264_pay_set_property; + gobject_class->get_property = gst_rtp_h264_pay_get_property; + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_SPROP_PARAMETER_SETS, g_param_spec_string ("sprop-parameter-sets", + "sprop-parameter-sets", + "The base64 sprop-parameter-sets to set in out caps (set to NULL to " + "extract from stream)", + DEFAULT_SPROP_PARAMETER_SETS, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_CONFIG_INTERVAL, + g_param_spec_uint ("config-interval", + "SPS PPS Send Interval", + "Send SPS and PPS Insertion Interval in seconds (sprop parameter sets " + "will be multiplexed in the data stream when detected.) (0 = disabled)", + 0, 3600, DEFAULT_CONFIG_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gobject_class->finalize = gst_rtp_h264_pay_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h264_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_h264_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP H264 payloader", + "Codec/Payloader/Network/RTP", + "Payload-encode H264 video into RTP packets (RFC 3984)", + "Laurent Glayal <spglegle@yahoo.fr>"); + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rtp_h264_pay_change_state); + + gstrtpbasepayload_class->get_caps = gst_rtp_h264_pay_getcaps; + gstrtpbasepayload_class->set_caps = gst_rtp_h264_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_h264_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_h264_pay_sink_event; + + GST_DEBUG_CATEGORY_INIT (rtph264pay_debug, "rtph264pay", 0, + "H264 RTP Payloader"); +} + +static void +gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay) +{ + rtph264pay->queue = g_array_new (FALSE, FALSE, sizeof (guint)); + rtph264pay->profile = 0; + rtph264pay->sps = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gst_buffer_unref); + rtph264pay->pps = g_ptr_array_new_with_free_func ( + (GDestroyNotify) gst_buffer_unref); + rtph264pay->last_spspps = -1; + rtph264pay->spspps_interval = DEFAULT_CONFIG_INTERVAL; + rtph264pay->delta_unit = FALSE; + rtph264pay->discont = FALSE; + + rtph264pay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_h264_pay_clear_sps_pps (GstRtpH264Pay * rtph264pay) +{ + g_ptr_array_set_size (rtph264pay->sps, 0); + g_ptr_array_set_size (rtph264pay->pps, 0); +} + +static void +gst_rtp_h264_pay_finalize (GObject * object) +{ + GstRtpH264Pay *rtph264pay; + + rtph264pay = GST_RTP_H264_PAY (object); + + g_array_free (rtph264pay->queue, TRUE); + + g_ptr_array_free (rtph264pay->sps, TRUE); + g_ptr_array_free (rtph264pay->pps, TRUE); + + g_free (rtph264pay->sprop_parameter_sets); + + g_object_unref (rtph264pay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const gchar all_levels[][4] = { + "1", + "1b", + "1.1", + "1.2", + "1.3", + "2", + "2.1", + "2.2", + "3", + "3.1", + "3.2", + "4", + "4.1", + "4.2", + "5", + "5.1" +}; + +static GstCaps * +gst_rtp_h264_pay_getcaps (GstRTPBasePayload * payload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *template_caps; + GstCaps *allowed_caps; + GstCaps *caps, *icaps; + gboolean append_unrestricted; + guint i; + + allowed_caps = + gst_pad_peer_query_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload), NULL); + + if (allowed_caps == NULL) + return NULL; + + template_caps = + gst_static_pad_template_get_caps (&gst_rtp_h264_pay_sink_template); + + if (gst_caps_is_any (allowed_caps)) { + caps = gst_caps_ref (template_caps); + goto done; + } + + if (gst_caps_is_empty (allowed_caps)) { + caps = gst_caps_ref (allowed_caps); + goto done; + } + + caps = gst_caps_new_empty (); + + append_unrestricted = FALSE; + for (i = 0; i < gst_caps_get_size (allowed_caps); i++) { + GstStructure *s = gst_caps_get_structure (allowed_caps, i); + GstStructure *new_s = gst_structure_new_empty ("video/x-h264"); + const gchar *profile_level_id; + + profile_level_id = gst_structure_get_string (s, "profile-level-id"); + + if (profile_level_id && strlen (profile_level_id) == 6) { + const gchar *profile; + const gchar *level; + long int spsint; + guint8 sps[3]; + + spsint = strtol (profile_level_id, NULL, 16); + sps[0] = spsint >> 16; + sps[1] = spsint >> 8; + sps[2] = spsint; + + profile = gst_codec_utils_h264_get_profile (sps, 3); + level = gst_codec_utils_h264_get_level (sps, 3); + + if (profile && level) { + GST_LOG_OBJECT (payload, "In caps, have profile %s and level %s", + profile, level); + + if (!strcmp (profile, "constrained-baseline")) + gst_structure_set (new_s, "profile", G_TYPE_STRING, profile, NULL); + else { + GValue val = { 0, }; + GValue profiles = { 0, }; + + g_value_init (&profiles, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + g_value_set_static_string (&val, profile); + gst_value_list_append_value (&profiles, &val); + + g_value_set_static_string (&val, "constrained-baseline"); + gst_value_list_append_value (&profiles, &val); + + gst_structure_take_value (new_s, "profile", &profiles); + } + + if (!strcmp (level, "1")) + gst_structure_set (new_s, "level", G_TYPE_STRING, level, NULL); + else { + GValue levels = { 0, }; + GValue val = { 0, }; + int j; + + g_value_init (&levels, GST_TYPE_LIST); + g_value_init (&val, G_TYPE_STRING); + + for (j = 0; j < G_N_ELEMENTS (all_levels); j++) { + g_value_set_static_string (&val, all_levels[j]); + gst_value_list_prepend_value (&levels, &val); + if (!strcmp (level, all_levels[j])) + break; + } + gst_structure_take_value (new_s, "level", &levels); + } + } else { + /* Invalid profile-level-id means baseline */ + + gst_structure_set (new_s, + "profile", G_TYPE_STRING, "constrained-baseline", NULL); + } + } else { + /* No profile-level-id means baseline or unrestricted */ + + gst_structure_set (new_s, + "profile", G_TYPE_STRING, "constrained-baseline", NULL); + append_unrestricted = TRUE; + } + + caps = gst_caps_merge_structure (caps, new_s); + } + + if (append_unrestricted) { + caps = + gst_caps_merge_structure (caps, gst_structure_new ("video/x-h264", NULL, + NULL)); + } + + icaps = gst_caps_intersect (caps, template_caps); + gst_caps_unref (caps); + caps = icaps; + +done: + + gst_caps_unref (template_caps); + gst_caps_unref (allowed_caps); + + GST_LOG_OBJECT (payload, "returning caps %" GST_PTR_FORMAT, caps); + return caps; +} + +/* take the currently configured SPS and PPS lists and set them on the caps as + * sprop-parameter-sets */ +static gboolean +gst_rtp_h264_pay_set_sps_pps (GstRTPBasePayload * basepayload) +{ + GstRtpH264Pay *payloader = GST_RTP_H264_PAY (basepayload); + gchar *profile; + gchar *set; + GString *sprops; + guint count; + gboolean res; + GstMapInfo map; + guint i; + + sprops = g_string_new (""); + count = 0; + + /* build the sprop-parameter-sets */ + for (i = 0; i < payloader->sps->len; i++) { + GstBuffer *sps_buf = + GST_BUFFER_CAST (g_ptr_array_index (payloader->sps, i)); + + gst_buffer_map (sps_buf, &map, GST_MAP_READ); + set = g_base64_encode (map.data, map.size); + gst_buffer_unmap (sps_buf, &map); + + g_string_append_printf (sprops, "%s%s", count ? "," : "", set); + g_free (set); + count++; + } + for (i = 0; i < payloader->pps->len; i++) { + GstBuffer *pps_buf = + GST_BUFFER_CAST (g_ptr_array_index (payloader->pps, i)); + + gst_buffer_map (pps_buf, &map, GST_MAP_READ); + set = g_base64_encode (map.data, map.size); + gst_buffer_unmap (pps_buf, &map); + + g_string_append_printf (sprops, "%s%s", count ? "," : "", set); + g_free (set); + count++; + } + + if (G_LIKELY (count)) { + if (payloader->profile != 0) { + /* profile is 24 bit. Force it to respect the limit */ + profile = g_strdup_printf ("%06x", payloader->profile & 0xffffff); + /* combine into output caps */ + res = gst_rtp_base_payload_set_outcaps (basepayload, + "packetization-mode", G_TYPE_STRING, "1", + "profile-level-id", G_TYPE_STRING, profile, + "sprop-parameter-sets", G_TYPE_STRING, sprops->str, NULL); + g_free (profile); + } else { + res = gst_rtp_base_payload_set_outcaps (basepayload, + "packetization-mode", G_TYPE_STRING, "1", + "sprop-parameter-sets", G_TYPE_STRING, sprops->str, NULL); + } + + } else { + res = gst_rtp_base_payload_set_outcaps (basepayload, NULL); + } + g_string_free (sprops, TRUE); + + return res; +} + + +static gboolean +gst_rtp_h264_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpH264Pay *rtph264pay; + GstStructure *str; + const GValue *value; + GstMapInfo map; + guint8 *data; + gsize size; + GstBuffer *buffer; + const gchar *alignment, *stream_format; + + rtph264pay = GST_RTP_H264_PAY (basepayload); + + str = gst_caps_get_structure (caps, 0); + + /* we can only set the output caps when we found the sprops and profile + * NALs */ + gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "H264", 90000); + + rtph264pay->alignment = GST_H264_ALIGNMENT_UNKNOWN; + alignment = gst_structure_get_string (str, "alignment"); + if (alignment) { + if (g_str_equal (alignment, "au")) + rtph264pay->alignment = GST_H264_ALIGNMENT_AU; + if (g_str_equal (alignment, "nal")) + rtph264pay->alignment = GST_H264_ALIGNMENT_NAL; + } + + rtph264pay->stream_format = GST_H264_STREAM_FORMAT_UNKNOWN; + stream_format = gst_structure_get_string (str, "stream-format"); + if (stream_format) { + if (g_str_equal (stream_format, "avc")) + rtph264pay->stream_format = GST_H264_STREAM_FORMAT_AVC; + if (g_str_equal (stream_format, "byte-stream")) + rtph264pay->stream_format = GST_H264_STREAM_FORMAT_BYTESTREAM; + } + + /* packetized AVC video has a codec_data */ + if ((value = gst_structure_get_value (str, "codec_data"))) { + guint num_sps, num_pps; + gint i, nal_size; + + GST_DEBUG_OBJECT (rtph264pay, "have packetized h264"); + + buffer = gst_value_get_buffer (value); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + /* parse the avcC data */ + if (size < 7) + goto avcc_too_small; + /* parse the version, this must be 1 */ + if (data[0] != 1) + goto wrong_version; + + /* AVCProfileIndication */ + /* profile_compat */ + /* AVCLevelIndication */ + rtph264pay->profile = (data[1] << 16) | (data[2] << 8) | data[3]; + GST_DEBUG_OBJECT (rtph264pay, "profile %06x", rtph264pay->profile); + + /* 6 bits reserved | 2 bits lengthSizeMinusOne */ + /* this is the number of bytes in front of the NAL units to mark their + * length */ + rtph264pay->nal_length_size = (data[4] & 0x03) + 1; + GST_DEBUG_OBJECT (rtph264pay, "nal length %u", rtph264pay->nal_length_size); + /* 3 bits reserved | 5 bits numOfSequenceParameterSets */ + num_sps = data[5] & 0x1f; + GST_DEBUG_OBJECT (rtph264pay, "num SPS %u", num_sps); + + data += 6; + size -= 6; + + /* create the sprop-parameter-sets */ + for (i = 0; i < num_sps; i++) { + GstBuffer *sps_buf; + + if (size < 2) + goto avcc_error; + + nal_size = (data[0] << 8) | data[1]; + data += 2; + size -= 2; + + GST_LOG_OBJECT (rtph264pay, "SPS %d size %d", i, nal_size); + + if (size < nal_size) + goto avcc_error; + + /* make a buffer out of it and add to SPS list */ + sps_buf = gst_buffer_new_and_alloc (nal_size); + gst_buffer_fill (sps_buf, 0, data, nal_size); + gst_rtp_h264_add_sps_pps (GST_ELEMENT (rtph264pay), rtph264pay->sps, + rtph264pay->pps, sps_buf); + data += nal_size; + size -= nal_size; + } + if (size < 1) + goto avcc_error; + + /* 8 bits numOfPictureParameterSets */ + num_pps = data[0]; + data += 1; + size -= 1; + + GST_DEBUG_OBJECT (rtph264pay, "num PPS %u", num_pps); + for (i = 0; i < num_pps; i++) { + GstBuffer *pps_buf; + + if (size < 2) + goto avcc_error; + + nal_size = (data[0] << 8) | data[1]; + data += 2; + size -= 2; + + GST_LOG_OBJECT (rtph264pay, "PPS %d size %d", i, nal_size); + + if (size < nal_size) + goto avcc_error; + + /* make a buffer out of it and add to PPS list */ + pps_buf = gst_buffer_new_and_alloc (nal_size); + gst_buffer_fill (pps_buf, 0, data, nal_size); + gst_rtp_h264_add_sps_pps (GST_ELEMENT (rtph264pay), rtph264pay->sps, + rtph264pay->pps, pps_buf); + + data += nal_size; + size -= nal_size; + } + + /* and update the caps with the collected data */ + if (!gst_rtp_h264_pay_set_sps_pps (basepayload)) + goto set_sps_pps_failed; + + gst_buffer_unmap (buffer, &map); + } else { + GST_DEBUG_OBJECT (rtph264pay, "have bytestream h264"); + } + + return TRUE; + +avcc_too_small: + { + GST_ERROR_OBJECT (rtph264pay, "avcC size %" G_GSIZE_FORMAT " < 7", size); + goto error; + } +wrong_version: + { + GST_ERROR_OBJECT (rtph264pay, "wrong avcC version"); + goto error; + } +avcc_error: + { + GST_ERROR_OBJECT (rtph264pay, "avcC too small "); + goto error; + } +set_sps_pps_failed: + { + GST_ERROR_OBJECT (rtph264pay, "failed to set sps/pps"); + goto error; + } +error: + { + gst_buffer_unmap (buffer, &map); + return FALSE; + } +} + +static void +gst_rtp_h264_pay_parse_sprop_parameter_sets (GstRtpH264Pay * rtph264pay) +{ + const gchar *ps; + gchar **params; + guint len; + gint i; + GstBuffer *buf; + + ps = rtph264pay->sprop_parameter_sets; + if (ps == NULL) + return; + + gst_rtp_h264_pay_clear_sps_pps (rtph264pay); + + params = g_strsplit (ps, ",", 0); + len = g_strv_length (params); + + GST_DEBUG_OBJECT (rtph264pay, "we have %d params", len); + + for (i = 0; params[i]; i++) { + gsize nal_len; + GstMapInfo map; + guint8 *nalp; + guint save = 0; + gint state = 0; + + nal_len = strlen (params[i]); + buf = gst_buffer_new_and_alloc (nal_len); + + gst_buffer_map (buf, &map, GST_MAP_WRITE); + nalp = map.data; + nal_len = g_base64_decode_step (params[i], nal_len, nalp, &state, &save); + gst_buffer_unmap (buf, &map); + gst_buffer_resize (buf, 0, nal_len); + + if (!nal_len) { + gst_buffer_unref (buf); + continue; + } + + gst_rtp_h264_add_sps_pps (GST_ELEMENT (rtph264pay), rtph264pay->sps, + rtph264pay->pps, buf); + } + g_strfreev (params); +} + +static guint +next_start_code (const guint8 * data, guint size) +{ + /* Boyer-Moore string matching algorithm, in a degenerative + * sense because our search 'alphabet' is binary - 0 & 1 only. + * This allow us to simplify the general BM algorithm to a very + * simple form. */ + /* assume 1 is in the 3th byte */ + guint offset = 2; + + while (offset < size) { + if (1 == data[offset]) { + unsigned int shift = offset; + + if (0 == data[--shift]) { + if (0 == data[--shift]) { + return shift; + } + } + /* The jump is always 3 because of the 1 previously matched. + * All the 0's must be after this '1' matched at offset */ + offset += 3; + } else if (0 == data[offset]) { + /* maybe next byte is 1? */ + offset++; + } else { + /* can jump 3 bytes forward */ + offset += 3; + } + /* at each iteration, we rescan in a backward manner until + * we match 0.0.1 in reverse order. Since our search string + * has only 2 'alpabets' (i.e. 0 & 1), we know that any + * mismatch will force us to shift a fixed number of steps */ + } + GST_DEBUG ("Cannot find next NAL start code. returning %u", size); + + return size; +} + +static gboolean +gst_rtp_h264_pay_decode_nal (GstRtpH264Pay * payloader, + const guint8 * data, guint size, GstClockTime dts, GstClockTime pts) +{ + guint8 header, type; + gboolean updated; + + /* default is no update */ + updated = FALSE; + + GST_DEBUG ("NAL payload len=%u", size); + + header = data[0]; + type = header & 0x1f; + + /* We record the timestamp of the last SPS/PPS so + * that we can insert them at regular intervals and when needed. */ + if (SPS_TYPE_ID == type || PPS_TYPE_ID == type) { + GstBuffer *nal; + + /* encode the entire SPS NAL in base64 */ + GST_DEBUG ("Found %s %x %x %x Len=%u", type == SPS_TYPE_ID ? "SPS" : "PPS", + (header >> 7), (header >> 5) & 3, type, size); + + nal = gst_buffer_new_allocate (NULL, size, NULL); + gst_buffer_fill (nal, 0, data, size); + + updated = gst_rtp_h264_add_sps_pps (GST_ELEMENT (payloader), + payloader->sps, payloader->pps, nal); + + /* remember when we last saw SPS */ + if (updated && pts != -1) + payloader->last_spspps = pts; + } else { + GST_DEBUG ("NAL: %x %x %x Len = %u", (header >> 7), + (header >> 5) & 3, type, size); + } + + return updated; +} + +static GstFlowReturn +gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, + GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au, + gboolean delta_unit, gboolean discont); + +static GstFlowReturn +gst_rtp_h264_pay_send_sps_pps (GstRTPBasePayload * basepayload, + GstRtpH264Pay * rtph264pay, GstClockTime dts, GstClockTime pts) +{ + GstFlowReturn ret = GST_FLOW_OK; + gboolean sent_all_sps_pps = TRUE; + guint i; + + for (i = 0; i < rtph264pay->sps->len; i++) { + GstBuffer *sps_buf = + GST_BUFFER_CAST (g_ptr_array_index (rtph264pay->sps, i)); + + GST_DEBUG_OBJECT (rtph264pay, "inserting SPS in the stream"); + /* resend SPS */ + ret = gst_rtp_h264_pay_payload_nal (basepayload, gst_buffer_ref (sps_buf), + dts, pts, FALSE, FALSE, FALSE); + /* Not critical here; but throw a warning */ + if (ret != GST_FLOW_OK) { + sent_all_sps_pps = FALSE; + GST_WARNING ("Problem pushing SPS"); + } + } + for (i = 0; i < rtph264pay->pps->len; i++) { + GstBuffer *pps_buf = + GST_BUFFER_CAST (g_ptr_array_index (rtph264pay->pps, i)); + + GST_DEBUG_OBJECT (rtph264pay, "inserting PPS in the stream"); + /* resend PPS */ + ret = gst_rtp_h264_pay_payload_nal (basepayload, gst_buffer_ref (pps_buf), + dts, pts, FALSE, FALSE, FALSE); + /* Not critical here; but throw a warning */ + if (ret != GST_FLOW_OK) { + sent_all_sps_pps = FALSE; + GST_WARNING ("Problem pushing PPS"); + } + } + + if (pts != -1 && sent_all_sps_pps) + rtph264pay->last_spspps = pts; + + return ret; +} + +/* @delta_unit: if %FALSE the first packet sent won't have the + * GST_BUFFER_FLAG_DELTA_UNIT flag. + * @discont: if %TRUE the first packet sent will have the + * GST_BUFFER_FLAG_DISCONT flag. + */ +static GstFlowReturn +gst_rtp_h264_pay_payload_nal (GstRTPBasePayload * basepayload, + GstBuffer * paybuf, GstClockTime dts, GstClockTime pts, gboolean end_of_au, + gboolean delta_unit, gboolean discont) +{ + GstRtpH264Pay *rtph264pay; + GstFlowReturn ret; + guint8 nalHeader; + guint8 nalType; + guint packet_len, payload_len, mtu; + GstBuffer *outbuf; + guint8 *payload; + GstBufferList *list = NULL; + gboolean send_spspps; + GstRTPBuffer rtp = { NULL }; + guint size = gst_buffer_get_size (paybuf); + + rtph264pay = GST_RTP_H264_PAY (basepayload); + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtph264pay); + + gst_buffer_extract (paybuf, 0, &nalHeader, 1); + nalType = nalHeader & 0x1f; + + GST_DEBUG_OBJECT (rtph264pay, "Processing Buffer with NAL TYPE=%d", nalType); + + /* should set src caps before pushing stuff, + * and if we did not see enough SPS/PPS, that may not be the case */ + if (G_UNLIKELY (!gst_pad_has_current_caps (GST_RTP_BASE_PAYLOAD_SRCPAD + (basepayload)))) + gst_rtp_h264_pay_set_sps_pps (basepayload); + + send_spspps = FALSE; + + /* check if we need to emit an SPS/PPS now */ + if (nalType == IDR_TYPE_ID && rtph264pay->spspps_interval > 0) { + if (rtph264pay->last_spspps != -1) { + guint64 diff; + + GST_LOG_OBJECT (rtph264pay, + "now %" GST_TIME_FORMAT ", last SPS/PPS %" GST_TIME_FORMAT, + GST_TIME_ARGS (pts), GST_TIME_ARGS (rtph264pay->last_spspps)); + + /* calculate diff between last SPS/PPS in milliseconds */ + if (pts > rtph264pay->last_spspps) + diff = pts - rtph264pay->last_spspps; + else + diff = 0; + + GST_DEBUG_OBJECT (rtph264pay, + "interval since last SPS/PPS %" GST_TIME_FORMAT, + GST_TIME_ARGS (diff)); + + /* bigger than interval, queue SPS/PPS */ + if (GST_TIME_AS_SECONDS (diff) >= rtph264pay->spspps_interval) { + GST_DEBUG_OBJECT (rtph264pay, "time to send SPS/PPS"); + send_spspps = TRUE; + } + } else { + /* no know previous SPS/PPS time, send now */ + GST_DEBUG_OBJECT (rtph264pay, "no previous SPS/PPS time, send now"); + send_spspps = TRUE; + } + } + + if (send_spspps || rtph264pay->send_spspps) { + /* we need to send SPS/PPS now first. FIXME, don't use the pts for + * checking when we need to send SPS/PPS but convert to running_time first. */ + rtph264pay->send_spspps = FALSE; + ret = gst_rtp_h264_pay_send_sps_pps (basepayload, rtph264pay, dts, pts); + if (ret != GST_FLOW_OK) { + gst_buffer_unref (paybuf); + return ret; + } + } + + packet_len = gst_rtp_buffer_calc_packet_len (size, 0, 0); + + if (packet_len < mtu) { + /* will fit in one packet */ + GST_DEBUG_OBJECT (basepayload, + "NAL Unit fit in one packet datasize=%d mtu=%d", size, mtu); + + /* create buffer without payload containing only the RTP header + * (memory block at index 0) */ + outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* only set the marker bit on packets containing access units */ + if (IS_ACCESS_UNIT (nalType) && end_of_au) { + gst_rtp_buffer_set_marker (&rtp, 1); + } + + /* timestamp the outbuffer */ + GST_BUFFER_PTS (outbuf) = pts; + GST_BUFFER_DTS (outbuf) = dts; + + if (!delta_unit) + /* Only the first packet sent should not have the flag */ + delta_unit = TRUE; + else + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first packet sent should have the flag */ + discont = FALSE; + } + + gst_rtp_buffer_unmap (&rtp); + + /* insert payload memory block */ + outbuf = gst_buffer_append (outbuf, paybuf); + + /* push the buffer to the next element */ + ret = gst_rtp_base_payload_push (basepayload, outbuf); + } else { + /* fragmentation Units FU-A */ + guint limitedSize; + int ii = 0, start = 1, end = 0, pos = 0; + + GST_DEBUG_OBJECT (basepayload, + "NAL Unit DOES NOT fit in one packet datasize=%d mtu=%d", size, mtu); + + pos++; + size--; + + ret = GST_FLOW_OK; + + GST_DEBUG_OBJECT (basepayload, "Using FU-A fragmentation for data size=%d", + size); + + /* We keep 2 bytes for FU indicator and FU Header */ + payload_len = gst_rtp_buffer_calc_payload_len (mtu - 2, 0, 0); + + list = gst_buffer_list_new_sized ((size / payload_len) + 1); + + while (end == 0) { + limitedSize = size < payload_len ? size : payload_len; + GST_DEBUG_OBJECT (basepayload, + "Inside FU-A fragmentation limitedSize=%d iteration=%d", limitedSize, + ii); + + /* use buffer lists + * create buffer without payload containing only the RTP header + * (memory block at index 0) */ + outbuf = gst_rtp_buffer_new_allocate (2, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + GST_BUFFER_DTS (outbuf) = dts; + GST_BUFFER_PTS (outbuf) = pts; + payload = gst_rtp_buffer_get_payload (&rtp); + + if (limitedSize == size) { + GST_DEBUG_OBJECT (basepayload, "end size=%d iteration=%d", size, ii); + end = 1; + } + if (IS_ACCESS_UNIT (nalType)) { + gst_rtp_buffer_set_marker (&rtp, end && end_of_au); + } + + /* FU indicator */ + payload[0] = (nalHeader & 0x60) | 28; + + /* FU Header */ + payload[1] = (start << 7) | (end << 6) | (nalHeader & 0x1f); + + gst_rtp_buffer_unmap (&rtp); + + /* insert payload memory block */ + gst_buffer_append (outbuf, + gst_buffer_copy_region (paybuf, GST_BUFFER_COPY_MEMORY, pos, + limitedSize)); + + if (!delta_unit) + /* Only the first packet sent should not have the flag */ + delta_unit = TRUE; + else + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first packet sent should have the flag */ + discont = FALSE; + } + + /* add the buffer to the buffer list */ + gst_buffer_list_add (list, outbuf); + + + size -= limitedSize; + pos += limitedSize; + ii++; + start = 0; + } + + ret = gst_rtp_base_payload_push_list (basepayload, list); + gst_buffer_unref (paybuf); + } + return ret; +} + +static GstFlowReturn +gst_rtp_h264_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpH264Pay *rtph264pay; + GstFlowReturn ret; + gsize size; + guint nal_len, i; + GstMapInfo map; + const guint8 *data; + GstClockTime dts, pts; + GArray *nal_queue; + gboolean avc; + GstBuffer *paybuf = NULL; + gsize skip; + gboolean delayed_not_delta_unit = FALSE; + gboolean delayed_discont = FALSE; + + rtph264pay = GST_RTP_H264_PAY (basepayload); + + /* the input buffer contains one or more NAL units */ + + avc = rtph264pay->stream_format == GST_H264_STREAM_FORMAT_AVC; + + if (avc) { + /* In AVC mode, there is no adapter, so nothign to flush */ + if (buffer == NULL) + return GST_FLOW_OK; + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + pts = GST_BUFFER_PTS (buffer); + dts = GST_BUFFER_DTS (buffer); + rtph264pay->delta_unit = GST_BUFFER_FLAG_IS_SET (buffer, + GST_BUFFER_FLAG_DELTA_UNIT); + rtph264pay->discont = GST_BUFFER_IS_DISCONT (buffer); + GST_DEBUG_OBJECT (basepayload, "got %" G_GSIZE_FORMAT " bytes", size); + } else { + dts = gst_adapter_prev_dts (rtph264pay->adapter, NULL); + pts = gst_adapter_prev_pts (rtph264pay->adapter, NULL); + if (buffer) { + if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { + if (gst_adapter_available (rtph264pay->adapter) == 0) + rtph264pay->delta_unit = FALSE; + else + /* This buffer contains a key frame but the adapter isn't empty. So + * we'll purge it first by sending a first packet and then the second + * one won't have the DELTA_UNIT flag. */ + delayed_not_delta_unit = TRUE; + } + + if (GST_BUFFER_IS_DISCONT (buffer)) { + if (gst_adapter_available (rtph264pay->adapter) == 0) + rtph264pay->discont = TRUE; + else + /* This buffer has the DISCONT flag but the adapter isn't empty. So + * we'll purge it first by sending a first packet and then the second + * one will have the DISCONT flag set. */ + delayed_discont = TRUE; + } + + if (!GST_CLOCK_TIME_IS_VALID (dts)) + dts = GST_BUFFER_DTS (buffer); + if (!GST_CLOCK_TIME_IS_VALID (pts)) + pts = GST_BUFFER_PTS (buffer); + + gst_adapter_push (rtph264pay->adapter, buffer); + } + size = gst_adapter_available (rtph264pay->adapter); + /* Nothing to do here if the adapter is empty, e.g. on EOS */ + if (size == 0) + return GST_FLOW_OK; + data = gst_adapter_map (rtph264pay->adapter, size); + GST_DEBUG_OBJECT (basepayload, + "got %" G_GSIZE_FORMAT " bytes (%" G_GSIZE_FORMAT ")", size, + buffer ? gst_buffer_get_size (buffer) : 0); + } + + ret = GST_FLOW_OK; + + /* now loop over all NAL units and put them in a packet + * FIXME, we should really try to pack multiple NAL units into one RTP packet + * if we can, especially for the config packets that wont't cause decoder + * latency. */ + if (avc) { + guint nal_length_size; + gsize offset = 0; + + nal_length_size = rtph264pay->nal_length_size; + + while (size > nal_length_size) { + gint i; + gboolean end_of_au = FALSE; + + nal_len = 0; + for (i = 0; i < nal_length_size; i++) { + nal_len = ((nal_len << 8) + data[i]); + } + + /* skip the length bytes, make sure we don't run past the buffer size */ + data += nal_length_size; + offset += nal_length_size; + size -= nal_length_size; + + if (size >= nal_len) { + GST_DEBUG_OBJECT (basepayload, "got NAL of size %u", nal_len); + } else { + nal_len = size; + GST_DEBUG_OBJECT (basepayload, "got incomplete NAL of size %u", + nal_len); + } + + /* If we're at the end of the buffer, then we're at the end of the + * access unit + */ + if (rtph264pay->alignment == GST_H264_ALIGNMENT_AU + && size - nal_len <= nal_length_size) { + end_of_au = TRUE; + } + + paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, offset, + nal_len); + ret = + gst_rtp_h264_pay_payload_nal (basepayload, paybuf, dts, pts, + end_of_au, rtph264pay->delta_unit, rtph264pay->discont); + + if (!rtph264pay->delta_unit) + /* Only the first outgoing packet doesn't have the DELTA_UNIT flag */ + rtph264pay->delta_unit = TRUE; + + if (rtph264pay->discont) + /* Only the first outgoing packet have the DISCONT flag */ + rtph264pay->discont = FALSE; + + if (ret != GST_FLOW_OK) + break; + + data += nal_len; + offset += nal_len; + size -= nal_len; + } + } else { + guint next; + gboolean update = FALSE; + + /* get offset of first start code */ + next = next_start_code (data, size); + + /* skip to start code, if no start code is found, next will be size and we + * will not collect data. */ + data += next; + size -= next; + nal_queue = rtph264pay->queue; + skip = next; + + /* array must be empty when we get here */ + g_assert (nal_queue->len == 0); + + GST_DEBUG_OBJECT (basepayload, + "found first start at %u, bytes left %" G_GSIZE_FORMAT, next, size); + + /* first pass to locate NALs and parse SPS/PPS */ + while (size > 4) { + /* skip start code */ + data += 3; + size -= 3; + + /* use next_start_code() to scan buffer. + * next_start_code() returns the offset in data, + * starting from zero to the first byte of 0.0.0.1 + * If no start code is found, it returns the value of the + * 'size' parameter. + * data is unchanged by the call to next_start_code() + */ + next = next_start_code (data, size); + + /* nal or au aligned input needs no delaying until next time */ + if (next == size && buffer != NULL && + rtph264pay->alignment == GST_H264_ALIGNMENT_UNKNOWN) { + /* Didn't find the start of next NAL and it's not EOS, + * handle it next time */ + break; + } + + /* nal length is distance to next start code */ + nal_len = next; + + GST_DEBUG_OBJECT (basepayload, "found next start at %u of size %u", next, + nal_len); + + if (rtph264pay->sprop_parameter_sets != NULL) { + /* explicitly set profile and sprop, use those */ + if (rtph264pay->update_caps) { + if (!gst_rtp_base_payload_set_outcaps (basepayload, + "sprop-parameter-sets", G_TYPE_STRING, + rtph264pay->sprop_parameter_sets, NULL)) + goto caps_rejected; + + /* parse SPS and PPS from provided parameter set (for insertion) */ + gst_rtp_h264_pay_parse_sprop_parameter_sets (rtph264pay); + + rtph264pay->update_caps = FALSE; + + GST_DEBUG ("outcaps update: sprop-parameter-sets=%s", + rtph264pay->sprop_parameter_sets); + } + } else { + /* We know our stream is a valid H264 NAL packet, + * go parse it for SPS/PPS to enrich the caps */ + /* order: make sure to check nal */ + update = + gst_rtp_h264_pay_decode_nal (rtph264pay, data, nal_len, dts, pts) + || update; + } + /* move to next NAL packet */ + data += nal_len; + size -= nal_len; + + g_array_append_val (nal_queue, nal_len); + } + + /* if has new SPS & PPS, update the output caps */ + if (G_UNLIKELY (update)) + if (!gst_rtp_h264_pay_set_sps_pps (basepayload)) + goto caps_rejected; + + /* second pass to payload and push */ + + if (nal_queue->len != 0) + gst_adapter_flush (rtph264pay->adapter, skip); + + for (i = 0; i < nal_queue->len; i++) { + guint size; + gboolean end_of_au = FALSE; + + nal_len = g_array_index (nal_queue, guint, i); + /* skip start code */ + gst_adapter_flush (rtph264pay->adapter, 3); + + /* Trim the end unless we're the last NAL in the stream. + * In case we're not at the end of the buffer we know the next block + * starts with 0x000001 so all the 0x00 bytes at the end of this one are + * trailing 0x0 that can be discarded */ + size = nal_len; + data = gst_adapter_map (rtph264pay->adapter, size); + if (i + 1 != nal_queue->len || buffer != NULL) + for (; size > 1 && data[size - 1] == 0x0; size--) + /* skip */ ; + + + /* If it's the last nal unit we have in non-bytestream mode, we can + * assume it's the end of an access-unit + * + * FIXME: We need to wait until the next packet or EOS to + * actually payload the NAL so we can know if the current NAL is + * the last one of an access unit or not if we are in bytestream mode + */ + if ((rtph264pay->alignment == GST_H264_ALIGNMENT_AU || buffer == NULL) && + i == nal_queue->len - 1) + end_of_au = TRUE; + paybuf = gst_adapter_take_buffer (rtph264pay->adapter, size); + g_assert (paybuf); + + /* put the data in one or more RTP packets */ + ret = + gst_rtp_h264_pay_payload_nal (basepayload, paybuf, dts, pts, + end_of_au, rtph264pay->delta_unit, rtph264pay->discont); + + if (delayed_not_delta_unit) { + rtph264pay->delta_unit = FALSE; + delayed_not_delta_unit = FALSE; + } else { + /* Only the first outgoing packet doesn't have the DELTA_UNIT flag */ + rtph264pay->delta_unit = TRUE; + } + + if (delayed_discont) { + rtph264pay->discont = TRUE; + delayed_discont = FALSE; + } else { + /* Only the first outgoing packet have the DISCONT flag */ + rtph264pay->discont = FALSE; + } + + if (ret != GST_FLOW_OK) { + break; + } + + /* move to next NAL packet */ + /* Skips the trailing zeros */ + gst_adapter_flush (rtph264pay->adapter, nal_len - size); + } + g_array_set_size (nal_queue, 0); + } + +done: + if (avc) { + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + } else { + gst_adapter_unmap (rtph264pay->adapter); + } + + return ret; + +caps_rejected: + { + GST_WARNING_OBJECT (basepayload, "Could not set outcaps"); + g_array_set_size (nal_queue, 0); + ret = GST_FLOW_NOT_NEGOTIATED; + goto done; + } +} + +static gboolean +gst_rtp_h264_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + gboolean res; + const GstStructure *s; + GstRtpH264Pay *rtph264pay = GST_RTP_H264_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_adapter_clear (rtph264pay->adapter); + break; + case GST_EVENT_CUSTOM_DOWNSTREAM: + s = gst_event_get_structure (event); + if (gst_structure_has_name (s, "GstForceKeyUnit")) { + gboolean resend_codec_data; + + if (gst_structure_get_boolean (s, "all-headers", + &resend_codec_data) && resend_codec_data) + rtph264pay->send_spspps = TRUE; + } + break; + case GST_EVENT_EOS: + { + /* call handle_buffer with NULL to flush last NAL from adapter + * in byte-stream mode + */ + gst_rtp_h264_pay_handle_buffer (payload, NULL); + break; + } + case GST_EVENT_STREAM_START: + GST_DEBUG_OBJECT (rtph264pay, "New stream detected => Clear SPS and PPS"); + gst_rtp_h264_pay_clear_sps_pps (rtph264pay); + break; + default: + break; + } + + res = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); + + return res; +} + +static GstStateChangeReturn +gst_rtp_h264_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstRtpH264Pay *rtph264pay = GST_RTP_H264_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + rtph264pay->send_spspps = FALSE; + gst_adapter_clear (rtph264pay->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + rtph264pay->last_spspps = -1; + gst_rtp_h264_pay_clear_sps_pps (rtph264pay); + break; + default: + break; + } + + return ret; +} + +static void +gst_rtp_h264_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpH264Pay *rtph264pay; + + rtph264pay = GST_RTP_H264_PAY (object); + + switch (prop_id) { + case PROP_SPROP_PARAMETER_SETS: + g_free (rtph264pay->sprop_parameter_sets); + rtph264pay->sprop_parameter_sets = g_value_dup_string (value); + rtph264pay->update_caps = TRUE; + break; + case PROP_CONFIG_INTERVAL: + rtph264pay->spspps_interval = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_h264_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpH264Pay *rtph264pay; + + rtph264pay = GST_RTP_H264_PAY (object); + + switch (prop_id) { + case PROP_SPROP_PARAMETER_SETS: + g_value_set_string (value, rtph264pay->sprop_parameter_sets); + break; + case PROP_CONFIG_INTERVAL: + g_value_set_uint (value, rtph264pay->spspps_interval); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_h264_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtph264pay", + GST_RANK_SECONDARY, GST_TYPE_RTP_H264_PAY); +} diff --git a/gst/rtp/gstrtph264pay.h b/gst/rtp/gstrtph264pay.h new file mode 100755 index 0000000..44f7af4 --- /dev/null +++ b/gst/rtp/gstrtph264pay.h @@ -0,0 +1,95 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_H264_PAY_H__ +#define __GST_RTP_H264_PAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_H264_PAY \ + (gst_rtp_h264_pay_get_type()) +#define GST_RTP_H264_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_H264_PAY,GstRtpH264Pay)) +#define GST_RTP_H264_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_H264_PAY,GstRtpH264PayClass)) +#define GST_IS_RTP_H264_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_H264_PAY)) +#define GST_IS_RTP_H264_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_H264_PAY)) + +typedef struct _GstRtpH264Pay GstRtpH264Pay; +typedef struct _GstRtpH264PayClass GstRtpH264PayClass; + +typedef enum +{ + GST_H264_STREAM_FORMAT_UNKNOWN, + GST_H264_STREAM_FORMAT_BYTESTREAM, + GST_H264_STREAM_FORMAT_AVC +} GstH264StreamFormat; + +typedef enum +{ + GST_H264_ALIGNMENT_UNKNOWN, + GST_H264_ALIGNMENT_NAL, + GST_H264_ALIGNMENT_AU +} GstH264Alignment; + +struct _GstRtpH264Pay +{ + GstRTPBasePayload payload; + + guint profile; + GPtrArray *sps, *pps; + + GstH264StreamFormat stream_format; + GstH264Alignment alignment; + guint nal_length_size; + GArray *queue; + + gchar *sprop_parameter_sets; + gboolean update_caps; + + GstAdapter *adapter; + + guint spspps_interval; + gboolean send_spspps; + GstClockTime last_spspps; + + /* TRUE if the next NALU processed should have the DELTA_UNIT flag */ + gboolean delta_unit; + /* TRUE if the next NALU processed should have the DISCONT flag */ + gboolean discont; +}; + +struct _GstRtpH264PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_h264_pay_get_type (void); + +gboolean gst_rtp_h264_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_H264_PAY_H__ */ diff --git a/gst/rtp/gstrtpilbcdepay.c b/gst/rtp/gstrtpilbcdepay.c new file mode 100755 index 0000000..5c7dc25 --- /dev/null +++ b/gst/rtp/gstrtpilbcdepay.c @@ -0,0 +1,237 @@ +/* GStreamer + * Copyright (C) <2006> Philippe Khalaf <burger@speedy.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpilbcdepay.h" + +/* RtpiLBCDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_MODE GST_ILBC_MODE_30 + +enum +{ + PROP_0, + PROP_MODE +}; + +/* FIXME, mode should be string because it is a parameter in SDP fmtp */ +static GstStaticPadTemplate gst_rtp_ilbc_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"ILBC\"") + /* "mode = (string) { \"20\", \"30\" }" */ + ); + +static GstStaticPadTemplate gst_rtp_ilbc_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-iLBC, " "mode = (int) { 20, 30 }") + ); + +static void gst_ilbc_depay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec); +static void gst_ilbc_depay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec); + +static GstBuffer *gst_rtp_ilbc_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_ilbc_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_ilbc_depay_parent_class parent_class +G_DEFINE_TYPE (GstRTPiLBCDepay, gst_rtp_ilbc_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +#define GST_TYPE_ILBC_MODE (gst_ilbc_mode_get_type()) +static GType +gst_ilbc_mode_get_type (void) +{ + static GType ilbc_mode_type = 0; + static const GEnumValue ilbc_modes[] = { + {GST_ILBC_MODE_20, "20ms frames", "20ms"}, + {GST_ILBC_MODE_30, "30ms frames", "30ms"}, + {0, NULL, NULL}, + }; + + if (!ilbc_mode_type) { + ilbc_mode_type = g_enum_register_static ("iLBCMode", ilbc_modes); + } + return ilbc_mode_type; +} + +static void +gst_rtp_ilbc_depay_class_init (GstRTPiLBCDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->set_property = gst_ilbc_depay_set_property; + gobject_class->get_property = gst_ilbc_depay_get_property; + + /* FIXME, mode is in the caps */ + g_object_class_install_property (gobject_class, PROP_MODE, + g_param_spec_enum ("mode", "Mode", "iLBC frame mode", + GST_TYPE_ILBC_MODE, DEFAULT_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ilbc_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ilbc_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP iLBC depayloader", "Codec/Depayloader/Network/RTP", + "Extracts iLBC audio from RTP packets (RFC 3952)", + "Philippe Kalaf <philippe.kalaf@collabora.co.uk>"); + + gstrtpbasedepayload_class->process = gst_rtp_ilbc_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_ilbc_depay_setcaps; +} + +static void +gst_rtp_ilbc_depay_init (GstRTPiLBCDepay * rtpilbcdepay) +{ + /* Set default mode */ + rtpilbcdepay->mode = DEFAULT_MODE; +} + +static gboolean +gst_rtp_ilbc_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstRTPiLBCDepay *rtpilbcdepay = GST_RTP_ILBC_DEPAY (depayload); + GstCaps *srccaps; + GstStructure *structure; + const gchar *mode_str = NULL; + gint mode, clock_rate; + gboolean ret; + + structure = gst_caps_get_structure (caps, 0); + + mode = rtpilbcdepay->mode; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; + depayload->clock_rate = clock_rate; + + /* parse mode, if we can */ + mode_str = gst_structure_get_string (structure, "mode"); + if (mode_str) { + mode = strtol (mode_str, NULL, 10); + if (mode != 20 && mode != 30) + mode = rtpilbcdepay->mode; + } + + rtpilbcdepay->mode = mode; + + srccaps = gst_caps_new_simple ("audio/x-iLBC", + "mode", G_TYPE_INT, rtpilbcdepay->mode, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + + GST_DEBUG ("set caps on source: %" GST_PTR_FORMAT " (ret=%d)", srccaps, ret); + gst_caps_unref (srccaps); + + return ret; +} + +static GstBuffer * +gst_rtp_ilbc_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + + gst_rtp_buffer_unmap (&rtp); + + if (marker && outbuf) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + + return outbuf; +} + +static void +gst_ilbc_depay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRTPiLBCDepay *rtpilbcdepay = GST_RTP_ILBC_DEPAY (object); + + switch (prop_id) { + case PROP_MODE: + rtpilbcdepay->mode = g_value_get_enum (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_ilbc_depay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRTPiLBCDepay *rtpilbcdepay = GST_RTP_ILBC_DEPAY (object); + + switch (prop_id) { + case PROP_MODE: + g_value_set_enum (value, rtpilbcdepay->mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_ilbc_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpilbcdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_ILBC_DEPAY); +} diff --git a/gst/rtp/gstrtpilbcdepay.h b/gst/rtp/gstrtpilbcdepay.h new file mode 100755 index 0000000..01fd225 --- /dev/null +++ b/gst/rtp/gstrtpilbcdepay.h @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) <2006> Philippe Khalaf <burger@speedy.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_ILBC_DEPAY_H__ +#define __GST_RTP_ILBC_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPiLBCDepay GstRTPiLBCDepay; +typedef struct _GstRTPiLBCDepayClass GstRTPiLBCDepayClass; + +#define GST_TYPE_RTP_ILBC_DEPAY \ + (gst_rtp_ilbc_depay_get_type()) +#define GST_RTP_ILBC_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ILBC_DEPAY,GstRTPiLBCDepay)) +#define GST_RTP_ILBC_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ILBC_DEPAY,GstRTPiLBCDepayClass)) +#define GST_IS_RTP_ILBC_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ILBC_DEPAY)) +#define GST_IS_RTP_ILBC_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ILBC_DEPAY)) + +typedef enum { + GST_ILBC_MODE_20 = 20, + GST_ILBC_MODE_30 = 30 +} GstiLBCMode; + +struct _GstRTPiLBCDepay +{ + GstRTPBaseDepayload depayload; + + GstiLBCMode mode; +}; + +struct _GstRTPiLBCDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_ilbc_depay_get_type (void); + +gboolean gst_rtp_ilbc_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_ILBC_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpilbcpay.c b/gst/rtp/gstrtpilbcpay.c new file mode 100755 index 0000000..7d6ea28 --- /dev/null +++ b/gst/rtp/gstrtpilbcpay.c @@ -0,0 +1,217 @@ +/* GStreamer + * Copyright (C) <2006> Philippe Khalaf <burger@speedy.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpilbcpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpilbcpay_debug); +#define GST_CAT_DEFAULT (rtpilbcpay_debug) + +static GstStaticPadTemplate gst_rtp_ilbc_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-iLBC, " "mode = (int) {20, 30}") + ); + +static GstStaticPadTemplate gst_rtp_ilbc_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"ILBC\", " + "mode = (string) { \"20\", \"30\" }") + ); + + +static GstCaps *gst_rtp_ilbc_pay_sink_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static gboolean gst_rtp_ilbc_pay_sink_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); + +#define gst_rtp_ilbc_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPILBCPay, gst_rtp_ilbc_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_ilbc_pay_class_init (GstRTPILBCPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpilbcpay_debug, "rtpilbcpay", 0, + "iLBC audio RTP payloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ilbc_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_ilbc_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP iLBC Payloader", + "Codec/Payloader/Network/RTP", + "Packetize iLBC audio streams into RTP packets", + "Philippe Kalaf <philippe.kalaf@collabora.co.uk>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_ilbc_pay_sink_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_ilbc_pay_sink_getcaps; +} + +static void +gst_rtp_ilbc_pay_init (GstRTPILBCPay * rtpilbcpay) +{ + GstRTPBasePayload *rtpbasepayload; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbasepayload = GST_RTP_BASE_PAYLOAD (rtpilbcpay); + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpilbcpay); + + /* we don't set the payload type, it should be set by the application using + * the pt property or the default 96 will be used */ + rtpbasepayload->clock_rate = 8000; + + rtpilbcpay->mode = -1; + + /* tell rtpbaseaudiopayload that this is a frame based codec */ + gst_rtp_base_audio_payload_set_frame_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_ilbc_pay_sink_setcaps (GstRTPBasePayload * rtpbasepayload, + GstCaps * caps) +{ + GstRTPILBCPay *rtpilbcpay; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + gboolean ret; + gint mode; + gchar *mode_str; + GstStructure *structure; + const char *payload_name; + + rtpilbcpay = GST_RTP_ILBC_PAY (rtpbasepayload); + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpbasepayload); + + structure = gst_caps_get_structure (caps, 0); + + payload_name = gst_structure_get_name (structure); + if (g_ascii_strcasecmp ("audio/x-iLBC", payload_name)) + goto wrong_caps; + + if (!gst_structure_get_int (structure, "mode", &mode)) + goto no_mode; + + if (mode != 20 && mode != 30) + goto wrong_mode; + + gst_rtp_base_payload_set_options (rtpbasepayload, "audio", TRUE, "ILBC", + 8000); + /* set options for this frame based audio codec */ + gst_rtp_base_audio_payload_set_frame_options (rtpbaseaudiopayload, + mode, mode == 30 ? 50 : 38); + + mode_str = g_strdup_printf ("%d", mode); + ret = + gst_rtp_base_payload_set_outcaps (rtpbasepayload, "mode", G_TYPE_STRING, + mode_str, NULL); + g_free (mode_str); + + if (mode != rtpilbcpay->mode && rtpilbcpay->mode != -1) + goto mode_changed; + + rtpilbcpay->mode = mode; + + return ret; + + /* ERRORS */ +wrong_caps: + { + GST_ERROR_OBJECT (rtpilbcpay, "expected audio/x-iLBC, received %s", + payload_name); + return FALSE; + } +no_mode: + { + GST_ERROR_OBJECT (rtpilbcpay, "did not receive a mode"); + return FALSE; + } +wrong_mode: + { + GST_ERROR_OBJECT (rtpilbcpay, "mode must be 20 or 30, received %d", mode); + return FALSE; + } +mode_changed: + { + GST_ERROR_OBJECT (rtpilbcpay, "Mode has changed from %d to %d! " + "Mode cannot change while streaming", rtpilbcpay->mode, mode); + return FALSE; + } +} + +/* we return the padtemplate caps with the mode field fixated to a value if we + * can */ +static GstCaps * +gst_rtp_ilbc_pay_sink_getcaps (GstRTPBasePayload * rtppayload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + otherpadcaps = gst_pad_get_allowed_caps (rtppayload->srcpad); + caps = gst_pad_get_pad_template_caps (pad); + + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *structure; + const gchar *mode_str; + gint mode; + + structure = gst_caps_get_structure (otherpadcaps, 0); + + /* parse mode, if we can */ + mode_str = gst_structure_get_string (structure, "mode"); + if (mode_str) { + mode = strtol (mode_str, NULL, 10); + if (mode == 20 || mode == 30) { + caps = gst_caps_make_writable (caps); + structure = gst_caps_get_structure (caps, 0); + gst_structure_set (structure, "mode", G_TYPE_INT, mode, NULL); + } + } + } + gst_caps_unref (otherpadcaps); + } + return caps; +} + +gboolean +gst_rtp_ilbc_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpilbcpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_ILBC_PAY); +} diff --git a/gst/rtp/gstrtpilbcpay.h b/gst/rtp/gstrtpilbcpay.h new file mode 100755 index 0000000..14363c0 --- /dev/null +++ b/gst/rtp/gstrtpilbcpay.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) <2006> Philippe Khalaf <burger@speedy.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_ILBC_PAY_H__ +#define __GST_RTP_ILBC_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_ILBC_PAY \ + (gst_rtp_ilbc_pay_get_type()) +#define GST_RTP_ILBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_ILBC_PAY,GstRTPILBCPay)) +#define GST_RTP_ILBC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_ILBC_PAY,GstRTPILBCPayClass)) +#define GST_IS_RTP_ILBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_ILBC_PAY)) +#define GST_IS_RTP_ILBC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_ILBC_PAY)) + +typedef struct _GstRTPILBCPay GstRTPILBCPay; +typedef struct _GstRTPILBCPayClass GstRTPILBCPayClass; + +struct _GstRTPILBCPay +{ + GstRTPBaseAudioPayload audiopayload; + + gint mode; +}; + +struct _GstRTPILBCPayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_ilbc_pay_get_type (void); + +gboolean gst_rtp_ilbc_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_ILBC_PAY_H__ */ diff --git a/gst/rtp/gstrtpj2kdepay.c b/gst/rtp/gstrtpj2kdepay.c new file mode 100755 index 0000000..9829258 --- /dev/null +++ b/gst/rtp/gstrtpj2kdepay.c @@ -0,0 +1,646 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpj2kdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpj2kdepay_debug); +#define GST_CAT_DEFAULT (rtpj2kdepay_debug) + +static GstStaticPadTemplate gst_rtp_j2k_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/x-jpc") + ); + +static GstStaticPadTemplate gst_rtp_j2k_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG2000\"") + ); + +typedef enum +{ + J2K_MARKER = 0xFF, + J2K_MARKER_SOC = 0x4F, + J2K_MARKER_SOT = 0x90, + J2K_MARKER_SOP = 0x91, + J2K_MARKER_SOD = 0x93, + J2K_MARKER_EOC = 0xD9 +} RtpJ2KMarker; + +enum +{ + PROP_0, + PROP_LAST +}; + +#define gst_rtp_j2k_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpJ2KDepay, gst_rtp_j2k_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_j2k_depay_finalize (GObject * object); + +static void gst_rtp_j2k_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_j2k_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static GstStateChangeReturn +gst_rtp_j2k_depay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_j2k_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_j2k_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_j2k_depay_class_init (GstRtpJ2KDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_j2k_depay_finalize; + + gobject_class->set_property = gst_rtp_j2k_depay_set_property; + gobject_class->get_property = gst_rtp_j2k_depay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_j2k_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_j2k_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP JPEG 2000 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts JPEG 2000 video from RTP packets (RFC 5371)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstelement_class->change_state = gst_rtp_j2k_depay_change_state; + + gstrtpbasedepayload_class->set_caps = gst_rtp_j2k_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_j2k_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpj2kdepay_debug, "rtpj2kdepay", 0, + "J2K Video RTP Depayloader"); +} + +static void +gst_rtp_j2k_depay_init (GstRtpJ2KDepay * rtpj2kdepay) +{ + rtpj2kdepay->pu_adapter = gst_adapter_new (); + rtpj2kdepay->t_adapter = gst_adapter_new (); + rtpj2kdepay->f_adapter = gst_adapter_new (); +} + +static void +store_mheader (GstRtpJ2KDepay * rtpj2kdepay, guint idx, GstBuffer * buf) +{ + GstBuffer *old; + + GST_DEBUG_OBJECT (rtpj2kdepay, "storing main header %p at index %u", buf, + idx); + if ((old = rtpj2kdepay->MH[idx])) + gst_buffer_unref (old); + rtpj2kdepay->MH[idx] = buf; +} + +static void +clear_mheaders (GstRtpJ2KDepay * rtpj2kdepay) +{ + guint i; + + for (i = 0; i < 8; i++) + store_mheader (rtpj2kdepay, i, NULL); +} + +static void +gst_rtp_j2k_depay_reset (GstRtpJ2KDepay * rtpj2kdepay) +{ + clear_mheaders (rtpj2kdepay); + gst_adapter_clear (rtpj2kdepay->pu_adapter); + gst_adapter_clear (rtpj2kdepay->t_adapter); + gst_adapter_clear (rtpj2kdepay->f_adapter); + rtpj2kdepay->next_frag = 0; +} + +static void +gst_rtp_j2k_depay_finalize (GObject * object) +{ + GstRtpJ2KDepay *rtpj2kdepay; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (object); + + clear_mheaders (rtpj2kdepay); + + g_object_unref (rtpj2kdepay->pu_adapter); + g_object_unref (rtpj2kdepay->t_adapter); + g_object_unref (rtpj2kdepay->f_adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_j2k_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + gint clock_rate; + GstCaps *outcaps; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + outcaps = + gst_caps_new_simple ("image/x-jpc", "framerate", GST_TYPE_FRACTION, 0, 1, + "fields", G_TYPE_INT, 1, "colorspace", G_TYPE_STRING, "sYUV", NULL); + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static void +gst_rtp_j2k_depay_clear_pu (GstRtpJ2KDepay * rtpj2kdepay) +{ + gst_adapter_clear (rtpj2kdepay->pu_adapter); + rtpj2kdepay->have_sync = FALSE; +} + +static GstFlowReturn +gst_rtp_j2k_depay_flush_pu (GstRTPBaseDepayload * depayload) +{ + GstRtpJ2KDepay *rtpj2kdepay; + GstBuffer *mheader; + guint avail, MHF, mh_id; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + /* take all available buffers */ + avail = gst_adapter_available (rtpj2kdepay->pu_adapter); + if (avail == 0) + goto done; + + MHF = rtpj2kdepay->pu_MHF; + mh_id = rtpj2kdepay->last_mh_id; + + GST_DEBUG_OBJECT (rtpj2kdepay, "flushing PU of size %u", avail); + + if (MHF == 0) { + GList *packets, *walk; + + packets = gst_adapter_take_list (rtpj2kdepay->pu_adapter, avail); + /* append packets */ + for (walk = packets; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + GST_DEBUG_OBJECT (rtpj2kdepay, + "append pu packet of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (buf)); + gst_adapter_push (rtpj2kdepay->t_adapter, buf); + } + g_list_free (packets); + } else { + /* we have a header */ + GST_DEBUG_OBJECT (rtpj2kdepay, "keeping header %u", mh_id); + /* we managed to see the start and end of the header, take all from + * adapter and keep in header */ + mheader = gst_adapter_take_buffer (rtpj2kdepay->pu_adapter, avail); + + store_mheader (rtpj2kdepay, mh_id, mheader); + } + +done: + rtpj2kdepay->have_sync = FALSE; + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_rtp_j2k_depay_flush_tile (GstRTPBaseDepayload * depayload) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint avail, mh_id; + GList *packets, *walk; + guint8 end[2]; + GstFlowReturn ret = GST_FLOW_OK; + GstMapInfo map; + GstBuffer *buf; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + /* flush pending PU */ + gst_rtp_j2k_depay_flush_pu (depayload); + + /* take all available buffers */ + avail = gst_adapter_available (rtpj2kdepay->t_adapter); + if (avail == 0) + goto done; + + mh_id = rtpj2kdepay->last_mh_id; + + GST_DEBUG_OBJECT (rtpj2kdepay, "flushing tile of size %u", avail); + + if (gst_adapter_available (rtpj2kdepay->f_adapter) == 0) { + GstBuffer *mheader; + + /* we need a header now */ + if ((mheader = rtpj2kdepay->MH[mh_id]) == NULL) + goto waiting_header; + + /* push header in the adapter */ + GST_DEBUG_OBJECT (rtpj2kdepay, "pushing header %u", mh_id); + gst_adapter_push (rtpj2kdepay->f_adapter, gst_buffer_ref (mheader)); + } + + /* check for last bytes */ + gst_adapter_copy (rtpj2kdepay->t_adapter, end, avail - 2, 2); + + /* now append the tile packets to the frame */ + packets = gst_adapter_take_list (rtpj2kdepay->t_adapter, avail); + for (walk = packets; walk; walk = g_list_next (walk)) { + buf = GST_BUFFER_CAST (walk->data); + + if (walk == packets) { + /* first buffer should contain the SOT */ + gst_buffer_map (buf, &map, GST_MAP_READ); + + if (map.size < 12) + goto invalid_tile; + + if (map.data[0] == 0xff && map.data[1] == J2K_MARKER_SOT) { + guint Psot, nPsot; + + if (end[0] == 0xff && end[1] == J2K_MARKER_EOC) + nPsot = avail - 2; + else + nPsot = avail; + + Psot = GST_READ_UINT32_BE (&map.data[6]); + if (Psot != nPsot && Psot != 0) { + /* Psot must match the size of the tile */ + GST_DEBUG_OBJECT (rtpj2kdepay, "set Psot from %u to %u", Psot, nPsot); + gst_buffer_unmap (buf, &map); + + buf = gst_buffer_make_writable (buf); + + gst_buffer_map (buf, &map, GST_MAP_WRITE); + GST_WRITE_UINT32_BE (&map.data[6], nPsot); + } + } + gst_buffer_unmap (buf, &map); + } + + GST_DEBUG_OBJECT (rtpj2kdepay, "append pu packet of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (buf)); + gst_adapter_push (rtpj2kdepay->f_adapter, buf); + } + g_list_free (packets); + +done: + rtpj2kdepay->last_tile = -1; + + return ret; + + /* errors */ +waiting_header: + { + GST_DEBUG_OBJECT (rtpj2kdepay, "waiting for header %u", mh_id); + gst_adapter_clear (rtpj2kdepay->t_adapter); + rtpj2kdepay->last_tile = -1; + return ret; + } +invalid_tile: + { + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, ("Invalid tile"), (NULL)); + gst_buffer_unmap (buf, &map); + gst_adapter_clear (rtpj2kdepay->t_adapter); + rtpj2kdepay->last_tile = -1; + return ret; + } +} + +static GstFlowReturn +gst_rtp_j2k_depay_flush_frame (GstRTPBaseDepayload * depayload) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint8 end[2]; + guint avail; + + GstFlowReturn ret = GST_FLOW_OK; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + /* flush pending tile */ + gst_rtp_j2k_depay_flush_tile (depayload); + + /* last buffer take all data out of the adapter */ + avail = gst_adapter_available (rtpj2kdepay->f_adapter); + if (avail == 0) + goto done; + + if (avail > 2) { + GList *list, *walk; + GstBufferList *buflist; + + /* take the last bytes of the JPEG 2000 data to see if there is an EOC + * marker */ + gst_adapter_copy (rtpj2kdepay->f_adapter, end, avail - 2, 2); + + if (end[0] != 0xff && end[1] != 0xd9) { + GstBuffer *outbuf; + + end[0] = 0xff; + end[1] = 0xd9; + + GST_DEBUG_OBJECT (rtpj2kdepay, "no EOC marker, adding one"); + + /* no EOI marker, add one */ + outbuf = gst_buffer_new_and_alloc (2); + gst_buffer_fill (outbuf, 0, end, 2); + + gst_adapter_push (rtpj2kdepay->f_adapter, outbuf); + avail += 2; + } + + GST_DEBUG_OBJECT (rtpj2kdepay, "pushing buffer list of %u bytes", avail); + list = gst_adapter_take_list (rtpj2kdepay->f_adapter, avail); + + buflist = gst_buffer_list_new (); + + for (walk = list; walk; walk = g_list_next (walk)) + gst_buffer_list_add (buflist, GST_BUFFER_CAST (walk->data)); + + g_list_free (list); + + ret = gst_rtp_base_depayload_push_list (depayload, buflist); + } else { + GST_WARNING_OBJECT (rtpj2kdepay, "empty packet"); + gst_adapter_clear (rtpj2kdepay->f_adapter); + } + + /* we accept any mh_id now */ + rtpj2kdepay->last_mh_id = -1; + + /* reset state */ + rtpj2kdepay->next_frag = 0; + rtpj2kdepay->have_sync = FALSE; + +done: + /* we can't keep headers with mh_id of 0 */ + store_mheader (rtpj2kdepay, 0, NULL); + + return ret; +} + +static GstBuffer * +gst_rtp_j2k_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpJ2KDepay *rtpj2kdepay; + guint8 *payload; + guint MHF, mh_id, frag_offset, tile, payload_len, j2klen; + gint gap; + guint32 rtptime; + GstRTPBuffer rtp = { NULL }; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload = gst_rtp_buffer_get_payload (&rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + /* we need at least a header */ + if (payload_len < 8) + goto empty_packet; + + rtptime = gst_rtp_buffer_get_timestamp (&rtp); + + /* new timestamp marks new frame */ + if (rtpj2kdepay->last_rtptime != rtptime) { + rtpj2kdepay->last_rtptime = rtptime; + /* flush pending frame */ + gst_rtp_j2k_depay_flush_frame (depayload); + } + + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |tp |MHF|mh_id|T| priority | tile number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |reserved | fragment offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + MHF = (payload[0] & 0x30) >> 4; + mh_id = (payload[0] & 0xe) >> 1; + + if (rtpj2kdepay->last_mh_id == -1) + rtpj2kdepay->last_mh_id = mh_id; + else if (rtpj2kdepay->last_mh_id != mh_id) + goto wrong_mh_id; + + tile = (payload[2] << 8) | payload[3]; + frag_offset = (payload[5] << 16) | (payload[6] << 8) | payload[7]; + j2klen = payload_len - 8; + + GST_DEBUG_OBJECT (rtpj2kdepay, "MHF %u, tile %u, frag %u, expected %u", MHF, + tile, frag_offset, rtpj2kdepay->next_frag); + + /* calculate the gap between expected frag */ + gap = frag_offset - rtpj2kdepay->next_frag; + /* calculate next frag */ + rtpj2kdepay->next_frag = frag_offset + j2klen; + + if (gap != 0) { + GST_DEBUG_OBJECT (rtpj2kdepay, "discont of %d, clear PU", gap); + /* discont, clear pu adapter and resync */ + gst_rtp_j2k_depay_clear_pu (rtpj2kdepay); + } + + /* check for sync code */ + if (j2klen > 2 && payload[8] == 0xff) { + guint marker = payload[9]; + + /* packets must start with SOC, SOT or SOP */ + switch (marker) { + case J2K_MARKER_SOC: + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOC packet"); + /* flush the previous frame, should have happened when the timestamp + * changed above. */ + gst_rtp_j2k_depay_flush_frame (depayload); + rtpj2kdepay->have_sync = TRUE; + break; + case J2K_MARKER_SOT: + /* flush the previous tile */ + gst_rtp_j2k_depay_flush_tile (depayload); + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOT packet"); + rtpj2kdepay->have_sync = TRUE; + /* we sync on the tile now */ + rtpj2kdepay->last_tile = tile; + break; + case J2K_MARKER_SOP: + GST_DEBUG_OBJECT (rtpj2kdepay, "found SOP packet"); + /* flush the previous PU */ + gst_rtp_j2k_depay_flush_pu (depayload); + if (rtpj2kdepay->last_tile != tile) { + /* wrong tile, we lose sync and we need a new SOT or SOC to regain + * sync. First flush out the previous tile if we have one. */ + if (rtpj2kdepay->last_tile != -1) + gst_rtp_j2k_depay_flush_tile (depayload); + /* now we have no more valid tile and no sync */ + rtpj2kdepay->last_tile = -1; + rtpj2kdepay->have_sync = FALSE; + } else { + rtpj2kdepay->have_sync = TRUE; + } + break; + default: + GST_DEBUG_OBJECT (rtpj2kdepay, "no sync packet 0x%02d", marker); + break; + } + } + + if (rtpj2kdepay->have_sync) { + GstBuffer *pu_frag; + + if (gst_adapter_available (rtpj2kdepay->pu_adapter) == 0) { + /* first part of pu, record state */ + GST_DEBUG_OBJECT (rtpj2kdepay, "first PU"); + rtpj2kdepay->pu_MHF = MHF; + } + /* and push in pu adapter */ + GST_DEBUG_OBJECT (rtpj2kdepay, "push pu of size %u in adapter", j2klen); + pu_frag = gst_rtp_buffer_get_payload_subbuffer (&rtp, 8, -1); + gst_adapter_push (rtpj2kdepay->pu_adapter, pu_frag); + + if (MHF & 2) { + /* last part of main header received, we can flush it */ + GST_DEBUG_OBJECT (rtpj2kdepay, "header end, flush pu"); + gst_rtp_j2k_depay_flush_pu (depayload); + } + } else { + GST_DEBUG_OBJECT (rtpj2kdepay, "discard packet, no sync"); + } + + /* marker bit finishes the frame */ + if (gst_rtp_buffer_get_marker (&rtp)) { + GST_DEBUG_OBJECT (rtpj2kdepay, "marker set, last buffer"); + /* then flush frame */ + gst_rtp_j2k_depay_flush_frame (depayload); + } + gst_rtp_buffer_unmap (&rtp); + + return NULL; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +wrong_mh_id: + { + GST_ELEMENT_WARNING (rtpj2kdepay, STREAM, DECODE, + ("Invalid mh_id %u, expected %u", mh_id, rtpj2kdepay->last_mh_id), + (NULL)); + gst_rtp_j2k_depay_clear_pu (rtpj2kdepay); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static void +gst_rtp_j2k_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_j2k_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static GstStateChangeReturn +gst_rtp_j2k_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpJ2KDepay *rtpj2kdepay; + GstStateChangeReturn ret; + + rtpj2kdepay = GST_RTP_J2K_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_j2k_depay_reset (rtpj2kdepay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_j2k_depay_reset (rtpj2kdepay); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_j2k_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpj2kdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_J2K_DEPAY); +} diff --git a/gst/rtp/gstrtpj2kdepay.h b/gst/rtp/gstrtpj2kdepay.h new file mode 100755 index 0000000..e5b9d6b --- /dev/null +++ b/gst/rtp/gstrtpj2kdepay.h @@ -0,0 +1,75 @@ +/* GStreamer + * Copyright (C) <2009> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_J2K_DEPAY_H__ +#define __GST_RTP_J2K_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_J2K_DEPAY \ + (gst_rtp_j2k_depay_get_type()) +#define GST_RTP_J2K_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_J2K_DEPAY,GstRtpJ2KDepay)) +#define GST_RTP_J2K_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_J2K_DEPAY,GstRtpJ2KDepayClass)) +#define GST_IS_RTP_J2K_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_J2K_DEPAY)) +#define GST_IS_RTP_J2K_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_J2K_DEPAY)) + +typedef struct _GstRtpJ2KDepay GstRtpJ2KDepay; +typedef struct _GstRtpJ2KDepayClass GstRtpJ2KDepayClass; + +struct _GstRtpJ2KDepay +{ + GstRTPBaseDepayload depayload; + + guint64 last_rtptime; + guint last_mh_id; + guint last_tile; + + GstBuffer *MH[8]; + + guint pu_MHF; + GstAdapter *pu_adapter; + GstAdapter *t_adapter; + GstAdapter *f_adapter; + + guint next_frag; + gboolean have_sync; + + gint width, height; +}; + +struct _GstRtpJ2KDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_j2k_depay_get_type (void); + +gboolean gst_rtp_j2k_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_J2K_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpj2kpay.c b/gst/rtp/gstrtpj2kpay.c new file mode 100755 index 0000000..98880b2 --- /dev/null +++ b/gst/rtp/gstrtpj2kpay.c @@ -0,0 +1,536 @@ +/* GStreamer + * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpj2kpay + * + * Payload encode JPEG 2000 pictures into RTP packets according to RFC 5371. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc5371.txt + * + * The payloader takes a JPEG 2000 picture, scans the header for packetization + * units and constructs the RTP packet header followed by the actual JPEG 2000 + * codestream. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpj2kpay.h" + +static GstStaticPadTemplate gst_rtp_j2k_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/x-jpc") + ); + +static GstStaticPadTemplate gst_rtp_j2k_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + " media = (string) \"video\", " + " payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + " clock-rate = (int) 90000, " + " encoding-name = (string) \"JPEG2000\"") + ); + +GST_DEBUG_CATEGORY_STATIC (rtpj2kpay_debug); +#define GST_CAT_DEFAULT (rtpj2kpay_debug) + +/* + * RtpJ2KMarker: + * @J2K_MARKER: Prefix for JPEG 2000 marker + * @J2K_MARKER_SOC: Start of Codestream + * @J2K_MARKER_SOT: Start of tile + * @J2K_MARKER_EOC: End of Codestream + * + * Identifers for markers in JPEG 2000 codestreams + */ +typedef enum +{ + J2K_MARKER = 0xFF, + J2K_MARKER_SOC = 0x4F, + J2K_MARKER_SOT = 0x90, + J2K_MARKER_SOP = 0x91, + J2K_MARKER_EPH = 0x92, + J2K_MARKER_SOD = 0x93, + J2K_MARKER_EOC = 0xD9 +} RtpJ2KMarker; + +enum +{ + PROP_0, + PROP_LAST +}; + +typedef struct +{ + guint tp:2; + guint MHF:2; + guint mh_id:3; + guint T:1; + guint priority:8; + guint tile:16; + guint offset:24; +} RtpJ2KHeader; + +#define HEADER_SIZE 8 + +static void gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); + +static GstFlowReturn gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); + +#define gst_rtp_j2k_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpJ2KPay, gst_rtp_j2k_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_j2k_pay_class_init (GstRtpJ2KPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_j2k_pay_set_property; + gobject_class->get_property = gst_rtp_j2k_pay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_j2k_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_j2k_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP JPEG 2000 payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes JPEG 2000 pictures into RTP packets (RFC 5371)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_j2k_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_j2k_pay_handle_buffer; + + GST_DEBUG_CATEGORY_INIT (rtpj2kpay_debug, "rtpj2kpay", 0, + "JPEG 2000 RTP Payloader"); +} + +static void +gst_rtp_j2k_pay_init (GstRtpJ2KPay * pay) +{ +} + +static gboolean +gst_rtp_j2k_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstStructure *caps_structure = gst_caps_get_structure (caps, 0); + GstRtpJ2KPay *pay; + gint width = 0, height = 0; + gboolean res; + + pay = GST_RTP_J2K_PAY (basepayload); + + /* these properties are not mandatory, we can get them from the stream */ + if (gst_structure_get_int (caps_structure, "height", &height)) { + pay->height = height; + } + if (gst_structure_get_int (caps_structure, "width", &width)) { + pay->width = width; + } + + gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG2000", + 90000); + res = gst_rtp_base_payload_set_outcaps (basepayload, NULL); + + return res; +} + + +static guint +gst_rtp_j2k_pay_header_size (const guint8 * data, guint offset) +{ + return data[offset] << 8 | data[offset + 1]; +} + +static RtpJ2KMarker +gst_rtp_j2k_pay_scan_marker (const guint8 * data, guint size, guint * offset) +{ + while ((data[(*offset)++] != J2K_MARKER) && ((*offset) < size)); + + if (G_UNLIKELY ((*offset) >= size)) { + return J2K_MARKER_EOC; + } else { + guint8 marker = data[(*offset)++]; + return marker; + } +} + +typedef struct +{ + RtpJ2KHeader header; + gboolean bitstream; + guint n_tiles; + guint next_sot; + gboolean force_packet; +} RtpJ2KState; + +static guint +find_pu_end (GstRtpJ2KPay * pay, const guint8 * data, guint size, + guint offset, RtpJ2KState * state) +{ + gboolean cut_sop = FALSE; + RtpJ2KMarker marker; + + /* parse the j2k header for 'start of codestream' */ + GST_LOG_OBJECT (pay, "checking from offset %u", offset); + while (offset < size) { + marker = gst_rtp_j2k_pay_scan_marker (data, size, &offset); + + if (state->bitstream) { + /* parsing bitstream, only look for SOP */ + switch (marker) { + case J2K_MARKER_SOP: + GST_LOG_OBJECT (pay, "found SOP at %u", offset); + if (cut_sop) + return offset - 2; + cut_sop = TRUE; + break; + case J2K_MARKER_EPH: + /* just skip over EPH */ + GST_LOG_OBJECT (pay, "found EPH at %u", offset); + break; + default: + if (offset >= state->next_sot) { + GST_LOG_OBJECT (pay, "reached next SOT at %u", offset); + state->bitstream = FALSE; + state->force_packet = TRUE; + if (marker == J2K_MARKER_EOC && state->next_sot + 2 <= size) + /* include EOC but never go past the max size */ + return state->next_sot + 2; + else + return state->next_sot; + } + break; + } + } else { + switch (marker) { + case J2K_MARKER_SOC: + GST_LOG_OBJECT (pay, "found SOC at %u", offset); + state->header.MHF = 1; + break; + case J2K_MARKER_SOT: + { + guint len, Psot; + + GST_LOG_OBJECT (pay, "found SOT at %u", offset); + /* we found SOT but also had a header first */ + if (state->header.MHF) { + state->force_packet = TRUE; + return offset - 2; + } + + /* parse SOT but do some sanity checks first */ + len = gst_rtp_j2k_pay_header_size (data, offset); + GST_LOG_OBJECT (pay, "SOT length %u", len); + if (len < 8) + return size; + if (offset + len >= size) + return size; + + if (state->n_tiles == 0) + /* first tile, T is valid */ + state->header.T = 0; + else + /* more tiles, T becomes invalid */ + state->header.T = 1; + state->header.tile = GST_READ_UINT16_BE (&data[offset + 2]); + state->n_tiles++; + + /* get offset of next tile, if it's 0, it goes all the way to the end of + * the data */ + Psot = GST_READ_UINT32_BE (&data[offset + 4]); + if (Psot == 0) + state->next_sot = size; + else + state->next_sot = offset - 2 + Psot; + + offset += len; + GST_LOG_OBJECT (pay, "Isot %u, Psot %u, next %u", state->header.tile, + Psot, state->next_sot); + break; + } + case J2K_MARKER_SOD: + GST_LOG_OBJECT (pay, "found SOD at %u", offset); + /* can't have more tiles now */ + state->n_tiles = 0; + /* go to bitstream parsing */ + state->bitstream = TRUE; + /* cut at the next SOP or else include all data */ + cut_sop = TRUE; + /* force a new packet when we see SOP, this can be optional but the + * spec recommends packing headers separately */ + state->force_packet = TRUE; + break; + case J2K_MARKER_EOC: + GST_LOG_OBJECT (pay, "found EOC at %u", offset); + return offset; + default: + { + guint len = gst_rtp_j2k_pay_header_size (data, offset); + GST_LOG_OBJECT (pay, "skip 0x%02x len %u", marker, len); + offset += len; + break; + } + } + } + } + GST_DEBUG_OBJECT (pay, "reached end of data"); + return size; +} + +static GstFlowReturn +gst_rtp_j2k_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpJ2KPay *pay; + GstClockTime timestamp; + GstFlowReturn ret = GST_FLOW_ERROR; + RtpJ2KState state; + GstBufferList *list = NULL; + GstMapInfo map; + guint mtu, max_size; + guint offset; + guint end, pos; + + pay = GST_RTP_J2K_PAY (basepayload); + mtu = GST_RTP_BASE_PAYLOAD_MTU (pay); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + offset = pos = end = 0; + + GST_LOG_OBJECT (pay, + "got buffer size %" G_GSIZE_FORMAT ", timestamp %" GST_TIME_FORMAT, + map.size, GST_TIME_ARGS (timestamp)); + + /* do some header defaults first */ + state.header.tp = 0; /* only progressive scan */ + state.header.MHF = 0; /* no header */ + state.header.mh_id = 0; /* always 0 for now */ + state.header.T = 1; /* invalid tile */ + state.header.priority = 255; /* always 255 for now */ + state.header.tile = 0; /* no tile number */ + state.header.offset = 0; /* offset of 0 */ + state.bitstream = FALSE; + state.n_tiles = 0; + state.next_sot = 0; + state.force_packet = FALSE; + + /* get max packet length */ + max_size = gst_rtp_buffer_calc_payload_len (mtu - HEADER_SIZE, 0, 0); + + list = gst_buffer_list_new_sized ((mtu / max_size) + 1); + + do { + GstBuffer *outbuf; + guint8 *header; + guint payload_size; + guint pu_size; + GstRTPBuffer rtp = { NULL }; + + /* try to pack as much as we can */ + do { + /* see how much we have scanned already */ + pu_size = end - offset; + GST_DEBUG_OBJECT (pay, "scanned pu size %u", pu_size); + + /* we need to make a new packet */ + if (state.force_packet) { + GST_DEBUG_OBJECT (pay, "need to force a new packet"); + state.force_packet = FALSE; + pos = end; + break; + } + + /* else see if we have enough */ + if (pu_size > max_size) { + if (pos != offset) + /* the packet became too large, use previous scanpos */ + pu_size = pos - offset; + else + /* the already scanned data was already too big, make sure we start + * scanning from the last searched position */ + pos = end; + + GST_DEBUG_OBJECT (pay, "max size exceeded pu_size %u", pu_size); + break; + } + + pos = end; + + /* exit when finished */ + if (pos == map.size) + break; + + /* scan next packetization unit and fill in the header */ + end = find_pu_end (pay, map.data, map.size, pos, &state); + } while (TRUE); + + while (pu_size > 0) { + guint packet_size, data_size; + GstBuffer *paybuf; + + /* calculate the packet size */ + packet_size = + gst_rtp_buffer_calc_packet_len (pu_size + HEADER_SIZE, 0, 0); + + if (packet_size > mtu) { + GST_DEBUG_OBJECT (pay, "needed packet size %u clamped to MTU %u", + packet_size, mtu); + packet_size = mtu; + } else { + GST_DEBUG_OBJECT (pay, "needed packet size %u fits in MTU %u", + packet_size, mtu); + } + + /* get total payload size and data size */ + payload_size = gst_rtp_buffer_calc_payload_len (packet_size, 0, 0); + data_size = payload_size - HEADER_SIZE; + + /* make buffer for header */ + outbuf = gst_rtp_buffer_new_allocate (HEADER_SIZE, 0, 0); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* get pointer to header */ + header = gst_rtp_buffer_get_payload (&rtp); + + pu_size -= data_size; + if (pu_size == 0) { + /* reached the end of a packetization unit */ + if (state.header.MHF) { + /* we were doing a header, see if all fit in one packet or if + * we had to fragment it */ + if (offset == 0) + state.header.MHF = 3; + else + state.header.MHF = 2; + } + if (end >= map.size) + gst_rtp_buffer_set_marker (&rtp, TRUE); + } + + /* + * RtpJ2KHeader: + * @tp: type (0 progressive, 1 odd field, 2 even field) + * @MHF: Main Header Flag + * @mh_id: Main Header Identification + * @T: Tile field invalidation flag + * @priority: priority + * @tile number: the tile number of the payload + * @reserved: set to 0 + * @fragment offset: the byte offset of the current payload + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |tp |MHF|mh_id|T| priority | tile number | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |reserved | fragment offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + header[0] = (state.header.tp << 6) | (state.header.MHF << 4) | + (state.header.mh_id << 1) | state.header.T; + header[1] = state.header.priority; + header[2] = state.header.tile >> 8; + header[3] = state.header.tile & 0xff; + header[4] = 0; + header[5] = state.header.offset >> 16; + header[6] = (state.header.offset >> 8) & 0xff; + header[7] = state.header.offset & 0xff; + + gst_rtp_buffer_unmap (&rtp); + + /* make subbuffer of j2k data */ + paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, + offset, data_size); + + outbuf = gst_buffer_append (outbuf, paybuf); + + gst_buffer_list_add (list, outbuf); + + /* reset header for next round */ + state.header.MHF = 0; + state.header.T = 1; + state.header.tile = 0; + + offset += data_size; + } + offset = pos; + } while (offset < map.size); + + gst_buffer_unref (buffer); + + /* push the whole buffer list at once */ + ret = gst_rtp_base_payload_push_list (basepayload, list); + + return ret; +} + +static void +gst_rtp_j2k_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_j2k_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + switch (prop_id) { + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpj2kpay", GST_RANK_SECONDARY, + GST_TYPE_RTP_J2K_PAY); +} diff --git a/gst/rtp/gstrtpj2kpay.h b/gst/rtp/gstrtpj2kpay.h new file mode 100755 index 0000000..7160a62 --- /dev/null +++ b/gst/rtp/gstrtpj2kpay.h @@ -0,0 +1,61 @@ +/* GStreamer + * Copyright (C) 2009 Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_J2K_PAY_H__ +#define __GST_RTP_J2K_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_J2K_PAY \ + (gst_rtp_j2k_pay_get_type()) +#define GST_RTP_J2K_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_J2K_PAY,GstRtpJ2KPay)) +#define GST_RTP_J2K_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_J2K_PAY,GstRtpJ2KPayClass)) +#define GST_IS_RTP_J2K_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_J2K_PAY)) +#define GST_IS_RTP_J2K_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_J2K_PAY)) + +typedef struct _GstRtpJ2KPay GstRtpJ2KPay; +typedef struct _GstRtpJ2KPayClass GstRtpJ2KPayClass; + +struct _GstRtpJ2KPay +{ + GstRTPBasePayload payload; + + gint height; + gint width; +}; + +struct _GstRtpJ2KPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_j2k_pay_get_type (void); + +gboolean gst_rtp_j2k_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_J2K_PAY_H__ */ diff --git a/gst/rtp/gstrtpjpegdepay.c b/gst/rtp/gstrtpjpegdepay.c new file mode 100755 index 0000000..57055e8 --- /dev/null +++ b/gst/rtp/gstrtpjpegdepay.c @@ -0,0 +1,790 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "gstrtpjpegdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpjpegdepay_debug); +#define GST_CAT_DEFAULT (rtpjpegdepay_debug) + +static GstStaticPadTemplate gst_rtp_jpeg_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/jpeg") + ); + +static GstStaticPadTemplate gst_rtp_jpeg_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG\"; " + /* optional SDP attributes */ + /* + * "a-framerate = (string) 0.00, " + * "x-framerate = (string) 0.00, " + * "x-dimensions = (string) \"1234,1234\", " + */ + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_JPEG_STRING ", " + "clock-rate = (int) 90000" + /* optional SDP attributes */ + /* + * "a-framerate = (string) 0.00, " + * "x-framerate = (string) 0.00, " + * "x-dimensions = (string) \"1234,1234\"" + */ + ) + ); + +#define gst_rtp_jpeg_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpJPEGDepay, gst_rtp_jpeg_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_jpeg_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_jpeg_depay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_jpeg_depay_finalize; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP JPEG depayloader", "Codec/Depayloader/Network/RTP", + "Extracts JPEG video from RTP packets (RFC 2435)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstelement_class->change_state = gst_rtp_jpeg_depay_change_state; + + gstrtpbasedepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_jpeg_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpjpegdepay_debug, "rtpjpegdepay", 0, + "JPEG Video RTP Depayloader"); +} + +static void +gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay) +{ + rtpjpegdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_jpeg_depay_reset (GstRtpJPEGDepay * depay) +{ + gint i; + + depay->width = 0; + depay->height = 0; + depay->media_width = 0; + depay->media_height = 0; + depay->frate_num = 0; + depay->frate_denom = 1; + depay->discont = TRUE; + + for (i = 0; i < 255; i++) { + g_free (depay->qtables[i]); + depay->qtables[i] = NULL; + } + + gst_adapter_clear (depay->adapter); +} + +static void +gst_rtp_jpeg_depay_finalize (GObject * object) +{ + GstRtpJPEGDepay *rtpjpegdepay; + + rtpjpegdepay = GST_RTP_JPEG_DEPAY (object); + + gst_rtp_jpeg_depay_reset (rtpjpegdepay); + + g_object_unref (rtpjpegdepay->adapter); + rtpjpegdepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const int zigzag[] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +/* + * Table K.1 from JPEG spec. + */ +static const int jpeg_luma_quantizer[64] = { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 +}; + +/* + * Table K.2 from JPEG spec. + */ +static const int jpeg_chroma_quantizer[64] = { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 +}; + +/* Call MakeTables with the Q factor and a guint8[128] return array + */ +static void +MakeTables (GstRtpJPEGDepay * rtpjpegdepay, gint Q, guint8 qtable[128]) +{ + gint i; + guint factor; + + factor = CLAMP (Q, 1, 99); + + if (Q < 50) + Q = 5000 / factor; + else + Q = 200 - factor * 2; + + for (i = 0; i < 64; i++) { + gint lq = (jpeg_luma_quantizer[zigzag[i]] * Q + 50) / 100; + gint cq = (jpeg_chroma_quantizer[zigzag[i]] * Q + 50) / 100; + + /* Limit the quantizers to 1 <= q <= 255 */ + qtable[i] = CLAMP (lq, 1, 255); + qtable[i + 64] = CLAMP (cq, 1, 255); + } +} + +static const guint8 lum_dc_codelens[] = { + 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +static const guint8 lum_dc_symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const guint8 lum_ac_codelens[] = { + 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; + +static const guint8 lum_ac_symbols[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, + 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, + 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, + 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, + 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, + 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, + 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, + 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, + 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static const guint8 chm_dc_codelens[] = { + 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; + +static const guint8 chm_dc_symbols[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const guint8 chm_ac_codelens[] = { + 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +static const guint8 chm_ac_symbols[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, + 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, + 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, + 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, + 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, + 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, + 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, + 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, + 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa +}; + +static guint8 * +MakeQuantHeader (guint8 * p, guint8 * qt, gint size, gint tableNo) +{ + *p++ = 0xff; + *p++ = 0xdb; /* DQT */ + *p++ = 0; /* length msb */ + *p++ = size + 3; /* length lsb */ + *p++ = tableNo; + memcpy (p, qt, size); + + return (p + size); +} + +static guint8 * +MakeHuffmanHeader (guint8 * p, const guint8 * codelens, int ncodes, + const guint8 * symbols, int nsymbols, int tableNo, int tableClass) +{ + *p++ = 0xff; + *p++ = 0xc4; /* DHT */ + *p++ = 0; /* length msb */ + *p++ = 3 + ncodes + nsymbols; /* length lsb */ + *p++ = (tableClass << 4) | tableNo; + memcpy (p, codelens, ncodes); + p += ncodes; + memcpy (p, symbols, nsymbols); + p += nsymbols; + + return (p); +} + +static guint8 * +MakeDRIHeader (guint8 * p, guint16 dri) +{ + *p++ = 0xff; + *p++ = 0xdd; /* DRI */ + *p++ = 0x0; /* length msb */ + *p++ = 4; /* length lsb */ + *p++ = dri >> 8; /* dri msb */ + *p++ = dri & 0xff; /* dri lsb */ + + return (p); +} + +/* + * Arguments: + * type, width, height: as supplied in RTP/JPEG header + * qt: quantization tables as either derived from + * the Q field using MakeTables() or as specified + * in section 4.2. + * dri: restart interval in MCUs, or 0 if no restarts. + * + * p: pointer to return area + * + * Return value: + * The length of the generated headers. + * + * Generate a frame and scan headers that can be prepended to the + * RTP/JPEG data payload to produce a JPEG compressed image in + * interchange format (except for possible trailing garbage and + * absence of an EOI marker to terminate the scan). + */ +static guint +MakeHeaders (guint8 * p, int type, int width, int height, guint8 * qt, + guint precision, guint16 dri) +{ + guint8 *start = p; + gint size; + + *p++ = 0xff; + *p++ = 0xd8; /* SOI */ + + size = ((precision & 1) ? 128 : 64); + p = MakeQuantHeader (p, qt, size, 0); + qt += size; + + size = ((precision & 2) ? 128 : 64); + p = MakeQuantHeader (p, qt, size, 1); + qt += size; + + if (dri != 0) + p = MakeDRIHeader (p, dri); + + *p++ = 0xff; + *p++ = 0xc0; /* SOF */ + *p++ = 0; /* length msb */ + *p++ = 17; /* length lsb */ + *p++ = 8; /* 8-bit precision */ + *p++ = height >> 8; /* height msb */ + *p++ = height; /* height lsb */ + *p++ = width >> 8; /* width msb */ + *p++ = width; /* width lsb */ + *p++ = 3; /* number of components */ + *p++ = 0; /* comp 0 */ + if ((type & 0x3f) == 0) + *p++ = 0x21; /* hsamp = 2, vsamp = 1 */ + else + *p++ = 0x22; /* hsamp = 2, vsamp = 2 */ + *p++ = 0; /* quant table 0 */ + *p++ = 1; /* comp 1 */ + *p++ = 0x11; /* hsamp = 1, vsamp = 1 */ + *p++ = 1; /* quant table 1 */ + *p++ = 2; /* comp 2 */ + *p++ = 0x11; /* hsamp = 1, vsamp = 1 */ + *p++ = 1; /* quant table 1 */ + + p = MakeHuffmanHeader (p, lum_dc_codelens, + sizeof (lum_dc_codelens), lum_dc_symbols, sizeof (lum_dc_symbols), 0, 0); + p = MakeHuffmanHeader (p, lum_ac_codelens, + sizeof (lum_ac_codelens), lum_ac_symbols, sizeof (lum_ac_symbols), 0, 1); + p = MakeHuffmanHeader (p, chm_dc_codelens, + sizeof (chm_dc_codelens), chm_dc_symbols, sizeof (chm_dc_symbols), 1, 0); + p = MakeHuffmanHeader (p, chm_ac_codelens, + sizeof (chm_ac_codelens), chm_ac_symbols, sizeof (chm_ac_symbols), 1, 1); + + *p++ = 0xff; + *p++ = 0xda; /* SOS */ + *p++ = 0; /* length msb */ + *p++ = 12; /* length lsb */ + *p++ = 3; /* 3 components */ + *p++ = 0; /* comp 0 */ + *p++ = 0; /* huffman table 0 */ + *p++ = 1; /* comp 1 */ + *p++ = 0x11; /* huffman table 1 */ + *p++ = 2; /* comp 2 */ + *p++ = 0x11; /* huffman table 1 */ + *p++ = 0; /* first DCT coeff */ + *p++ = 63; /* last DCT coeff */ + *p++ = 0; /* sucessive approx. */ + + return (p - start); +}; + +static gboolean +gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstRtpJPEGDepay *rtpjpegdepay; + GstStructure *structure; + gint clock_rate; + const gchar *media_attr; + + rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + GST_DEBUG_OBJECT (rtpjpegdepay, "Caps set: %" GST_PTR_FORMAT, caps); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + /* reset defaults */ + rtpjpegdepay->width = 0; + rtpjpegdepay->height = 0; + rtpjpegdepay->media_width = 0; + rtpjpegdepay->media_height = 0; + rtpjpegdepay->frate_num = 0; + rtpjpegdepay->frate_denom = 1; + + /* check for optional SDP attributes */ + if ((media_attr = gst_structure_get_string (structure, "x-dimensions"))) { + gint w, h; + + if (sscanf (media_attr, "%d,%d", &w, &h) == 2) { + rtpjpegdepay->media_width = w; + rtpjpegdepay->media_height = h; + } + } + + /* try to get a framerate */ + media_attr = gst_structure_get_string (structure, "a-framerate"); + if (!media_attr) + media_attr = gst_structure_get_string (structure, "x-framerate"); + + if (media_attr) { + GValue src = { 0 }; + GValue dest = { 0 }; + gchar *s; + + /* canonicalise floating point string so we can handle framerate strings + * in the form "24.930" or "24,930" irrespective of the current locale */ + s = g_strdup (media_attr); + g_strdelimit (s, ",", '.'); + + /* convert the float to a fraction */ + g_value_init (&src, G_TYPE_DOUBLE); + g_value_set_double (&src, g_ascii_strtod (s, NULL)); + g_value_init (&dest, GST_TYPE_FRACTION); + g_value_transform (&src, &dest); + + rtpjpegdepay->frate_num = gst_value_get_fraction_numerator (&dest); + rtpjpegdepay->frate_denom = gst_value_get_fraction_denominator (&dest); + + g_free (s); + } + + return TRUE; +} + +static GstBuffer * +gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpJPEGDepay *rtpjpegdepay; + GstBuffer *outbuf; + gint payload_len, header_len; + guint8 *payload; + guint frag_offset; + gint Q; + guint type, width, height; + guint16 dri, precision, length; + guint8 *qtable; + GstRTPBuffer rtp = { NULL }; + + rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload); + + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_DEBUG_OBJECT (depayload, "DISCONT, reset adapter"); + gst_adapter_clear (rtpjpegdepay->adapter); + rtpjpegdepay->discont = TRUE; + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len < 8) + goto empty_packet; + + payload = gst_rtp_buffer_get_payload (&rtp); + header_len = 0; + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type-specific | Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Q | Width | Height | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = (payload[1] << 16) | (payload[2] << 8) | payload[3]; + type = payload[4]; + Q = payload[5]; + width = payload[6] * 8; + height = payload[7] * 8; + + /* allow frame dimensions > 2040, passed in SDP session or media attributes + * from gstrtspsrc.c (gst_rtspsrc_sdp_attributes_to_caps), or in caps */ + if (!width) + width = rtpjpegdepay->media_width; + + if (!height) + height = rtpjpegdepay->media_height; + + if (width == 0 || height == 0) + goto invalid_dimension; + + GST_DEBUG_OBJECT (rtpjpegdepay, "frag %u, type %u, Q %d, width %u, height %u", + frag_offset, type, Q, width, height); + + header_len += 8; + payload += 8; + payload_len -= 8; + + dri = 0; + if (type > 63) { + if (payload_len < 4) + goto empty_packet; + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Restart Interval |F|L| Restart Count | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + dri = (payload[0] << 8) | payload[1]; + + GST_DEBUG_OBJECT (rtpjpegdepay, "DRI %" G_GUINT16_FORMAT, dri); + + payload += 4; + header_len += 4; + payload_len -= 4; + } + + if (Q >= 128 && frag_offset == 0) { + if (payload_len < 4) + goto empty_packet; + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | Precision | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Quantization Table Data | + * | ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + precision = payload[1]; + length = (payload[2] << 8) | payload[3]; + + GST_DEBUG_OBJECT (rtpjpegdepay, "precision %04x, length %" G_GUINT16_FORMAT, + precision, length); + + if (Q == 255 && length == 0) + goto empty_packet; + + payload += 4; + header_len += 4; + payload_len -= 4; + + if (length > payload_len) + goto empty_packet; + + if (length > 0) + qtable = payload; + else + qtable = rtpjpegdepay->qtables[Q]; + + payload += length; + header_len += length; + payload_len -= length; + } else { + length = 0; + qtable = NULL; + precision = 0; + } + + if (frag_offset == 0) { + GstMapInfo map; + guint size; + + if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) { + GstCaps *outcaps; + + outcaps = + gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION, + rtpjpegdepay->frate_num, rtpjpegdepay->frate_denom, "width", + G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL); + gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + rtpjpegdepay->width = width; + rtpjpegdepay->height = height; + } + + GST_LOG_OBJECT (rtpjpegdepay, "first packet, length %" G_GUINT16_FORMAT, + length); + + /* first packet */ + if (length == 0) { + if (Q < 128) { + /* no quant table, see if we have one cached */ + qtable = rtpjpegdepay->qtables[Q]; + if (!qtable) { + GST_DEBUG_OBJECT (rtpjpegdepay, "making Q %d table", Q); + /* make and cache the table */ + qtable = g_new (guint8, 128); + MakeTables (rtpjpegdepay, Q, qtable); + rtpjpegdepay->qtables[Q] = qtable; + } else { + GST_DEBUG_OBJECT (rtpjpegdepay, "using cached table for Q %d", Q); + } + /* all 8 bit quantizers */ + precision = 0; + } else { + if (!qtable) + goto no_qtable; + } + } + + /* I think we can get here with a NULL qtable, so make sure we don't + go dereferencing it in MakeHeaders if we do */ + if (!qtable) + goto no_qtable; + + /* max header length, should be big enough */ + outbuf = gst_buffer_new_and_alloc (1000); + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + size = MakeHeaders (map.data, type, width, height, qtable, precision, dri); + gst_buffer_unmap (outbuf, &map); + gst_buffer_resize (outbuf, 0, size); + + GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size); + + gst_adapter_push (rtpjpegdepay->adapter, outbuf); + } + + /* take JPEG data, push in the adapter */ + GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len); + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, header_len, -1); + gst_adapter_push (rtpjpegdepay->adapter, outbuf); + outbuf = NULL; + + if (gst_rtp_buffer_get_marker (&rtp)) { + guint avail; + guint8 end[2]; + GstMapInfo map; + + /* last buffer take all data out of the adapter */ + avail = gst_adapter_available (rtpjpegdepay->adapter); + GST_DEBUG_OBJECT (rtpjpegdepay, "marker set, last buffer"); + + if (avail < 2) + goto invalid_packet; + + /* take the last bytes of the jpeg data to see if there is an EOI + * marker */ + gst_adapter_copy (rtpjpegdepay->adapter, end, avail - 2, 2); + + if (end[0] != 0xff && end[1] != 0xd9) { + GST_DEBUG_OBJECT (rtpjpegdepay, "no EOI marker, adding one"); + + /* no EOI marker, add one */ + outbuf = gst_buffer_new_and_alloc (2); + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + map.data[0] = 0xff; + map.data[1] = 0xd9; + gst_buffer_unmap (outbuf, &map); + + gst_adapter_push (rtpjpegdepay->adapter, outbuf); + avail += 2; + } + outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail); + + if (rtpjpegdepay->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + rtpjpegdepay->discont = FALSE; + } + + GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail); + } + + gst_rtp_buffer_unmap (&rtp); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_dimension: + { + GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT, + ("Invalid Dimension %dx%d.", width, height), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +no_qtable: + { + GST_WARNING_OBJECT (rtpjpegdepay, "no qtable"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_packet: + { + GST_WARNING_OBJECT (rtpjpegdepay, "invalid packet"); + gst_adapter_flush (rtpjpegdepay->adapter, + gst_adapter_available (rtpjpegdepay->adapter)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + + +static GstStateChangeReturn +gst_rtp_jpeg_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpJPEGDepay *rtpjpegdepay; + GstStateChangeReturn ret; + + rtpjpegdepay = GST_RTP_JPEG_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_jpeg_depay_reset (rtpjpegdepay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + break; + default: + break; + } + return ret; +} + + +gboolean +gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpjpegdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_JPEG_DEPAY); +} diff --git a/gst/rtp/gstrtpjpegdepay.h b/gst/rtp/gstrtpjpegdepay.h new file mode 100755 index 0000000..cb74f12 --- /dev/null +++ b/gst/rtp/gstrtpjpegdepay.h @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_JPEG_DEPAY_H__ +#define __GST_RTP_JPEG_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_JPEG_DEPAY \ + (gst_rtp_jpeg_depay_get_type()) +#define GST_RTP_JPEG_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_JPEG_DEPAY,GstRtpJPEGDepay)) +#define GST_RTP_JPEG_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_JPEG_DEPAY,GstRtpJPEGDepayClass)) +#define GST_IS_RTP_JPEG_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_JPEG_DEPAY)) +#define GST_IS_RTP_JPEG_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_JPEG_DEPAY)) + +typedef struct _GstRtpJPEGDepay GstRtpJPEGDepay; +typedef struct _GstRtpJPEGDepayClass GstRtpJPEGDepayClass; + +struct _GstRtpJPEGDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + gboolean discont; + + /* cached quant tables */ + guint8 * qtables[255]; + gint frate_num; + gint frate_denom; + gint media_width; + gint media_height; + gint width, height; +}; + +struct _GstRtpJPEGDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_jpeg_depay_get_type (void); + +gboolean gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_JPEG_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpjpegpay.c b/gst/rtp/gstrtpjpegpay.c new file mode 100755 index 0000000..bf4ed55 --- /dev/null +++ b/gst/rtp/gstrtpjpegpay.c @@ -0,0 +1,996 @@ +/* GStreamer + * Copyright (C) 2008 Axis Communications <dev-gstreamer@axis.com> + * @author Bjorn Ostby <bjorn.ostby@axis.com> + * @author Peter Kjellerstedt <peter.kjellerstedt@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpjpegpay + * + * Payload encode JPEG pictures into RTP packets according to RFC 2435. + * For detailed information see: http://www.rfc-editor.org/rfc/rfc2435.txt + * + * The payloader takes a JPEG picture, scans the header for quantization + * tables (if needed) and constructs the RTP packet header followed by + * the actual JPEG entropy scan. + * + * The payloader assumes that correct width and height is found in the caps. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpjpegpay.h" + +static GstStaticPadTemplate gst_rtp_jpeg_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("image/jpeg; " "video/x-jpeg") + ); + +static GstStaticPadTemplate gst_rtp_jpeg_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + " media = (string) \"video\", " + " payload = (int) 26 , " + " clock-rate = (int) 90000, " + " encoding-name = (string) \"JPEG\", " + " width = (int) [ 1, 65536 ], " " height = (int) [ 1, 65536 ]") + ); + +GST_DEBUG_CATEGORY_STATIC (rtpjpegpay_debug); +#define GST_CAT_DEFAULT (rtpjpegpay_debug) + +/* + * QUANT_PREFIX_LEN: + * + * Prefix length in the header before the quantization tables: + * Two size bytes and one byte for precision + */ +#define QUANT_PREFIX_LEN 3 + + +typedef enum _RtpJpegMarker RtpJpegMarker; + +/* + * RtpJpegMarker: + * @JPEG_MARKER: Prefix for JPEG marker + * @JPEG_MARKER_SOI: Start of Image marker + * @JPEG_MARKER_JFIF: JFIF marker + * @JPEG_MARKER_CMT: Comment marker + * @JPEG_MARKER_DQT: Define Quantization Table marker + * @JPEG_MARKER_SOF: Start of Frame marker + * @JPEG_MARKER_DHT: Define Huffman Table marker + * @JPEG_MARKER_SOS: Start of Scan marker + * @JPEG_MARKER_EOI: End of Image marker + * @JPEG_MARKER_DRI: Define Restart Interval marker + * @JPEG_MARKER_H264: H264 marker + * + * Identifers for markers in JPEG header + */ +enum _RtpJpegMarker +{ + JPEG_MARKER = 0xFF, + JPEG_MARKER_SOI = 0xD8, + JPEG_MARKER_JFIF = 0xE0, + JPEG_MARKER_CMT = 0xFE, + JPEG_MARKER_DQT = 0xDB, + JPEG_MARKER_SOF = 0xC0, + JPEG_MARKER_DHT = 0xC4, + JPEG_MARKER_SOS = 0xDA, + JPEG_MARKER_EOI = 0xD9, + JPEG_MARKER_DRI = 0xDD, + JPEG_MARKER_H264 = 0xE4 +}; + +#define DEFAULT_JPEG_QUANT 255 + +#define DEFAULT_JPEG_QUALITY 255 +#define DEFAULT_JPEG_TYPE 1 + +enum +{ + PROP_0, + PROP_JPEG_QUALITY, + PROP_JPEG_TYPE +}; + +enum +{ + Q_TABLE_0 = 0, + Q_TABLE_1, + Q_TABLE_MAX /* only support for two tables at the moment */ +}; + +typedef struct _RtpJpegHeader RtpJpegHeader; + +/* + * RtpJpegHeader: + * @type_spec: type specific + * @offset: fragment offset + * @type: type field + * @q: quantization table for this frame + * @width: width of image in 8-pixel multiples + * @height: height of image in 8-pixel multiples + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type-specific | Fragment Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Q | Width | Height | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +struct _RtpJpegHeader +{ + guint type_spec:8; + guint offset:24; + guint8 type; + guint8 q; + guint8 width; + guint8 height; +}; + +/* + * RtpQuantHeader + * @mbz: must be zero + * @precision: specify size of quantization tables + * @length: length of quantization data + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | Precision | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Quantization Table Data | + * | ... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ +typedef struct +{ + guint8 mbz; + guint8 precision; + guint16 length; +} RtpQuantHeader; + +typedef struct +{ + guint8 size; + const guint8 *data; +} RtpQuantTable; + +/* + * RtpRestartMarkerHeader: + * @restartInterval: number of MCUs that appear between restart markers + * @restartFirstLastCount: a combination of the first packet mark in the chunk + * last packet mark in the chunk and the position of the + * first restart interval in the current "chunk" + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Restart Interval |F|L| Restart Count | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * The restart marker header is implemented according to the following + * methodology specified in section 3.1.7 of rfc2435.txt. + * + * "If the restart intervals in a frame are not guaranteed to be aligned + * with packet boundaries, the F (first) and L (last) bits MUST be set + * to 1 and the Restart Count MUST be set to 0x3FFF. This indicates + * that a receiver MUST reassemble the entire frame before decoding it." + * + */ + +typedef struct +{ + guint16 restart_interval; + guint16 restart_count; +} RtpRestartMarkerHeader; + +typedef struct +{ + guint8 id; + guint8 samp; + guint8 qt; +} CompInfo; + +/* FIXME: restart marker header currently unsupported */ + +static void gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static void gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); + +static GstFlowReturn gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); + +#define gst_rtp_jpeg_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpJPEGPay, gst_rtp_jpeg_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_jpeg_pay_class_init (GstRtpJPEGPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_jpeg_pay_set_property; + gobject_class->get_property = gst_rtp_jpeg_pay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_jpeg_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_jpeg_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP JPEG payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes JPEG pictures into RTP packets (RFC 2435)", + "Axis Communications <dev-gstreamer@axis.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_jpeg_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_jpeg_pay_handle_buffer; + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_QUALITY, + g_param_spec_int ("quality", "Quality", + "Quality factor on JPEG data (unused)", 0, 255, 255, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_JPEG_TYPE, + g_param_spec_int ("type", "Type", + "Default JPEG Type, overwritten by SOF when present", 0, 255, + DEFAULT_JPEG_TYPE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + GST_DEBUG_CATEGORY_INIT (rtpjpegpay_debug, "rtpjpegpay", 0, + "Motion JPEG RTP Payloader"); +} + +static void +gst_rtp_jpeg_pay_init (GstRtpJPEGPay * pay) +{ + pay->quality = DEFAULT_JPEG_QUALITY; + pay->quant = DEFAULT_JPEG_QUANT; + pay->type = DEFAULT_JPEG_TYPE; + pay->width = -1; + pay->height = -1; +} + +static gboolean +gst_rtp_jpeg_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstStructure *caps_structure = gst_caps_get_structure (caps, 0); + GstRtpJPEGPay *pay; + gboolean res; + gint width = -1, height = -1; + gint num = 0, denom; + gchar *rate = NULL; + gchar *dim = NULL; + + pay = GST_RTP_JPEG_PAY (basepayload); + + /* these properties are mandatory, but they might be adjusted by the SOF, if there + * is one. */ + if (!gst_structure_get_int (caps_structure, "height", &height) || height <= 0) { + goto invalid_dimension; + } + + if (!gst_structure_get_int (caps_structure, "width", &width) || width <= 0) { + goto invalid_dimension; + } + + if (gst_structure_get_fraction (caps_structure, "framerate", &num, &denom) && + (num < 0 || denom <= 0)) { + goto invalid_framerate; + } + + if (height > 2040 || width > 2040) { + pay->height = 0; + pay->width = 0; + } else { + pay->height = GST_ROUND_UP_8 (height) / 8; + pay->width = GST_ROUND_UP_8 (width) / 8; + } + + gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "JPEG", 90000); + + if (num > 0) { + gdouble framerate; + gst_util_fraction_to_double (num, denom, &framerate); + rate = g_strdup_printf ("%f", framerate); + } + + if (pay->width == 0) { + GST_DEBUG_OBJECT (pay, + "width or height are greater than 2040, adding x-dimensions to caps"); + dim = g_strdup_printf ("%d,%d", width, height); + } + + if (rate != NULL && dim != NULL) { + res = gst_rtp_base_payload_set_outcaps (basepayload, "a-framerate", + G_TYPE_STRING, rate, "x-dimensions", G_TYPE_STRING, dim, NULL); + } else if (rate != NULL && dim == NULL) { + res = gst_rtp_base_payload_set_outcaps (basepayload, "a-framerate", + G_TYPE_STRING, rate, NULL); + } else if (rate == NULL && dim != NULL) { + res = gst_rtp_base_payload_set_outcaps (basepayload, "x-dimensions", + G_TYPE_STRING, dim, NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (basepayload, NULL); + } + + if (dim != NULL) + g_free (dim); + if (rate != NULL) + g_free (rate); + + return res; + + /* ERRORS */ +invalid_dimension: + { + GST_ERROR_OBJECT (pay, "Invalid width/height from caps"); + return FALSE; + } +invalid_framerate: + { + GST_ERROR_OBJECT (pay, "Invalid framerate from caps"); + return FALSE; + } +} + +static guint +gst_rtp_jpeg_pay_header_size (const guint8 * data, guint offset) +{ + return data[offset] << 8 | data[offset + 1]; +} + +static guint +gst_rtp_jpeg_pay_read_quant_table (const guint8 * data, guint size, + guint offset, RtpQuantTable tables[]) +{ + guint quant_size, tab_size; + guint8 prec; + guint8 id; + + if (offset + 2 > size) + goto too_small; + + quant_size = gst_rtp_jpeg_pay_header_size (data, offset); + if (quant_size < 2) + goto small_quant_size; + + /* clamp to available data */ + if (offset + quant_size > size) + quant_size = size - offset; + + offset += 2; + quant_size -= 2; + + while (quant_size > 0) { + /* not enough to read the id */ + if (offset + 1 > size) + break; + + id = data[offset] & 0x0f; + if (id == 15) + /* invalid id received - corrupt data */ + goto invalid_id; + + prec = (data[offset] & 0xf0) >> 4; + if (prec) + tab_size = 128; + else + tab_size = 64; + + /* there is not enough for the table */ + if (quant_size < tab_size + 1) + goto no_table; + + GST_LOG ("read quant table %d, tab_size %d, prec %02x", id, tab_size, prec); + + tables[id].size = tab_size; + tables[id].data = &data[offset + 1]; + + tab_size += 1; + quant_size -= tab_size; + offset += tab_size; + } +done: + return offset + quant_size; + + /* ERRORS */ +too_small: + { + GST_WARNING ("not enough data"); + return size; + } +small_quant_size: + { + GST_WARNING ("quant_size too small (%u < 2)", quant_size); + return size; + } +invalid_id: + { + GST_WARNING ("invalid id"); + goto done; + } +no_table: + { + GST_WARNING ("not enough data for table (%u < %u)", quant_size, + tab_size + 1); + goto done; + } +} + +static gboolean +gst_rtp_jpeg_pay_read_sof (GstRtpJPEGPay * pay, const guint8 * data, + guint size, guint * offset, CompInfo info[], RtpQuantTable tables[], + gulong tables_elements) +{ + guint sof_size, off; + guint width, height, infolen; + CompInfo elem; + gint i, j; + + off = *offset; + + /* we need at least 17 bytes for the SOF */ + if (off + 17 > size) + goto wrong_size; + + sof_size = gst_rtp_jpeg_pay_header_size (data, off); + if (sof_size < 17) + goto wrong_length; + + *offset += sof_size; + + /* skip size */ + off += 2; + + /* precision should be 8 */ + if (data[off++] != 8) + goto bad_precision; + + /* read dimensions */ + height = data[off] << 8 | data[off + 1]; + width = data[off + 2] << 8 | data[off + 3]; + off += 4; + + GST_LOG_OBJECT (pay, "got dimensions %ux%u", height, width); + + if (height == 0) { + goto invalid_dimension; + } + if (height > 2040) { + height = 0; + } + if (width == 0) { + goto invalid_dimension; + } + if (width > 2040) { + width = 0; + } + + if (height == 0 || width == 0) { + pay->height = 0; + pay->width = 0; + } else { + pay->height = GST_ROUND_UP_8 (height) / 8; + pay->width = GST_ROUND_UP_8 (width) / 8; + } + + /* we only support 3 components */ + if (data[off++] != 3) + goto bad_components; + + infolen = 0; + for (i = 0; i < 3; i++) { + elem.id = data[off++]; + elem.samp = data[off++]; + elem.qt = data[off++]; + GST_LOG_OBJECT (pay, "got comp %d, samp %02x, qt %d", elem.id, elem.samp, + elem.qt); + /* insertion sort from the last element to the first */ + for (j = infolen; j > 1; j--) { + if (G_LIKELY (info[j - 1].id < elem.id)) + break; + info[j] = info[j - 1]; + } + info[j] = elem; + infolen++; + } + + /* see that the components are supported */ + if (info[0].samp == 0x21) + pay->type = 0; + else if (info[0].samp == 0x22) + pay->type = 1; + else + goto invalid_comp; + + if (!(info[1].samp == 0x11)) + goto invalid_comp; + + if (!(info[2].samp == 0x11)) + goto invalid_comp; + + /* the other components are free to use any quant table but they have to + * have the same table id */ + if (info[1].qt != info[2].qt) { + /* Some MJPG (like the one from the Logitech C-920 camera) uses different + * quant tables for component 1 and 2 but both tables contain the exact + * same data, so we could consider them as being the same tables */ + if (!(info[1].qt < tables_elements && + info[2].qt < tables_elements && + tables[info[1].qt].size > 0 && + tables[info[1].qt].size == tables[info[2].qt].size && + memcmp (tables[info[1].qt].data, tables[info[2].qt].data, + tables[info[1].qt].size) == 0)) + goto invalid_comp; + } + + return TRUE; + + /* ERRORS */ +wrong_size: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, + ("Wrong size %u (needed %u).", size, off + 17), (NULL)); + return FALSE; + } +wrong_length: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, + ("Wrong SOF length %u.", sof_size), (NULL)); + return FALSE; + } +bad_precision: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, + ("Wrong precision, expecting 8."), (NULL)); + return FALSE; + } +invalid_dimension: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, + ("Wrong dimension, size %ux%u", width, height), (NULL)); + return FALSE; + } +bad_components: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, + ("Wrong number of components"), (NULL)); + return FALSE; + } +invalid_comp: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Invalid component"), (NULL)); + return FALSE; + } +} + +static gboolean +gst_rtp_jpeg_pay_read_dri (GstRtpJPEGPay * pay, const guint8 * data, + guint size, guint * offset, RtpRestartMarkerHeader * dri) +{ + guint dri_size, off; + + off = *offset; + + /* we need at least 4 bytes for the DRI */ + if (off + 4 > size) + goto wrong_size; + + dri_size = gst_rtp_jpeg_pay_header_size (data, off); + if (dri_size < 4) + goto wrong_length; + + *offset += dri_size; + off += 2; + + dri->restart_interval = g_htons ((data[off] << 8) | (data[off + 1])); + dri->restart_count = g_htons (0xFFFF); + + return dri->restart_interval > 0; + +wrong_size: + { + GST_WARNING ("not enough data for DRI"); + *offset = size; + return FALSE; + } +wrong_length: + { + GST_WARNING ("DRI size too small (%u)", dri_size); + *offset += dri_size; + return FALSE; + } +} + +static RtpJpegMarker +gst_rtp_jpeg_pay_scan_marker (const guint8 * data, guint size, guint * offset) +{ + while ((data[(*offset)++] != JPEG_MARKER) && ((*offset) < size)); + + if (G_UNLIKELY ((*offset) >= size)) { + GST_LOG ("found EOI marker"); + return JPEG_MARKER_EOI; + } else { + guint8 marker; + + marker = data[*offset]; + GST_LOG ("found 0x%02x marker at offset %u", marker, *offset); + (*offset)++; + return marker; + } +} + +#define RTP_HEADER_LEN 12 + +static GstFlowReturn +gst_rtp_jpeg_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpJPEGPay *pay; + GstClockTime timestamp; + GstFlowReturn ret = GST_FLOW_ERROR; + RtpJpegHeader jpeg_header; + RtpQuantHeader quant_header; + RtpRestartMarkerHeader restart_marker_header; + RtpQuantTable tables[15] = { {0, NULL}, }; + CompInfo info[3] = { {0,}, }; + guint quant_data_size; + GstMapInfo map; + guint8 *data; + gsize size; + guint mtu, max_payload_size; + guint bytes_left; + guint jpeg_header_size = 0; + guint offset; + gboolean frame_done; + gboolean sos_found, sof_found, dqt_found, dri_found; + gint i; + GstBufferList *list = NULL; + gboolean discont; + + pay = GST_RTP_JPEG_PAY (basepayload); + mtu = GST_RTP_BASE_PAYLOAD_MTU (pay); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + timestamp = GST_BUFFER_TIMESTAMP (buffer); + offset = 0; + discont = GST_BUFFER_IS_DISCONT (buffer); + + GST_LOG_OBJECT (pay, "got buffer size %" G_GSIZE_FORMAT + " , timestamp %" GST_TIME_FORMAT, size, GST_TIME_ARGS (timestamp)); + + /* parse the jpeg header for 'start of scan' and read quant tables if needed */ + sos_found = FALSE; + dqt_found = FALSE; + sof_found = FALSE; + dri_found = FALSE; + + while (!sos_found && (offset < size)) { + GST_LOG_OBJECT (pay, "checking from offset %u", offset); + switch (gst_rtp_jpeg_pay_scan_marker (data, size, &offset)) { + case JPEG_MARKER_JFIF: + case JPEG_MARKER_CMT: + case JPEG_MARKER_DHT: + case JPEG_MARKER_H264: + GST_LOG_OBJECT (pay, "skipping marker"); + offset += gst_rtp_jpeg_pay_header_size (data, offset); + break; + case JPEG_MARKER_SOF: + if (!gst_rtp_jpeg_pay_read_sof (pay, data, size, &offset, info, tables, + G_N_ELEMENTS (tables))) + goto invalid_format; + sof_found = TRUE; + break; + case JPEG_MARKER_DQT: + GST_LOG ("DQT found"); + offset = gst_rtp_jpeg_pay_read_quant_table (data, size, offset, tables); + dqt_found = TRUE; + break; + case JPEG_MARKER_SOS: + sos_found = TRUE; + GST_LOG_OBJECT (pay, "SOS found"); + jpeg_header_size = offset + gst_rtp_jpeg_pay_header_size (data, offset); + break; + case JPEG_MARKER_EOI: + GST_WARNING_OBJECT (pay, "EOI reached before SOS!"); + break; + case JPEG_MARKER_SOI: + GST_LOG_OBJECT (pay, "SOI found"); + break; + case JPEG_MARKER_DRI: + GST_LOG_OBJECT (pay, "DRI found"); + if (gst_rtp_jpeg_pay_read_dri (pay, data, size, &offset, + &restart_marker_header)) + dri_found = TRUE; + break; + default: + break; + } + } + if (!dqt_found || !sof_found) + goto unsupported_jpeg; + + /* by now we should either have negotiated the width/height or the SOF header + * should have filled us in */ + if (pay->width < 0 || pay->height < 0) { + goto no_dimension; + } + + GST_LOG_OBJECT (pay, "header size %u", jpeg_header_size); + + size -= jpeg_header_size; + data += jpeg_header_size; + offset = 0; + + if (dri_found) + pay->type += 64; + + /* prepare stuff for the jpeg header */ + jpeg_header.type_spec = 0; + jpeg_header.type = pay->type; + jpeg_header.q = pay->quant; + jpeg_header.width = pay->width; + jpeg_header.height = pay->height; + + /* collect the quant headers sizes */ + quant_header.mbz = 0; + quant_header.precision = 0; + quant_header.length = 0; + quant_data_size = 0; + + if (pay->quant > 127) { + /* for the Y and U component, look up the quant table and its size. quant + * tables for U and V should be the same */ + for (i = 0; i < 2; i++) { + guint qsize; + guint qt; + + qt = info[i].qt; + if (qt >= G_N_ELEMENTS (tables)) + goto invalid_quant; + + qsize = tables[qt].size; + if (qsize == 0) + goto invalid_quant; + + quant_header.precision |= (qsize == 64 ? 0 : (1 << i)); + quant_data_size += qsize; + } + quant_header.length = g_htons (quant_data_size); + quant_data_size += sizeof (quant_header); + } + + GST_LOG_OBJECT (pay, "quant_data size %u", quant_data_size); + + bytes_left = sizeof (jpeg_header) + quant_data_size + size; + + if (dri_found) + bytes_left += sizeof (restart_marker_header); + + max_payload_size = mtu - (RTP_HEADER_LEN + sizeof (jpeg_header)); + list = gst_buffer_list_new_sized ((bytes_left / max_payload_size) + 1); + + frame_done = FALSE; + do { + GstBuffer *outbuf; + guint8 *payload; + guint payload_size; + guint header_size; + GstBuffer *paybuf; + GstRTPBuffer rtp = { NULL }; + guint rtp_header_size = gst_rtp_buffer_calc_header_len (0); + + /* The available room is the packet MTU, minus the RTP header length. */ + payload_size = + (bytes_left < (mtu - rtp_header_size) ? bytes_left : + (mtu - rtp_header_size)); + + header_size = sizeof (jpeg_header) + quant_data_size; + if (dri_found) + header_size += sizeof (restart_marker_header); + + outbuf = gst_rtp_buffer_new_allocate (header_size, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + if (payload_size == bytes_left) { + GST_LOG_OBJECT (pay, "last packet of frame"); + frame_done = TRUE; + gst_rtp_buffer_set_marker (&rtp, 1); + } + + payload = gst_rtp_buffer_get_payload (&rtp); + + /* update offset */ +#if (G_BYTE_ORDER == G_LITTLE_ENDIAN) + jpeg_header.offset = ((offset & 0x0000FF) << 16) | + ((offset & 0xFF0000) >> 16) | (offset & 0x00FF00); +#else + jpeg_header.offset = offset; +#endif + memcpy (payload, &jpeg_header, sizeof (jpeg_header)); + payload += sizeof (jpeg_header); + payload_size -= sizeof (jpeg_header); + + if (dri_found) { + memcpy (payload, &restart_marker_header, sizeof (restart_marker_header)); + payload += sizeof (restart_marker_header); + payload_size -= sizeof (restart_marker_header); + } + + /* only send quant table with first packet */ + if (G_UNLIKELY (quant_data_size > 0)) { + memcpy (payload, &quant_header, sizeof (quant_header)); + payload += sizeof (quant_header); + + /* copy the quant tables for luma and chrominance */ + for (i = 0; i < 2; i++) { + guint qsize; + guint qt; + + qt = info[i].qt; + qsize = tables[qt].size; + memcpy (payload, tables[qt].data, qsize); + + GST_LOG_OBJECT (pay, "component %d using quant %d, size %d", i, qt, + qsize); + + payload += qsize; + } + payload_size -= quant_data_size; + bytes_left -= quant_data_size; + quant_data_size = 0; + } + GST_LOG_OBJECT (pay, "sending payload size %d", payload_size); + gst_rtp_buffer_unmap (&rtp); + + /* create a new buf to hold the payload */ + paybuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, + jpeg_header_size + offset, payload_size); + + /* join memory parts */ + outbuf = gst_buffer_append (outbuf, paybuf); + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first outputted buffer has the DISCONT flag */ + discont = FALSE; + } + + /* and add to list */ + gst_buffer_list_insert (list, -1, outbuf); + + bytes_left -= payload_size; + offset += payload_size; + data += payload_size; + } + while (!frame_done); + + /* push the whole buffer list at once */ + ret = gst_rtp_base_payload_push_list (basepayload, list); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return ret; + + /* ERRORS */ +unsupported_jpeg: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Unsupported JPEG"), (NULL)); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +no_dimension: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("No size given"), (NULL)); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +invalid_format: + { + /* error was posted */ + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +invalid_quant: + { + GST_ELEMENT_WARNING (pay, STREAM, FORMAT, ("Invalid quant tables"), (NULL)); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +} + +static void +gst_rtp_jpeg_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpJPEGPay *rtpjpegpay; + + rtpjpegpay = GST_RTP_JPEG_PAY (object); + + switch (prop_id) { + case PROP_JPEG_QUALITY: + rtpjpegpay->quality = g_value_get_int (value); + GST_DEBUG_OBJECT (object, "quality = %d", rtpjpegpay->quality); + break; + case PROP_JPEG_TYPE: + rtpjpegpay->type = g_value_get_int (value); + GST_DEBUG_OBJECT (object, "type = %d", rtpjpegpay->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_jpeg_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpJPEGPay *rtpjpegpay; + + rtpjpegpay = GST_RTP_JPEG_PAY (object); + + switch (prop_id) { + case PROP_JPEG_QUALITY: + g_value_set_int (value, rtpjpegpay->quality); + break; + case PROP_JPEG_TYPE: + g_value_set_int (value, rtpjpegpay->type); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpjpegpay", GST_RANK_SECONDARY, + GST_TYPE_RTP_JPEG_PAY); +} diff --git a/gst/rtp/gstrtpjpegpay.h b/gst/rtp/gstrtpjpegpay.h new file mode 100755 index 0000000..4d65ea7 --- /dev/null +++ b/gst/rtp/gstrtpjpegpay.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) 2008 Axis Communications <dev-gstreamer@axis.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_JPEG_PAY_H__ +#define __GST_RTP_JPEG_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS +#define GST_TYPE_RTP_JPEG_PAY \ + (gst_rtp_jpeg_pay_get_type()) +#define GST_RTP_JPEG_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_JPEG_PAY,GstRtpJPEGPay)) +#define GST_RTP_JPEG_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_JPEG_PAY,GstRtpJPEGPayClass)) +#define GST_IS_RTP_JPEG_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_JPEG_PAY)) +#define GST_IS_RTP_JPEG_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_JPEG_PAY)) +typedef struct _GstRtpJPEGPay GstRtpJPEGPay; +typedef struct _GstRtpJPEGPayClass GstRtpJPEGPayClass; + +struct _GstRtpJPEGPay +{ + GstRTPBasePayload payload; + + guint8 quality; + guint8 type; + + gint height; + gint width; + + guint8 quant; +}; + +struct _GstRtpJPEGPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_jpeg_pay_get_type (void); + +gboolean gst_rtp_jpeg_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_JPEG_PAY_H__ */ diff --git a/gst/rtp/gstrtpmp1sdepay.c b/gst/rtp/gstrtpmp1sdepay.c new file mode 100755 index 0000000..63545e7 --- /dev/null +++ b/gst/rtp/gstrtpmp1sdepay.c @@ -0,0 +1,142 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmp1sdepay.h" + +/* RtpMP1SDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0, + PROP_LAST +}; + +static GstStaticPadTemplate gst_rtp_mp1s_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg,systemstream=(boolean)true") + ); + +/* The spec says video/MP1S but I have seen streams with other/MP1S so we will + * allow them both */ +static GstStaticPadTemplate gst_rtp_mp1s_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"other\", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP1S\";" + "application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP1S\"") + ); + +G_DEFINE_TYPE (GstRtpMP1SDepay, gst_rtp_mp1s_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_mp1s_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mp1s_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_mp1s_depay_class_init (GstRtpMP1SDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_mp1s_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_mp1s_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp1s_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp1s_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG1 System Stream depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG1 System Streams from RTP packets (RFC 3555)", + "Wim Taymans <wim.taymans@gmail.com>"); +} + +static void +gst_rtp_mp1s_depay_init (GstRtpMP1SDepay * rtpmp1sdepay) +{ +} + +static gboolean +gst_rtp_mp1s_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure; + gint clock_rate; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("video/mpeg", + "systemstream", G_TYPE_BOOLEAN, TRUE, NULL); + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static GstBuffer * +gst_rtp_mp1s_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (outbuf) + GST_DEBUG ("gst_rtp_mp1s_depay_chain: pushing buffer of size %" + G_GSIZE_FORMAT, gst_buffer_get_size (outbuf)); + + return outbuf; +} + +gboolean +gst_rtp_mp1s_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp1sdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP1S_DEPAY); +} diff --git a/gst/rtp/gstrtpmp1sdepay.h b/gst/rtp/gstrtpmp1sdepay.h new file mode 100755 index 0000000..582933b --- /dev/null +++ b/gst/rtp/gstrtpmp1sdepay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP1S_DEPAY_H__ +#define __GST_RTP_MP1S_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP1S_DEPAY \ + (gst_rtp_mp1s_depay_get_type()) +#define GST_RTP_MP1S_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP1S_DEPAY,GstRtpMP1SDepay)) +#define GST_RTP_MP1S_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP1S_DEPAY,GstRtpMP1SDepayClass)) +#define GST_IS_RTP_MP1S_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP1S_DEPAY)) +#define GST_IS_RTP_MP1S_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP1S_DEPAY)) +typedef struct _GstRtpMP1SDepay GstRtpMP1SDepay; +typedef struct _GstRtpMP1SDepayClass GstRtpMP1SDepayClass; + +struct _GstRtpMP1SDepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpMP1SDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mp1s_depay_get_type (void); + +gboolean gst_rtp_mp1s_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP1S_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmp2tdepay.c b/gst/rtp/gstrtpmp2tdepay.c new file mode 100755 index 0000000..345d906 --- /dev/null +++ b/gst/rtp/gstrtpmp2tdepay.c @@ -0,0 +1,243 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmp2tdepay.h" + +/* RtpMP2TDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +#define DEFAULT_SKIP_FIRST_BYTES 0 + +enum +{ + PROP_0, + PROP_SKIP_FIRST_BYTES +}; + +static GstStaticPadTemplate gst_rtp_mp2t_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpegts," + "packetsize=(int)188," "systemstream=(boolean)true") + ); + +static GstStaticPadTemplate gst_rtp_mp2t_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) { MP2T, MP2T-ES } ;" + /* All optional parameters + * + * "profile-level-id=[1,MAX]" + * "config=" + */ + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_MP2T_STRING ", " + "clock-rate = (int) [1, MAX ]") + ); + +G_DEFINE_TYPE (GstRtpMP2TDepay, gst_rtp_mp2t_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_mp2t_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mp2t_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void gst_rtp_mp2t_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_mp2t_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_rtp_mp2t_depay_class_init (GstRtpMP2TDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_mp2t_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_mp2t_depay_setcaps; + + gobject_class->set_property = gst_rtp_mp2t_depay_set_property; + gobject_class->get_property = gst_rtp_mp2t_depay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp2t_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp2t_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG Transport Stream depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG2 TS from RTP packets (RFC 2250)", + "Wim Taymans <wim.taymans@gmail.com>, " + "Thijs Vermeir <thijs.vermeir@barco.com>"); + + g_object_class_install_property (gobject_class, PROP_SKIP_FIRST_BYTES, + g_param_spec_uint ("skip-first-bytes", + "Skip first bytes", + "The amount of bytes that need to be skipped at the beginning of the payload", + 0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + +} + +static void +gst_rtp_mp2t_depay_init (GstRtpMP2TDepay * rtpmp2tdepay) +{ + rtpmp2tdepay->skip_first_bytes = DEFAULT_SKIP_FIRST_BYTES; +} + +static gboolean +gst_rtp_mp2t_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure; + gint clock_rate; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("video/mpegts", + "packetsize", G_TYPE_INT, 188, + "systemstream", G_TYPE_BOOLEAN, TRUE, NULL); + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static GstBuffer * +gst_rtp_mp2t_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMP2TDepay *rtpmp2tdepay; + GstBuffer *outbuf; + gint payload_len, leftover; + GstRTPBuffer rtp = { NULL }; + + rtpmp2tdepay = GST_RTP_MP2T_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (G_UNLIKELY (payload_len <= rtpmp2tdepay->skip_first_bytes)) + goto empty_packet; + + payload_len -= rtpmp2tdepay->skip_first_bytes; + + /* RFC 2250 + * + * 2. Encapsulation of MPEG System and Transport Streams + * + * For MPEG2 Transport Streams the RTP payload will contain an integral + * number of MPEG transport packets. + */ + leftover = payload_len % 188; + if (G_UNLIKELY (leftover)) { + GST_WARNING ("We don't have an integral number of buffers (leftover: %d)", + leftover); + + payload_len -= leftover; + } + + outbuf = + gst_rtp_buffer_get_payload_subbuffer (&rtp, + rtpmp2tdepay->skip_first_bytes, payload_len); + + gst_rtp_buffer_unmap (&rtp); + if (outbuf) + GST_DEBUG ("gst_rtp_mp2t_depay_chain: pushing buffer of size %" + G_GSIZE_FORMAT, gst_buffer_get_size (outbuf)); + + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpmp2tdepay, STREAM, DECODE, + (NULL), ("Packet was empty")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static void +gst_rtp_mp2t_depay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpMP2TDepay *rtpmp2tdepay; + + rtpmp2tdepay = GST_RTP_MP2T_DEPAY (object); + + switch (prop_id) { + case PROP_SKIP_FIRST_BYTES: + rtpmp2tdepay->skip_first_bytes = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_mp2t_depay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpMP2TDepay *rtpmp2tdepay; + + rtpmp2tdepay = GST_RTP_MP2T_DEPAY (object); + + switch (prop_id) { + case PROP_SKIP_FIRST_BYTES: + g_value_set_uint (value, rtpmp2tdepay->skip_first_bytes); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_mp2t_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp2tdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP2T_DEPAY); +} diff --git a/gst/rtp/gstrtpmp2tdepay.h b/gst/rtp/gstrtpmp2tdepay.h new file mode 100755 index 0000000..aa936dc --- /dev/null +++ b/gst/rtp/gstrtpmp2tdepay.h @@ -0,0 +1,60 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP2T_DEPAY_H__ +#define __GST_RTP_MP2T_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP2T_DEPAY \ + (gst_rtp_mp2t_depay_get_type()) +#define GST_RTP_MP2T_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP2T_DEPAY,GstRtpMP2TDepay)) +#define GST_RTP_MP2T_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP2T_DEPAY,GstRtpMP2TDepayClass)) +#define GST_IS_RTP_MP2T_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP2T_DEPAY)) +#define GST_IS_RTP_MP2T_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP2T_DEPAY)) +typedef struct _GstRtpMP2TDepay GstRtpMP2TDepay; +typedef struct _GstRtpMP2TDepayClass GstRtpMP2TDepayClass; + +struct _GstRtpMP2TDepay +{ + GstRTPBaseDepayload depayload; + + guint8 skip_first_bytes; +}; + +struct _GstRtpMP2TDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mp2t_depay_get_type (void); + +gboolean gst_rtp_mp2t_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP2T_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmp2tpay.c b/gst/rtp/gstrtpmp2tpay.c new file mode 100755 index 0000000..1c6ec51 --- /dev/null +++ b/gst/rtp/gstrtpmp2tpay.c @@ -0,0 +1,234 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmp2tpay.h" + +static GstStaticPadTemplate gst_rtp_mp2t_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpegts," + "packetsize=(int)188," "systemstream=(boolean)true") + ); + +static GstStaticPadTemplate gst_rtp_mp2t_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_MP2T_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MP2T\" ; " + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MP2T\"") + ); + +static gboolean gst_rtp_mp2t_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_mp2t_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); +static GstFlowReturn gst_rtp_mp2t_pay_flush (GstRTPMP2TPay * rtpmp2tpay); +static void gst_rtp_mp2t_pay_finalize (GObject * object); + +#define gst_rtp_mp2t_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPMP2TPay, gst_rtp_mp2t_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_mp2t_pay_class_init (GstRTPMP2TPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp2t_pay_finalize; + + gstrtpbasepayload_class->set_caps = gst_rtp_mp2t_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mp2t_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp2t_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp2t_pay_src_template)); + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG2 Transport Stream payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes MPEG2 TS into RTP packets (RFC 2250)", + "Wim Taymans <wim.taymans@gmail.com>"); +} + +static void +gst_rtp_mp2t_pay_init (GstRTPMP2TPay * rtpmp2tpay) +{ + GST_RTP_BASE_PAYLOAD (rtpmp2tpay)->clock_rate = 90000; + GST_RTP_BASE_PAYLOAD_PT (rtpmp2tpay) = GST_RTP_PAYLOAD_MP2T; + + rtpmp2tpay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mp2t_pay_finalize (GObject * object) +{ + GstRTPMP2TPay *rtpmp2tpay; + + rtpmp2tpay = GST_RTP_MP2T_PAY (object); + + g_object_unref (rtpmp2tpay->adapter); + rtpmp2tpay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_mp2t_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + + gst_rtp_base_payload_set_options (payload, "video", TRUE, "MP2T", 90000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +static GstFlowReturn +gst_rtp_mp2t_pay_flush (GstRTPMP2TPay * rtpmp2tpay) +{ + guint avail, mtu; + GstFlowReturn ret = GST_FLOW_OK; + GstBuffer *outbuf; + + avail = gst_adapter_available (rtpmp2tpay->adapter); + + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp2tpay); + + while (avail > 0 && (ret == GST_FLOW_OK)) { + guint towrite; + guint payload_len; + guint packet_len; + GstBuffer *paybuf; + + /* this will be the total length of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + payload_len -= payload_len % 188; + + /* need whole packets */ + if (!payload_len) + break; + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); + + /* get payload */ + paybuf = gst_adapter_take_buffer_fast (rtpmp2tpay->adapter, payload_len); + outbuf = gst_buffer_append (outbuf, paybuf); + avail -= payload_len; + + GST_BUFFER_TIMESTAMP (outbuf) = rtpmp2tpay->first_ts; + GST_BUFFER_DURATION (outbuf) = rtpmp2tpay->duration; + + GST_DEBUG_OBJECT (rtpmp2tpay, "pushing buffer of size %u", + (guint) gst_buffer_get_size (outbuf)); + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp2tpay), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_rtp_mp2t_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRTPMP2TPay *rtpmp2tpay; + guint size, avail, packet_len; + GstClockTime timestamp, duration; + GstFlowReturn ret; + + rtpmp2tpay = GST_RTP_MP2T_PAY (basepayload); + + size = gst_buffer_get_size (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + +again: + ret = GST_FLOW_OK; + avail = gst_adapter_available (rtpmp2tpay->adapter); + + /* Initialize new RTP payload */ + if (avail == 0) { + rtpmp2tpay->first_ts = timestamp; + rtpmp2tpay->duration = duration; + } + + /* get packet length of previous data and this new data */ + packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0); + + /* if this buffer is going to overflow the packet, flush what we have, + * or if upstream is handing us several packets, to keep latency low */ + if (!size || gst_rtp_base_payload_is_filled (basepayload, + packet_len, rtpmp2tpay->duration + duration)) { + ret = gst_rtp_mp2t_pay_flush (rtpmp2tpay); + rtpmp2tpay->first_ts = timestamp; + rtpmp2tpay->duration = duration; + + /* keep filling the payload */ + } else { + if (GST_CLOCK_TIME_IS_VALID (duration)) + rtpmp2tpay->duration += duration; + } + + /* copy buffer to adapter */ + if (buffer) { + gst_adapter_push (rtpmp2tpay->adapter, buffer); + buffer = NULL; + } + + if (size >= (188 * 2)) { + size = 0; + goto again; + } + + return ret; + +} + +gboolean +gst_rtp_mp2t_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp2tpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP2T_PAY); +} diff --git a/gst/rtp/gstrtpmp2tpay.h b/gst/rtp/gstrtpmp2tpay.h new file mode 100755 index 0000000..12f4959 --- /dev/null +++ b/gst/rtp/gstrtpmp2tpay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_RTP_MP2T_PAY_H__ +#define __GST_RTP_MP2T_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPMP2TPay GstRTPMP2TPay; +typedef struct _GstRTPMP2TPayClass GstRTPMP2TPayClass; + +#define GST_TYPE_RTP_MP2T_PAY \ + (gst_rtp_mp2t_pay_get_type()) +#define GST_RTP_MP2T_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP2T_PAY,GstRTPMP2TPay)) +#define GST_RTP_MP2T_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP2T_PAY,GstRTPMP2TPayClass)) +#define GST_IS_RTP_MP2T_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP2T_PAY)) +#define GST_IS_RTP_MP2T_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP2T_PAY)) + +struct _GstRTPMP2TPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + GstClockTime duration; +}; + +struct _GstRTPMP2TPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mp2t_pay_get_type (void); + +gboolean gst_rtp_mp2t_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP2T_PAY_H__ */ diff --git a/gst/rtp/gstrtpmp4adepay.c b/gst/rtp/gstrtpmp4adepay.c new file mode 100755 index 0000000..e70d5b3 --- /dev/null +++ b/gst/rtp/gstrtpmp4adepay.c @@ -0,0 +1,445 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation (contact <stefan.kost@nokia.com>) + * <2007> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/base/gstbitreader.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmp4adepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4adepay_debug); +#define GST_CAT_DEFAULT (rtpmp4adepay_debug) + +static GstStaticPadTemplate gst_rtp_mp4a_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg," + "mpegversion = (int) 4," "framed = (boolean) true, " + "stream-format = (string) raw") + ); + +static GstStaticPadTemplate gst_rtp_mp4a_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) \"MP4A-LATM\"" + /* All optional parameters + * + * "profile-level-id=[1,MAX]" + * "config=" + */ + ) + ); + +#define gst_rtp_mp4a_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4ADepay, gst_rtp_mp4a_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_mp4a_depay_finalize (GObject * object); + +static gboolean gst_rtp_mp4a_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static GstStateChangeReturn gst_rtp_mp4a_depay_change_state (GstElement * + element, GstStateChange transition); + + +static void +gst_rtp_mp4a_depay_class_init (GstRtpMP4ADepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp4a_depay_finalize; + + gstelement_class->change_state = gst_rtp_mp4a_depay_change_state; + + gstrtpbasedepayload_class->process = gst_rtp_mp4a_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_mp4a_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4a_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4a_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG4 audio from RTP packets (RFC 3016)", + "Nokia Corporation (contact <stefan.kost@nokia.com>), " + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpmp4adepay_debug, "rtpmp4adepay", 0, + "MPEG4 audio RTP Depayloader"); +} + +static void +gst_rtp_mp4a_depay_init (GstRtpMP4ADepay * rtpmp4adepay) +{ + rtpmp4adepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mp4a_depay_finalize (GObject * object) +{ + GstRtpMP4ADepay *rtpmp4adepay; + + rtpmp4adepay = GST_RTP_MP4A_DEPAY (object); + + g_object_unref (rtpmp4adepay->adapter); + rtpmp4adepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, + 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350 +}; + +static gboolean +gst_rtp_mp4a_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpMP4ADepay *rtpmp4adepay; + GstCaps *srccaps; + const gchar *str; + gint clock_rate; + gint object_type; + gint channels = 2; /* default */ + gboolean res; + + rtpmp4adepay = GST_RTP_MP4A_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + if (!gst_structure_get_int (structure, "object", &object_type)) + object_type = 2; /* AAC LC default */ + + srccaps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, + "framed", G_TYPE_BOOLEAN, TRUE, "channels", G_TYPE_INT, channels, + "stream-format", G_TYPE_STRING, "raw", NULL); + + if ((str = gst_structure_get_string (structure, "config"))) { + GValue v = { 0 }; + + g_value_init (&v, GST_TYPE_BUFFER); + if (gst_value_deserialize (&v, str)) { + GstBuffer *buffer; + GstMapInfo map; + guint8 *data; + gsize size; + gint i; + guint32 rate = 0; + guint8 obj_type = 0, sr_idx = 0, channels = 0; + GstBitReader br; + + buffer = gst_value_get_buffer (&v); + gst_buffer_ref (buffer); + g_value_unset (&v); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + if (size < 2) { + GST_WARNING_OBJECT (depayload, "config too short (%d < 2)", + (gint) size); + goto bad_config; + } + + /* Parse StreamMuxConfig according to ISO/IEC 14496-3: + * + * audioMuxVersion == 0 (1 bit) + * allStreamsSameTimeFraming == 1 (1 bit) + * numSubFrames == rtpmp4adepay->numSubFrames (6 bits) + * numProgram == 0 (4 bits) + * numLayer == 0 (3 bits) + * + * We only require audioMuxVersion == 0; + * + * The remaining bit of the second byte and the rest of the bits are used + * for audioSpecificConfig which we need to set in codec_info. + */ + if ((data[0] & 0x80) != 0x00) { + GST_WARNING_OBJECT (depayload, "unknown audioMuxVersion 1"); + goto bad_config; + } + + rtpmp4adepay->numSubFrames = (data[0] & 0x3F); + + GST_LOG_OBJECT (rtpmp4adepay, "numSubFrames %d", + rtpmp4adepay->numSubFrames); + + /* shift rest of string 15 bits down */ + size -= 2; + for (i = 0; i < size; i++) { + data[i] = ((data[i + 1] & 1) << 7) | ((data[i + 2] & 0xfe) >> 1); + } + + gst_bit_reader_init (&br, data, size); + + /* any object type is fine, we need to copy it to the profile-level-id field. */ + if (!gst_bit_reader_get_bits_uint8 (&br, &obj_type, 5)) + goto bad_config; + if (obj_type == 0) { + GST_WARNING_OBJECT (depayload, "invalid object type 0"); + goto bad_config; + } + + if (!gst_bit_reader_get_bits_uint8 (&br, &sr_idx, 4)) + goto bad_config; + if (sr_idx >= G_N_ELEMENTS (aac_sample_rates) && sr_idx != 15) { + GST_WARNING_OBJECT (depayload, "invalid sample rate index %d", sr_idx); + goto bad_config; + } + GST_LOG_OBJECT (rtpmp4adepay, "sample rate index %u", sr_idx); + + if (!gst_bit_reader_get_bits_uint8 (&br, &channels, 4)) + goto bad_config; + if (channels > 7) { + GST_WARNING_OBJECT (depayload, "invalid channels %u", (guint) channels); + goto bad_config; + } + + /* rtp rate depends on sampling rate of the audio */ + if (sr_idx == 15) { + /* index of 15 means we get the rate in the next 24 bits */ + if (!gst_bit_reader_get_bits_uint32 (&br, &rate, 24)) + goto bad_config; + } else if (sr_idx >= G_N_ELEMENTS (aac_sample_rates)) { + goto bad_config; + } else { + /* else use the rate from the table */ + rate = aac_sample_rates[sr_idx]; + } + + rtpmp4adepay->frame_len = 1024; + + switch (obj_type) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + { + guint8 frameLenFlag = 0; + + if (gst_bit_reader_get_bits_uint8 (&br, &frameLenFlag, 1)) + if (frameLenFlag) + rtpmp4adepay->frame_len = 960; + break; + } + default: + break; + } + + /* ignore remaining bit, we're only interested in full bytes */ + gst_buffer_resize (buffer, 0, size); + gst_buffer_unmap (buffer, &map); + data = NULL; + + gst_caps_set_simple (srccaps, + "channels", G_TYPE_INT, (gint) channels, + "rate", G_TYPE_INT, (gint) rate, + "codec_data", GST_TYPE_BUFFER, buffer, NULL); + bad_config: + if (data) + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + } else { + g_warning ("cannot convert config to buffer"); + } + } + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static GstBuffer * +gst_rtp_mp4a_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMP4ADepay *rtpmp4adepay; + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + GstMapInfo map; + + rtpmp4adepay = GST_RTP_MP4A_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + gst_adapter_clear (rtpmp4adepay->adapter); + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + + outbuf = gst_buffer_make_writable (outbuf); + GST_BUFFER_TIMESTAMP (outbuf) = GST_BUFFER_TIMESTAMP (buf); + gst_adapter_push (rtpmp4adepay->adapter, outbuf); + + /* RTP marker bit indicates the last packet of the AudioMuxElement => create + * and push a buffer */ + if (gst_rtp_buffer_get_marker (&rtp)) { + guint avail; + guint i; + guint8 *data; + guint pos; + GstClockTime timestamp; + + avail = gst_adapter_available (rtpmp4adepay->adapter); + timestamp = gst_adapter_prev_pts (rtpmp4adepay->adapter, NULL); + + GST_LOG_OBJECT (rtpmp4adepay, "have marker and %u available", avail); + + outbuf = gst_adapter_take_buffer (rtpmp4adepay->adapter, avail); + gst_buffer_map (outbuf, &map, GST_MAP_READ); + data = map.data; + /* position in data we are at */ + pos = 0; + + /* looping through the number of sub-frames in the audio payload */ + for (i = 0; i <= rtpmp4adepay->numSubFrames; i++) { + /* determine payload length and set buffer data pointer accordingly */ + guint skip; + guint data_len; + GstBuffer *tmp = NULL; + + /* each subframe starts with a variable length encoding */ + data_len = 0; + for (skip = 0; skip < avail; skip++) { + data_len += data[skip]; + if (data[skip] != 0xff) + break; + } + skip++; + + /* this can not be possible, we have not enough data or the length + * decoding failed because we ran out of data. */ + if (skip + data_len > avail) + goto wrong_size; + + GST_LOG_OBJECT (rtpmp4adepay, + "subframe %u, header len %u, data len %u, left %u", i, skip, data_len, + avail); + + /* take data out, skip the header */ + pos += skip; + tmp = gst_buffer_copy_region (outbuf, GST_BUFFER_COPY_MEMORY, pos, + data_len); + + /* skip data too */ + skip += data_len; + pos += data_len; + + /* update our pointers whith what we consumed */ + data += skip; + avail -= skip; + + GST_BUFFER_TIMESTAMP (tmp) = timestamp; + gst_rtp_base_depayload_push (depayload, tmp); + + /* shift ts for next buffers */ + if (rtpmp4adepay->frame_len && timestamp != -1 + && depayload->clock_rate != 0) { + timestamp += + gst_util_uint64_scale_int (rtpmp4adepay->frame_len, GST_SECOND, + depayload->clock_rate); + } + } + + /* just a check that lengths match */ + if (avail) { + GST_ELEMENT_WARNING (depayload, STREAM, DECODE, + ("Packet invalid"), ("Not all payload consumed: " + "possible wrongly encoded packet.")); + } + + gst_buffer_unmap (outbuf, &map); + gst_buffer_unref (outbuf); + } + gst_rtp_buffer_unmap (&rtp); + return NULL; + + /* ERRORS */ +wrong_size: + { + GST_ELEMENT_WARNING (rtpmp4adepay, STREAM, DECODE, + ("Packet did not validate"), ("wrong packet size")); + gst_buffer_unmap (outbuf, &map); + gst_buffer_unref (outbuf); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_mp4a_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpMP4ADepay *rtpmp4adepay; + GstStateChangeReturn ret; + + rtpmp4adepay = GST_RTP_MP4A_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtpmp4adepay->adapter); + rtpmp4adepay->frame_len = 0; + rtpmp4adepay->numSubFrames = 0; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + default: + break; + } + return ret; +} + +gboolean +gst_rtp_mp4a_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4adepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4A_DEPAY); +} diff --git a/gst/rtp/gstrtpmp4adepay.h b/gst/rtp/gstrtpmp4adepay.h new file mode 100755 index 0000000..b44349f --- /dev/null +++ b/gst/rtp/gstrtpmp4adepay.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <2007> Nokia Corporation (contact <stefan.kost@nokia.com>) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4A_DEPAY_H__ +#define __GST_RTP_MP4A_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4A_DEPAY \ + (gst_rtp_mp4a_depay_get_type()) +#define GST_RTP_MP4A_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4A_DEPAY,GstRtpMP4ADepay)) +#define GST_RTP_MP4A_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4A_DEPAY,GstRtpMP4ADepayClass)) +#define GST_IS_RTP_MP4A_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4A_DEPAY)) +#define GST_IS_RTP_MP4A_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4A_DEPAY)) + +typedef struct _GstRtpMP4ADepay GstRtpMP4ADepay; +typedef struct _GstRtpMP4ADepayClass GstRtpMP4ADepayClass; + +struct _GstRtpMP4ADepay +{ + GstRTPBaseDepayload depayload; + GstAdapter *adapter; + guint8 numSubFrames; + guint frame_len; +}; + +struct _GstRtpMP4ADepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mp4a_depay_get_type (void); + +gboolean gst_rtp_mp4a_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4A_DEPAY_H__ */ + diff --git a/gst/rtp/gstrtpmp4apay.c b/gst/rtp/gstrtpmp4apay.c new file mode 100755 index 0000000..17a80a8 --- /dev/null +++ b/gst/rtp/gstrtpmp4apay.c @@ -0,0 +1,452 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmp4apay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4apay_debug); +#define GST_CAT_DEFAULT (rtpmp4apay_debug) + +/* FIXME: add framed=(boolean)true once our encoders have this field set + * on their output caps */ +static GstStaticPadTemplate gst_rtp_mp4a_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, mpegversion=(int)4, " + "stream-format=(string)raw") + ); + +static GstStaticPadTemplate gst_rtp_mp4a_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) \"MP4A-LATM\"" + /* All optional parameters + * + * "cpresent = (string) \"0\"" + * "config=" + */ + ) + ); + +static void gst_rtp_mp4a_pay_finalize (GObject * object); + +static gboolean gst_rtp_mp4a_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_mp4a_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +#define gst_rtp_mp4a_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4APay, gst_rtp_mp4a_pay, GST_TYPE_RTP_BASE_PAYLOAD) + + static void gst_rtp_mp4a_pay_class_init (GstRtpMP4APayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp4a_pay_finalize; + + gstrtpbasepayload_class->set_caps = gst_rtp_mp4a_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4a_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4a_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4a_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 audio payloader", "Codec/Payloader/Network/RTP", + "Payload MPEG4 audio as RTP packets (RFC 3016)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpmp4apay_debug, "rtpmp4apay", 0, + "MP4A-LATM RTP Payloader"); +} + +static void +gst_rtp_mp4a_pay_init (GstRtpMP4APay * rtpmp4apay) +{ + rtpmp4apay->rate = 90000; + rtpmp4apay->profile = g_strdup ("1"); +} + +static void +gst_rtp_mp4a_pay_finalize (GObject * object) +{ + GstRtpMP4APay *rtpmp4apay; + + rtpmp4apay = GST_RTP_MP4A_PAY (object); + + g_free (rtpmp4apay->params); + rtpmp4apay->params = NULL; + + if (rtpmp4apay->config) + gst_buffer_unref (rtpmp4apay->config); + rtpmp4apay->config = NULL; + + g_free (rtpmp4apay->profile); + rtpmp4apay->profile = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const unsigned int sampling_table[16] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +static gboolean +gst_rtp_mp4a_pay_parse_audio_config (GstRtpMP4APay * rtpmp4apay, + GstBuffer * buffer) +{ + GstMapInfo map; + guint8 *data; + gsize size; + guint8 objectType; + guint8 samplingIdx; + guint8 channelCfg; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + if (size < 2) + goto too_short; + + /* any object type is fine, we need to copy it to the profile-level-id field. */ + objectType = (data[0] & 0xf8) >> 3; + if (objectType == 0) + goto invalid_object; + + samplingIdx = ((data[0] & 0x07) << 1) | ((data[1] & 0x80) >> 7); + /* only fixed values for now */ + if (samplingIdx > 12 && samplingIdx != 15) + goto wrong_freq; + + channelCfg = ((data[1] & 0x78) >> 3); + if (channelCfg > 7) + goto wrong_channels; + + /* rtp rate depends on sampling rate of the audio */ + if (samplingIdx == 15) { + if (size < 5) + goto too_short; + + /* index of 15 means we get the rate in the next 24 bits */ + rtpmp4apay->rate = ((data[1] & 0x7f) << 17) | + ((data[2]) << 9) | ((data[3]) << 1) | ((data[4] & 0x80) >> 7); + } else { + /* else use the rate from the table */ + rtpmp4apay->rate = sampling_table[samplingIdx]; + } + /* extra rtp params contain the number of channels */ + g_free (rtpmp4apay->params); + rtpmp4apay->params = g_strdup_printf ("%d", channelCfg); + /* audio stream type */ + rtpmp4apay->streamtype = "5"; + /* profile */ + g_free (rtpmp4apay->profile); + rtpmp4apay->profile = g_strdup_printf ("%d", objectType); + + GST_DEBUG_OBJECT (rtpmp4apay, + "objectType: %d, samplingIdx: %d (%d), channelCfg: %d", objectType, + samplingIdx, rtpmp4apay->rate, channelCfg); + + gst_buffer_unmap (buffer, &map); + + return TRUE; + + /* ERROR */ +too_short: + { + GST_ELEMENT_ERROR (rtpmp4apay, STREAM, FORMAT, + (NULL), + ("config string too short, expected 2 bytes, got %" G_GSIZE_FORMAT, + size)); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +invalid_object: + { + GST_ELEMENT_ERROR (rtpmp4apay, STREAM, FORMAT, + (NULL), ("invalid object type 0")); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +wrong_freq: + { + GST_ELEMENT_ERROR (rtpmp4apay, STREAM, NOT_IMPLEMENTED, + (NULL), ("unsupported frequency index %d", samplingIdx)); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +wrong_channels: + { + GST_ELEMENT_ERROR (rtpmp4apay, STREAM, NOT_IMPLEMENTED, + (NULL), ("unsupported number of channels %d, must < 8", channelCfg)); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +} + +static gboolean +gst_rtp_mp4a_pay_new_caps (GstRtpMP4APay * rtpmp4apay) +{ + gchar *config; + GValue v = { 0 }; + gboolean res; + + g_value_init (&v, GST_TYPE_BUFFER); + gst_value_set_buffer (&v, rtpmp4apay->config); + config = gst_value_serialize (&v); + + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4apay), + "cpresent", G_TYPE_STRING, "0", "config", G_TYPE_STRING, config, NULL); + + g_value_unset (&v); + g_free (config); + + return res; +} + +static gboolean +gst_rtp_mp4a_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpMP4APay *rtpmp4apay; + GstStructure *structure; + const GValue *codec_data; + gboolean res, framed = TRUE; + const gchar *stream_format; + + rtpmp4apay = GST_RTP_MP4A_PAY (payload); + + structure = gst_caps_get_structure (caps, 0); + + /* this is already handled by the template caps, but it is better + * to leave here to have meaningful warning messages when linking + * fails */ + stream_format = gst_structure_get_string (structure, "stream-format"); + if (stream_format) { + if (strcmp (stream_format, "raw") != 0) { + GST_WARNING_OBJECT (rtpmp4apay, "AAC's stream-format must be 'raw', " + "%s is not supported", stream_format); + return FALSE; + } + } else { + GST_WARNING_OBJECT (rtpmp4apay, "AAC's stream-format not specified, " + "assuming 'raw'"); + } + + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + GST_LOG_OBJECT (rtpmp4apay, "got codec_data"); + if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { + GstBuffer *buffer, *cbuffer; + GstMapInfo map; + GstMapInfo cmap; + guint i; + + buffer = gst_value_get_buffer (codec_data); + GST_LOG_OBJECT (rtpmp4apay, "configuring codec_data"); + + /* parse buffer */ + res = gst_rtp_mp4a_pay_parse_audio_config (rtpmp4apay, buffer); + + if (!res) + goto config_failed; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + /* make the StreamMuxConfig, we need 15 bits for the header */ + cbuffer = gst_buffer_new_and_alloc (map.size + 2); + gst_buffer_map (cbuffer, &cmap, GST_MAP_WRITE); + + memset (cmap.data, 0, map.size + 2); + + /* Create StreamMuxConfig according to ISO/IEC 14496-3: + * + * audioMuxVersion == 0 (1 bit) + * allStreamsSameTimeFraming == 1 (1 bit) + * numSubFrames == numSubFrames (6 bits) + * numProgram == 0 (4 bits) + * numLayer == 0 (3 bits) + */ + cmap.data[0] = 0x40; + cmap.data[1] = 0x00; + + /* append the config bits, shifting them 1 bit left */ + for (i = 0; i < map.size; i++) { + cmap.data[i + 1] |= ((map.data[i] & 0x80) >> 7); + cmap.data[i + 2] |= ((map.data[i] & 0x7f) << 1); + } + + gst_buffer_unmap (cbuffer, &cmap); + gst_buffer_unmap (buffer, &map); + + /* now we can configure the buffer */ + if (rtpmp4apay->config) + gst_buffer_unref (rtpmp4apay->config); + rtpmp4apay->config = cbuffer; + } + } + + if (gst_structure_get_boolean (structure, "framed", &framed) && !framed) { + GST_WARNING_OBJECT (payload, "Need framed AAC data as input!"); + } + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, "MP4A-LATM", + rtpmp4apay->rate); + + res = gst_rtp_mp4a_pay_new_caps (rtpmp4apay); + + return res; + + /* ERRORS */ +config_failed: + { + GST_DEBUG_OBJECT (rtpmp4apay, "failed to parse config"); + return FALSE; + } +} + +/* we expect buffers as exactly one complete AU + */ +static GstFlowReturn +gst_rtp_mp4a_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpMP4APay *rtpmp4apay; + GstFlowReturn ret; + GstBuffer *outbuf; + guint count, mtu; + GstMapInfo map; + gsize size; + guint8 *data; + gboolean fragmented; + GstClockTime timestamp; + + ret = GST_FLOW_OK; + + rtpmp4apay = GST_RTP_MP4A_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + size = map.size; + data = map.data; + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + fragmented = FALSE; + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4apay); + + while (size > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + GstRTPBuffer rtp = { NULL }; + + /* this will be the total lenght of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (size, 0, 0); + + if (!fragmented) { + /* first packet calculate space for the packet including the header */ + count = size; + while (count >= 0xff) { + packet_len++; + count -= 0xff; + } + packet_len++; + } + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + GST_DEBUG_OBJECT (rtpmp4apay, + "avail %" G_GSIZE_FORMAT ", towrite %d, packet_len %d, payload_len %d", + size, towrite, packet_len, payload_len); + + /* create buffer to hold the payload. */ + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + + /* copy payload */ + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + if (!fragmented) { + /* first packet write the header */ + count = size; + while (count >= 0xff) { + *payload++ = 0xff; + payload_len--; + count -= 0xff; + } + *payload++ = count; + payload_len--; + } + + /* copy data to payload */ + memcpy (payload, data, payload_len); + data += payload_len; + size -= payload_len; + + /* marker only if the packet is complete */ + gst_rtp_buffer_set_marker (&rtp, size == 0); + + gst_rtp_buffer_unmap (&rtp); + + /* copy incomming timestamp (if any) to outgoing buffers */ + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4apay), outbuf); + + fragmented = TRUE; + } + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + return ret; +} + +gboolean +gst_rtp_mp4a_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4apay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4A_PAY); +} diff --git a/gst/rtp/gstrtpmp4apay.h b/gst/rtp/gstrtpmp4apay.h new file mode 100755 index 0000000..49d9b65 --- /dev/null +++ b/gst/rtp/gstrtpmp4apay.h @@ -0,0 +1,65 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4A_PAY_H__ +#define __GST_RTP_MP4A_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4A_PAY \ + (gst_rtp_mp4a_pay_get_type()) +#define GST_RTP_MP4A_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4A_PAY,GstRtpMP4APay)) +#define GST_RTP_MP4A_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4A_PAY,GstRtpMP4APayClass)) +#define GST_IS_RTP_MP4A_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4A_PAY)) +#define GST_IS_RTP_MP4A_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4A_PAY)) + +typedef struct _GstRtpMP4APay GstRtpMP4APay; +typedef struct _GstRtpMP4APayClass GstRtpMP4APayClass; + +struct _GstRtpMP4APay +{ + GstRTPBasePayload payload; + + gint rate; + gchar *params; + gchar *profile; + const gchar *streamtype; + GstBuffer *config; +}; + +struct _GstRtpMP4APayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mp4a_pay_get_type (void); + +gboolean gst_rtp_mp4a_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4A_PAY_H__ */ diff --git a/gst/rtp/gstrtpmp4gdepay.c b/gst/rtp/gstrtpmp4gdepay.c new file mode 100755 index 0000000..db472da --- /dev/null +++ b/gst/rtp/gstrtpmp4gdepay.c @@ -0,0 +1,774 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmp4gdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4gdepay_debug); +#define GST_CAT_DEFAULT (rtpmp4gdepay_debug) + +static GstStaticPadTemplate gst_rtp_mp4g_depay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg," + "mpegversion=(int) 4," + "systemstream=(boolean)false;" + "audio/mpeg," "mpegversion=(int) 4, " "stream-format=(string)raw") + ); + +static GstStaticPadTemplate gst_rtp_mp4g_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) { \"video\", \"audio\", \"application\" }, " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) \"MPEG4-GENERIC\", " + /* required string params */ + /* "streamtype = (string) { \"4\", \"5\" }, " Not set by Wowza 4 = video, 5 = audio */ + /* "profile-level-id = (string) [1,MAX], " */ + /* "config = (string) [1,MAX]" */ + "mode = (string) { \"generic\", \"CELP-cbr\", \"CELP-vbr\", \"AAC-lbr\", \"AAC-hbr\" } " + /* Optional general parameters */ + /* "objecttype = (string) [1,MAX], " */ + /* "constantsize = (string) [1,MAX], " *//* constant size of each AU */ + /* "constantduration = (string) [1,MAX], " *//* constant duration of each AU */ + /* "maxdisplacement = (string) [1,MAX], " */ + /* "de-interleavebuffersize = (string) [1,MAX], " */ + /* Optional configuration parameters */ + /* "sizelength = (string) [1, 32], " */ + /* "indexlength = (string) [1, 32], " */ + /* "indexdeltalength = (string) [1, 32], " */ + /* "ctsdeltalength = (string) [1, 32], " */ + /* "dtsdeltalength = (string) [1, 32], " */ + /* "randomaccessindication = (string) {0, 1}, " */ + /* "streamstateindication = (string) [0, 32], " */ + /* "auxiliarydatasizelength = (string) [0, 32]" */ ) + ); + +/* simple bitstream parser */ +typedef struct +{ + const guint8 *data; + const guint8 *end; + gint head; /* bitpos in the cache of next bit */ + guint64 cache; /* cached bytes */ +} GstBsParse; + +static void +gst_bs_parse_init (GstBsParse * bs, const guint8 * data, guint size) +{ + bs->data = data; + bs->end = data + size; + bs->head = 0; + bs->cache = 0xffffffff; +} + +static guint32 +gst_bs_parse_read (GstBsParse * bs, guint n) +{ + guint32 res = 0; + gint shift; + + if (n == 0) + return res; + + /* fill up the cache if we need to */ + while (bs->head < n) { + if (bs->data >= bs->end) { + /* we're at the end, can't produce more than head number of bits */ + n = bs->head; + break; + } + /* shift bytes in cache, moving the head bits of the cache left */ + bs->cache = (bs->cache << 8) | *bs->data++; + bs->head += 8; + } + + /* bring the required bits down and truncate */ + if ((shift = bs->head - n) > 0) + res = bs->cache >> shift; + else + res = bs->cache; + + /* mask out required bits */ + if (n < 32) + res &= (1 << n) - 1; + + bs->head = shift; + + return res; +} + + +#define gst_rtp_mp4g_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4GDepay, gst_rtp_mp4g_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_mp4g_depay_finalize (GObject * object); + +static gboolean gst_rtp_mp4g_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mp4g_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_mp4g_depay_handle_event (GstRTPBaseDepayload * filter, + GstEvent * event); + +static GstStateChangeReturn gst_rtp_mp4g_depay_change_state (GstElement * + element, GstStateChange transition); + + +static void +gst_rtp_mp4g_depay_class_init (GstRtpMP4GDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp4g_depay_finalize; + + gstelement_class->change_state = gst_rtp_mp4g_depay_change_state; + + gstrtpbasedepayload_class->process = gst_rtp_mp4g_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_mp4g_depay_setcaps; + gstrtpbasedepayload_class->handle_event = gst_rtp_mp4g_depay_handle_event; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4g_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4g_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 ES depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG4 elementary streams from RTP packets (RFC 3640)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpmp4gdepay_debug, "rtpmp4gdepay", 0, + "MP4-generic RTP Depayloader"); +} + +static void +gst_rtp_mp4g_depay_init (GstRtpMP4GDepay * rtpmp4gdepay) +{ + rtpmp4gdepay->adapter = gst_adapter_new (); + rtpmp4gdepay->packets = g_queue_new (); +} + +static void +gst_rtp_mp4g_depay_finalize (GObject * object) +{ + GstRtpMP4GDepay *rtpmp4gdepay; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (object); + + g_object_unref (rtpmp4gdepay->adapter); + rtpmp4gdepay->adapter = NULL; + g_queue_free (rtpmp4gdepay->packets); + rtpmp4gdepay->packets = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gint +gst_rtp_mp4g_depay_parse_int (GstStructure * structure, const gchar * field, + gint def) +{ + const gchar *str; + gint res; + + if ((str = gst_structure_get_string (structure, field))) + return atoi (str); + + if (gst_structure_get_int (structure, field, &res)) + return res; + + return def; +} + +static gboolean +gst_rtp_mp4g_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpMP4GDepay *rtpmp4gdepay; + GstCaps *srccaps = NULL; + const gchar *str; + gint clock_rate; + gint someint; + gboolean res; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + if ((str = gst_structure_get_string (structure, "media"))) { + if (strcmp (str, "audio") == 0) { + srccaps = gst_caps_new_simple ("audio/mpeg", + "mpegversion", G_TYPE_INT, 4, "stream-format", G_TYPE_STRING, "raw", + NULL); + } else if (strcmp (str, "video") == 0) { + srccaps = gst_caps_new_simple ("video/mpeg", + "mpegversion", G_TYPE_INT, 4, + "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + } + } + if (srccaps == NULL) + goto unknown_media; + + /* these values are optional and have a default value of 0 (no header) */ + rtpmp4gdepay->sizelength = + gst_rtp_mp4g_depay_parse_int (structure, "sizelength", 0); + rtpmp4gdepay->indexlength = + gst_rtp_mp4g_depay_parse_int (structure, "indexlength", 0); + rtpmp4gdepay->indexdeltalength = + gst_rtp_mp4g_depay_parse_int (structure, "indexdeltalength", 0); + rtpmp4gdepay->ctsdeltalength = + gst_rtp_mp4g_depay_parse_int (structure, "ctsdeltalength", 0); + rtpmp4gdepay->dtsdeltalength = + gst_rtp_mp4g_depay_parse_int (structure, "dtsdeltalength", 0); + someint = + gst_rtp_mp4g_depay_parse_int (structure, "randomaccessindication", 0); + rtpmp4gdepay->randomaccessindication = someint > 0 ? 1 : 0; + rtpmp4gdepay->streamstateindication = + gst_rtp_mp4g_depay_parse_int (structure, "streamstateindication", 0); + rtpmp4gdepay->auxiliarydatasizelength = + gst_rtp_mp4g_depay_parse_int (structure, "auxiliarydatasizelength", 0); + rtpmp4gdepay->constantSize = + gst_rtp_mp4g_depay_parse_int (structure, "constantsize", 0); + rtpmp4gdepay->constantDuration = + gst_rtp_mp4g_depay_parse_int (structure, "constantduration", 0); + rtpmp4gdepay->maxDisplacement = + gst_rtp_mp4g_depay_parse_int (structure, "maxdisplacement", 0); + + + /* get config string */ + if ((str = gst_structure_get_string (structure, "config"))) { + GValue v = { 0 }; + + g_value_init (&v, GST_TYPE_BUFFER); + if (gst_value_deserialize (&v, str)) { + GstBuffer *buffer; + + buffer = gst_value_get_buffer (&v); + gst_caps_set_simple (srccaps, + "codec_data", GST_TYPE_BUFFER, buffer, NULL); + g_value_unset (&v); + } else { + g_warning ("cannot convert config to buffer"); + } + } + + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +unknown_media: + { + GST_DEBUG_OBJECT (rtpmp4gdepay, "Unknown media type"); + return FALSE; + } +} + +static void +gst_rtp_mp4g_depay_clear_queue (GstRtpMP4GDepay * rtpmp4gdepay) +{ + GstBuffer *outbuf; + + while ((outbuf = g_queue_pop_head (rtpmp4gdepay->packets))) + gst_buffer_unref (outbuf); +} + +static void +gst_rtp_mp4g_depay_reset (GstRtpMP4GDepay * rtpmp4gdepay) +{ + gst_adapter_clear (rtpmp4gdepay->adapter); + rtpmp4gdepay->max_AU_index = -1; + rtpmp4gdepay->next_AU_index = -1; + rtpmp4gdepay->prev_AU_index = -1; + rtpmp4gdepay->prev_rtptime = -1; + rtpmp4gdepay->last_AU_index = -1; + gst_rtp_mp4g_depay_clear_queue (rtpmp4gdepay); +} + +static void +gst_rtp_mp4g_depay_flush_queue (GstRtpMP4GDepay * rtpmp4gdepay) +{ + GstBuffer *outbuf; + gboolean discont = FALSE; + guint AU_index; + + while ((outbuf = g_queue_pop_head (rtpmp4gdepay->packets))) { + AU_index = GST_BUFFER_OFFSET (outbuf); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "next available AU_index %u", AU_index); + + if (rtpmp4gdepay->next_AU_index != AU_index) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "discont, expected AU_index %u", + rtpmp4gdepay->next_AU_index); + discont = TRUE; + } + + if (discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + discont = FALSE; + } + + GST_DEBUG_OBJECT (rtpmp4gdepay, "pushing AU_index %u", AU_index); + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmp4gdepay), outbuf); + rtpmp4gdepay->next_AU_index = AU_index + 1; + } +} + +static void +gst_rtp_mp4g_depay_queue (GstRtpMP4GDepay * rtpmp4gdepay, GstBuffer * outbuf) +{ + guint AU_index = GST_BUFFER_OFFSET (outbuf); + + if (rtpmp4gdepay->next_AU_index == -1) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "Init AU counter %u", AU_index); + rtpmp4gdepay->next_AU_index = AU_index; + } + + if (rtpmp4gdepay->next_AU_index == AU_index) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "pushing expected AU_index %u", AU_index); + + /* we received the expected packet, push it and flush as much as we can from + * the queue */ + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmp4gdepay), outbuf); + rtpmp4gdepay->next_AU_index++; + + while ((outbuf = g_queue_peek_head (rtpmp4gdepay->packets))) { + AU_index = GST_BUFFER_OFFSET (outbuf); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "next available AU_index %u", AU_index); + + if (rtpmp4gdepay->next_AU_index == AU_index) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "pushing expected AU_index %u", + AU_index); + outbuf = g_queue_pop_head (rtpmp4gdepay->packets); + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmp4gdepay), + outbuf); + rtpmp4gdepay->next_AU_index++; + } else { + GST_DEBUG_OBJECT (rtpmp4gdepay, "waiting for next AU_index %u", + rtpmp4gdepay->next_AU_index); + break; + } + } + } else { + GList *list; + + GST_DEBUG_OBJECT (rtpmp4gdepay, "queueing AU_index %u", AU_index); + + /* loop the list to skip strictly smaller AU_index buffers */ + for (list = rtpmp4gdepay->packets->head; list; list = g_list_next (list)) { + guint idx; + gint gap; + + idx = GST_BUFFER_OFFSET (GST_BUFFER_CAST (list->data)); + + /* compare the new seqnum to the one in the buffer */ + gap = (gint) (idx - AU_index); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "compare with AU_index %u, gap %d", idx, + gap); + + /* AU_index <= idx, we can stop looking */ + if (G_LIKELY (gap > 0)) + break; + } + if (G_LIKELY (list)) + g_queue_insert_before (rtpmp4gdepay->packets, list, outbuf); + else + g_queue_push_tail (rtpmp4gdepay->packets, outbuf); + } +} + +static GstBuffer * +gst_rtp_mp4g_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMP4GDepay *rtpmp4gdepay; + GstBuffer *outbuf = NULL; + GstClockTime timestamp; + GstRTPBuffer rtp = { NULL }; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "received DISCONT"); + gst_adapter_clear (rtpmp4gdepay->adapter); + } + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + { + gint payload_len, payload_AU; + guint8 *payload; + guint32 rtptime; + guint AU_headers_len; + guint AU_size, AU_index, AU_index_delta, payload_AU_size; + gboolean M; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "received payload of %d", payload_len); + + rtptime = gst_rtp_buffer_get_timestamp (&rtp); + M = gst_rtp_buffer_get_marker (&rtp); + + if (rtpmp4gdepay->sizelength > 0) { + gint num_AU_headers, AU_headers_bytes, i; + GstBsParse bs; + + if (payload_len < 2) + goto short_payload; + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ + * |AU-headers-length|AU-header|AU-header| |AU-header|padding| + * | | (1) | (2) | | (n) * | bits | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ + * + * The length is 2 bytes and contains the length of the following + * AU-headers in bits. + */ + AU_headers_len = (payload[0] << 8) | payload[1]; + AU_headers_bytes = (AU_headers_len + 7) / 8; + num_AU_headers = AU_headers_len / 16; + + GST_DEBUG_OBJECT (rtpmp4gdepay, "AU headers len %d, bytes %d, num %d", + AU_headers_len, AU_headers_bytes, num_AU_headers); + + /* skip header */ + payload += 2; + payload_len -= 2; + + if (payload_len < AU_headers_bytes) + goto short_payload; + + /* skip special headers, point to first payload AU */ + payload_AU = 2 + AU_headers_bytes; + payload_AU_size = payload_len - AU_headers_bytes; + + if (G_UNLIKELY (rtpmp4gdepay->auxiliarydatasizelength)) { + gint aux_size; + + /* point the bitstream parser to the first auxiliary data bit */ + gst_bs_parse_init (&bs, payload + AU_headers_bytes, + payload_len - AU_headers_bytes); + aux_size = + gst_bs_parse_read (&bs, rtpmp4gdepay->auxiliarydatasizelength); + /* convert to bytes */ + aux_size = (aux_size + 7) / 8; + /* AU data then follows auxiliary data */ + if (payload_AU_size < aux_size) + goto short_payload; + payload_AU += aux_size; + payload_AU_size -= aux_size; + } + + /* point the bitstream parser to the first AU header bit */ + gst_bs_parse_init (&bs, payload, payload_len); + AU_index = AU_index_delta = 0; + + for (i = 0; i < num_AU_headers && payload_AU_size > 0; i++) { + /* parse AU header + * +---------------------------------------+ + * | AU-size | + * +---------------------------------------+ + * | AU-Index / AU-Index-delta | + * +---------------------------------------+ + * | CTS-flag | + * +---------------------------------------+ + * | CTS-delta | + * +---------------------------------------+ + * | DTS-flag | + * +---------------------------------------+ + * | DTS-delta | + * +---------------------------------------+ + * | RAP-flag | + * +---------------------------------------+ + * | Stream-state | + * +---------------------------------------+ + */ + AU_size = gst_bs_parse_read (&bs, rtpmp4gdepay->sizelength); + + /* calculate the AU_index, which is only on the first AU of the packet + * and the AU_index_delta on the other AUs. This will be used to + * reconstruct the AU ordering when interleaving. */ + if (i == 0) { + AU_index = gst_bs_parse_read (&bs, rtpmp4gdepay->indexlength); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "AU index %u", AU_index); + + if (AU_index == 0 && rtpmp4gdepay->prev_AU_index == 0) { + gint diff; + gint cd; + + /* if we see two consecutive packets with AU_index of 0, we can + * assume we have constantDuration packets. Since we don't have + * the index we must use the AU duration to calculate the + * index. Get the diff between the timestamps first, this can be + * positive or negative. */ + if (rtpmp4gdepay->prev_rtptime <= rtptime) + diff = rtptime - rtpmp4gdepay->prev_rtptime; + else + diff = -(rtpmp4gdepay->prev_rtptime - rtptime); + + /* if no constantDuration was given, make one */ + if (rtpmp4gdepay->constantDuration != 0) { + cd = rtpmp4gdepay->constantDuration; + GST_DEBUG_OBJECT (depayload, "using constantDuration %d", cd); + } else if (rtpmp4gdepay->prev_AU_num > 0) { + /* use number of packets and of previous frame */ + cd = diff / rtpmp4gdepay->prev_AU_num; + GST_DEBUG_OBJECT (depayload, "guessing constantDuration %d", cd); + } else { + /* assume this frame has the same number of packets as the + * previous one */ + cd = diff / num_AU_headers; + GST_DEBUG_OBJECT (depayload, "guessing constantDuration %d", cd); + } + + if (cd > 0) { + /* get the number of packets by dividing with the duration */ + diff /= cd; + } else { + diff = 0; + } + + rtpmp4gdepay->last_AU_index += diff; + rtpmp4gdepay->prev_AU_index = AU_index; + + AU_index = rtpmp4gdepay->last_AU_index; + + GST_DEBUG_OBJECT (rtpmp4gdepay, "diff %d, AU index %u", diff, + AU_index); + } else { + rtpmp4gdepay->prev_AU_index = AU_index; + rtpmp4gdepay->last_AU_index = AU_index; + } + + /* keep track of the higest AU_index */ + if (rtpmp4gdepay->max_AU_index != -1 + && rtpmp4gdepay->max_AU_index <= AU_index) { + GST_DEBUG_OBJECT (rtpmp4gdepay, "new interleave group, flushing"); + /* a new interleave group started, flush */ + gst_rtp_mp4g_depay_flush_queue (rtpmp4gdepay); + } + if (G_UNLIKELY (!rtpmp4gdepay->maxDisplacement && + rtpmp4gdepay->max_AU_index != -1 + && rtpmp4gdepay->max_AU_index >= AU_index)) { + GstBuffer *outbuf; + + /* some broken non-interleaved streams have AU-index jumping around + * all over the place, apparently assuming receiver disregards */ + GST_DEBUG_OBJECT (rtpmp4gdepay, "non-interleaved broken AU indices;" + " forcing continuous flush"); + /* reset AU to avoid repeated DISCONT in such case */ + outbuf = g_queue_peek_head (rtpmp4gdepay->packets); + if (G_LIKELY (outbuf)) { + rtpmp4gdepay->next_AU_index = GST_BUFFER_OFFSET (outbuf); + gst_rtp_mp4g_depay_flush_queue (rtpmp4gdepay); + } + /* rebase next_AU_index to current rtp's first AU_index */ + rtpmp4gdepay->next_AU_index = AU_index; + } + rtpmp4gdepay->prev_rtptime = rtptime; + rtpmp4gdepay->prev_AU_num = num_AU_headers; + } else { + AU_index_delta = + gst_bs_parse_read (&bs, rtpmp4gdepay->indexdeltalength); + AU_index += AU_index_delta + 1; + } + /* keep track of highest AU_index */ + if (rtpmp4gdepay->max_AU_index == -1 + || AU_index > rtpmp4gdepay->max_AU_index) + rtpmp4gdepay->max_AU_index = AU_index; + + /* the presentation time offset, a 2s-complement value, we need this to + * calculate the timestamp on the output packet. */ + if (rtpmp4gdepay->ctsdeltalength > 0) { + if (gst_bs_parse_read (&bs, 1)) + gst_bs_parse_read (&bs, rtpmp4gdepay->ctsdeltalength); + } + /* the decoding time offset, a 2s-complement value */ + if (rtpmp4gdepay->dtsdeltalength > 0) { + if (gst_bs_parse_read (&bs, 1)) + gst_bs_parse_read (&bs, rtpmp4gdepay->dtsdeltalength); + } + /* RAP-flag to indicate that the AU contains a keyframe */ + if (rtpmp4gdepay->randomaccessindication) + gst_bs_parse_read (&bs, 1); + /* stream-state */ + if (rtpmp4gdepay->streamstateindication > 0) + gst_bs_parse_read (&bs, rtpmp4gdepay->streamstateindication); + + GST_DEBUG_OBJECT (rtpmp4gdepay, "size %d, index %d, delta %d", AU_size, + AU_index, AU_index_delta); + + /* fragmented pakets have the AU_size set to the size of the + * unfragmented AU. */ + if (AU_size > payload_AU_size) + AU_size = payload_AU_size; + + /* collect stuff in the adapter, strip header from payload and push in + * the adapter */ + outbuf = + gst_rtp_buffer_get_payload_subbuffer (&rtp, payload_AU, AU_size); + gst_adapter_push (rtpmp4gdepay->adapter, outbuf); + + if (M) { + guint avail; + + /* packet is complete, flush */ + avail = gst_adapter_available (rtpmp4gdepay->adapter); + + outbuf = gst_adapter_take_buffer (rtpmp4gdepay->adapter, avail); + + /* copy some of the fields we calculated above on the buffer. We also + * copy the AU_index so that we can sort the packets in our queue. */ + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + GST_BUFFER_OFFSET (outbuf) = AU_index; + + /* make sure we don't use the timestamp again for other AUs in this + * RTP packet. */ + timestamp = -1; + + GST_DEBUG_OBJECT (depayload, + "pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + + gst_rtp_mp4g_depay_queue (rtpmp4gdepay, outbuf); + + } + payload_AU += AU_size; + payload_AU_size -= AU_size; + } + } else { + /* push complete buffer in adapter */ + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, 0, payload_len); + gst_adapter_push (rtpmp4gdepay->adapter, outbuf); + + /* if this was the last packet of the VOP, create and push a buffer */ + if (M) { + guint avail; + + avail = gst_adapter_available (rtpmp4gdepay->adapter); + + outbuf = gst_adapter_take_buffer (rtpmp4gdepay->adapter, avail); + + GST_DEBUG ("gst_rtp_mp4g_depay_chain: pushing buffer of size %" + G_GSIZE_FORMAT, gst_buffer_get_size (outbuf)); + + gst_rtp_buffer_unmap (&rtp); + return outbuf; + } + } + } + + gst_rtp_buffer_unmap (&rtp); + return NULL; + + /* ERRORS */ +short_payload: + { + GST_ELEMENT_WARNING (rtpmp4gdepay, STREAM, DECODE, + ("Packet payload was too short."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static gboolean +gst_rtp_mp4g_depay_handle_event (GstRTPBaseDepayload * filter, GstEvent * event) +{ + gboolean ret; + GstRtpMP4GDepay *rtpmp4gdepay; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (filter); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); + break; + default: + break; + } + + ret = + GST_RTP_BASE_DEPAYLOAD_CLASS (parent_class)->handle_event (filter, event); + + return ret; +} + +static GstStateChangeReturn +gst_rtp_mp4g_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpMP4GDepay *rtpmp4gdepay; + GstStateChangeReturn ret; + + rtpmp4gdepay = GST_RTP_MP4G_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mp4g_depay_reset (rtpmp4gdepay); + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_mp4g_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4gdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4G_DEPAY); +} diff --git a/gst/rtp/gstrtpmp4gdepay.h b/gst/rtp/gstrtpmp4gdepay.h new file mode 100755 index 0000000..5d5997a --- /dev/null +++ b/gst/rtp/gstrtpmp4gdepay.h @@ -0,0 +1,86 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4G_DEPAY_H__ +#define __GST_RTP_MP4G_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4G_DEPAY \ + (gst_rtp_mp4g_depay_get_type()) +#define GST_RTP_MP4G_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4G_DEPAY,GstRtpMP4GDepay)) +#define GST_RTP_MP4G_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4G_DEPAY,GstRtpMP4GDepayClass)) +#define GST_IS_RTP_MP4G_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4G_DEPAY)) +#define GST_IS_RTP_MP4G_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4G_DEPAY)) + +typedef struct _GstRtpMP4GDepay GstRtpMP4GDepay; +typedef struct _GstRtpMP4GDepayClass GstRtpMP4GDepayClass; + +struct _GstRtpMP4GDepay +{ + GstRTPBaseDepayload depayload; + + gint profile_level_id; + gint streamtype; + + gint constantSize; + gint constantDuration; + gint maxDisplacement; + + gint sizelength; + gint indexlength; + gint indexdeltalength; + gint ctsdeltalength; + gint dtsdeltalength; + gint randomaccessindication; + gint streamstateindication; + gint auxiliarydatasizelength; + + guint max_AU_index; + guint prev_AU_index; + guint last_AU_index; + guint next_AU_index; + guint32 prev_rtptime; + guint prev_AU_num; + + GQueue *packets; + + GstAdapter *adapter; +}; + +struct _GstRtpMP4GDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mp4g_depay_get_type (void); + +gboolean gst_rtp_mp4g_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4G_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmp4gpay.c b/gst/rtp/gstrtpmp4gpay.c new file mode 100755 index 0000000..e374e5c --- /dev/null +++ b/gst/rtp/gstrtpmp4gpay.c @@ -0,0 +1,641 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/base/gstbitreader.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmp4gpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4gpay_debug); +#define GST_CAT_DEFAULT (rtpmp4gpay_debug) + +static GstStaticPadTemplate gst_rtp_mp4g_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg," + "mpegversion=(int) 4," + "systemstream=(boolean)false;" + "audio/mpeg," "mpegversion=(int) 4, " "stream-format=(string) raw") + ); + +static GstStaticPadTemplate gst_rtp_mp4g_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) { \"video\", \"audio\", \"application\" }, " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " + "encoding-name = (string) \"MPEG4-GENERIC\", " + /* required string params */ + "streamtype = (string) { \"4\", \"5\" }, " /* 4 = video, 5 = audio */ + /* "profile-level-id = (string) [1,MAX], " */ + /* "config = (string) [1,MAX]" */ + "mode = (string) { \"generic\", \"CELP-cbr\", \"CELP-vbr\", \"AAC-lbr\", \"AAC-hbr\" } " + /* Optional general parameters */ + /* "objecttype = (string) [1,MAX], " */ + /* "constantsize = (string) [1,MAX], " *//* constant size of each AU */ + /* "constantduration = (string) [1,MAX], " *//* constant duration of each AU */ + /* "maxdisplacement = (string) [1,MAX], " */ + /* "de-interleavebuffersize = (string) [1,MAX], " */ + /* Optional configuration parameters */ + /* "sizelength = (string) [1, 16], " *//* max 16 bits, should be enough... */ + /* "indexlength = (string) [1, 8], " */ + /* "indexdeltalength = (string) [1, 8], " */ + /* "ctsdeltalength = (string) [1, 64], " */ + /* "dtsdeltalength = (string) [1, 64], " */ + /* "randomaccessindication = (string) {0, 1}, " */ + /* "streamstateindication = (string) [0, 64], " */ + /* "auxiliarydatasizelength = (string) [0, 64]" */ ) + ); + + +static void gst_rtp_mp4g_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_mp4g_pay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_mp4g_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_mp4g_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); +static gboolean gst_rtp_mp4g_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); + +#define gst_rtp_mp4g_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4GPay, gst_rtp_mp4g_pay, GST_TYPE_RTP_BASE_PAYLOAD) + + static void gst_rtp_mp4g_pay_class_init (GstRtpMP4GPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp4g_pay_finalize; + + gstelement_class->change_state = gst_rtp_mp4g_pay_change_state; + + gstrtpbasepayload_class->set_caps = gst_rtp_mp4g_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4g_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_mp4g_pay_sink_event; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4g_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4g_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 ES payloader", + "Codec/Payloader/Network/RTP", + "Payload MPEG4 elementary streams as RTP packets (RFC 3640)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpmp4gpay_debug, "rtpmp4gpay", 0, + "MP4-generic RTP Payloader"); +} + +static void +gst_rtp_mp4g_pay_init (GstRtpMP4GPay * rtpmp4gpay) +{ + rtpmp4gpay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mp4g_pay_reset (GstRtpMP4GPay * rtpmp4gpay) +{ + GST_DEBUG_OBJECT (rtpmp4gpay, "reset"); + + gst_adapter_clear (rtpmp4gpay->adapter); + rtpmp4gpay->offset = 0; +} + +static void +gst_rtp_mp4g_pay_cleanup (GstRtpMP4GPay * rtpmp4gpay) +{ + gst_rtp_mp4g_pay_reset (rtpmp4gpay); + + g_free (rtpmp4gpay->params); + rtpmp4gpay->params = NULL; + + if (rtpmp4gpay->config) + gst_buffer_unref (rtpmp4gpay->config); + rtpmp4gpay->config = NULL; + + g_free (rtpmp4gpay->profile); + rtpmp4gpay->profile = NULL; + + rtpmp4gpay->streamtype = NULL; + rtpmp4gpay->mode = NULL; + + rtpmp4gpay->frame_len = 0; +} + +static void +gst_rtp_mp4g_pay_finalize (GObject * object) +{ + GstRtpMP4GPay *rtpmp4gpay; + + rtpmp4gpay = GST_RTP_MP4G_PAY (object); + + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); + + g_object_unref (rtpmp4gpay->adapter); + rtpmp4gpay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static const unsigned int sampling_table[16] = { + 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, + 16000, 12000, 11025, 8000, 7350, 0, 0, 0 +}; + +static gboolean +gst_rtp_mp4g_pay_parse_audio_config (GstRtpMP4GPay * rtpmp4gpay, + GstBuffer * buffer) +{ + GstMapInfo map; + guint8 objectType = 0; + guint8 samplingIdx = 0; + guint8 channelCfg = 0; + GstBitReader br; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + gst_bit_reader_init (&br, map.data, map.size); + + /* any object type is fine, we need to copy it to the profile-level-id field. */ + if (!gst_bit_reader_get_bits_uint8 (&br, &objectType, 5)) + goto too_short; + if (objectType == 0) + goto invalid_object; + + if (!gst_bit_reader_get_bits_uint8 (&br, &samplingIdx, 4)) + goto too_short; + /* only fixed values for now */ + if (samplingIdx > 12 && samplingIdx != 15) + goto wrong_freq; + + if (!gst_bit_reader_get_bits_uint8 (&br, &channelCfg, 4)) + goto too_short; + if (channelCfg > 7) + goto wrong_channels; + + /* rtp rate depends on sampling rate of the audio */ + if (samplingIdx == 15) { + guint32 rate = 0; + + /* index of 15 means we get the rate in the next 24 bits */ + if (!gst_bit_reader_get_bits_uint32 (&br, &rate, 24)) + goto too_short; + + rtpmp4gpay->rate = rate; + } else { + /* else use the rate from the table */ + rtpmp4gpay->rate = sampling_table[samplingIdx]; + } + + rtpmp4gpay->frame_len = 1024; + + switch (objectType) { + case 1: + case 2: + case 3: + case 4: + case 6: + case 7: + { + guint8 frameLenFlag = 0; + + if (gst_bit_reader_get_bits_uint8 (&br, &frameLenFlag, 1)) + if (frameLenFlag) + rtpmp4gpay->frame_len = 960; + + break; + } + default: + break; + } + + /* extra rtp params contain the number of channels */ + g_free (rtpmp4gpay->params); + rtpmp4gpay->params = g_strdup_printf ("%d", channelCfg); + /* audio stream type */ + rtpmp4gpay->streamtype = "5"; + /* mode only high bitrate for now */ + rtpmp4gpay->mode = "AAC-hbr"; + /* profile */ + g_free (rtpmp4gpay->profile); + rtpmp4gpay->profile = g_strdup_printf ("%d", objectType); + + GST_DEBUG_OBJECT (rtpmp4gpay, + "objectType: %d, samplingIdx: %d (%d), channelCfg: %d, frame_len %d", + objectType, samplingIdx, rtpmp4gpay->rate, channelCfg, + rtpmp4gpay->frame_len); + + gst_buffer_unmap (buffer, &map); + return TRUE; + + /* ERROR */ +too_short: + { + GST_ELEMENT_ERROR (rtpmp4gpay, STREAM, FORMAT, + (NULL), ("config string too short")); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +invalid_object: + { + GST_ELEMENT_ERROR (rtpmp4gpay, STREAM, FORMAT, + (NULL), ("invalid object type")); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +wrong_freq: + { + GST_ELEMENT_ERROR (rtpmp4gpay, STREAM, NOT_IMPLEMENTED, + (NULL), ("unsupported frequency index %d", samplingIdx)); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +wrong_channels: + { + GST_ELEMENT_ERROR (rtpmp4gpay, STREAM, NOT_IMPLEMENTED, + (NULL), ("unsupported number of channels %d, must < 8", channelCfg)); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +} + +#define VOS_STARTCODE 0x000001B0 + +static gboolean +gst_rtp_mp4g_pay_parse_video_config (GstRtpMP4GPay * rtpmp4gpay, + GstBuffer * buffer) +{ + GstMapInfo map; + guint32 code; + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + if (map.size < 5) + goto too_short; + + code = GST_READ_UINT32_BE (map.data); + + g_free (rtpmp4gpay->profile); + if (code == VOS_STARTCODE) { + /* get profile */ + rtpmp4gpay->profile = g_strdup_printf ("%d", (gint) map.data[4]); + } else { + GST_ELEMENT_WARNING (rtpmp4gpay, STREAM, FORMAT, + (NULL), ("profile not found in config string, assuming \'1\'")); + rtpmp4gpay->profile = g_strdup ("1"); + } + + /* fixed rate */ + rtpmp4gpay->rate = 90000; + /* video stream type */ + rtpmp4gpay->streamtype = "4"; + /* no params for video */ + rtpmp4gpay->params = NULL; + /* mode */ + rtpmp4gpay->mode = "generic"; + + GST_LOG_OBJECT (rtpmp4gpay, "profile %s", rtpmp4gpay->profile); + + gst_buffer_unmap (buffer, &map); + + return TRUE; + + /* ERROR */ +too_short: + { + GST_ELEMENT_ERROR (rtpmp4gpay, STREAM, FORMAT, + (NULL), ("config string too short")); + gst_buffer_unmap (buffer, &map); + return FALSE; + } +} + +static gboolean +gst_rtp_mp4g_pay_new_caps (GstRtpMP4GPay * rtpmp4gpay) +{ + gchar *config; + GValue v = { 0 }; + gboolean res; + +#define MP4GCAPS \ + "streamtype", G_TYPE_STRING, rtpmp4gpay->streamtype, \ + "profile-level-id", G_TYPE_STRING, rtpmp4gpay->profile, \ + "mode", G_TYPE_STRING, rtpmp4gpay->mode, \ + "config", G_TYPE_STRING, config, \ + "sizelength", G_TYPE_STRING, "13", \ + "indexlength", G_TYPE_STRING, "3", \ + "indexdeltalength", G_TYPE_STRING, "3", \ + NULL + + g_value_init (&v, GST_TYPE_BUFFER); + gst_value_set_buffer (&v, rtpmp4gpay->config); + config = gst_value_serialize (&v); + + /* hmm, silly */ + if (rtpmp4gpay->params) { + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), + "encoding-params", G_TYPE_STRING, rtpmp4gpay->params, MP4GCAPS); + } else { + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), + MP4GCAPS); + } + + g_value_unset (&v); + g_free (config); + +#undef MP4GCAPS + return res; +} + +static gboolean +gst_rtp_mp4g_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpMP4GPay *rtpmp4gpay; + GstStructure *structure; + const GValue *codec_data; + const gchar *media_type = NULL; + gboolean res; + + rtpmp4gpay = GST_RTP_MP4G_PAY (payload); + + structure = gst_caps_get_structure (caps, 0); + + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + GST_LOG_OBJECT (rtpmp4gpay, "got codec_data"); + if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { + GstBuffer *buffer; + const gchar *name; + + buffer = gst_value_get_buffer (codec_data); + GST_LOG_OBJECT (rtpmp4gpay, "configuring codec_data"); + + name = gst_structure_get_name (structure); + + /* parse buffer */ + if (!strcmp (name, "audio/mpeg")) { + res = gst_rtp_mp4g_pay_parse_audio_config (rtpmp4gpay, buffer); + media_type = "audio"; + } else if (!strcmp (name, "video/mpeg")) { + res = gst_rtp_mp4g_pay_parse_video_config (rtpmp4gpay, buffer); + media_type = "video"; + } else { + res = FALSE; + } + if (!res) + goto config_failed; + + /* now we can configure the buffer */ + if (rtpmp4gpay->config) + gst_buffer_unref (rtpmp4gpay->config); + + rtpmp4gpay->config = gst_buffer_copy (buffer); + } + } + if (media_type == NULL) + goto config_failed; + + gst_rtp_base_payload_set_options (payload, media_type, TRUE, "MPEG4-GENERIC", + rtpmp4gpay->rate); + + res = gst_rtp_mp4g_pay_new_caps (rtpmp4gpay); + + return res; + + /* ERRORS */ +config_failed: + { + GST_DEBUG_OBJECT (rtpmp4gpay, "failed to parse config"); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_mp4g_pay_flush (GstRtpMP4GPay * rtpmp4gpay) +{ + guint avail, total; + GstBuffer *outbuf; + GstFlowReturn ret; + guint mtu; + + /* the data available in the adapter is either smaller + * than the MTU or bigger. In the case it is smaller, the complete + * adapter contents can be put in one packet. In the case the + * adapter has more than one MTU, we need to fragment the MPEG data + * over multiple packets. */ + total = avail = gst_adapter_available (rtpmp4gpay->adapter); + + ret = GST_FLOW_OK; + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4gpay); + + while (avail > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + GstRTPBuffer rtp = { NULL }; + GstBuffer *paybuf; + + /* this will be the total lenght of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0); + + /* fill one MTU or all available bytes, we need 4 spare bytes for + * the AU header. */ + towrite = MIN (packet_len, mtu - 4); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + GST_DEBUG_OBJECT (rtpmp4gpay, + "avail %d, towrite %d, packet_len %d, payload_len %d", avail, towrite, + packet_len, payload_len); + + /* create buffer to hold the payload, also make room for the 4 header bytes. */ + outbuf = gst_rtp_buffer_new_allocate (4, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + /* copy payload */ + payload = gst_rtp_buffer_get_payload (&rtp); + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ + * |AU-headers-length|AU-header|AU-header| |AU-header|padding| + * | | (1) | (2) | | (n) | bits | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+- .. -+-+-+-+-+-+-+-+-+-+ + */ + /* AU-headers-length, we only have 1 AU-header */ + payload[0] = 0x00; + payload[1] = 0x10; /* we use 16 bits for the header */ + + /* +---------------------------------------+ + * | AU-size | + * +---------------------------------------+ + * | AU-Index / AU-Index-delta | + * +---------------------------------------+ + * | CTS-flag | + * +---------------------------------------+ + * | CTS-delta | + * +---------------------------------------+ + * | DTS-flag | + * +---------------------------------------+ + * | DTS-delta | + * +---------------------------------------+ + * | RAP-flag | + * +---------------------------------------+ + * | Stream-state | + * +---------------------------------------+ + */ + /* The AU-header, no CTS, DTS, RAP, Stream-state + * + * AU-size is always the total size of the AU, not the fragmented size + */ + payload[2] = (total & 0x1fe0) >> 5; + payload[3] = (total & 0x1f) << 3; /* we use 13 bits for the size, 3 bits index */ + + /* marker only if the packet is complete */ + gst_rtp_buffer_set_marker (&rtp, avail <= payload_len); + + gst_rtp_buffer_unmap (&rtp); + + paybuf = gst_adapter_take_buffer_fast (rtpmp4gpay->adapter, payload_len); + outbuf = gst_buffer_append (outbuf, paybuf); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4gpay->first_timestamp; + GST_BUFFER_DURATION (outbuf) = rtpmp4gpay->first_duration; + + if (rtpmp4gpay->frame_len) { + GST_BUFFER_OFFSET (outbuf) = rtpmp4gpay->offset; + rtpmp4gpay->offset += rtpmp4gpay->frame_len; + } + + if (rtpmp4gpay->discont) { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); + /* Only the first outputted buffer has the DISCONT flag */ + rtpmp4gpay->discont = FALSE; + } + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmp4gpay), outbuf); + + avail -= payload_len; + } + + return ret; +} + +/* we expect buffers as exactly one complete AU + */ +static GstFlowReturn +gst_rtp_mp4g_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpMP4GPay *rtpmp4gpay; + + rtpmp4gpay = GST_RTP_MP4G_PAY (basepayload); + + rtpmp4gpay->first_timestamp = GST_BUFFER_TIMESTAMP (buffer); + rtpmp4gpay->first_duration = GST_BUFFER_DURATION (buffer); + rtpmp4gpay->discont = GST_BUFFER_IS_DISCONT (buffer); + + /* we always encode and flush a full AU */ + gst_adapter_push (rtpmp4gpay->adapter, buffer); + + return gst_rtp_mp4g_pay_flush (rtpmp4gpay); +} + +static gboolean +gst_rtp_mp4g_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + GstRtpMP4GPay *rtpmp4gpay; + + rtpmp4gpay = GST_RTP_MP4G_PAY (payload); + + GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + case GST_EVENT_EOS: + /* This flush call makes sure that the last buffer is always pushed + * to the base payloader */ + gst_rtp_mp4g_pay_flush (rtpmp4gpay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mp4g_pay_reset (rtpmp4gpay); + break; + default: + break; + } + + /* let parent handle event too */ + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); +} + +static GstStateChangeReturn +gst_rtp_mp4g_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstStateChangeReturn ret; + GstRtpMP4GPay *rtpmp4gpay; + + rtpmp4gpay = GST_RTP_MP4G_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mp4g_pay_cleanup (rtpmp4gpay); + break; + default: + break; + } + + return ret; +} + +gboolean +gst_rtp_mp4g_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4gpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4G_PAY); +} diff --git a/gst/rtp/gstrtpmp4gpay.h b/gst/rtp/gstrtpmp4gpay.h new file mode 100755 index 0000000..7506fad --- /dev/null +++ b/gst/rtp/gstrtpmp4gpay.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4G_PAY_H__ +#define __GST_RTP_MP4G_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4G_PAY \ + (gst_rtp_mp4g_pay_get_type()) +#define GST_RTP_MP4G_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4G_PAY,GstRtpMP4GPay)) +#define GST_RTP_MP4G_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4G_PAY,GstRtpMP4GPayClass)) +#define GST_IS_RTP_MP4G_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4G_PAY)) +#define GST_IS_RTP_MP4G_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4G_PAY)) + +typedef struct _GstRtpMP4GPay GstRtpMP4GPay; +typedef struct _GstRtpMP4GPayClass GstRtpMP4GPayClass; + +struct _GstRtpMP4GPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_timestamp; + GstClockTime first_duration; + gboolean discont; + GstClockTime duration; + guint64 offset; + + gint rate; + gchar *params; + gchar *profile; + const gchar *streamtype; + const gchar *mode; + GstBuffer *config; + guint frame_len; +}; + +struct _GstRtpMP4GPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mp4g_pay_get_type (void); + +gboolean gst_rtp_mp4g_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4G_PAY_H__ */ diff --git a/gst/rtp/gstrtpmp4vdepay.c b/gst/rtp/gstrtpmp4vdepay.c new file mode 100755 index 0000000..3ff58d5 --- /dev/null +++ b/gst/rtp/gstrtpmp4vdepay.c @@ -0,0 +1,226 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmp4vdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4vdepay_debug); +#define GST_CAT_DEFAULT (rtpmp4vdepay_debug) + +static GstStaticPadTemplate gst_rtp_mp4v_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg," + "mpegversion=(int) 4," "systemstream=(boolean)false") + ); + +static GstStaticPadTemplate gst_rtp_mp4v_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\"" + /* All optional parameters + * + * "profile-level-id=[1,MAX]" + * "config=" + */ + ) + ); + +#define gst_rtp_mp4v_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4VDepay, gst_rtp_mp4v_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_mp4v_depay_finalize (GObject * object); + +static gboolean gst_rtp_mp4v_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mp4v_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static GstStateChangeReturn gst_rtp_mp4v_depay_change_state (GstElement * + element, GstStateChange transition); + +static void +gst_rtp_mp4v_depay_class_init (GstRtpMP4VDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mp4v_depay_finalize; + + gstelement_class->change_state = gst_rtp_mp4v_depay_change_state; + + gstrtpbasedepayload_class->process = gst_rtp_mp4v_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_mp4v_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4v_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4v_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 video depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG4 video from RTP packets (RFC 3016)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpmp4vdepay_debug, "rtpmp4vdepay", 0, + "MPEG4 video RTP Depayloader"); +} + +static void +gst_rtp_mp4v_depay_init (GstRtpMP4VDepay * rtpmp4vdepay) +{ + rtpmp4vdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mp4v_depay_finalize (GObject * object) +{ + GstRtpMP4VDepay *rtpmp4vdepay; + + rtpmp4vdepay = GST_RTP_MP4V_DEPAY (object); + + g_object_unref (rtpmp4vdepay->adapter); + rtpmp4vdepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_mp4v_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstCaps *srccaps; + const gchar *str; + gint clock_rate; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("video/mpeg", + "mpegversion", G_TYPE_INT, 4, + "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + + if ((str = gst_structure_get_string (structure, "config"))) { + GValue v = { 0 }; + + g_value_init (&v, GST_TYPE_BUFFER); + if (gst_value_deserialize (&v, str)) { + GstBuffer *buffer; + + buffer = gst_value_get_buffer (&v); + gst_caps_set_simple (srccaps, + "codec_data", GST_TYPE_BUFFER, buffer, NULL); + /* caps takes ref */ + g_value_unset (&v); + } else { + g_warning ("cannot convert config to buffer"); + } + } + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static GstBuffer * +gst_rtp_mp4v_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMP4VDepay *rtpmp4vdepay; + GstBuffer *pbuf, *outbuf = NULL; + GstRTPBuffer rtp = { NULL }; + gboolean marker; + + rtpmp4vdepay = GST_RTP_MP4V_DEPAY (depayload); + + /* flush remaining data on discont */ + if (GST_BUFFER_IS_DISCONT (buf)) + gst_adapter_clear (rtpmp4vdepay->adapter); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + pbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + marker = gst_rtp_buffer_get_marker (&rtp); + gst_rtp_buffer_unmap (&rtp); + + gst_adapter_push (rtpmp4vdepay->adapter, pbuf); + + /* if this was the last packet of the VOP, create and push a buffer */ + if (marker) { + guint avail; + + avail = gst_adapter_available (rtpmp4vdepay->adapter); + outbuf = gst_adapter_take_buffer (rtpmp4vdepay->adapter, avail); + + GST_DEBUG ("gst_rtp_mp4v_depay_chain: pushing buffer of size %" + G_GSIZE_FORMAT, gst_buffer_get_size (outbuf)); + } + return outbuf; +} + +static GstStateChangeReturn +gst_rtp_mp4v_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpMP4VDepay *rtpmp4vdepay; + GstStateChangeReturn ret; + + rtpmp4vdepay = GST_RTP_MP4V_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtpmp4vdepay->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + default: + break; + } + return ret; +} + +gboolean +gst_rtp_mp4v_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4vdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_DEPAY); +} diff --git a/gst/rtp/gstrtpmp4vdepay.h b/gst/rtp/gstrtpmp4vdepay.h new file mode 100755 index 0000000..436e0db --- /dev/null +++ b/gst/rtp/gstrtpmp4vdepay.h @@ -0,0 +1,61 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4V_DEPAY_H__ +#define __GST_RTP_MP4V_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4V_DEPAY \ + (gst_rtp_mp4v_depay_get_type()) +#define GST_RTP_MP4V_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4V_DEPAY,GstRtpMP4VDepay)) +#define GST_RTP_MP4V_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4V_DEPAY,GstRtpMP4VDepayClass)) +#define GST_IS_RTP_MP4V_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4V_DEPAY)) +#define GST_IS_RTP_MP4V_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4V_DEPAY)) + +typedef struct _GstRtpMP4VDepay GstRtpMP4VDepay; +typedef struct _GstRtpMP4VDepayClass GstRtpMP4VDepayClass; + +struct _GstRtpMP4VDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; +}; + +struct _GstRtpMP4VDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mp4v_depay_get_type (void); + +gboolean gst_rtp_mp4v_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4V_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmp4vpay.c b/gst/rtp/gstrtpmp4vpay.c new file mode 100755 index 0000000..1ed1ac5 --- /dev/null +++ b/gst/rtp/gstrtpmp4vpay.c @@ -0,0 +1,617 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmp4vpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmp4vpay_debug); +#define GST_CAT_DEFAULT (rtpmp4vpay_debug) + +static GstStaticPadTemplate gst_rtp_mp4v_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg," + "mpegversion=(int) 4, systemstream=(boolean)false;" "video/x-divx") + ); + +static GstStaticPadTemplate gst_rtp_mp4v_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"MP4V-ES\"" + /* two string params + * + "profile-level-id = (string) [1,MAX]" + "config = (string) [1,MAX]" + */ + ) + ); + +#define DEFAULT_CONFIG_INTERVAL 0 + +enum +{ + PROP_0, + PROP_CONFIG_INTERVAL +}; + + +static void gst_rtp_mp4v_pay_finalize (GObject * object); + +static void gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gboolean gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); +static gboolean gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, + GstEvent * event); + +#define gst_rtp_mp4v_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMP4VPay, gst_rtp_mp4v_pay, GST_TYPE_RTP_BASE_PAYLOAD) + + static void gst_rtp_mp4v_pay_class_init (GstRtpMP4VPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_mp4v_pay_set_property; + gobject_class->get_property = gst_rtp_mp4v_pay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4v_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mp4v_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG4 Video payloader", "Codec/Payloader/Network/RTP", + "Payload MPEG-4 video as RTP packets (RFC 3016)", + "Wim Taymans <wim.taymans@gmail.com>"); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL, + g_param_spec_uint ("config-interval", "Config Send Interval", + "Send Config Insertion Interval in seconds (configuration headers " + "will be multiplexed in the data stream when detected.) (0 = disabled)", + 0, 3600, DEFAULT_CONFIG_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gobject_class->finalize = gst_rtp_mp4v_pay_finalize; + + gstrtpbasepayload_class->set_caps = gst_rtp_mp4v_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mp4v_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_mp4v_pay_sink_event; + + GST_DEBUG_CATEGORY_INIT (rtpmp4vpay_debug, "rtpmp4vpay", 0, + "MP4 video RTP Payloader"); +} + +static void +gst_rtp_mp4v_pay_init (GstRtpMP4VPay * rtpmp4vpay) +{ + rtpmp4vpay->adapter = gst_adapter_new (); + rtpmp4vpay->rate = 90000; + rtpmp4vpay->profile = 1; + rtpmp4vpay->need_config = TRUE; + rtpmp4vpay->config_interval = DEFAULT_CONFIG_INTERVAL; + rtpmp4vpay->last_config = -1; + + rtpmp4vpay->config = NULL; +} + +static void +gst_rtp_mp4v_pay_finalize (GObject * object) +{ + GstRtpMP4VPay *rtpmp4vpay; + + rtpmp4vpay = GST_RTP_MP4V_PAY (object); + + if (rtpmp4vpay->config) { + gst_buffer_unref (rtpmp4vpay->config); + rtpmp4vpay->config = NULL; + } + g_object_unref (rtpmp4vpay->adapter); + rtpmp4vpay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_mp4v_pay_new_caps (GstRtpMP4VPay * rtpmp4vpay) +{ + gchar *profile, *config; + GValue v = { 0 }; + gboolean res; + + profile = g_strdup_printf ("%d", rtpmp4vpay->profile); + g_value_init (&v, GST_TYPE_BUFFER); + gst_value_set_buffer (&v, rtpmp4vpay->config); + config = gst_value_serialize (&v); + + res = gst_rtp_base_payload_set_outcaps (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), + "profile-level-id", G_TYPE_STRING, profile, + "config", G_TYPE_STRING, config, NULL); + + g_value_unset (&v); + + g_free (profile); + g_free (config); + + return res; +} + +static gboolean +gst_rtp_mp4v_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpMP4VPay *rtpmp4vpay; + GstStructure *structure; + const GValue *codec_data; + gboolean res; + + rtpmp4vpay = GST_RTP_MP4V_PAY (payload); + + gst_rtp_base_payload_set_options (payload, "video", TRUE, "MP4V-ES", + rtpmp4vpay->rate); + + res = TRUE; + + structure = gst_caps_get_structure (caps, 0); + codec_data = gst_structure_get_value (structure, "codec_data"); + if (codec_data) { + GST_LOG_OBJECT (rtpmp4vpay, "got codec_data"); + if (G_VALUE_TYPE (codec_data) == GST_TYPE_BUFFER) { + GstBuffer *buffer; + + buffer = gst_value_get_buffer (codec_data); + + if (gst_buffer_get_size (buffer) < 5) + goto done; + + gst_buffer_extract (buffer, 4, &rtpmp4vpay->profile, 1); + GST_LOG_OBJECT (rtpmp4vpay, "configuring codec_data, profile %d", + rtpmp4vpay->profile); + + if (rtpmp4vpay->config) + gst_buffer_unref (rtpmp4vpay->config); + rtpmp4vpay->config = gst_buffer_copy (buffer); + res = gst_rtp_mp4v_pay_new_caps (rtpmp4vpay); + } + } + +done: + return res; +} + +static void +gst_rtp_mp4v_pay_empty (GstRtpMP4VPay * rtpmp4vpay) +{ + gst_adapter_clear (rtpmp4vpay->adapter); +} + +#define RTP_HEADER_LEN 12 + +static GstFlowReturn +gst_rtp_mp4v_pay_flush (GstRtpMP4VPay * rtpmp4vpay) +{ + guint avail, mtu; + GstBuffer *outbuf; + GstBuffer *outbuf_data = NULL; + GstFlowReturn ret; + GstBufferList *list = NULL; + + /* the data available in the adapter is either smaller + * than the MTU or bigger. In the case it is smaller, the complete + * adapter contents can be put in one packet. In the case the + * adapter has more than one MTU, we need to split the MP4V data + * over multiple packets. */ + avail = gst_adapter_available (rtpmp4vpay->adapter); + + if (rtpmp4vpay->config == NULL && rtpmp4vpay->need_config) { + /* when we don't have a config yet, flush things out */ + gst_adapter_flush (rtpmp4vpay->adapter, avail); + avail = 0; + } + + if (!avail) + return GST_FLOW_OK; + + mtu = GST_RTP_BASE_PAYLOAD_MTU (rtpmp4vpay); + + /* Use buffer lists. Each frame will be put into a list + * of buffers and the whole list will be pushed downstream + * at once */ + list = gst_buffer_list_new_sized ((avail / (mtu - RTP_HEADER_LEN)) + 1); + + while (avail > 0) { + guint towrite; + guint payload_len; + guint packet_len; + GstRTPBuffer rtp = { NULL }; + + /* this will be the total lenght of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, mtu); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer without payload. The payload will be put + * in next buffer instead. Both buffers will be merged */ + outbuf = gst_rtp_buffer_new_allocate (0, 0, 0); + + /* Take buffer with the payload from the adapter */ + outbuf_data = gst_adapter_take_buffer_fast (rtpmp4vpay->adapter, + payload_len); + + avail -= payload_len; + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + gst_rtp_buffer_set_marker (&rtp, avail == 0); + gst_rtp_buffer_unmap (&rtp); + + outbuf = gst_buffer_append (outbuf, outbuf_data); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpmp4vpay->first_timestamp; + + /* add to list */ + gst_buffer_list_insert (list, -1, outbuf); + } + + /* push the whole buffer list at once */ + ret = + gst_rtp_base_payload_push_list (GST_RTP_BASE_PAYLOAD (rtpmp4vpay), list); + + return ret; +} + +#define VOS_STARTCODE 0x000001B0 +#define VOS_ENDCODE 0x000001B1 +#define USER_DATA_STARTCODE 0x000001B2 +#define GOP_STARTCODE 0x000001B3 +#define VISUAL_OBJECT_STARTCODE 0x000001B5 +#define VOP_STARTCODE 0x000001B6 + +static gboolean +gst_rtp_mp4v_pay_depay_data (GstRtpMP4VPay * enc, guint8 * data, guint size, + gint * strip, gboolean * vopi) +{ + guint32 code; + gboolean result; + *vopi = FALSE; + + *strip = 0; + + if (size < 5) + return FALSE; + + code = GST_READ_UINT32_BE (data); + GST_DEBUG_OBJECT (enc, "start code 0x%08x", code); + + switch (code) { + case VOS_STARTCODE: + case 0x00000101: + { + gint i; + guint8 profile; + gboolean newprofile = FALSE; + gboolean equal; + + if (code == VOS_STARTCODE) { + /* profile_and_level_indication */ + profile = data[4]; + + GST_DEBUG_OBJECT (enc, "VOS profile 0x%08x", profile); + + if (profile != enc->profile) { + newprofile = TRUE; + enc->profile = profile; + } + } + + /* up to the next GOP_STARTCODE or VOP_STARTCODE is + * the config information */ + code = 0xffffffff; + for (i = 5; i < size - 4; i++) { + code = (code << 8) | data[i]; + if (code == GOP_STARTCODE || code == VOP_STARTCODE) + break; + } + i -= 3; + /* see if config changed */ + equal = FALSE; + if (enc->config) { + if (gst_buffer_get_size (enc->config) == i) { + equal = gst_buffer_memcmp (enc->config, 0, data, i) == 0; + } + } + /* if config string changed or new profile, make new caps */ + if (!equal || newprofile) { + if (enc->config) + gst_buffer_unref (enc->config); + enc->config = gst_buffer_new_and_alloc (i); + + gst_buffer_fill (enc->config, 0, data, i); + + gst_rtp_mp4v_pay_new_caps (enc); + } + *strip = i; + /* we need to flush out the current packet. */ + result = TRUE; + break; + } + case VOP_STARTCODE: + GST_DEBUG_OBJECT (enc, "VOP"); + /* VOP startcode, we don't have to flush the packet */ + result = FALSE; + /* vop-coding-type == I-frame */ + if (size > 4 && (data[4] >> 6 == 0)) { + GST_DEBUG_OBJECT (enc, "VOP-I"); + *vopi = TRUE; + } + break; + case GOP_STARTCODE: + GST_DEBUG_OBJECT (enc, "GOP"); + *vopi = TRUE; + result = TRUE; + break; + case 0x00000100: + enc->need_config = FALSE; + result = TRUE; + break; + default: + if (code >= 0x20 && code <= 0x2f) { + GST_DEBUG_OBJECT (enc, "short header"); + result = FALSE; + } else { + GST_DEBUG_OBJECT (enc, "other startcode"); + /* all other startcodes need a flush */ + result = TRUE; + } + break; + } + return result; +} + +/* we expect buffers starting on startcodes. + */ +static GstFlowReturn +gst_rtp_mp4v_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpMP4VPay *rtpmp4vpay; + GstFlowReturn ret; + guint avail; + guint packet_len; + GstMapInfo map; + gsize size; + gboolean flush; + gint strip; + GstClockTime timestamp, duration; + gboolean vopi; + gboolean send_config; + + ret = GST_FLOW_OK; + send_config = FALSE; + + rtpmp4vpay = GST_RTP_MP4V_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + size = map.size; + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + avail = gst_adapter_available (rtpmp4vpay->adapter); + + if (duration == -1) + duration = 0; + + /* empty buffer, take timestamp */ + if (avail == 0) { + rtpmp4vpay->first_timestamp = timestamp; + rtpmp4vpay->duration = 0; + } + + /* depay incomming data and see if we need to start a new RTP + * packet */ + flush = + gst_rtp_mp4v_pay_depay_data (rtpmp4vpay, map.data, size, &strip, &vopi); + gst_buffer_unmap (buffer, &map); + + if (strip) { + /* strip off config if requested */ + if (!(rtpmp4vpay->config_interval > 0)) { + GstBuffer *subbuf; + + GST_LOG_OBJECT (rtpmp4vpay, "stripping config at %d, size %d", strip, + (gint) size - strip); + + /* strip off header */ + subbuf = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_MEMORY, strip, + size - strip); + GST_BUFFER_TIMESTAMP (subbuf) = timestamp; + gst_buffer_unref (buffer); + buffer = subbuf; + + size = gst_buffer_get_size (buffer); + } else { + GST_LOG_OBJECT (rtpmp4vpay, "found config in stream"); + rtpmp4vpay->last_config = timestamp; + } + } + + /* there is a config request, see if we need to insert it */ + if (vopi && (rtpmp4vpay->config_interval > 0) && rtpmp4vpay->config) { + if (rtpmp4vpay->last_config != -1) { + guint64 diff; + + GST_LOG_OBJECT (rtpmp4vpay, + "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpmp4vpay->last_config)); + + /* calculate diff between last config in milliseconds */ + if (timestamp > rtpmp4vpay->last_config) { + diff = timestamp - rtpmp4vpay->last_config; + } else { + diff = 0; + } + + GST_DEBUG_OBJECT (rtpmp4vpay, + "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); + + /* bigger than interval, queue config */ + /* FIXME should convert timestamps to running time */ + if (GST_TIME_AS_SECONDS (diff) >= rtpmp4vpay->config_interval) { + GST_DEBUG_OBJECT (rtpmp4vpay, "time to send config"); + send_config = TRUE; + } + } else { + /* no known previous config time, send now */ + GST_DEBUG_OBJECT (rtpmp4vpay, "no previous config time, send now"); + send_config = TRUE; + } + + if (send_config) { + /* we need to send config now first */ + GST_LOG_OBJECT (rtpmp4vpay, "inserting config in stream"); + + /* insert header */ + buffer = gst_buffer_append (gst_buffer_ref (rtpmp4vpay->config), buffer); + + GST_BUFFER_TIMESTAMP (buffer) = timestamp; + size = gst_buffer_get_size (buffer); + + if (timestamp != -1) { + rtpmp4vpay->last_config = timestamp; + } + } + } + + /* if we need to flush, do so now */ + if (flush) { + ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay); + rtpmp4vpay->first_timestamp = timestamp; + rtpmp4vpay->duration = 0; + avail = 0; + } + + /* get packet length of data and see if we exceeded MTU. */ + packet_len = gst_rtp_buffer_calc_packet_len (avail + size, 0, 0); + + if (gst_rtp_base_payload_is_filled (basepayload, + packet_len, rtpmp4vpay->duration + duration)) { + ret = gst_rtp_mp4v_pay_flush (rtpmp4vpay); + rtpmp4vpay->first_timestamp = timestamp; + rtpmp4vpay->duration = 0; + } + + /* push new data */ + gst_adapter_push (rtpmp4vpay->adapter, buffer); + + rtpmp4vpay->duration += duration; + + return ret; +} + +static gboolean +gst_rtp_mp4v_pay_sink_event (GstRTPBasePayload * pay, GstEvent * event) +{ + GstRtpMP4VPay *rtpmp4vpay; + + rtpmp4vpay = GST_RTP_MP4V_PAY (pay); + + GST_DEBUG ("Got event: %s", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_SEGMENT: + case GST_EVENT_EOS: + /* This flush call makes sure that the last buffer is always pushed + * to the base payloader */ + gst_rtp_mp4v_pay_flush (rtpmp4vpay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mp4v_pay_empty (rtpmp4vpay); + break; + default: + break; + } + + /* let parent handle event too */ + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (pay, event); +} + +static void +gst_rtp_mp4v_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpMP4VPay *rtpmp4vpay; + + rtpmp4vpay = GST_RTP_MP4V_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + rtpmp4vpay->config_interval = g_value_get_uint (value); + break; + default: + break; + } +} + +static void +gst_rtp_mp4v_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpMP4VPay *rtpmp4vpay; + + rtpmp4vpay = GST_RTP_MP4V_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + g_value_set_uint (value, rtpmp4vpay->config_interval); + break; + default: + break; + } +} + +gboolean +gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmp4vpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MP4V_PAY); +} diff --git a/gst/rtp/gstrtpmp4vpay.h b/gst/rtp/gstrtpmp4vpay.h new file mode 100755 index 0000000..a974a91 --- /dev/null +++ b/gst/rtp/gstrtpmp4vpay.h @@ -0,0 +1,74 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MP4V_PAY_H__ +#define __GST_RTP_MP4V_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MP4V_PAY \ + (gst_rtp_mp4v_pay_get_type()) +#define GST_RTP_MP4V_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MP4V_PAY,GstRtpMP4VPay)) +#define GST_RTP_MP4V_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MP4V_PAY,GstRtpMP4VPayClass)) +#define GST_IS_RTP_MP4V_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MP4V_PAY)) +#define GST_IS_RTP_MP4V_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MP4V_PAY)) + +typedef struct _GstRtpMP4VPay GstRtpMP4VPay; +typedef struct _GstRtpMP4VPayClass GstRtpMP4VPayClass; + +struct _GstRtpMP4VPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_timestamp; + GstClockTime duration; + + gint rate; + gint profile; + GstBuffer *config; + gboolean send_config; + gboolean need_config; + + /* naming might be confusing with send_config; but naming matches h264 + * payloader */ + guint config_interval; + GstClockTime last_config; +}; + +struct _GstRtpMP4VPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mp4v_pay_get_type (void); + +gboolean gst_rtp_mp4v_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MP4V_PAY_H__ */ diff --git a/gst/rtp/gstrtpmpadepay.c b/gst/rtp/gstrtpmpadepay.c new file mode 100755 index 0000000..b77ac06 --- /dev/null +++ b/gst/rtp/gstrtpmpadepay.c @@ -0,0 +1,181 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmpadepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmpadepay_debug); +#define GST_CAT_DEFAULT (rtpmpadepay_debug) + +static GstStaticPadTemplate gst_rtp_mpa_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_mpa_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", " + "clock-rate = (int) 90000 ;" + "application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"MPA\", clock-rate = (int) [1, MAX]") + ); + +#define gst_rtp_mpa_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMPADepay, gst_rtp_mpa_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_mpa_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mpa_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_mpa_depay_class_init (GstRtpMPADepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpmpadepay_debug, "rtpmpadepay", 0, + "MPEG Audio RTP Depayloader"); + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG audio from RTP packets (RFC 2038)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->set_caps = gst_rtp_mpa_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_mpa_depay_process; +} + +static void +gst_rtp_mpa_depay_init (GstRtpMPADepay * rtpmpadepay) +{ +} + +static gboolean +gst_rtp_mpa_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstCaps *outcaps; + gint clock_rate; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + outcaps = + gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, NULL); + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static GstBuffer * +gst_rtp_mpa_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMPADepay *rtpmpadepay; + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + gint payload_len; +#if 0 + guint8 *payload; + guint16 frag_offset; +#endif + gboolean marker; + + rtpmpadepay = GST_RTP_MPA_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len <= 4) + goto empty_packet; + +#if 0 + payload = gst_rtp_buffer_get_payload (&rtp); + /* strip off header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + frag_offset = (payload[2] << 8) | payload[3]; +#endif + + /* subbuffer skipping the 4 header bytes */ + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, 4, -1); + marker = gst_rtp_buffer_get_marker (&rtp); + + if (marker) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + GST_DEBUG_OBJECT (rtpmpadepay, + "gst_rtp_mpa_depay_chain: pushing buffer of size %" G_GSIZE_FORMAT "", + gst_buffer_get_size (outbuf)); + + gst_rtp_buffer_unmap (&rtp); + + /* FIXME, we can push half mpeg frames when they are split over multiple + * RTP packets */ + return outbuf; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpmpadepay, STREAM, DECODE, + ("Empty Payload."), (NULL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_mpa_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmpadepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_DEPAY); +} diff --git a/gst/rtp/gstrtpmpadepay.h b/gst/rtp/gstrtpmpadepay.h new file mode 100755 index 0000000..1070d77 --- /dev/null +++ b/gst/rtp/gstrtpmpadepay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MPA_DEPAY_H__ +#define __GST_RTP_MPA_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MPA_DEPAY \ + (gst_rtp_mpa_depay_get_type()) +#define GST_RTP_MPA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPA_DEPAY,GstRtpMPADepay)) +#define GST_RTP_MPA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPA_DEPAY,GstRtpMPADepayClass)) +#define GST_IS_RTP_MPA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPA_DEPAY)) +#define GST_IS_RTP_MPA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPA_DEPAY)) + +typedef struct _GstRtpMPADepay GstRtpMPADepay; +typedef struct _GstRtpMPADepayClass GstRtpMPADepayClass; + +struct _GstRtpMPADepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpMPADepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mpa_depay_get_type (void); + +gboolean gst_rtp_mpa_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MPA_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmpapay.c b/gst/rtp/gstrtpmpapay.c new file mode 100755 index 0000000..2a7061c --- /dev/null +++ b/gst/rtp/gstrtpmpapay.c @@ -0,0 +1,329 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmpapay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmpapay_debug); +#define GST_CAT_DEFAULT (rtpmpapay_debug) + +static GstStaticPadTemplate gst_rtp_mpa_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_mpa_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_MPA_STRING ", " + "clock-rate = (int) 90000; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MPA\"") + ); + +static void gst_rtp_mpa_pay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_mpa_pay_change_state (GstElement * element, + GstStateChange transition); + +static gboolean gst_rtp_mpa_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static gboolean gst_rtp_mpa_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); +static GstFlowReturn gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay); +static GstFlowReturn gst_rtp_mpa_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); + +#define gst_rtp_mpa_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMPAPay, gst_rtp_mpa_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_mpa_pay_class_init (GstRtpMPAPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpmpapay_debug, "rtpmpapay", 0, + "MPEG Audio RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mpa_pay_finalize; + + gstelement_class->change_state = gst_rtp_mpa_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG audio payloader", "Codec/Payloader/Network/RTP", + "Payload MPEG audio as RTP packets (RFC 2038)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_mpa_pay_setcaps; + gstrtpbasepayload_class->sink_event = gst_rtp_mpa_pay_sink_event; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mpa_pay_handle_buffer; +} + +static void +gst_rtp_mpa_pay_init (GstRtpMPAPay * rtpmpapay) +{ + rtpmpapay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mpa_pay_finalize (GObject * object) +{ + GstRtpMPAPay *rtpmpapay; + + rtpmpapay = GST_RTP_MPA_PAY (object); + + g_object_unref (rtpmpapay->adapter); + rtpmpapay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_mpa_pay_reset (GstRtpMPAPay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + +static gboolean +gst_rtp_mpa_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, "MPA", 90000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +static gboolean +gst_rtp_mpa_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + gboolean ret; + GstRtpMPAPay *rtpmpapay; + + rtpmpapay = GST_RTP_MPA_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_mpa_pay_flush (rtpmpapay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } + + ret = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); + + return ret; +} + +static GstFlowReturn +gst_rtp_mpa_pay_flush (GstRtpMPAPay * rtpmpapay) +{ + guint avail; + GstBuffer *outbuf; + GstFlowReturn ret; + guint16 frag_offset; + + /* the data available in the adapter is either smaller + * than the MTU or bigger. In the case it is smaller, the complete + * adapter contents can be put in one packet. In the case the + * adapter has more than one MTU, we need to split the MPA data + * over multiple packets. The frag_offset in each packet header + * needs to be updated with the position in the MPA frame. */ + avail = gst_adapter_available (rtpmpapay->adapter); + + ret = GST_FLOW_OK; + + frag_offset = 0; + while (avail > 0) { + guint towrite; + guint8 *payload; + guint payload_len; + guint packet_len; + GstRTPBuffer rtp = { NULL }; + GstBuffer *paybuf; + + /* this will be the total length of the packet */ + packet_len = gst_rtp_buffer_calc_packet_len (4 + avail, 0, 0); + + /* fill one MTU or all available bytes */ + towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmpapay)); + + /* this is the payload length */ + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + /* create buffer to hold the payload */ + outbuf = gst_rtp_buffer_new_allocate (4, 0, 0); + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + payload_len -= 4; + + gst_rtp_buffer_set_payload_type (&rtp, GST_RTP_PAYLOAD_MPA); + + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ | Frag_offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + payload = gst_rtp_buffer_get_payload (&rtp); + payload[0] = 0; + payload[1] = 0; + payload[2] = frag_offset >> 8; + payload[3] = frag_offset & 0xff; + + avail -= payload_len; + frag_offset += payload_len; + + if (avail == 0) + gst_rtp_buffer_set_marker (&rtp, TRUE); + + gst_rtp_buffer_unmap (&rtp); + + paybuf = gst_adapter_take_buffer_fast (rtpmpapay->adapter, payload_len); + outbuf = gst_buffer_append (outbuf, paybuf); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpmpapay->first_ts; + GST_BUFFER_DURATION (outbuf) = rtpmpapay->duration; + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmpapay), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_rtp_mpa_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpMPAPay *rtpmpapay; + GstFlowReturn ret; + guint size, avail; + guint packet_len; + GstClockTime duration, timestamp; + + rtpmpapay = GST_RTP_MPA_PAY (basepayload); + + size = gst_buffer_get_size (buffer); + duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpmpapay, "DISCONT"); + gst_rtp_mpa_pay_reset (rtpmpapay); + } + + avail = gst_adapter_available (rtpmpapay->adapter); + + /* get packet length of previous data and this new data, + * payload length includes a 4 byte header */ + packet_len = gst_rtp_buffer_calc_packet_len (4 + avail + size, 0, 0); + + /* if this buffer is going to overflow the packet, flush what we + * have. */ + if (gst_rtp_base_payload_is_filled (basepayload, + packet_len, rtpmpapay->duration + duration)) { + ret = gst_rtp_mpa_pay_flush (rtpmpapay); + avail = 0; + } else { + ret = GST_FLOW_OK; + } + + if (avail == 0) { + GST_DEBUG_OBJECT (rtpmpapay, + "first packet, save timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp)); + rtpmpapay->first_ts = timestamp; + rtpmpapay->duration = 0; + } + + gst_adapter_push (rtpmpapay->adapter, buffer); + rtpmpapay->duration = duration; + + return ret; +} + +static GstStateChangeReturn +gst_rtp_mpa_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpMPAPay *rtpmpapay; + GstStateChangeReturn ret; + + rtpmpapay = GST_RTP_MPA_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mpa_pay_reset (rtpmpapay); + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_mpa_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmpapay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_PAY); +} diff --git a/gst/rtp/gstrtpmpapay.h b/gst/rtp/gstrtpmpapay.h new file mode 100755 index 0000000..db29852 --- /dev/null +++ b/gst/rtp/gstrtpmpapay.h @@ -0,0 +1,63 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MPA_PAY_H__ +#define __GST_RTP_MPA_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MPA_PAY \ + (gst_rtp_mpa_pay_get_type()) +#define GST_RTP_MPA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPA_PAY,GstRtpMPAPay)) +#define GST_RTP_MPA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPA_PAY,GstRtpMPAPayClass)) +#define GST_IS_RTP_MPA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPA_PAY)) +#define GST_IS_RTP_MPA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPA_PAY)) + +typedef struct _GstRtpMPAPay GstRtpMPAPay; +typedef struct _GstRtpMPAPayClass GstRtpMPAPayClass; + +struct _GstRtpMPAPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + GstClockTime duration; +}; + +struct _GstRtpMPAPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mpa_pay_get_type (void); + +gboolean gst_rtp_mpa_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MPA_PAY_H__ */ diff --git a/gst/rtp/gstrtpmparobustdepay.c b/gst/rtp/gstrtpmparobustdepay.c new file mode 100755 index 0000000..afdd2ba --- /dev/null +++ b/gst/rtp/gstrtpmparobustdepay.c @@ -0,0 +1,814 @@ +/* GStreamer + * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * Copyright (C) <2010> Nokia Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <stdio.h> +#include <string.h> +#include "gstrtpmparobustdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmparobustdepay_debug); +#define GST_CAT_DEFAULT (rtpmparobustdepay_debug) + +static GstStaticPadTemplate gst_rtp_mpa_robust_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_mpa_robust_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"MPA-ROBUST\" " "; " + /* draft versions appear still in use out there */ + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [1, MAX], " + "encoding-name = (string) { \"X-MP3-DRAFT-00\", \"X-MP3-DRAFT-01\", " + " \"X-MP3-DRAFT-02\", \"X-MP3-DRAFT-03\", \"X-MP3-DRAFT-04\", " + " \"X-MP3-DRAFT-05\", \"X-MP3-DRAFT-06\" }") + ); + +typedef struct _GstADUFrame +{ + guint32 header; + gint size; + gint side_info; + gint data_size; + gint layer; + gint backpointer; + + GstBuffer *buffer; +} GstADUFrame; + +#define gst_rtp_mpa_robust_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpMPARobustDepay, gst_rtp_mpa_robust_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static GstStateChangeReturn gst_rtp_mpa_robust_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_mpa_robust_depay_setcaps (GstRTPBaseDepayload * + depayload, GstCaps * caps); +static GstBuffer *gst_rtp_mpa_robust_depay_process (GstRTPBaseDepayload * + depayload, GstBuffer * buf); + +static void +gst_rtp_mpa_robust_depay_finalize (GObject * object) +{ + GstRtpMPARobustDepay *rtpmpadepay; + + rtpmpadepay = (GstRtpMPARobustDepay *) object; + + g_object_unref (rtpmpadepay->adapter); + g_queue_free (rtpmpadepay->adu_frames); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_mpa_robust_depay_class_init (GstRtpMPARobustDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + GST_DEBUG_CATEGORY_INIT (rtpmparobustdepay_debug, "rtpmparobustdepay", 0, + "Robust MPEG Audio RTP Depayloader"); + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mpa_robust_depay_finalize; + + gstelement_class->change_state = + GST_DEBUG_FUNCPTR (gst_rtp_mpa_robust_change_state); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_robust_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpa_robust_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG audio depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG audio from RTP packets (RFC 5219)", + "Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>"); + + gstrtpbasedepayload_class->set_caps = gst_rtp_mpa_robust_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_mpa_robust_depay_process; +} + +static void +gst_rtp_mpa_robust_depay_init (GstRtpMPARobustDepay * rtpmpadepay) +{ + rtpmpadepay->adapter = gst_adapter_new (); + rtpmpadepay->adu_frames = g_queue_new (); +} + +static gboolean +gst_rtp_mpa_robust_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps) +{ + GstRtpMPARobustDepay *rtpmpadepay; + GstStructure *structure; + GstCaps *outcaps; + gint clock_rate, draft; + gboolean res; + const gchar *encoding; + + rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; + depayload->clock_rate = clock_rate; + + rtpmpadepay->has_descriptor = TRUE; + if ((encoding = gst_structure_get_string (structure, "encoding-name"))) { + if (sscanf (encoding, "X-MP3-DRAFT-%d", &draft) && (draft == 0)) + rtpmpadepay->has_descriptor = FALSE; + } + + outcaps = + gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, NULL); + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +/* thanks again go to mp3parse ... */ + +static const guint mp3types_bitrates[2][3][16] = { + { + {0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,}, + {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,}, + {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,} + }, + { + {0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}, + {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,} + }, +}; + +static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000}, +{22050, 24000, 16000}, +{11025, 12000, 8000} +}; + +static inline guint +mp3_type_frame_length_from_header (GstElement * mp3parse, guint32 header, + guint * put_version, guint * put_layer, guint * put_channels, + guint * put_bitrate, guint * put_samplerate, guint * put_mode, + guint * put_crc) +{ + guint length; + gulong mode, samplerate, bitrate, layer, channels, padding, crc; + gulong version; + gint lsf, mpg25; + + if (header & (1 << 20)) { + lsf = (header & (1 << 19)) ? 0 : 1; + mpg25 = 0; + } else { + lsf = 1; + mpg25 = 1; + } + + version = 1 + lsf + mpg25; + + layer = 4 - ((header >> 17) & 0x3); + + crc = (header >> 16) & 0x1; + + bitrate = (header >> 12) & 0xF; + bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000; + /* The caller has ensured we have a valid header, so bitrate can't be + zero here. */ + if (bitrate == 0) { + GST_DEBUG_OBJECT (mp3parse, "invalid bitrate"); + return 0; + } + + samplerate = (header >> 10) & 0x3; + samplerate = mp3types_freqs[lsf + mpg25][samplerate]; + + padding = (header >> 9) & 0x1; + + mode = (header >> 6) & 0x3; + channels = (mode == 3) ? 1 : 2; + + switch (layer) { + case 1: + length = 4 * ((bitrate * 12) / samplerate + padding); + break; + case 2: + length = (bitrate * 144) / samplerate + padding; + break; + default: + case 3: + length = (bitrate * 144) / (samplerate << lsf) + padding; + break; + } + + GST_LOG_OBJECT (mp3parse, "Calculated mp3 frame length of %u bytes", length); + GST_LOG_OBJECT (mp3parse, "samplerate = %lu, bitrate = %lu, version = %lu, " + "layer = %lu, channels = %lu, mode = %lu", samplerate, bitrate, version, + layer, channels, mode); + + if (put_version) + *put_version = version; + if (put_layer) + *put_layer = layer; + if (put_channels) + *put_channels = channels; + if (put_bitrate) + *put_bitrate = bitrate; + if (put_samplerate) + *put_samplerate = samplerate; + if (put_mode) + *put_mode = mode; + if (put_crc) + *put_crc = crc; + + GST_LOG_OBJECT (mp3parse, "size = %u", length); + return length; +} + +/* generate empty/silent/dummy frame that mimics @frame, + * except for rate, where maximum possible is selected */ +static GstADUFrame * +gst_rtp_mpa_robust_depay_generate_dummy_frame (GstRtpMPARobustDepay * + rtpmpadepay, GstADUFrame * frame) +{ + GstADUFrame *dummy; + GstMapInfo map; + + dummy = g_slice_dup (GstADUFrame, frame); + + /* go for maximum bitrate */ + dummy->header = (frame->header & ~(0xf << 12)) | (0xe << 12); + dummy->size = + mp3_type_frame_length_from_header (GST_ELEMENT_CAST (rtpmpadepay), + dummy->header, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + dummy->data_size = dummy->size - 4 - dummy->side_info; + dummy->backpointer = 0; + + dummy->buffer = gst_buffer_new_and_alloc (dummy->side_info + 4); + + gst_buffer_map (dummy->buffer, &map, GST_MAP_WRITE); + memset (map.data, 0, map.size); + GST_WRITE_UINT32_BE (map.data, dummy->header); + gst_buffer_unmap (dummy->buffer, &map); + + GST_BUFFER_TIMESTAMP (dummy->buffer) = GST_BUFFER_TIMESTAMP (frame->buffer); + + return dummy; +} + +/* validates and parses @buf, and queues for further transformation if valid, + * otherwise discards @buf + * Takes ownership of @buf. */ +static gboolean +gst_rtp_mpa_robust_depay_queue_frame (GstRtpMPARobustDepay * rtpmpadepay, + GstBuffer * buf) +{ + GstADUFrame *frame = NULL; + guint version, layer, channels, size; + guint crc; + GstMapInfo map; + + g_return_val_if_fail (buf != NULL, FALSE); + + gst_buffer_map (buf, &map, GST_MAP_READ); + + if (map.size < 6) + goto corrupt_frame; + + frame = g_slice_new0 (GstADUFrame); + frame->header = GST_READ_UINT32_BE (map.data); + + size = mp3_type_frame_length_from_header (GST_ELEMENT_CAST (rtpmpadepay), + frame->header, &version, &layer, &channels, NULL, NULL, NULL, &crc); + if (!size) + goto corrupt_frame; + + frame->size = size; + frame->layer = layer; + if (version == 1 && channels == 2) + frame->side_info = 32; + else if ((version == 1 && channels == 1) || (version >= 2 && channels == 2)) + frame->side_info = 17; + else if (version >= 2 && channels == 1) + frame->side_info = 9; + else { + g_assert_not_reached (); + goto corrupt_frame; + } + + /* backpointer */ + if (layer == 3) { + frame->backpointer = GST_READ_UINT16_BE (map.data + 4); + frame->backpointer >>= 7; + GST_LOG_OBJECT (rtpmpadepay, "backpointer: %d", frame->backpointer); + } + + if (!crc) + frame->side_info += 2; + + GST_LOG_OBJECT (rtpmpadepay, "side info: %d", frame->side_info); + frame->data_size = frame->size - 4 - frame->side_info; + + /* some size validation checks */ + if (4 + frame->side_info > map.size) + goto corrupt_frame; + + /* ADU data would then extend past MP3 frame, + * even using past byte reservoir */ + if (-frame->backpointer + (gint) (map.size) > frame->size) + goto corrupt_frame; + + gst_buffer_unmap (buf, &map); + + /* ok, take buffer and queue */ + frame->buffer = buf; + g_queue_push_tail (rtpmpadepay->adu_frames, frame); + + return TRUE; + + /* ERRORS */ +corrupt_frame: + { + GST_DEBUG_OBJECT (rtpmpadepay, "frame is corrupt"); + gst_buffer_unmap (buf, &map); + gst_buffer_unref (buf); + if (frame) + g_slice_free (GstADUFrame, frame); + return FALSE; + } +} + +static inline void +gst_rtp_mpa_robust_depay_free_frame (GstADUFrame * frame) +{ + if (frame->buffer) + gst_buffer_unref (frame->buffer); + g_slice_free (GstADUFrame, frame); +} + +static inline void +gst_rtp_mpa_robust_depay_dequeue_frame (GstRtpMPARobustDepay * rtpmpadepay) +{ + GstADUFrame *head; + + GST_LOG_OBJECT (rtpmpadepay, "dequeueing ADU frame"); + + if (rtpmpadepay->adu_frames->head == rtpmpadepay->cur_adu_frame) + rtpmpadepay->cur_adu_frame = NULL; + + head = g_queue_pop_head (rtpmpadepay->adu_frames); + g_assert (head->buffer); + gst_rtp_mpa_robust_depay_free_frame (head); + + return; +} + +/* returns TRUE if at least one new ADU frame was enqueued for MP3 conversion. + * Takes ownership of @buf. */ +static gboolean +gst_rtp_mpa_robust_depay_deinterleave (GstRtpMPARobustDepay * rtpmpadepay, + GstBuffer * buf) +{ + gboolean ret = FALSE; + GstMapInfo map; + guint val, iindex, icc; + + gst_buffer_map (buf, &map, GST_MAP_READ); + val = GST_READ_UINT16_BE (map.data) >> 5; + gst_buffer_unmap (buf, &map); + + iindex = val >> 3; + icc = val & 0x7; + + GST_LOG_OBJECT (rtpmpadepay, "sync: 0x%x, index: %u, cycle count: %u", + val, iindex, icc); + + /* basic case; no interleaving ever seen */ + if (val == 0x7ff && rtpmpadepay->last_icc < 0) { + ret = gst_rtp_mpa_robust_depay_queue_frame (rtpmpadepay, buf); + } else { + if (G_UNLIKELY (rtpmpadepay->last_icc < 0)) { + rtpmpadepay->last_icc = icc; + rtpmpadepay->last_ii = iindex; + } + if (icc != rtpmpadepay->last_icc || iindex == rtpmpadepay->last_ii) { + gint i; + + for (i = 0; i < 256; ++i) { + if (rtpmpadepay->deinter[i] != NULL) { + ret |= gst_rtp_mpa_robust_depay_queue_frame (rtpmpadepay, + rtpmpadepay->deinter[i]); + rtpmpadepay->deinter[i] = NULL; + } + } + } + /* rewrite buffer sync header */ + gst_buffer_map (buf, &map, GST_MAP_READWRITE); + val = GST_READ_UINT16_BE (map.data); + val = (0x7ff << 5) | val; + GST_WRITE_UINT16_BE (map.data, val); + gst_buffer_unmap (buf, &map); + /* store and keep track of last indices */ + rtpmpadepay->last_icc = icc; + rtpmpadepay->last_ii = iindex; + rtpmpadepay->deinter[iindex] = buf; + } + + return ret; +} + +/* Head ADU frame corresponds to mp3_frame (i.e. in header in side-info) that + * is currently being written + * cur_adu_frame refers to ADU frame whose data should be bytewritten next + * (possibly starting from offset rather than start 0) (and is typicall tail + * at time of last push round). + * If at start, position where it should start writing depends on (data) sizes + * of previous mp3 frames (corresponding to foregoing ADU frames) kept in size, + * and its backpointer */ +static GstFlowReturn +gst_rtp_mpa_robust_depay_push_mp3_frames (GstRtpMPARobustDepay * rtpmpadepay) +{ + GstBuffer *buf; + GstADUFrame *frame, *head; + gint av; + GstFlowReturn ret = GST_FLOW_OK; + + while (1) { + GstMapInfo map; + + if (G_UNLIKELY (!rtpmpadepay->cur_adu_frame)) { + rtpmpadepay->cur_adu_frame = rtpmpadepay->adu_frames->head; + rtpmpadepay->offset = 0; + rtpmpadepay->size = 0; + } + + if (G_UNLIKELY (!rtpmpadepay->cur_adu_frame)) + break; + + frame = (GstADUFrame *) rtpmpadepay->cur_adu_frame->data; + head = (GstADUFrame *) rtpmpadepay->adu_frames->head->data; + + /* special case: non-layer III are sent straight through */ + if (G_UNLIKELY (frame->layer != 3)) { + GST_DEBUG_OBJECT (rtpmpadepay, "layer %d frame, sending as-is", + frame->layer); + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmpadepay), + frame->buffer); + frame->buffer = NULL; + /* and remove it from any further consideration */ + g_slice_free (GstADUFrame, frame); + g_queue_delete_link (rtpmpadepay->adu_frames, rtpmpadepay->cur_adu_frame); + rtpmpadepay->cur_adu_frame = NULL; + continue; + } + + if (rtpmpadepay->offset == gst_buffer_get_size (frame->buffer)) { + if (g_list_next (rtpmpadepay->cur_adu_frame)) { + rtpmpadepay->size += frame->data_size; + rtpmpadepay->cur_adu_frame = g_list_next (rtpmpadepay->cur_adu_frame); + frame = (GstADUFrame *) rtpmpadepay->cur_adu_frame->data; + rtpmpadepay->offset = 0; + GST_LOG_OBJECT (rtpmpadepay, + "moving to next ADU frame, size %d, side_info %d, backpointer %d", + frame->size, frame->side_info, frame->backpointer); + /* layer I and II packets have no bitreservoir and must be sent as-is; + * so flush any pending frame */ + if (G_UNLIKELY (frame->layer != 3 && rtpmpadepay->mp3_frame)) + goto flush; + } else { + break; + } + } + + if (G_UNLIKELY (!rtpmpadepay->mp3_frame)) { + GST_LOG_OBJECT (rtpmpadepay, + "setting up new MP3 frame of size %d, side_info %d", + head->size, head->side_info); + rtpmpadepay->mp3_frame = gst_byte_writer_new_with_size (head->size, TRUE); + /* 0-fill possible gaps */ + gst_byte_writer_fill_unchecked (rtpmpadepay->mp3_frame, 0, head->size); + gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, 0); + /* bytewriter corresponds to head frame, + * i.e. the header and the side info must match */ + g_assert (4 + head->side_info <= head->size); + gst_buffer_map (head->buffer, &map, GST_MAP_READ); + gst_byte_writer_put_data_unchecked (rtpmpadepay->mp3_frame, + map.data, 4 + head->side_info); + gst_buffer_unmap (head->buffer, &map); + } + + buf = frame->buffer; + av = gst_byte_writer_get_remaining (rtpmpadepay->mp3_frame); + GST_LOG_OBJECT (rtpmpadepay, "current mp3 frame remaining: %d", av); + GST_LOG_OBJECT (rtpmpadepay, "accumulated ADU frame data_size: %d", + rtpmpadepay->size); + + if (rtpmpadepay->offset) { + gst_buffer_map (buf, &map, GST_MAP_READ); + /* no need to position, simply append */ + g_assert (map.size > rtpmpadepay->offset); + av = MIN (av, map.size - rtpmpadepay->offset); + GST_LOG_OBJECT (rtpmpadepay, + "appending %d bytes from ADU frame at offset %d", av, + rtpmpadepay->offset); + gst_byte_writer_put_data_unchecked (rtpmpadepay->mp3_frame, + map.data + rtpmpadepay->offset, av); + rtpmpadepay->offset += av; + gst_buffer_unmap (buf, &map); + } else { + gint pos, tpos; + + /* position writing according to ADU frame backpointer */ + pos = gst_byte_writer_get_pos (rtpmpadepay->mp3_frame); + tpos = rtpmpadepay->size - frame->backpointer + 4 + head->side_info; + GST_LOG_OBJECT (rtpmpadepay, "current MP3 frame at position %d, " + "starting new ADU frame data at offset %d", pos, tpos); + if (tpos < pos) { + GstADUFrame *dummy; + + /* try to insert as few frames as possible, + * so go for a reasonably large dummy frame size */ + GST_LOG_OBJECT (rtpmpadepay, + "overlapping previous data; inserting dummy frame"); + dummy = + gst_rtp_mpa_robust_depay_generate_dummy_frame (rtpmpadepay, frame); + g_queue_insert_before (rtpmpadepay->adu_frames, + rtpmpadepay->cur_adu_frame, dummy); + /* offset is known to be zero, so we can shift current one */ + rtpmpadepay->cur_adu_frame = rtpmpadepay->cur_adu_frame->prev; + if (!rtpmpadepay->size) { + g_assert (rtpmpadepay->cur_adu_frame == + rtpmpadepay->adu_frames->head); + GST_LOG_OBJECT (rtpmpadepay, "... which is new head frame"); + gst_byte_writer_free (rtpmpadepay->mp3_frame); + rtpmpadepay->mp3_frame = NULL; + } + /* ... and continue adding that empty one immediately, + * and then see if that provided enough extra space */ + continue; + } else if (tpos >= pos + av) { + /* ADU frame no longer needs current MP3 frame; move to its end */ + GST_LOG_OBJECT (rtpmpadepay, "passed current MP3 frame"); + gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, pos + av); + } else { + /* position and append */ + gst_buffer_map (buf, &map, GST_MAP_READ); + GST_LOG_OBJECT (rtpmpadepay, "adding to current MP3 frame"); + gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, tpos); + av -= (tpos - pos); + g_assert (map.size >= 4 + frame->side_info); + av = MIN (av, map.size - 4 - frame->side_info); + gst_byte_writer_put_data_unchecked (rtpmpadepay->mp3_frame, + map.data + 4 + frame->side_info, av); + rtpmpadepay->offset += av + 4 + frame->side_info; + gst_buffer_unmap (buf, &map); + } + } + + /* if mp3 frame filled, send on its way */ + if (gst_byte_writer_get_remaining (rtpmpadepay->mp3_frame) == 0) { + flush: + buf = gst_byte_writer_free_and_get_buffer (rtpmpadepay->mp3_frame); + rtpmpadepay->mp3_frame = NULL; + GST_BUFFER_TIMESTAMP (buf) = GST_BUFFER_TIMESTAMP (head->buffer); + /* no longer need head ADU frame header and side info */ + /* NOTE maybe head == current, then size and offset go off a bit, + * but current gets reset to NULL, and then also offset and size */ + rtpmpadepay->size -= head->data_size; + gst_rtp_mpa_robust_depay_dequeue_frame (rtpmpadepay); + /* send */ + ret = gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpmpadepay), + buf); + } + } + + return ret; +} + +/* process ADU frame @buf through: + * - deinterleaving + * - converting to MP3 frames + * Takes ownership of @buf. + */ +static GstFlowReturn +gst_rtp_mpa_robust_depay_submit_adu (GstRtpMPARobustDepay * rtpmpadepay, + GstBuffer * buf) +{ + if (gst_rtp_mpa_robust_depay_deinterleave (rtpmpadepay, buf)) + return gst_rtp_mpa_robust_depay_push_mp3_frames (rtpmpadepay); + + return GST_FLOW_OK; +} + +static GstBuffer * +gst_rtp_mpa_robust_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf) +{ + GstRtpMPARobustDepay *rtpmpadepay; + gint payload_len, offset; + guint8 *payload; + gboolean cont, dtype; + guint av, size; + GstClockTime timestamp; + GstRTPBuffer rtp = { NULL }; + + rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (depayload); + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + if (payload_len <= 1) + goto short_read; + + payload = gst_rtp_buffer_get_payload (&rtp); + offset = 0; + GST_LOG_OBJECT (rtpmpadepay, "payload_len: %d", payload_len); + + /* strip off descriptor + * + * 0 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C|T| ADU size | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * C: if 1, data is continuation + * T: if 1, size is 14 bits, otherwise 6 bits + * ADU size: size of following packet (not including descriptor) + */ + while (payload_len) { + if (G_LIKELY (rtpmpadepay->has_descriptor)) { + cont = ! !(payload[offset] & 0x80); + dtype = ! !(payload[offset] & 0x40); + if (dtype) { + size = (payload[offset] & 0x3f) << 8 | payload[offset + 1]; + payload_len--; + offset++; + } else if (payload_len >= 2) { + size = (payload[offset] & 0x3f); + payload_len -= 2; + offset += 2; + } else { + goto short_read; + } + } else { + cont = FALSE; + dtype = -1; + size = payload_len; + } + + GST_LOG_OBJECT (rtpmpadepay, "offset %d has cont: %d, dtype: %d, size: %d", + offset, cont, dtype, size); + + buf = gst_rtp_buffer_get_payload_subbuffer (&rtp, offset, + MIN (size, payload_len)); + + if (cont) { + av = gst_adapter_available (rtpmpadepay->adapter); + if (G_UNLIKELY (!av)) { + GST_DEBUG_OBJECT (rtpmpadepay, + "discarding continuation fragment without prior fragment"); + gst_buffer_unref (buf); + } else { + av += gst_buffer_get_size (buf); + gst_adapter_push (rtpmpadepay->adapter, buf); + if (av == size) { + timestamp = gst_adapter_prev_pts (rtpmpadepay->adapter, NULL); + buf = gst_adapter_take_buffer (rtpmpadepay->adapter, size); + GST_BUFFER_TIMESTAMP (buf) = timestamp; + gst_rtp_mpa_robust_depay_submit_adu (rtpmpadepay, buf); + } else if (av > size) { + GST_DEBUG_OBJECT (rtpmpadepay, + "assembled ADU size %d larger than expected %d; discarding", + av, size); + gst_adapter_clear (rtpmpadepay->adapter); + } + } + size = payload_len; + } else { + /* not continuation, first fragment or whole ADU */ + if (payload_len == size) { + /* whole ADU */ + GST_BUFFER_TIMESTAMP (buf) = timestamp; + gst_rtp_mpa_robust_depay_submit_adu (rtpmpadepay, buf); + } else if (payload_len < size) { + /* first fragment */ + gst_adapter_push (rtpmpadepay->adapter, buf); + size = payload_len; + } + } + + offset += size; + payload_len -= size; + + /* timestamp applies to first payload, no idea for subsequent ones */ + timestamp = GST_CLOCK_TIME_NONE; + } + gst_rtp_buffer_unmap (&rtp); + + return NULL; + + /* ERRORS */ +short_read: + { + GST_ELEMENT_WARNING (rtpmpadepay, STREAM, DECODE, + (NULL), ("Packet contains invalid data")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_mpa_robust_change_state (GstElement * element, + GstStateChange transition) +{ + GstStateChangeReturn ret; + GstRtpMPARobustDepay *rtpmpadepay; + + rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + rtpmpadepay->last_ii = -1; + rtpmpadepay->last_icc = -1; + rtpmpadepay->size = 0; + rtpmpadepay->offset = 0; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + if (ret != GST_STATE_CHANGE_SUCCESS) + return ret; + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + { + gint i; + + gst_adapter_clear (rtpmpadepay->adapter); + for (i = 0; i < G_N_ELEMENTS (rtpmpadepay->deinter); i++) { + gst_buffer_replace (&rtpmpadepay->deinter[i], NULL); + } + rtpmpadepay->cur_adu_frame = NULL; + g_queue_foreach (rtpmpadepay->adu_frames, + (GFunc) gst_rtp_mpa_robust_depay_free_frame, NULL); + g_queue_clear (rtpmpadepay->adu_frames); + if (rtpmpadepay->mp3_frame) + gst_byte_writer_free (rtpmpadepay->mp3_frame); + break; + } + default: + break; + } + + return ret; +} + +gboolean +gst_rtp_mpa_robust_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmparobustdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MPA_ROBUST_DEPAY); +} diff --git a/gst/rtp/gstrtpmparobustdepay.h b/gst/rtp/gstrtpmparobustdepay.h new file mode 100755 index 0000000..fc9ec0b --- /dev/null +++ b/gst/rtp/gstrtpmparobustdepay.h @@ -0,0 +1,78 @@ +/* GStreamer + * Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk> + * Copyright (C) <2010> Nokia Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MPA_ROBUST_DEPAY_H__ +#define __GST_RTP_MPA_ROBUST_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> +#include <gst/base/gstadapter.h> +#include <gst/base/gstbytewriter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MPA_ROBUST_DEPAY \ + (gst_rtp_mpa_robust_depay_get_type()) +#define GST_RTP_MPA_ROBUST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPA_ROBUST_DEPAY,GstRtpMPARobustDepay)) +#define GST_RTP_MPA_ROBUST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPA_ROBUST_DEPAY,GstRtpMPARobustDepayClass)) +#define GST_IS_RTP_MPA_ROBUST_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPA_ROBUST_DEPAY)) +#define GST_IS_RTP_MPA_ROBUST_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPA_ROBUST_DEPAY)) + +typedef struct _GstRtpMPARobustDepay GstRtpMPARobustDepay; +typedef struct _GstRtpMPARobustDepayClass GstRtpMPARobustDepayClass; + +struct _GstRtpMPARobustDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + gboolean has_descriptor; + + /* last interleave index */ + gint last_ii; + /* last interleave cycle count */ + gint last_icc; + /* buffers pending deinterleaving */ + GstBuffer *deinter[256]; + + /* ADU buffers pending MP3 transformation */ + GQueue *adu_frames; + GList *cur_adu_frame; + gint offset; + gint size; + GstByteWriter *mp3_frame; +}; + +struct _GstRtpMPARobustDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mpa_robust_depay_get_type (void); + +gboolean gst_rtp_mpa_robust_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MPA_ROBUST_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmpvdepay.c b/gst/rtp/gstrtpmpvdepay.c new file mode 100755 index 0000000..7d8646c --- /dev/null +++ b/gst/rtp/gstrtpmpvdepay.c @@ -0,0 +1,198 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpmpvdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmpvdepay_debug); +#define GST_CAT_DEFAULT (rtpmpvdepay_debug) + +/* FIXME, we set the mpeg version to 2, we should ideally be looking at contents + * of the stream to figure out the version */ +static GstStaticPadTemplate gst_rtp_mpv_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS + ("video/mpeg, mpegversion = (int) 2, systemstream = (boolean) FALSE") + ); + +static GstStaticPadTemplate gst_rtp_mpv_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MPV\";" + "application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_MPV_STRING ", " + "clock-rate = (int) 90000") + ); + +G_DEFINE_TYPE (GstRtpMPVDepay, gst_rtp_mpv_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_mpv_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_mpv_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void +gst_rtp_mpv_depay_class_init (GstRtpMPVDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpv_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpv_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG video depayloader", "Codec/Depayloader/Network/RTP", + "Extracts MPEG video from RTP packets (RFC 2250)", + "Wim Taymans <wim.taymans@gmail.com>"); + + gstrtpbasedepayload_class->set_caps = gst_rtp_mpv_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_mpv_depay_process; + + GST_DEBUG_CATEGORY_INIT (rtpmpvdepay_debug, "rtpmpvdepay", 0, + "MPEG Video RTP Depayloader"); +} + +static void +gst_rtp_mpv_depay_init (GstRtpMPVDepay * rtpmpvdepay) +{ +} + +static gboolean +gst_rtp_mpv_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + gint clock_rate; + GstCaps *outcaps; + gboolean res; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + outcaps = gst_caps_new_simple ("video/mpeg", + "mpegversion", G_TYPE_INT, 2, + "systemstream", G_TYPE_BOOLEAN, FALSE, NULL); + res = gst_pad_set_caps (depayload->srcpad, outcaps); + gst_caps_unref (outcaps); + + return res; +} + +static GstBuffer * +gst_rtp_mpv_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpMPVDepay *rtpmpvdepay; + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + + rtpmpvdepay = GST_RTP_MPV_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + { + gint payload_len, payload_header; + guint8 *payload; + guint8 T; + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + payload_header = 0; + + if (payload_len <= 4) + goto empty_packet; + + /* 3.4 MPEG Video-specific header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ |T| TR | |N|S|B|E| P | | BFC | | FFC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * AN FBV FFV + */ + T = (payload[0] & 0x04); + + payload_len -= 4; + payload_header += 4; + payload += 4; + + if (T) { + /* + * 3.4.1 MPEG-2 Video-specific header extension + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |X|E|f_[0,0]|f_[0,1]|f_[1,0]|f_[1,1]| DC| PS|T|P|C|Q|V|A|R|H|G|D| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (payload_len <= 4) + goto empty_packet; + + payload_len -= 4; + payload_header += 4; + payload += 4; + } + + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, payload_header, -1); + + if (outbuf) { + GST_DEBUG_OBJECT (rtpmpvdepay, + "gst_rtp_mpv_depay_chain: pushing buffer of size %" G_GSIZE_FORMAT, + gst_buffer_get_size (outbuf)); + } + return outbuf; + } + + return NULL; + + /* ERRORS */ +empty_packet: + { + GST_ELEMENT_WARNING (rtpmpvdepay, STREAM, DECODE, + (NULL), ("Empty payload.")); + return NULL; + } +} + +gboolean +gst_rtp_mpv_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmpvdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MPV_DEPAY); +} diff --git a/gst/rtp/gstrtpmpvdepay.h b/gst/rtp/gstrtpmpvdepay.h new file mode 100755 index 0000000..80f6c43 --- /dev/null +++ b/gst/rtp/gstrtpmpvdepay.h @@ -0,0 +1,58 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_MPV_DEPAY_H__ +#define __GST_RTP_MPV_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_MPV_DEPAY \ + (gst_rtp_mpv_depay_get_type()) +#define GST_RTP_MPV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPV_DEPAY,GstRtpMPVDepay)) +#define GST_RTP_MPV_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPV_DEPAY,GstRtpMPVDepayClass)) +#define GST_IS_RTP_MPV_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPV_DEPAY)) +#define GST_IS_RTP_MPV_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPV_DEPAY)) + +typedef struct _GstRtpMPVDepay GstRtpMPVDepay; +typedef struct _GstRtpMPVDepayClass GstRtpMPVDepayClass; + +struct _GstRtpMPVDepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpMPVDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_mpv_depay_get_type (void); + +gboolean gst_rtp_mpv_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MPV_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpmpvpay.c b/gst/rtp/gstrtpmpvpay.c new file mode 100755 index 0000000..0751bcd --- /dev/null +++ b/gst/rtp/gstrtpmpvpay.c @@ -0,0 +1,313 @@ +/* GStreamer + * Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpmpvpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpmpvpay_debug); +#define GST_CAT_DEFAULT (rtpmpvpay_debug) + +static GstStaticPadTemplate gst_rtp_mpv_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg, " + "mpegversion = (int) 2, systemstream = (boolean) FALSE") + ); + +static GstStaticPadTemplate gst_rtp_mpv_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_MPV_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"MPV\"") + ); + +static GstStateChangeReturn gst_rtp_mpv_pay_change_state (GstElement * element, + GstStateChange transition); + +static void gst_rtp_mpv_pay_finalize (GObject * object); + +static GstFlowReturn gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay); +static gboolean gst_rtp_mpv_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_mpv_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); +static gboolean gst_rtp_mpv_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); + +#define gst_rtp_mpv_pay_parent_class parent_class +G_DEFINE_TYPE (GstRTPMPVPay, gst_rtp_mpv_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_mpv_pay_class_init (GstRTPMPVPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->finalize = gst_rtp_mpv_pay_finalize; + + gstelement_class->change_state = gst_rtp_mpv_pay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpv_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_mpv_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP MPEG2 ES video payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes MPEG2 ES into RTP packets (RFC 2250)", + "Thijs Vermeir <thijsvermeir@gmail.com>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_mpv_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_mpv_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_mpv_pay_sink_event; + + GST_DEBUG_CATEGORY_INIT (rtpmpvpay_debug, "rtpmpvpay", 0, + "MPEG2 ES Video RTP Payloader"); +} + +static void +gst_rtp_mpv_pay_init (GstRTPMPVPay * rtpmpvpay) +{ + GST_RTP_BASE_PAYLOAD (rtpmpvpay)->clock_rate = 90000; + GST_RTP_BASE_PAYLOAD_PT (rtpmpvpay) = GST_RTP_PAYLOAD_MPV; + + rtpmpvpay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_mpv_pay_finalize (GObject * object) +{ + GstRTPMPVPay *rtpmpvpay; + + rtpmpvpay = GST_RTP_MPV_PAY (object); + + g_object_unref (rtpmpvpay->adapter); + rtpmpvpay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static void +gst_rtp_mpv_pay_reset (GstRTPMPVPay * pay) +{ + pay->first_ts = -1; + pay->duration = 0; + gst_adapter_clear (pay->adapter); + GST_DEBUG_OBJECT (pay, "reset depayloader"); +} + +static gboolean +gst_rtp_mpv_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gst_rtp_base_payload_set_options (payload, "video", FALSE, "MPV", 90000); + return gst_rtp_base_payload_set_outcaps (payload, NULL); +} + +static gboolean +gst_rtp_mpv_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + gboolean ret; + GstRTPMPVPay *rtpmpvpay; + + rtpmpvpay = GST_RTP_MPV_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + /* make sure we push the last packets in the adapter on EOS */ + gst_rtp_mpv_pay_flush (rtpmpvpay); + break; + case GST_EVENT_FLUSH_STOP: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + + ret = GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); + + return ret; +} + +static GstFlowReturn +gst_rtp_mpv_pay_flush (GstRTPMPVPay * rtpmpvpay) +{ + GstBuffer *outbuf; + GstFlowReturn ret; + guint avail; + + guint8 *payload; + + avail = gst_adapter_available (rtpmpvpay->adapter); + + ret = GST_FLOW_OK; + + while (avail > 0) { + guint towrite; + guint packet_len; + guint payload_len; + GstRTPBuffer rtp = { NULL }; + GstBuffer *paybuf; + + packet_len = gst_rtp_buffer_calc_packet_len (avail + 4, 0, 0); + + towrite = MIN (packet_len, GST_RTP_BASE_PAYLOAD_MTU (rtpmpvpay)); + + payload_len = gst_rtp_buffer_calc_payload_len (towrite, 0, 0); + + outbuf = gst_rtp_buffer_new_allocate (4, 0, 0); + + payload_len -= 4; + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + payload = gst_rtp_buffer_get_payload (&rtp); + /* enable MPEG Video-specific header + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | MBZ |T| TR | |N|S|B|E| P | | BFC | | FFC | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * AN FBV FFV + */ + + /* fill in the MPEG Video-specific header + * data is set to 0x0 here + */ + memset (payload, 0x0, 4); + + avail -= payload_len; + + gst_rtp_buffer_set_marker (&rtp, avail == 0); + gst_rtp_buffer_unmap (&rtp); + + paybuf = gst_adapter_take_buffer_fast (rtpmpvpay->adapter, payload_len); + outbuf = gst_buffer_append (outbuf, paybuf); + + GST_BUFFER_TIMESTAMP (outbuf) = rtpmpvpay->first_ts; + + ret = gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpmpvpay), outbuf); + } + + return ret; +} + +static GstFlowReturn +gst_rtp_mpv_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRTPMPVPay *rtpmpvpay; + guint avail, packet_len; + GstClockTime timestamp, duration; + GstFlowReturn ret = GST_FLOW_OK; + + rtpmpvpay = GST_RTP_MPV_PAY (basepayload); + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + + if (GST_BUFFER_IS_DISCONT (buffer)) { + GST_DEBUG_OBJECT (rtpmpvpay, "DISCONT"); + gst_rtp_mpv_pay_reset (rtpmpvpay); + } + + avail = gst_adapter_available (rtpmpvpay->adapter); + + if (duration == -1) + duration = 0; + + if (rtpmpvpay->first_ts == GST_CLOCK_TIME_NONE || avail == 0) + rtpmpvpay->first_ts = timestamp; + + if (avail == 0) { + rtpmpvpay->duration = duration; + } else { + rtpmpvpay->duration += duration; + } + + gst_adapter_push (rtpmpvpay->adapter, buffer); + avail = gst_adapter_available (rtpmpvpay->adapter); + + /* get packet length of previous data and this new data, + * payload length includes a 4 byte MPEG video-specific header */ + packet_len = gst_rtp_buffer_calc_packet_len (avail, 4, 0); + GST_LOG_OBJECT (rtpmpvpay, "available %d, rtp packet length %d", avail, + packet_len); + + if (gst_rtp_base_payload_is_filled (basepayload, + packet_len, rtpmpvpay->duration)) { + ret = gst_rtp_mpv_pay_flush (rtpmpvpay); + } else { + rtpmpvpay->first_ts = timestamp; + } + + return ret; +} + +static GstStateChangeReturn +gst_rtp_mpv_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRTPMPVPay *rtpmpvpay; + GstStateChangeReturn ret; + + rtpmpvpay = GST_RTP_MPV_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_mpv_pay_reset (rtpmpvpay); + break; + default: + break; + } + return ret; +} + + +gboolean +gst_rtp_mpv_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpmpvpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_MPV_PAY); +} diff --git a/gst/rtp/gstrtpmpvpay.h b/gst/rtp/gstrtpmpvpay.h new file mode 100755 index 0000000..bcebad9 --- /dev/null +++ b/gst/rtp/gstrtpmpvpay.h @@ -0,0 +1,64 @@ +/* GStreamer + * Copyright (C) <2007> Thijs Vermeir <thijsvermeir@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_RTP_MPV_PAY_H__ +#define __GST_RTP_MPV_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +typedef struct _GstRTPMPVPay GstRTPMPVPay; +typedef struct _GstRTPMPVPayClass GstRTPMPVPayClass; + +#define GST_TYPE_RTP_MPV_PAY \ + (gst_rtp_mpv_pay_get_type()) +#define GST_RTP_MPV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPV_PAY,GstRTPMPVPay)) +#define GST_RTP_MPV_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPV_PAY,GstRTPMPVPayClass)) +#define GST_IS_RTP_MPV_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPV_PAY)) +#define GST_IS_RTP_MPV_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPV_PAY)) + +struct _GstRTPMPVPay +{ + GstRTPBasePayload payload; + + GstAdapter *adapter; + GstClockTime first_ts; + GstClockTime duration; +}; + +struct _GstRTPMPVPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_mpv_pay_get_type (void); + +gboolean gst_rtp_mpv_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_MPV_PAY_H__ */ diff --git a/gst/rtp/gstrtppcmadepay.c b/gst/rtp/gstrtppcmadepay.c new file mode 100755 index 0000000..8f96250 --- /dev/null +++ b/gst/rtp/gstrtppcmadepay.c @@ -0,0 +1,165 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) <2005> Zeeshan Ali <zeenix@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtppcmadepay.h" + +/* RtpPcmaDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstStaticPadTemplate gst_rtp_pcma_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_PCMA_STRING ", " + "clock-rate = (int) 8000;" + "application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [1, MAX ], encoding-name = (string) \"PCMA\"") + ); + +static GstStaticPadTemplate gst_rtp_pcma_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-alaw, channels = (int) 1, rate = (int) [1, MAX ]") + ); + +static GstBuffer *gst_rtp_pcma_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_pcma_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_pcma_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpPcmaDepay, gst_rtp_pcma_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_pcma_depay_class_init (GstRtpPcmaDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcma_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcma_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP PCMA depayloader", "Codec/Depayloader/Network/RTP", + "Extracts PCMA audio from RTP packets", + "Edgard Lima <edgard.lima@indt.org.br>, Zeeshan Ali <zeenix@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_pcma_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_pcma_depay_setcaps; +} + +static void +gst_rtp_pcma_depay_init (GstRtpPcmaDepay * rtppcmadepay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtppcmadepay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); +} + +static gboolean +gst_rtp_pcma_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure; + gboolean ret; + gint clock_rate; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("audio/x-alaw", + "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, clock_rate, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; +} + +static GstBuffer * +gst_rtp_pcma_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf = NULL; + gboolean marker; + guint len; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + len = gst_rtp_buffer_get_payload_len (&rtp); + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (outbuf) { + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate); + + if (marker) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + } + + + return outbuf; +} + +gboolean +gst_rtp_pcma_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtppcmadepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMA_DEPAY); +} diff --git a/gst/rtp/gstrtppcmadepay.h b/gst/rtp/gstrtppcmadepay.h new file mode 100755 index 0000000..7ea0f45 --- /dev/null +++ b/gst/rtp/gstrtppcmadepay.h @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_PCMA_DEPAY_H__ +#define __GST_RTP_PCMA_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpPcmaDepay GstRtpPcmaDepay; +typedef struct _GstRtpPcmaDepayClass GstRtpPcmaDepayClass; + +#define GST_TYPE_RTP_PCMA_DEPAY \ + (gst_rtp_pcma_depay_get_type()) +#define GST_RTP_PCMA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMA_DEPAY,GstRtpPcmaDepay)) +#define GST_RTP_PCMA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMA_DEPAY,GstRtpPcmaDepayClass)) +#define GST_IS_RTP_PCMA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PCMA_DEPAY)) +#define GST_IS_RTP_PCMA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PCMA_DEPAY)) + +struct _GstRtpPcmaDepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpPcmaDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_pcma_depay_get_type (void); + +gboolean gst_rtp_pcma_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_PCMA_DEPAY_H__ */ diff --git a/gst/rtp/gstrtppcmapay.c b/gst/rtp/gstrtppcmapay.c new file mode 100755 index 0000000..7ec7e59 --- /dev/null +++ b/gst/rtp/gstrtppcmapay.c @@ -0,0 +1,116 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtppcmapay.h" + +static GstStaticPadTemplate gst_rtp_pcma_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-alaw, channels=(int)1, rate=(int)8000") + ); + +static GstStaticPadTemplate gst_rtp_pcma_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_PCMA_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"PCMA\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"PCMA\"") + ); + +static gboolean gst_rtp_pcma_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); + +#define gst_rtp_pcma_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpPcmaPay, gst_rtp_pcma_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_pcma_pay_class_init (GstRtpPcmaPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcma_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcma_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP PCMA payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes PCMA audio into a RTP packet", + "Edgard Lima <edgard.lima@indt.org.br>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_pcma_pay_setcaps; +} + +static void +gst_rtp_pcma_pay_init (GstRtpPcmaPay * rtppcmapay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtppcmapay); + + GST_RTP_BASE_PAYLOAD (rtppcmapay)->clock_rate = 8000; + + /* tell rtpbaseaudiopayload that this is a sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); + + /* octet-per-sample is 1 for PCM */ + gst_rtp_base_audio_payload_set_sample_options (rtpbaseaudiopayload, 1); +} + +static gboolean +gst_rtp_pcma_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + + payload->pt = GST_RTP_PAYLOAD_PCMA; + + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "PCMA", 8000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +gboolean +gst_rtp_pcma_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtppcmapay", + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMA_PAY); +} diff --git a/gst/rtp/gstrtppcmapay.h b/gst/rtp/gstrtppcmapay.h new file mode 100755 index 0000000..5fde1bf --- /dev/null +++ b/gst/rtp/gstrtppcmapay.h @@ -0,0 +1,54 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + + +#ifndef __GST_RTP_PCMA_PAY_H__ +#define __GST_RTP_PCMA_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpPcmaPay GstRtpPcmaPay; +typedef struct _GstRtpPcmaPayClass GstRtpPcmaPayClass; + +#define GST_TYPE_RTP_PCMA_PAY \ + (gst_rtp_pcma_pay_get_type()) +#define GST_RTP_PCMA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMA_PAY,GstRtpPcmaPay)) +#define GST_RTP_PCMA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMA_PAY,GstRtpPcmaPayClass)) +#define GST_IS_RTP_PCMA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PCMA_PAY)) +#define GST_IS_RTP_PCMA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PCMA_PAY)) + +struct _GstRtpPcmaPay +{ + GstRTPBaseAudioPayload audiopayload; +}; + +struct _GstRtpPcmaPayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_pcma_pay_get_type (void); + +gboolean gst_rtp_pcma_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_PCMA_PAY_H__ */ diff --git a/gst/rtp/gstrtppcmudepay.c b/gst/rtp/gstrtppcmudepay.c new file mode 100755 index 0000000..e75c282 --- /dev/null +++ b/gst/rtp/gstrtppcmudepay.c @@ -0,0 +1,165 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) <2005> Zeeshan Ali <zeenix@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtppcmudepay.h" + +/* RtpPcmuDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstStaticPadTemplate gst_rtp_pcmu_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_PCMU_STRING ", " + "clock-rate = (int) 8000; " + "application/x-rtp, " + "media = (string) \"audio\", " + "encoding-name = (string) \"PCMU\", clock-rate = (int) [1, MAX ]") + ); + +static GstStaticPadTemplate gst_rtp_pcmu_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-mulaw, " + "channels = (int) 1, rate = (int) [1, MAX ]") + ); + +static GstBuffer *gst_rtp_pcmu_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_pcmu_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +#define gst_rtp_pcmu_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpPcmuDepay, gst_rtp_pcmu_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_pcmu_depay_class_init (GstRtpPcmuDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcmu_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcmu_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP PCMU depayloader", "Codec/Depayloader/Network/RTP", + "Extracts PCMU audio from RTP packets", + "Edgard Lima <edgard.lima@indt.org.br>, Zeeshan Ali <zeenix@gmail.com>"); + + gstrtpbasedepayload_class->process = gst_rtp_pcmu_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_pcmu_depay_setcaps; +} + +static void +gst_rtp_pcmu_depay_init (GstRtpPcmuDepay * rtppcmudepay) +{ + GstRTPBaseDepayload *depayload; + + depayload = GST_RTP_BASE_DEPAYLOAD (rtppcmudepay); + + gst_pad_use_fixed_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); +} + +static gboolean +gst_rtp_pcmu_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + GstStructure *structure; + gboolean ret; + gint clock_rate; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 8000; /* default */ + depayload->clock_rate = clock_rate; + + srccaps = gst_caps_new_simple ("audio/x-mulaw", + "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, clock_rate, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return ret; +} + +static GstBuffer * +gst_rtp_pcmu_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf = NULL; + guint len; + gboolean marker; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + marker = gst_rtp_buffer_get_marker (&rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), marker, + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + len = gst_rtp_buffer_get_payload_len (&rtp); + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (outbuf) { + GST_BUFFER_DURATION (outbuf) = + gst_util_uint64_scale_int (len, GST_SECOND, depayload->clock_rate); + + if (marker) { + /* mark start of talkspurt with RESYNC */ + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_RESYNC); + } + } + + return outbuf; +} + +gboolean +gst_rtp_pcmu_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtppcmudepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMU_DEPAY); +} diff --git a/gst/rtp/gstrtppcmudepay.h b/gst/rtp/gstrtppcmudepay.h new file mode 100755 index 0000000..922ceb9 --- /dev/null +++ b/gst/rtp/gstrtppcmudepay.h @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_PCMU_DEPAY_H__ +#define __GST_RTP_PCMU_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpPcmuDepay GstRtpPcmuDepay; +typedef struct _GstRtpPcmuDepayClass GstRtpPcmuDepayClass; + +#define GST_TYPE_RTP_PCMU_DEPAY \ + (gst_rtp_pcmu_depay_get_type()) +#define GST_RTP_PCMU_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMU_DEPAY,GstRtpPcmuDepay)) +#define GST_RTP_PCMU_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMU_DEPAY,GstRtpPcmuDepayClass)) +#define GST_IS_RTP_PCMU_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PCMU_DEPAY)) +#define GST_IS_RTP_PCMU_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PCMU_DEPAY)) + +struct _GstRtpPcmuDepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpPcmuDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_pcmu_depay_get_type (void); + +gboolean gst_rtp_pcmu_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_PCMU_DEPAY_H__ */ diff --git a/gst/rtp/gstrtppcmupay.c b/gst/rtp/gstrtppcmupay.c new file mode 100755 index 0000000..3d69cb3 --- /dev/null +++ b/gst/rtp/gstrtppcmupay.c @@ -0,0 +1,116 @@ +/* GStreamer + * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * Copyright (C) <2005> Nokia Corporation <kai.vehmanen@nokia.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtppcmupay.h" + +static GstStaticPadTemplate gst_rtp_pcmu_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-mulaw, channels=(int)1, rate=(int)8000") + ); + +static GstStaticPadTemplate gst_rtp_pcmu_pay_src_template = + GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_PCMU_STRING ", " + "clock-rate = (int) 8000, " "encoding-name = (string) \"PCMU\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"PCMU\"") + ); + +static gboolean gst_rtp_pcmu_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); + +#define gst_rtp_pcmu_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpPcmuPay, gst_rtp_pcmu_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_pcmu_pay_class_init (GstRtpPcmuPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcmu_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_pcmu_pay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, "RTP PCMU payloader", + "Codec/Payloader/Network/RTP", + "Payload-encodes PCMU audio into a RTP packet", + "Edgard Lima <edgard.lima@indt.org.br>"); + + gstrtpbasepayload_class->set_caps = gst_rtp_pcmu_pay_setcaps; +} + +static void +gst_rtp_pcmu_pay_init (GstRtpPcmuPay * rtppcmupay) +{ + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtppcmupay); + + GST_RTP_BASE_PAYLOAD (rtppcmupay)->clock_rate = 8000; + + /* tell rtpbaseaudiopayload that this is a sample based codec */ + gst_rtp_base_audio_payload_set_sample_based (rtpbaseaudiopayload); + + /* octet-per-sample is 1 for PCM */ + gst_rtp_base_audio_payload_set_sample_options (rtpbaseaudiopayload, 1); +} + +static gboolean +gst_rtp_pcmu_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + gboolean res; + + payload->pt = GST_RTP_PAYLOAD_PCMU; + + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "PCMU", 8000); + res = gst_rtp_base_payload_set_outcaps (payload, NULL); + + return res; +} + +gboolean +gst_rtp_pcmu_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtppcmupay", + GST_RANK_SECONDARY, GST_TYPE_RTP_PCMU_PAY); +} diff --git a/gst/rtp/gstrtppcmupay.h b/gst/rtp/gstrtppcmupay.h new file mode 100755 index 0000000..58da803 --- /dev/null +++ b/gst/rtp/gstrtppcmupay.h @@ -0,0 +1,54 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + + +#ifndef __GST_RTP_PCMU_PAY_H__ +#define __GST_RTP_PCMU_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpPcmuPay GstRtpPcmuPay; +typedef struct _GstRtpPcmuPayClass GstRtpPcmuPayClass; + +#define GST_TYPE_RTP_PCMU_PAY \ + (gst_rtp_pcmu_pay_get_type()) +#define GST_RTP_PCMU_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_PCMU_PAY,GstRtpPcmuPay)) +#define GST_RTP_PCMU_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_PCMU_PAY,GstRtpPcmuPayClass)) +#define GST_IS_RTP_PCMU_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_PCMU_PAY)) +#define GST_IS_RTP_PCMU_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_PCMU_PAY)) + +struct _GstRtpPcmuPay +{ + GstRTPBaseAudioPayload audiopayload; +}; + +struct _GstRtpPcmuPayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_pcmu_pay_get_type (void); + +gboolean gst_rtp_pcmu_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_PCMU_PAY_H__ */ diff --git a/gst/rtp/gstrtpqcelpdepay.c b/gst/rtp/gstrtpqcelpdepay.c new file mode 100755 index 0000000..fc88f4a --- /dev/null +++ b/gst/rtp/gstrtpqcelpdepay.c @@ -0,0 +1,435 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <stdlib.h> +#include <string.h> +#include "gstrtpqcelpdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpqcelpdepay_debug); +#define GST_CAT_DEFAULT (rtpqcelpdepay_debug) + +/* references: + * + * RFC 2658 - RTP Payload Format for PureVoice(tm) Audio + */ +#define FRAME_DURATION (20 * GST_MSECOND) + +/* RtpQCELPDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstStaticPadTemplate gst_rtp_qcelp_depay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 8000, " + "encoding-name = (string) \"QCELP\"; " + "application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_QCELP_STRING ", " + "clock-rate = (int) 8000") + ); + +static GstStaticPadTemplate gst_rtp_qcelp_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/qcelp, " "channels = (int) 1," "rate = (int) 8000") + ); + +static void gst_rtp_qcelp_depay_finalize (GObject * object); + +static gboolean gst_rtp_qcelp_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_qcelp_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +#define gst_rtp_qcelp_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpQCELPDepay, gst_rtp_qcelp_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_qcelp_depay_class_init (GstRtpQCELPDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_qcelp_depay_finalize; + + gstrtpbasedepayload_class->process = gst_rtp_qcelp_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_qcelp_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_qcelp_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_qcelp_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP QCELP depayloader", "Codec/Depayloader/Network/RTP", + "Extracts QCELP (PureVoice) audio from RTP packets (RFC 2658)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpqcelpdepay_debug, "rtpqcelpdepay", 0, + "QCELP RTP Depayloader"); +} + +static void +gst_rtp_qcelp_depay_init (GstRtpQCELPDepay * rtpqcelpdepay) +{ +} + +static void +gst_rtp_qcelp_depay_finalize (GObject * object) +{ + GstRtpQCELPDepay *depay; + + depay = GST_RTP_QCELP_DEPAY (object); + + if (depay->packets != NULL) { + g_ptr_array_foreach (depay->packets, (GFunc) gst_buffer_unref, NULL); + g_ptr_array_free (depay->packets, TRUE); + depay->packets = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + + +static gboolean +gst_rtp_qcelp_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + gboolean res; + + srccaps = gst_caps_new_simple ("audio/qcelp", + "channels", G_TYPE_INT, 1, "rate", G_TYPE_INT, 8000, NULL); + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + return res; +} + +static const gint frame_size[16] = { + 1, 4, 8, 17, 35, -8, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0 +}; + +/* get the frame length, 0 is invalid, negative values are invalid but can be + * recovered from. */ +static gint +get_frame_len (GstRtpQCELPDepay * depay, guint8 frame_type) +{ + if (frame_type >= G_N_ELEMENTS (frame_size)) + return 0; + + return frame_size[frame_type]; +} + +static guint +count_packets (GstRtpQCELPDepay * depay, guint8 * data, guint size) +{ + guint count = 0; + + while (size > 0) { + gint frame_len; + + frame_len = get_frame_len (depay, data[0]); + + /* 0 is invalid and we throw away the remainder of the frames */ + if (frame_len == 0) + break; + + if (frame_len < 0) + frame_len = -frame_len; + + if (frame_len > size) + break; + + size -= frame_len; + data += frame_len; + count++; + } + return count; +} + +static void +flush_packets (GstRtpQCELPDepay * depay) +{ + guint i, size; + + GST_DEBUG_OBJECT (depay, "flushing packets"); + + size = depay->packets->len; + + for (i = 0; i < size; i++) { + GstBuffer *outbuf; + + outbuf = g_ptr_array_index (depay->packets, i); + g_ptr_array_index (depay->packets, i) = NULL; + + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (depay), outbuf); + } + + /* and reset interleaving state */ + depay->interleaved = FALSE; + depay->bundling = 0; +} + +static void +add_packet (GstRtpQCELPDepay * depay, guint LLL, guint NNN, guint index, + GstBuffer * outbuf) +{ + guint idx; + GstBuffer *old; + + /* figure out the position in the array, note that index is never 0 because we + * push those packets immediately. */ + idx = NNN + ((LLL + 1) * (index - 1)); + + GST_DEBUG_OBJECT (depay, "adding packet at index %u", idx); + /* free old buffer (should not happen) */ + old = g_ptr_array_index (depay->packets, idx); + if (old) + gst_buffer_unref (old); + + /* store new buffer */ + g_ptr_array_index (depay->packets, idx) = outbuf; +} + +static GstBuffer * +create_erasure_buffer (GstRtpQCELPDepay * depay) +{ + GstBuffer *outbuf; + GstMapInfo map; + + outbuf = gst_buffer_new_and_alloc (1); + gst_buffer_map (outbuf, &map, GST_MAP_WRITE); + map.data[0] = 14; + gst_buffer_unmap (outbuf, &map); + + return outbuf; +} + +static GstBuffer * +gst_rtp_qcelp_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpQCELPDepay *depay; + GstBuffer *outbuf; + GstClockTime timestamp; + guint payload_len, offset, index; + guint8 *payload; + guint LLL, NNN; + GstRTPBuffer rtp = { NULL }; + + depay = GST_RTP_QCELP_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len < 2) + goto too_small; + + timestamp = GST_BUFFER_TIMESTAMP (buf); + + payload = gst_rtp_buffer_get_payload (&rtp); + + /* 0 1 2 3 4 5 6 7 + * +-+-+-+-+-+-+-+-+ + * |RR | LLL | NNN | + * +-+-+-+-+-+-+-+-+ + */ + /* RR = payload[0] >> 6; */ + LLL = (payload[0] & 0x38) >> 3; + NNN = (payload[0] & 0x07); + + payload_len--; + payload++; + + GST_DEBUG_OBJECT (depay, "LLL %u, NNN %u", LLL, NNN); + + if (LLL > 5) + goto invalid_lll; + + if (NNN > LLL) + goto invalid_nnn; + + if (LLL != 0) { + /* we are interleaved */ + if (!depay->interleaved) { + guint size; + + GST_DEBUG_OBJECT (depay, "starting interleaving group"); + /* bundling is not allowed to change in one interleave group */ + depay->bundling = count_packets (depay, payload, payload_len); + GST_DEBUG_OBJECT (depay, "got bundling of %u", depay->bundling); + /* we have one bundle where NNN goes from 0 to L, we don't store the index + * 0 frames, so L+1 packets. Each packet has 'bundling - 1' packets */ + size = (depay->bundling - 1) * (LLL + 1); + /* create the array to hold the packets */ + if (depay->packets == NULL) + depay->packets = g_ptr_array_sized_new (size); + GST_DEBUG_OBJECT (depay, "created packet array of size %u", size); + g_ptr_array_set_size (depay->packets, size); + /* we were previously not interleaved, figure out how much space we + * need to deinterleave */ + depay->interleaved = TRUE; + } + } else { + /* we are not interleaved */ + if (depay->interleaved) { + GST_DEBUG_OBJECT (depay, "stopping interleaving"); + /* flush packets if we were previously interleaved */ + flush_packets (depay); + } + depay->bundling = 0; + } + + index = 0; + offset = 1; + + while (payload_len > 0) { + gint frame_len; + gboolean do_erasure; + + frame_len = get_frame_len (depay, payload[0]); + GST_DEBUG_OBJECT (depay, "got frame len %d", frame_len); + + if (frame_len == 0) + goto invalid_frame; + + if (frame_len < 0) { + /* need to add an erasure frame but we can recover */ + frame_len = -frame_len; + do_erasure = TRUE; + } else { + do_erasure = FALSE; + } + + if (frame_len > payload_len) + goto invalid_frame; + + if (do_erasure) { + /* create erasure frame */ + outbuf = create_erasure_buffer (depay); + } else { + /* each frame goes into its buffer */ + outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, offset, frame_len); + } + + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + GST_BUFFER_DURATION (outbuf) = FRAME_DURATION; + + if (!depay->interleaved || index == 0) { + /* not interleaved or first frame in packet, just push */ + gst_rtp_base_depayload_push (depayload, outbuf); + + if (timestamp != -1) + timestamp += FRAME_DURATION; + } else { + /* put in interleave buffer */ + add_packet (depay, LLL, NNN, index, outbuf); + + if (timestamp != -1) + timestamp += (FRAME_DURATION * (LLL + 1)); + } + + payload_len -= frame_len; + payload += frame_len; + offset += frame_len; + index++; + + /* discard excess packets */ + if (depay->bundling > 0 && depay->bundling <= index) + break; + } + while (index < depay->bundling) { + GST_DEBUG_OBJECT (depay, "filling with erasure buffer"); + /* fill remainder with erasure packets */ + outbuf = create_erasure_buffer (depay); + add_packet (depay, LLL, NNN, index, outbuf); + index++; + } + if (depay->interleaved && LLL == NNN) { + GST_DEBUG_OBJECT (depay, "interleave group ended, flushing"); + /* we have the complete interleave group, flush */ + flush_packets (depay); + } + + gst_rtp_buffer_unmap (&rtp); + return NULL; + + /* ERRORS */ +too_small: + { + GST_ELEMENT_WARNING (depay, STREAM, DECODE, + (NULL), ("QCELP RTP payload too small (%d)", payload_len)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_lll: + { + GST_ELEMENT_WARNING (depay, STREAM, DECODE, + (NULL), ("QCELP RTP invalid LLL received (%d)", LLL)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_nnn: + { + GST_ELEMENT_WARNING (depay, STREAM, DECODE, + (NULL), ("QCELP RTP invalid NNN received (%d)", NNN)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_frame: + { + GST_ELEMENT_WARNING (depay, STREAM, DECODE, + (NULL), ("QCELP RTP invalid frame received")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +gboolean +gst_rtp_qcelp_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpqcelpdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_QCELP_DEPAY); +} diff --git a/gst/rtp/gstrtpqcelpdepay.h b/gst/rtp/gstrtpqcelpdepay.h new file mode 100755 index 0000000..ade274d --- /dev/null +++ b/gst/rtp/gstrtpqcelpdepay.h @@ -0,0 +1,62 @@ +/* GStreamer + * Copyright (C) <2010> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_QCELP_DEPAY_H__ +#define __GST_RTP_QCELP_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_QCELP_DEPAY \ + (gst_rtp_qcelp_depay_get_type()) +#define GST_RTP_QCELP_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_QCELP_DEPAY,GstRtpQCELPDepay)) +#define GST_RTP_QCELP_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_QCELP_DEPAY,GstRtpQCELPDepayClass)) +#define GST_IS_RTP_QCELP_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_QCELP_DEPAY)) +#define GST_IS_RTP_QCELP_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_QCELP_DEPAY)) + +typedef struct _GstRtpQCELPDepay GstRtpQCELPDepay; +typedef struct _GstRtpQCELPDepayClass GstRtpQCELPDepayClass; + +struct _GstRtpQCELPDepay +{ + GstRTPBaseDepayload depayload; + + gboolean interleaved; + guint bundling; + GPtrArray *packets; +}; + +struct _GstRtpQCELPDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_qcelp_depay_get_type (void); + +gboolean gst_rtp_qcelp_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_QCELP_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpqdmdepay.c b/gst/rtp/gstrtpqdmdepay.c new file mode 100755 index 0000000..ba85018 --- /dev/null +++ b/gst/rtp/gstrtpqdmdepay.c @@ -0,0 +1,416 @@ +/* GStreamer + * Copyright (C) <2009> Edward Hervey <bilboed@bilboed.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpqdmdepay.h" + +GST_DEBUG_CATEGORY (rtpqdm2depay_debug); +#define GST_CAT_DEFAULT rtpqdm2depay_debug + +static GstStaticPadTemplate gst_rtp_qdm2_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-qdm2") + ); + +static GstStaticPadTemplate gst_rtp_qdm2_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " "encoding-name = (string)\"X-QDM\"") + ); + +#define gst_rtp_qdm2_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpQDM2Depay, gst_rtp_qdm2_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static const guint8 headheader[20] = { + 0x0, 0x0, 0x0, 0xc, 0x66, 0x72, 0x6d, 0x61, + 0x51, 0x44, 0x4d, 0x32, 0x0, 0x0, 0x0, 0x24, + 0x51, 0x44, 0x43, 0x41 +}; + +static void gst_rtp_qdm2_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_qdm2_depay_change_state (GstElement * + element, GstStateChange transition); + +static GstBuffer *gst_rtp_qdm2_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +gboolean gst_rtp_qdm2_depay_setcaps (GstRTPBaseDepayload * filter, + GstCaps * caps); + +static void +gst_rtp_qdm2_depay_class_init (GstRtpQDM2DepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_qdm2_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_qdm2_depay_setcaps; + + gobject_class->finalize = gst_rtp_qdm2_depay_finalize; + + gstelement_class->change_state = gst_rtp_qdm2_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_qdm2_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_qdm2_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP QDM2 depayloader", + "Codec/Depayloader/Network/RTP", + "Extracts QDM2 audio from RTP packets (no RFC)", + "Edward Hervey <bilboed@bilboed.com>"); +} + +static void +gst_rtp_qdm2_depay_init (GstRtpQDM2Depay * rtpqdm2depay) +{ + rtpqdm2depay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_qdm2_depay_finalize (GObject * object) +{ + GstRtpQDM2Depay *rtpqdm2depay; + + rtpqdm2depay = GST_RTP_QDM2_DEPAY (object); + + g_object_unref (rtpqdm2depay->adapter); + rtpqdm2depay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* only on the sink */ +gboolean +gst_rtp_qdm2_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps) +{ + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint clock_rate; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 44100; /* default */ + filter->clock_rate = clock_rate; + + /* will set caps later */ + + return TRUE; +} + +static void +flush_data (GstRtpQDM2Depay * depay) +{ + guint i; + guint avail; + + if ((avail = gst_adapter_available (depay->adapter))) + gst_adapter_flush (depay->adapter, avail); + + GST_DEBUG ("Flushing %d packets", depay->nbpackets); + + for (i = 0; depay->packets[i]; i++) { + QDM2Packet *pack = depay->packets[i]; + guint32 crc = 0; + int i = 0; + GstBuffer *buf; + guint8 *data; + + /* CRC is the sum of everything (including first bytes) */ + + data = pack->data; + + if (G_UNLIKELY (data == NULL)) + continue; + + /* If the packet size is bigger than 0xff, we need 2 bytes to store the size */ + if (depay->packetsize > 0xff) { + /* Expanded size 0x02 | 0x80 */ + data[0] = 0x82; + GST_WRITE_UINT16_BE (data + 1, depay->packetsize - 3); + } else { + data[0] = 0x2; + data[1] = depay->packetsize - 2; + } + + /* Calculate CRC */ + for (; i < depay->packetsize; i++) + crc += data[i]; + + GST_DEBUG ("CRC is 0x%x", crc); + + /* Write CRC */ + if (depay->packetsize > 0xff) + GST_WRITE_UINT16_BE (data + 3, crc); + else + GST_WRITE_UINT16_BE (data + 2, crc); + + GST_MEMDUMP ("Extracted packet", data, depay->packetsize); + + buf = gst_buffer_new (); + gst_buffer_append_memory (buf, + gst_memory_new_wrapped (0, data, depay->packetsize, 0, + depay->packetsize, data, g_free)); + + gst_adapter_push (depay->adapter, buf); + + pack->data = NULL; + } +} + +static void +add_packet (GstRtpQDM2Depay * depay, guint32 pid, guint32 len, guint8 * data) +{ + QDM2Packet *packet; + + if (G_UNLIKELY (!depay->configured)) + return; + + GST_DEBUG ("pid:%d, len:%d, data:%p", pid, len, data); + + if (G_UNLIKELY (depay->packets[pid] == NULL)) { + depay->packets[pid] = g_malloc0 (sizeof (QDM2Packet)); + depay->nbpackets = MAX (depay->nbpackets, pid + 1); + } + packet = depay->packets[pid]; + + GST_DEBUG ("packet:%p", packet); + GST_DEBUG ("packet->data:%p", packet->data); + + if (G_UNLIKELY (packet->data == NULL)) { + packet->data = g_malloc0 (depay->packetsize); + /* We leave space for the header/crc */ + if (depay->packetsize > 0xff) + packet->offs = 5; + else + packet->offs = 4; + } + + /* Finally copy the data over */ + memcpy (packet->data + packet->offs, data, len); + packet->offs += len; +} + +static GstBuffer * +gst_rtp_qdm2_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpQDM2Depay *rtpqdm2depay; + GstBuffer *outbuf = NULL; + guint16 seq; + GstRTPBuffer rtp = { NULL }; + + rtpqdm2depay = GST_RTP_QDM2_DEPAY (depayload); + + { + gint payload_len; + guint8 *payload; + guint avail; + guint pos = 0; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + if (payload_len < 3) + goto bad_packet; + + payload = gst_rtp_buffer_get_payload (&rtp); + seq = gst_rtp_buffer_get_seq (&rtp); + if (G_UNLIKELY (seq != rtpqdm2depay->nextseq)) { + GST_DEBUG ("GAP in sequence number, Resetting data !"); + /* Flush previous data */ + flush_data (rtpqdm2depay); + /* And store new timestamp */ + rtpqdm2depay->ptimestamp = rtpqdm2depay->timestamp; + rtpqdm2depay->timestamp = GST_BUFFER_TIMESTAMP (buf); + /* And that previous data will be pushed at the bottom */ + } + rtpqdm2depay->nextseq = seq + 1; + + GST_DEBUG ("Payload size %d 0x%x sequence:%d", payload_len, payload_len, + seq); + + GST_MEMDUMP ("Incoming payload", payload, payload_len); + + while (pos < payload_len) { + switch (payload[pos]) { + case 0x80:{ + GST_DEBUG ("Unrecognized 0x80 marker, skipping 12 bytes"); + pos += 12; + } + break; + case 0xff: + /* HEADERS */ + GST_DEBUG ("Headers"); + /* Store the incoming timestamp */ + rtpqdm2depay->ptimestamp = rtpqdm2depay->timestamp; + rtpqdm2depay->timestamp = GST_BUFFER_TIMESTAMP (buf); + /* flush the internal data if needed */ + flush_data (rtpqdm2depay); + if (G_UNLIKELY (!rtpqdm2depay->configured)) { + guint8 *ourdata; + GstBuffer *codecdata; + GstMapInfo cmap; + GstCaps *caps; + + /* First bytes are unknown */ + GST_MEMDUMP ("Header", payload + pos, 32); + ourdata = payload + pos + 10; + pos += 10; + rtpqdm2depay->channs = GST_READ_UINT32_BE (payload + pos + 4); + rtpqdm2depay->samplerate = GST_READ_UINT32_BE (payload + pos + 8); + rtpqdm2depay->bitrate = GST_READ_UINT32_BE (payload + pos + 12); + rtpqdm2depay->blocksize = GST_READ_UINT32_BE (payload + pos + 16); + rtpqdm2depay->framesize = GST_READ_UINT32_BE (payload + pos + 20); + rtpqdm2depay->packetsize = GST_READ_UINT32_BE (payload + pos + 24); + /* 16 bit empty block (0x02 0x00) */ + pos += 30; + GST_DEBUG + ("channs:%d, samplerate:%d, bitrate:%d, blocksize:%d, framesize:%d, packetsize:%d", + rtpqdm2depay->channs, rtpqdm2depay->samplerate, + rtpqdm2depay->bitrate, rtpqdm2depay->blocksize, + rtpqdm2depay->framesize, rtpqdm2depay->packetsize); + + /* Caps */ + codecdata = gst_buffer_new_and_alloc (48); + gst_buffer_map (codecdata, &cmap, GST_MAP_WRITE); + memcpy (cmap.data, headheader, 20); + memcpy (cmap.data + 20, ourdata, 28); + gst_buffer_unmap (codecdata, &cmap); + + caps = gst_caps_new_simple ("audio/x-qdm2", + "samplesize", G_TYPE_INT, 16, + "rate", G_TYPE_INT, rtpqdm2depay->samplerate, + "channels", G_TYPE_INT, rtpqdm2depay->channs, + "codec_data", GST_TYPE_BUFFER, codecdata, NULL); + gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), caps); + gst_caps_unref (caps); + rtpqdm2depay->configured = TRUE; + } else { + GST_DEBUG ("Already configured, skipping headers"); + pos += 40; + } + break; + default:{ + /* Shuffled packet contents */ + guint packetid = payload[pos++]; + guint packettype = payload[pos++]; + guint packlen = payload[pos++]; + guint hsize = 2; + + GST_DEBUG ("Packet id:%d, type:0x%x, len:%d", + packetid, packettype, packlen); + + /* Packets bigger than 0xff bytes have a type with the high bit set */ + if (G_UNLIKELY (packettype & 0x80)) { + packettype &= 0x7f; + packlen <<= 8; + packlen |= payload[pos++]; + hsize = 3; + GST_DEBUG ("Packet id:%d, type:0x%x, len:%d", + packetid, packettype, packlen); + } + + if (packettype > 0x7f) { + GST_ERROR ("HOUSTON WE HAVE A PROBLEM !!!!"); + } + add_packet (rtpqdm2depay, packetid, packlen + hsize, + payload + pos - hsize); + pos += packlen; + } + } + } + + GST_DEBUG ("final pos %d", pos); + + avail = gst_adapter_available (rtpqdm2depay->adapter); + if (G_UNLIKELY (avail)) { + GST_DEBUG ("Pushing out %d bytes of collected data", avail); + outbuf = gst_adapter_take_buffer (rtpqdm2depay->adapter, avail); + GST_BUFFER_TIMESTAMP (outbuf) = rtpqdm2depay->ptimestamp; + GST_DEBUG ("Outgoing buffer timestamp %" GST_TIME_FORMAT, + GST_TIME_ARGS (rtpqdm2depay->ptimestamp)); + } + } + + gst_rtp_buffer_unmap (&rtp); + return outbuf; + + /* ERRORS */ +bad_packet: + { + GST_ELEMENT_WARNING (rtpqdm2depay, STREAM, DECODE, + (NULL), ("Packet was too short")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_qdm2_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpQDM2Depay *rtpqdm2depay; + GstStateChangeReturn ret; + + rtpqdm2depay = GST_RTP_QDM2_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtpqdm2depay->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_qdm2_depay_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (rtpqdm2depay_debug, "rtpqdm2depay", 0, + "RTP QDM2 depayloader"); + + return gst_element_register (plugin, "rtpqdm2depay", + GST_RANK_SECONDARY, GST_TYPE_RTP_QDM2_DEPAY); +} diff --git a/gst/rtp/gstrtpqdmdepay.h b/gst/rtp/gstrtpqdmdepay.h new file mode 100755 index 0000000..8a6b2b2 --- /dev/null +++ b/gst/rtp/gstrtpqdmdepay.h @@ -0,0 +1,85 @@ +/* GStreamer + * Copyright (C) <2009> Edward Hervey <bilboed@bilboed.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_QDM2_DEPAY_H__ +#define __GST_RTP_QDM2_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_QDM2_DEPAY \ + (gst_rtp_qdm2_depay_get_type()) +#define GST_RTP_QDM2_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_QDM2_DEPAY,GstRtpQDM2Depay)) +#define GST_RTP_QDM2_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_QDM2_DEPAY,GstRtpQDM2DepayClass)) +#define GST_IS_RTP_QDM2_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_QDM2_DEPAY)) +#define GST_IS_RTP_QDM2_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_QDM2_DEPAY)) + +typedef struct _GstRtpQDM2Depay GstRtpQDM2Depay; +typedef struct _GstRtpQDM2DepayClass GstRtpQDM2DepayClass; + +typedef struct _QDM2Packet { + guint8* data; + guint offs; /* Starts at 4 to give room for the prefix */ +} QDM2Packet; + +#define MAX_SCRAMBLED_PACKETS 64 + +struct _GstRtpQDM2Depay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + + guint16 nextseq; + gboolean configured; + + GstClockTime timestamp; /* Timestamp of current incoming data */ + GstClockTime ptimestamp; /* Timestamp of data stored in the adapter */ + + guint32 channs; + guint32 samplerate; + guint32 bitrate; + guint32 blocksize; + guint32 framesize; + guint32 packetsize; + + guint nbpackets; /* Number of packets to unscramble */ + + QDM2Packet *packets[MAX_SCRAMBLED_PACKETS]; +}; + +struct _GstRtpQDM2DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_qdm2_depay_get_type (void); + +gboolean gst_rtp_qdm2_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_QDM2_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpsbcdepay.c b/gst/rtp/gstrtpsbcdepay.c new file mode 100755 index 0000000..28c00fc --- /dev/null +++ b/gst/rtp/gstrtpsbcdepay.c @@ -0,0 +1,287 @@ +/* + * GStreamer RTP SBC depayloader + * + * Copyright (C) 2012 Collabora Ltd. + * @author: Arun Raghavan <arun.raghavan@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpsbcdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpsbcdepay_debug); +#define GST_CAT_DEFAULT (rtpsbcdepay_debug) + +static GstStaticPadTemplate gst_rtp_sbc_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-sbc, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation-method = (string) { snr, loudness }, " + "bitpool = (int) [ 2, 64 ]") + ); + +static GstStaticPadTemplate gst_rtp_sbc_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) audio," + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, 44100, 48000 }," + "encoding-name = (string) SBC") + ); + +#define gst_rtp_sbc_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpSbcDepay, gst_rtp_sbc_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_sbc_depay_finalize (GObject * object); + +static gboolean gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base, + GstCaps * caps); +static GstBuffer *gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, + GstBuffer * in); + +static void +gst_rtp_sbc_depay_class_init (GstRtpSbcDepayClass * klass) +{ + GstRTPBaseDepayloadClass *gstbasertpdepayload_class = + GST_RTP_BASE_DEPAYLOAD_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_rtp_sbc_depay_finalize; + + gstbasertpdepayload_class->set_caps = gst_rtp_sbc_depay_setcaps; + gstbasertpdepayload_class->process = gst_rtp_sbc_depay_process; + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_sbc_depay_src_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_sbc_depay_sink_template)); + + GST_DEBUG_CATEGORY_INIT (rtpsbcdepay_debug, "rtpsbcdepay", 0, + "SBC Audio RTP Depayloader"); + + gst_element_class_set_static_metadata (element_class, + "RTP SBC audio depayloader", + "Codec/Depayloader/Network/RTP", + "Extracts SBC audio from RTP packets", + "Arun Raghavan <arun.raghavan@collabora.co.uk>"); +} + +static void +gst_rtp_sbc_depay_init (GstRtpSbcDepay * rtpsbcdepay) +{ + rtpsbcdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_sbc_depay_finalize (GObject * object) +{ + GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (object); + + gst_object_unref (depay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* FIXME: This duplicates similar functionality rtpsbcpay, but there isn't a + * simple way to consolidate the two. This is best done by moving the function + * to the codec-utils library in gst-plugins-base when these elements move to + * GStreamer. */ +static int +gst_rtp_sbc_depay_get_params (GstRtpSbcDepay * depay, const guint8 * data, + gint size, int *framelen, int *samples) +{ + int blocks, channel_mode, channels, subbands, bitpool; + int length; + + if (size < 3) { + /* Not enough data for the header */ + return -1; + } + + /* Sanity check */ + if (data[0] != 0x9c) { + GST_WARNING_OBJECT (depay, "Bad packet: couldn't find syncword"); + return -2; + } + + blocks = (data[1] >> 4) & 0x3; + blocks = (blocks + 1) * 4; + channel_mode = (data[1] >> 2) & 0x3; + channels = channel_mode ? 2 : 1; + subbands = (data[1] & 0x1); + subbands = (subbands + 1) * 4; + bitpool = data[2]; + + length = 4 + ((4 * subbands * channels) / 8); + + if (channel_mode == 0 || channel_mode == 1) { + /* Mono || Dual channel */ + length += ((blocks * channels * bitpool) + + 4 /* round up */ ) / 8; + } else { + /* Stereo || Joint stereo */ + gboolean joint = (channel_mode == 3); + + length += ((joint * subbands) + (blocks * bitpool) + + 4 /* round up */ ) / 8; + } + + *framelen = length; + *samples = blocks * subbands; + + return 0; +} + +static gboolean +gst_rtp_sbc_depay_setcaps (GstRTPBaseDepayload * base, GstCaps * caps) +{ + GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (base); + GstStructure *structure; + GstCaps *outcaps, *oldcaps; + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &depay->rate)) + goto bad_caps; + + outcaps = gst_caps_new_simple ("audio/x-sbc", "rate", G_TYPE_INT, + depay->rate, NULL); + + gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (base), outcaps); + + oldcaps = gst_pad_get_current_caps (GST_RTP_BASE_DEPAYLOAD_SINKPAD (base)); + if (oldcaps && !gst_caps_can_intersect (oldcaps, caps)) { + /* Caps have changed, flush old data */ + gst_adapter_clear (depay->adapter); + } + + gst_caps_unref (outcaps); + + return TRUE; + +bad_caps: + GST_WARNING_OBJECT (depay, "Can't support the caps we got: %" + GST_PTR_FORMAT, caps); + return FALSE; +} + +static GstBuffer * +gst_rtp_sbc_depay_process (GstRTPBaseDepayload * base, GstBuffer * in) +{ + GstRtpSbcDepay *depay = GST_RTP_SBC_DEPAY (base); + GstBuffer *data = NULL; + GstRTPBuffer rtp = { NULL }; + + gboolean fragment, start, last; + guint8 nframes; + guint8 *payload; + guint payload_len; + + gst_rtp_buffer_map (in, GST_MAP_READ, &rtp); + + GST_LOG_OBJECT (depay, "Got %" G_GSIZE_FORMAT " bytes", + gst_buffer_get_size (in)); + + if (gst_rtp_buffer_get_marker (&rtp)) { + /* Marker isn't supposed to be set */ + GST_WARNING_OBJECT (depay, "Marker bit was set"); + goto bad_packet; + } + + payload = gst_rtp_buffer_get_payload (&rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + fragment = payload[0] & 0x80; + start = payload[0] & 0x40; + last = payload[0] & 0x20; + nframes = payload[0] & 0x0f; + + payload += 1; + payload_len -= 1; + + data = gst_rtp_buffer_get_payload_subbuffer (&rtp, 1, -1); + + if (fragment) { + /* Got a packet with a fragment */ + GST_LOG_OBJECT (depay, "Got fragment"); + + if (start && gst_adapter_available (depay->adapter)) { + GST_WARNING_OBJECT (depay, "Missing last fragment"); + gst_adapter_clear (depay->adapter); + + } else if (!start && !gst_adapter_available (depay->adapter)) { + GST_WARNING_OBJECT (depay, "Missing start fragment"); + gst_buffer_unref (data); + data = NULL; + goto out; + } + + gst_adapter_push (depay->adapter, data); + + if (last) { + data = gst_adapter_take_buffer (depay->adapter, + gst_adapter_available (depay->adapter)); + } else + data = NULL; + + } else { + /* !fragment */ + gint framelen, samples; + + GST_LOG_OBJECT (depay, "Got %d frames", nframes); + + if (gst_rtp_sbc_depay_get_params (depay, payload, + payload_len, &framelen, &samples) < 0) { + gst_adapter_clear (depay->adapter); + goto bad_packet; + } + + GST_LOG_OBJECT (depay, "Got payload of %d", payload_len); + + if (nframes * framelen > (gint) payload_len) { + GST_WARNING_OBJECT (depay, "Short packet"); + goto bad_packet; + } else if (nframes * framelen < (gint) payload_len) { + GST_WARNING_OBJECT (depay, "Junk at end of packet"); + } + } + +out: + gst_rtp_buffer_unmap (&rtp); + return data; + +bad_packet: + GST_ELEMENT_WARNING (depay, STREAM, DECODE, + ("Received invalid RTP payload, dropping"), (NULL)); + goto out; +} + +gboolean +gst_rtp_sbc_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsbcdepay", GST_RANK_SECONDARY, + GST_TYPE_RTP_SBC_DEPAY); +} diff --git a/gst/rtp/gstrtpsbcdepay.h b/gst/rtp/gstrtpsbcdepay.h new file mode 100755 index 0000000..4147a31 --- /dev/null +++ b/gst/rtp/gstrtpsbcdepay.h @@ -0,0 +1,65 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Collabora Ltd. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __GST_RTP_SBC_DEPAY_H +#define __GST_RTP_SBC_DEPAY_H + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS +#define GST_TYPE_RTP_SBC_DEPAY \ + (gst_rtp_sbc_depay_get_type()) +#define GST_RTP_SBC_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_DEPAY,\ + GstRtpSbcDepay)) +#define GST_RTP_SBC_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_DEPAY,\ + GstRtpSbcDepayClass)) +#define GST_IS_RTP_SBC_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_DEPAY)) +#define GST_IS_RTP_SBC_DEPAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_DEPAY)) +typedef struct _GstRtpSbcDepay GstRtpSbcDepay; +typedef struct _GstRtpSbcDepayClass GstRtpSbcDepayClass; + +struct _GstRtpSbcDepay +{ + GstRTPBaseDepayload base; + + int rate; + GstAdapter *adapter; +}; + +struct _GstRtpSbcDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_sbc_depay_get_type (void); + +gboolean gst_rtp_sbc_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif diff --git a/gst/rtp/gstrtpsbcpay.c b/gst/rtp/gstrtpsbcpay.c new file mode 100755 index 0000000..8a8f5cd --- /dev/null +++ b/gst/rtp/gstrtpsbcpay.c @@ -0,0 +1,350 @@ +/* GStreamer RTP SBC payloader + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "gstrtpsbcpay.h" +#include <math.h> +#include <string.h> + +#define RTP_SBC_PAYLOAD_HEADER_SIZE 1 +#define DEFAULT_MIN_FRAMES 0 +#define RTP_SBC_HEADER_TOTAL (12 + RTP_SBC_PAYLOAD_HEADER_SIZE) + +#if G_BYTE_ORDER == G_LITTLE_ENDIAN + +/* FIXME: this seems all a bit over the top for a single byte.. */ +struct rtp_payload +{ + guint8 frame_count:4; + guint8 rfa0:1; + guint8 is_last_fragment:1; + guint8 is_first_fragment:1; + guint8 is_fragmented:1; +} __attribute__ ((packed)); + +#elif G_BYTE_ORDER == G_BIG_ENDIAN + +struct rtp_payload +{ + guint8 is_fragmented:1; + guint8 is_first_fragment:1; + guint8 is_last_fragment:1; + guint8 rfa0:1; + guint8 frame_count:4; +} __attribute__ ((packed)); + +#else +#error "Unknown byte order" +#endif + +enum +{ + PROP_0, + PROP_MIN_FRAMES +}; + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_sbc_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_sbc_pay_debug + +#define parent_class gst_rtp_sbc_pay_parent_class +G_DEFINE_TYPE (GstRtpSBCPay, gst_rtp_sbc_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static GstStaticPadTemplate gst_rtp_sbc_pay_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-sbc, " + "rate = (int) { 16000, 32000, 44100, 48000 }, " + "channels = (int) [ 1, 2 ], " + "channel-mode = (string) { mono, dual, stereo, joint }, " + "blocks = (int) { 4, 8, 12, 16 }, " + "subbands = (int) { 4, 8 }, " + "allocation-method = (string) { snr, loudness }, " + "bitpool = (int) [ 2, 64 ]") + ); + +static GstStaticPadTemplate gst_rtp_sbc_pay_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) audio," + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) { 16000, 32000, 44100, 48000 }," + "encoding-name = (string) SBC") + ); + +static void gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static gint +gst_rtp_sbc_pay_get_frame_len (gint subbands, gint channels, + gint blocks, gint bitpool, const gchar * channel_mode) +{ + gint len; + gint join; + + len = 4 + (4 * subbands * channels) / 8; + + if (strcmp (channel_mode, "mono") == 0 || strcmp (channel_mode, "dual") == 0) + len += ((blocks * channels * bitpool) + 7) / 8; + else { + join = strcmp (channel_mode, "joint") == 0 ? 1 : 0; + len += ((join * subbands + blocks * bitpool) + 7) / 8; + } + + return len; +} + +static gboolean +gst_rtp_sbc_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpSBCPay *sbcpay; + gint rate, subbands, channels, blocks, bitpool; + gint frame_len; + const gchar *channel_mode; + GstStructure *structure; + + sbcpay = GST_RTP_SBC_PAY (payload); + + structure = gst_caps_get_structure (caps, 0); + if (!gst_structure_get_int (structure, "rate", &rate)) + return FALSE; + if (!gst_structure_get_int (structure, "channels", &channels)) + return FALSE; + if (!gst_structure_get_int (structure, "blocks", &blocks)) + return FALSE; + if (!gst_structure_get_int (structure, "bitpool", &bitpool)) + return FALSE; + if (!gst_structure_get_int (structure, "subbands", &subbands)) + return FALSE; + + channel_mode = gst_structure_get_string (structure, "channel-mode"); + if (!channel_mode) + return FALSE; + + frame_len = gst_rtp_sbc_pay_get_frame_len (subbands, channels, blocks, + bitpool, channel_mode); + + sbcpay->frame_length = frame_len; + + gst_rtp_base_payload_set_options (payload, "audio", TRUE, "SBC", rate); + + GST_DEBUG_OBJECT (payload, "calculated frame length: %d ", frame_len); + + return gst_rtp_base_payload_set_outcaps (payload, NULL); +} + +static GstFlowReturn +gst_rtp_sbc_pay_flush_buffers (GstRtpSBCPay * sbcpay) +{ + GstRTPBuffer rtp = GST_RTP_BUFFER_INIT; + guint available; + guint max_payload; + GstBuffer *outbuf; + guint8 *payload_data; + guint frame_count; + guint payload_length; + struct rtp_payload *payload; + + if (sbcpay->frame_length == 0) { + GST_ERROR_OBJECT (sbcpay, "Frame length is 0"); + return GST_FLOW_ERROR; + } + + available = gst_adapter_available (sbcpay->adapter); + + max_payload = + gst_rtp_buffer_calc_payload_len (GST_RTP_BASE_PAYLOAD_MTU (sbcpay) - + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); + + max_payload = MIN (max_payload, available); + frame_count = max_payload / sbcpay->frame_length; + payload_length = frame_count * sbcpay->frame_length; + if (payload_length == 0) /* Nothing to send */ + return GST_FLOW_OK; + + outbuf = gst_rtp_buffer_new_allocate (payload_length + + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, 0); + + /* get payload */ + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + + gst_rtp_buffer_set_payload_type (&rtp, GST_RTP_BASE_PAYLOAD_PT (sbcpay)); + + /* write header and copy data into payload */ + payload_data = gst_rtp_buffer_get_payload (&rtp); + payload = (struct rtp_payload *) payload_data; + memset (payload, 0, sizeof (struct rtp_payload)); + payload->frame_count = frame_count; + + gst_adapter_copy (sbcpay->adapter, payload_data + + RTP_SBC_PAYLOAD_HEADER_SIZE, 0, payload_length); + + gst_rtp_buffer_unmap (&rtp); + + gst_adapter_flush (sbcpay->adapter, payload_length); + + /* FIXME: what about duration? */ + GST_BUFFER_TIMESTAMP (outbuf) = sbcpay->timestamp; + GST_DEBUG_OBJECT (sbcpay, "Pushing %d bytes", payload_length); + + return gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (sbcpay), outbuf); +} + +static GstFlowReturn +gst_rtp_sbc_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + GstRtpSBCPay *sbcpay; + guint available; + + /* FIXME check for negotiation */ + + sbcpay = GST_RTP_SBC_PAY (payload); + sbcpay->timestamp = GST_BUFFER_TIMESTAMP (buffer); + + gst_adapter_push (sbcpay->adapter, buffer); + + available = gst_adapter_available (sbcpay->adapter); + if (available + RTP_SBC_HEADER_TOTAL >= + GST_RTP_BASE_PAYLOAD_MTU (sbcpay) || + (available > (sbcpay->min_frames * sbcpay->frame_length))) + return gst_rtp_sbc_pay_flush_buffers (sbcpay); + + return GST_FLOW_OK; +} + +static gboolean +gst_rtp_sbc_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_EOS: + gst_rtp_sbc_pay_flush_buffers (sbcpay); + break; + default: + break; + } + + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); +} + +static void +gst_rtp_sbc_pay_finalize (GObject * object) +{ + GstRtpSBCPay *sbcpay = GST_RTP_SBC_PAY (object); + + g_object_unref (sbcpay->adapter); + + GST_CALL_PARENT (G_OBJECT_CLASS, finalize, (object)); +} + +static void +gst_rtp_sbc_pay_class_init (GstRtpSBCPayClass * klass) +{ + GstRTPBasePayloadClass *payload_class = GST_RTP_BASE_PAYLOAD_CLASS (klass); + GstElementClass *element_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + + gobject_class->finalize = gst_rtp_sbc_pay_finalize; + gobject_class->set_property = gst_rtp_sbc_pay_set_property; + gobject_class->get_property = gst_rtp_sbc_pay_get_property; + + payload_class->set_caps = GST_DEBUG_FUNCPTR (gst_rtp_sbc_pay_set_caps); + payload_class->handle_buffer = + GST_DEBUG_FUNCPTR (gst_rtp_sbc_pay_handle_buffer); + payload_class->sink_event = GST_DEBUG_FUNCPTR (gst_rtp_sbc_pay_sink_event); + + /* properties */ + g_object_class_install_property (G_OBJECT_CLASS (klass), + PROP_MIN_FRAMES, + g_param_spec_int ("min-frames", "minimum frame number", + "Minimum quantity of frames to send in one packet " + "(-1 for maximum allowed by the mtu)", + -1, G_MAXINT, DEFAULT_MIN_FRAMES, G_PARAM_READWRITE)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_sbc_pay_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_sbc_pay_src_factory)); + + gst_element_class_set_static_metadata (element_class, "RTP packet payloader", + "Codec/Payloader/Network", "Payload SBC audio as RTP packets", + "Thiago Sousa Santos <thiagoss@lcc.ufcg.edu.br>"); + + GST_DEBUG_CATEGORY_INIT (gst_rtp_sbc_pay_debug, "rtpsbcpay", 0, + "RTP SBC payloader"); +} + +static void +gst_rtp_sbc_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + sbcpay->min_frames = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_sbc_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpSBCPay *sbcpay; + + sbcpay = GST_RTP_SBC_PAY (object); + + switch (prop_id) { + case PROP_MIN_FRAMES: + g_value_set_int (value, sbcpay->min_frames); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_sbc_pay_init (GstRtpSBCPay * self) +{ + self->adapter = gst_adapter_new (); + self->frame_length = 0; + self->timestamp = 0; + + self->min_frames = DEFAULT_MIN_FRAMES; +} + +gboolean +gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsbcpay", GST_RANK_NONE, + GST_TYPE_RTP_SBC_PAY); +} diff --git a/gst/rtp/gstrtpsbcpay.h b/gst/rtp/gstrtpsbcpay.h new file mode 100755 index 0000000..42cf02e --- /dev/null +++ b/gst/rtp/gstrtpsbcpay.h @@ -0,0 +1,64 @@ +/* GStreamer RTP SBC payloader + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/rtp/gstrtpbuffer.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_SBC_PAY \ + (gst_rtp_sbc_pay_get_type()) +#define GST_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPay)) +#define GST_RTP_SBC_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SBC_PAY,\ + GstRtpSBCPayClass)) +#define GST_IS_RTP_SBC_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SBC_PAY)) +#define GST_IS_RTP_SBC_PAY_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SBC_PAY)) + +typedef struct _GstRtpSBCPay GstRtpSBCPay; +typedef struct _GstRtpSBCPayClass GstRtpSBCPayClass; + +struct _GstRtpSBCPay { + GstRTPBasePayload base; + + GstAdapter *adapter; + GstClockTime timestamp; + + guint frame_length; + + guint min_frames; +}; + +struct _GstRtpSBCPayClass { + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_sbc_pay_get_type(void); + +gboolean gst_rtp_sbc_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS diff --git a/gst/rtp/gstrtpsirendepay.c b/gst/rtp/gstrtpsirendepay.c new file mode 100755 index 0000000..1bbbda7 --- /dev/null +++ b/gst/rtp/gstrtpsirendepay.c @@ -0,0 +1,123 @@ +/* + * Siren Depayloader Gst Element + * + * @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpsirendepay.h" + +static GstStaticPadTemplate gst_rtp_siren_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) 16000, " + "encoding-name = (string) \"SIREN\"") + /* This is the default, so the peer doesn't have to specify it */ + /* " "dct-length = (int) 320") */ + ); + +static GstStaticPadTemplate gst_rtp_siren_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-siren, " "dct-length = (int) 320") + ); + +static GstBuffer *gst_rtp_siren_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_siren_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +G_DEFINE_TYPE (GstRTPSirenDepay, gst_rtp_siren_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_siren_depay_class_init (GstRTPSirenDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_siren_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_siren_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_siren_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_siren_depay_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, + "RTP Siren packet depayloader", "Codec/Depayloader/Network/RTP", + "Extracts Siren audio from RTP packets", + "Philippe Kalaf <philippe.kalaf@collabora.co.uk>"); +} + +static void +gst_rtp_siren_depay_init (GstRTPSirenDepay * rtpsirendepay) +{ + +} + +static gboolean +gst_rtp_siren_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstCaps *srccaps; + gboolean ret; + + srccaps = gst_caps_new_simple ("audio/x-siren", + "dct-length", G_TYPE_INT, 320, NULL); + ret = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + + GST_DEBUG ("set caps on source: %" GST_PTR_FORMAT " (ret=%d)", srccaps, ret); + gst_caps_unref (srccaps); + + /* always fixed clock rate of 16000 */ + depayload->clock_rate = 16000; + + return ret; +} + +static GstBuffer * +gst_rtp_siren_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + return outbuf; +} + +gboolean +gst_rtp_siren_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsirendepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_SIREN_DEPAY); +} diff --git a/gst/rtp/gstrtpsirendepay.h b/gst/rtp/gstrtpsirendepay.h new file mode 100755 index 0000000..d1ffb11 --- /dev/null +++ b/gst/rtp/gstrtpsirendepay.h @@ -0,0 +1,59 @@ +/* + * Siren Depayloader Gst Element + * + * @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_SIREN_DEPAY_H__ +#define __GST_RTP_SIREN_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS typedef struct _GstRTPSirenDepay GstRTPSirenDepay; +typedef struct _GstRTPSirenDepayClass GstRTPSirenDepayClass; + +#define GST_TYPE_RTP_SIREN_DEPAY \ + (gst_rtp_siren_depay_get_type()) +#define GST_RTP_SIREN_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SIREN_DEPAY,GstRTPSirenDepay)) +#define GST_RTP_SIREN_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SIREN_DEPAY,GstRTPSirenDepayClass)) +#define GST_IS_RTP_SIREN_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SIREN_DEPAY)) +#define GST_IS_RTP_SIREN_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SIREN_DEPAY)) + + +struct _GstRTPSirenDepay +{ + GstRTPBaseDepayload depayload; + +}; + +struct _GstRTPSirenDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_siren_depay_get_type (void); + +gboolean gst_rtp_siren_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_SIREN_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpsirenpay.c b/gst/rtp/gstrtpsirenpay.c new file mode 100755 index 0000000..2277fec --- /dev/null +++ b/gst/rtp/gstrtpsirenpay.c @@ -0,0 +1,147 @@ +/* + * Siren Payloader Gst Element + * + * @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstrtpsirenpay.h" +#include <gst/rtp/gstrtpbuffer.h> + +GST_DEBUG_CATEGORY_STATIC (rtpsirenpay_debug); +#define GST_CAT_DEFAULT (rtpsirenpay_debug) + +static GstStaticPadTemplate gst_rtp_siren_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-siren, " "dct-length = (int) 320") + ); + +static GstStaticPadTemplate gst_rtp_siren_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 16000, " + "encoding-name = (string) \"SIREN\", " + "bitrate = (string) \"16000\", " "dct-length = (int) 320") + ); + +static gboolean gst_rtp_siren_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); + +G_DEFINE_TYPE (GstRTPSirenPay, gst_rtp_siren_pay, + GST_TYPE_RTP_BASE_AUDIO_PAYLOAD); + +static void +gst_rtp_siren_pay_class_init (GstRTPSirenPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstrtpbasepayload_class->set_caps = gst_rtp_siren_pay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_siren_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_siren_pay_src_template)); + gst_element_class_set_static_metadata (gstelement_class, + "RTP Payloader for Siren Audio", "Codec/Payloader/Network/RTP", + "Packetize Siren audio streams into RTP packets", + "Youness Alaoui <kakaroto@kakaroto.homelinux.net>"); + + GST_DEBUG_CATEGORY_INIT (rtpsirenpay_debug, "rtpsirenpay", 0, + "siren audio RTP payloader"); +} + +static void +gst_rtp_siren_pay_init (GstRTPSirenPay * rtpsirenpay) +{ + GstRTPBasePayload *rtpbasepayload; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + + rtpbasepayload = GST_RTP_BASE_PAYLOAD (rtpsirenpay); + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpsirenpay); + + /* we don't set the payload type, it should be set by the application using + * the pt property or the default 96 will be used */ + rtpbasepayload->clock_rate = 16000; + + /* tell rtpbaseaudiopayload that this is a frame based codec */ + gst_rtp_base_audio_payload_set_frame_based (rtpbaseaudiopayload); +} + +static gboolean +gst_rtp_siren_pay_setcaps (GstRTPBasePayload * rtpbasepayload, GstCaps * caps) +{ + GstRTPSirenPay *rtpsirenpay; + GstRTPBaseAudioPayload *rtpbaseaudiopayload; + gint dct_length; + GstStructure *structure; + const char *payload_name; + + rtpsirenpay = GST_RTP_SIREN_PAY (rtpbasepayload); + rtpbaseaudiopayload = GST_RTP_BASE_AUDIO_PAYLOAD (rtpbasepayload); + + structure = gst_caps_get_structure (caps, 0); + + gst_structure_get_int (structure, "dct-length", &dct_length); + if (dct_length != 320) + goto wrong_dct; + + payload_name = gst_structure_get_name (structure); + if (g_ascii_strcasecmp ("audio/x-siren", payload_name)) + goto wrong_caps; + + gst_rtp_base_payload_set_options (rtpbasepayload, "audio", TRUE, "SIREN", + 16000); + /* set options for this frame based audio codec */ + gst_rtp_base_audio_payload_set_frame_options (rtpbaseaudiopayload, 20, 40); + + return gst_rtp_base_payload_set_outcaps (rtpbasepayload, NULL); + + /* ERRORS */ +wrong_dct: + { + GST_ERROR_OBJECT (rtpsirenpay, "dct-length must be 320, received %d", + dct_length); + return FALSE; + } +wrong_caps: + { + GST_ERROR_OBJECT (rtpsirenpay, "expected audio/x-siren, received %s", + payload_name); + return FALSE; + } +} + +gboolean +gst_rtp_siren_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpsirenpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_SIREN_PAY); +} diff --git a/gst/rtp/gstrtpsirenpay.h b/gst/rtp/gstrtpsirenpay.h new file mode 100755 index 0000000..eaba5b5 --- /dev/null +++ b/gst/rtp/gstrtpsirenpay.h @@ -0,0 +1,57 @@ +/* + * Siren Payloader Gst Element + * + * @author: Youness Alaoui <kakaroto@kakaroto.homelinux.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_SIREN_PAY_H__ +#define __GST_RTP_SIREN_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbaseaudiopayload.h> + +G_BEGIN_DECLS +#define GST_TYPE_RTP_SIREN_PAY \ + (gst_rtp_siren_pay_get_type()) +#define GST_RTP_SIREN_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SIREN_PAY,GstRTPSirenPay)) +#define GST_RTP_SIREN_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SIREN_PAY,GstRTPSirenPayClass)) +#define GST_IS_RTP_SIREN_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SIREN_PAY)) +#define GST_IS_RTP_SIREN_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SIREN_PAY)) +typedef struct _GstRTPSirenPay GstRTPSirenPay; +typedef struct _GstRTPSirenPayClass GstRTPSirenPayClass; + +struct _GstRTPSirenPay +{ + GstRTPBaseAudioPayload audiopayload; +}; + +struct _GstRTPSirenPayClass +{ + GstRTPBaseAudioPayloadClass parent_class; +}; + +GType gst_rtp_siren_pay_get_type (void); + +gboolean gst_rtp_siren_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS +#endif /* __GST_RTP_SIREN_PAY_H__ */ diff --git a/gst/rtp/gstrtpspeexdepay.c b/gst/rtp/gstrtpspeexdepay.c new file mode 100755 index 0000000..efe7357 --- /dev/null +++ b/gst/rtp/gstrtpspeexdepay.c @@ -0,0 +1,225 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> +#include <stdlib.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpspeexdepay.h" + +/* RtpSPEEXDepay signals and args */ +enum +{ + /* FILL ME */ + LAST_SIGNAL +}; + +enum +{ + PROP_0 +}; + +static GstStaticPadTemplate gst_rtp_speex_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [6000, 48000], " + "encoding-name = (string) \"SPEEX\"") + /* "encoding-params = (string) \"1\"" */ + ); + +static GstStaticPadTemplate gst_rtp_speex_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-speex") + ); + +static GstBuffer *gst_rtp_speex_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_speex_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); + +G_DEFINE_TYPE (GstRtpSPEEXDepay, gst_rtp_speex_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void +gst_rtp_speex_depay_class_init (GstRtpSPEEXDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_speex_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_speex_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_speex_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_speex_depay_sink_template)); + gst_element_class_set_static_metadata (gstelement_class, + "RTP Speex depayloader", "Codec/Depayloader/Network/RTP", + "Extracts Speex audio from RTP packets", + "Edgard Lima <edgard.lima@indt.org.br>"); +} + +static void +gst_rtp_speex_depay_init (GstRtpSPEEXDepay * rtpspeexdepay) +{ +} + +static gint +gst_rtp_speex_depay_get_mode (gint rate) +{ + if (rate > 25000) + return 2; + else if (rate > 12500) + return 1; + else + return 0; +} + +/* len 4 bytes LE, + * vendor string (len bytes), + * user_len 4 (0) bytes LE + */ +static const gchar gst_rtp_speex_comment[] = + "\045\0\0\0Depayloaded with GStreamer speexdepay\0\0\0\0"; + +static gboolean +gst_rtp_speex_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpSPEEXDepay *rtpspeexdepay; + gint clock_rate, nb_channels; + GstBuffer *buf; + GstMapInfo map; + guint8 *data; + const gchar *params; + GstCaps *srccaps; + gboolean res; + + rtpspeexdepay = GST_RTP_SPEEX_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + goto no_clockrate; + depayload->clock_rate = clock_rate; + + if (!(params = gst_structure_get_string (structure, "encoding-params"))) + nb_channels = 1; + else { + nb_channels = atoi (params); + } + + /* construct minimal header and comment packet for the decoder */ + buf = gst_buffer_new_and_alloc (80); + gst_buffer_map (buf, &map, GST_MAP_WRITE); + data = map.data; + memcpy (data, "Speex ", 8); + data += 8; + memcpy (data, "1.1.12", 7); + data += 20; + GST_WRITE_UINT32_LE (data, 1); /* version */ + data += 4; + GST_WRITE_UINT32_LE (data, 80); /* header_size */ + data += 4; + GST_WRITE_UINT32_LE (data, clock_rate); /* rate */ + data += 4; + GST_WRITE_UINT32_LE (data, gst_rtp_speex_depay_get_mode (clock_rate)); /* mode */ + data += 4; + GST_WRITE_UINT32_LE (data, 4); /* mode_bitstream_version */ + data += 4; + GST_WRITE_UINT32_LE (data, nb_channels); /* nb_channels */ + data += 4; + GST_WRITE_UINT32_LE (data, -1); /* bitrate */ + data += 4; + GST_WRITE_UINT32_LE (data, 0xa0); /* frame_size */ + data += 4; + GST_WRITE_UINT32_LE (data, 0); /* VBR */ + data += 4; + GST_WRITE_UINT32_LE (data, 1); /* frames_per_packet */ + data += 4; + GST_WRITE_UINT32_LE (data, 0); /* extra_headers */ + data += 4; + GST_WRITE_UINT32_LE (data, 0); /* reserved1 */ + data += 4; + GST_WRITE_UINT32_LE (data, 0); /* reserved2 */ + gst_buffer_unmap (buf, &map); + + srccaps = gst_caps_new_empty_simple ("audio/x-speex"); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpspeexdepay), buf); + + buf = gst_buffer_new_and_alloc (sizeof (gst_rtp_speex_comment)); + gst_buffer_fill (buf, 0, gst_rtp_speex_comment, + sizeof (gst_rtp_speex_comment)); + + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpspeexdepay), buf); + + return res; + + /* ERRORS */ +no_clockrate: + { + GST_DEBUG_OBJECT (depayload, "no clock-rate specified"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_speex_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstBuffer *outbuf = NULL; + GstRTPBuffer rtp = { NULL }; + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + GST_DEBUG ("process : got %" G_GSIZE_FORMAT " bytes, mark %d ts %u seqn %d", + gst_buffer_get_size (buf), + gst_rtp_buffer_get_marker (&rtp), + gst_rtp_buffer_get_timestamp (&rtp), gst_rtp_buffer_get_seq (&rtp)); + + /* nothing special to be done */ + outbuf = gst_rtp_buffer_get_payload_buffer (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (outbuf) + GST_BUFFER_DURATION (outbuf) = 20 * GST_MSECOND; + + return outbuf; +} + +gboolean +gst_rtp_speex_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpspeexdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_DEPAY); +} diff --git a/gst/rtp/gstrtpspeexdepay.h b/gst/rtp/gstrtpspeexdepay.h new file mode 100755 index 0000000..f78d32c --- /dev/null +++ b/gst/rtp/gstrtpspeexdepay.h @@ -0,0 +1,53 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + +#ifndef __GST_RTP_SPEEX_DEPAY_H__ +#define __GST_RTP_SPEEX_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpSPEEXDepay GstRtpSPEEXDepay; +typedef struct _GstRtpSPEEXDepayClass GstRtpSPEEXDepayClass; + +#define GST_TYPE_RTP_SPEEX_DEPAY \ + (gst_rtp_speex_depay_get_type()) +#define GST_RTP_SPEEX_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SPEEX_DEPAY,GstRtpSPEEXDepay)) +#define GST_RTP_SPEEX_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SPEEX_DEPAY,GstRtpSPEEXDepayClass)) +#define GST_IS_RTP_SPEEX_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SPEEX_DEPAY)) +#define GST_IS_RTP_SPEEX_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SPEEX_DEPAY)) + +struct _GstRtpSPEEXDepay +{ + GstRTPBaseDepayload depayload; +}; + +struct _GstRtpSPEEXDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_speex_depay_get_type (void); + +gboolean gst_rtp_speex_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_SPEEX_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpspeexpay.c b/gst/rtp/gstrtpspeexpay.c new file mode 100755 index 0000000..1f83866 --- /dev/null +++ b/gst/rtp/gstrtpspeexpay.c @@ -0,0 +1,352 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdlib.h> +#include <string.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpspeexpay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpspeexpay_debug); +#define GST_CAT_DEFAULT (rtpspeexpay_debug) + +static GstStaticPadTemplate gst_rtp_speex_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-speex, " + "rate = (int) [ 6000, 48000 ], " "channels = (int) 1") + ); + +static GstStaticPadTemplate gst_rtp_speex_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [ 6000, 48000 ], " + "encoding-name = (string) \"SPEEX\", " + "encoding-params = (string) \"1\"") + ); + +static GstStateChangeReturn gst_rtp_speex_pay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstCaps *gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload, + GstPad * pad, GstCaps * filter); +static GstFlowReturn gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); + +#define gst_rtp_speex_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpSPEEXPay, gst_rtp_speex_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static void +gst_rtp_speex_pay_class_init (GstRtpSPEEXPayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstelement_class->change_state = gst_rtp_speex_pay_change_state; + + gstrtpbasepayload_class->set_caps = gst_rtp_speex_pay_setcaps; + gstrtpbasepayload_class->get_caps = gst_rtp_speex_pay_getcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_speex_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_speex_pay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_speex_pay_src_template)); + gst_element_class_set_static_metadata (gstelement_class, + "RTP Speex payloader", "Codec/Payloader/Network/RTP", + "Payload-encodes Speex audio into a RTP packet", + "Edgard Lima <edgard.lima@indt.org.br>"); + + GST_DEBUG_CATEGORY_INIT (rtpspeexpay_debug, "rtpspeexpay", 0, + "Speex RTP Payloader"); +} + +static void +gst_rtp_speex_pay_init (GstRtpSPEEXPay * rtpspeexpay) +{ + GST_RTP_BASE_PAYLOAD (rtpspeexpay)->clock_rate = 8000; + GST_RTP_BASE_PAYLOAD_PT (rtpspeexpay) = 110; /* Create String */ +} + +static gboolean +gst_rtp_speex_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + /* don't configure yet, we wait for the ident packet */ + return TRUE; +} + + +static GstCaps * +gst_rtp_speex_pay_getcaps (GstRTPBasePayload * payload, GstPad * pad, + GstCaps * filter) +{ + GstCaps *otherpadcaps; + GstCaps *caps; + + otherpadcaps = gst_pad_get_allowed_caps (payload->srcpad); + caps = gst_pad_get_pad_template_caps (pad); + + if (otherpadcaps) { + if (!gst_caps_is_empty (otherpadcaps)) { + GstStructure *ps; + GstStructure *s; + gint clock_rate; + + ps = gst_caps_get_structure (otherpadcaps, 0); + caps = gst_caps_make_writable (caps); + s = gst_caps_get_structure (caps, 0); + + if (gst_structure_get_int (ps, "clock-rate", &clock_rate)) { + gst_structure_fixate_field_nearest_int (s, "rate", clock_rate); + } + } + gst_caps_unref (otherpadcaps); + } + + if (filter) { + GstCaps *tcaps = caps; + + caps = gst_caps_intersect_full (filter, tcaps, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (tcaps); + } + + return caps; +} + +static gboolean +gst_rtp_speex_pay_parse_ident (GstRtpSPEEXPay * rtpspeexpay, + const guint8 * data, guint size) +{ + guint32 version, header_size, rate, mode, nb_channels; + GstRTPBasePayload *payload; + gchar *cstr; + gboolean res; + + /* we need the header string (8), the version string (20), the version + * and the header length. */ + if (size < 36) + goto too_small; + + if (!g_str_has_prefix ((const gchar *) data, "Speex ")) + goto wrong_header; + + /* skip header and version string */ + data += 28; + + version = GST_READ_UINT32_LE (data); + if (version != 1) + goto wrong_version; + + data += 4; + /* ensure sizes */ + header_size = GST_READ_UINT32_LE (data); + if (header_size < 80) + goto header_too_small; + + if (size < header_size) + goto payload_too_small; + + data += 4; + rate = GST_READ_UINT32_LE (data); + data += 4; + mode = GST_READ_UINT32_LE (data); + data += 8; + nb_channels = GST_READ_UINT32_LE (data); + + GST_DEBUG_OBJECT (rtpspeexpay, "rate %d, mode %d, nb_channels %d", + rate, mode, nb_channels); + + payload = GST_RTP_BASE_PAYLOAD (rtpspeexpay); + + gst_rtp_base_payload_set_options (payload, "audio", FALSE, "SPEEX", rate); + cstr = g_strdup_printf ("%d", nb_channels); + res = gst_rtp_base_payload_set_outcaps (payload, "encoding-params", + G_TYPE_STRING, cstr, NULL); + g_free (cstr); + + return res; + + /* ERRORS */ +too_small: + { + GST_DEBUG_OBJECT (rtpspeexpay, + "ident packet too small, need at least 32 bytes"); + return FALSE; + } +wrong_header: + { + GST_DEBUG_OBJECT (rtpspeexpay, + "ident packet does not start with \"Speex \""); + return FALSE; + } +wrong_version: + { + GST_DEBUG_OBJECT (rtpspeexpay, "can only handle version 1, have version %d", + version); + return FALSE; + } +header_too_small: + { + GST_DEBUG_OBJECT (rtpspeexpay, + "header size too small, need at least 80 bytes, " "got only %d", + header_size); + return FALSE; + } +payload_too_small: + { + GST_DEBUG_OBJECT (rtpspeexpay, + "payload too small, need at least %d bytes, got only %d", header_size, + size); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_speex_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpSPEEXPay *rtpspeexpay; + guint payload_len; + GstMapInfo map; + GstBuffer *outbuf; + guint8 *payload; + GstClockTime timestamp, duration; + GstFlowReturn ret; + GstRTPBuffer rtp = { NULL }; + + rtpspeexpay = GST_RTP_SPEEX_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + + switch (rtpspeexpay->packet) { + case 0: + /* ident packet. We need to parse the headers to construct the RTP + * properties. */ + if (!gst_rtp_speex_pay_parse_ident (rtpspeexpay, map.data, map.size)) + goto parse_error; + + ret = GST_FLOW_OK; + goto done; + case 1: + /* comment packet, we ignore it */ + ret = GST_FLOW_OK; + goto done; + default: + /* other packets go in the payload */ + break; + } + + if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_GAP)) { + ret = GST_FLOW_OK; + goto done; + } + + timestamp = GST_BUFFER_TIMESTAMP (buffer); + duration = GST_BUFFER_DURATION (buffer); + + /* FIXME, only one SPEEX frame per RTP packet for now */ + payload_len = map.size; + + outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0); + /* FIXME, assert for now */ + g_assert (payload_len <= GST_RTP_BASE_PAYLOAD_MTU (rtpspeexpay)); + + /* copy timestamp and duration */ + GST_BUFFER_TIMESTAMP (outbuf) = timestamp; + GST_BUFFER_DURATION (outbuf) = duration; + + gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp); + /* get payload */ + payload = gst_rtp_buffer_get_payload (&rtp); + + /* copy data in payload */ + memcpy (&payload[0], map.data, map.size); + + gst_rtp_buffer_unmap (&rtp); + + ret = gst_rtp_base_payload_push (basepayload, outbuf); + +done: + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + + rtpspeexpay->packet++; + + return ret; + + /* ERRORS */ +parse_error: + { + GST_ELEMENT_ERROR (rtpspeexpay, STREAM, DECODE, (NULL), + ("Error parsing first identification packet.")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } +} + +static GstStateChangeReturn +gst_rtp_speex_pay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpSPEEXPay *rtpspeexpay; + GstStateChangeReturn ret; + + rtpspeexpay = GST_RTP_SPEEX_PAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + rtpspeexpay->packet = 0; + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_speex_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpspeexpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_SPEEX_PAY); +} diff --git a/gst/rtp/gstrtpspeexpay.h b/gst/rtp/gstrtpspeexpay.h new file mode 100755 index 0000000..253978e --- /dev/null +++ b/gst/rtp/gstrtpspeexpay.h @@ -0,0 +1,56 @@ +/* GStreamer + * Copyright (C) <2005> Edgard Lima <edgard.lima@indt.org.br> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more + */ + + +#ifndef __GST_RTP_SPEEX_PAY_H__ +#define __GST_RTP_SPEEX_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +typedef struct _GstRtpSPEEXPay GstRtpSPEEXPay; +typedef struct _GstRtpSPEEXPayClass GstRtpSPEEXPayClass; + +#define GST_TYPE_RTP_SPEEX_PAY \ + (gst_rtp_speex_pay_get_type()) +#define GST_RTP_SPEEX_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SPEEX_PAY,GstRtpSPEEXPay)) +#define GST_RTP_SPEEX_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SPEEX_PAY,GstRtpSPEEXPayClass)) +#define GST_IS_RTP_SPEEX_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SPEEX_PAY)) +#define GST_IS_RTP_SPEEX_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SPEEX_PAY)) + +struct _GstRtpSPEEXPay +{ + GstRTPBasePayload payload; + + guint64 packet; +}; + +struct _GstRtpSPEEXPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_speex_pay_get_type (void); + +gboolean gst_rtp_speex_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_SPEEX_PAY_H__ */ diff --git a/gst/rtp/gstrtpstreamdepay.c b/gst/rtp/gstrtpstreamdepay.c new file mode 100755 index 0000000..26a2e32 --- /dev/null +++ b/gst/rtp/gstrtpstreamdepay.c @@ -0,0 +1,219 @@ +/* GStreamer + * Copyright (C) 2013 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpstreamdepay + * + * Implements stream depayloading of RTP and RTCP packets for connection-oriented + * transport protocols according to RFC4571. + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch-1.0 audiotestsrc ! "audio/x-raw,rate=48000" ! vorbisenc ! rtpvorbispay config-interval=1 ! rtpstreampay ! tcpserversink port=5678 + * gst-launch-1.0 tcpclientsrc port=5678 host=127.0.0.1 do-timestamp=true ! "application/x-rtp-stream,media=audio,clock-rate=48000,encoding-name=VORBIS" ! rtpstreamdepay ! rtpvorbisdepay ! decodebin ! audioconvert ! audioresample ! autoaudiosink + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstrtpstreamdepay.h" + +GST_DEBUG_CATEGORY (gst_rtp_stream_depay_debug); +#define GST_CAT_DEFAULT gst_rtp_stream_depay_debug + +static GstStaticPadTemplate src_template = + GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp; application/x-rtcp;" + "application/x-srtp; application/x-srtcp") + ); + +static GstStaticPadTemplate sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp-stream; application/x-rtcp-stream;" + "application/x-srtp-stream; application/x-srtcp-stream") + ); + +#define parent_class gst_rtp_stream_depay_parent_class +G_DEFINE_TYPE (GstRtpStreamDepay, gst_rtp_stream_depay, GST_TYPE_BASE_PARSE); + +static gboolean gst_rtp_stream_depay_set_sink_caps (GstBaseParse * parse, + GstCaps * caps); +static GstCaps *gst_rtp_stream_depay_get_sink_caps (GstBaseParse * parse, + GstCaps * filter); +static GstFlowReturn gst_rtp_stream_depay_handle_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, gint * skipsize); + +static void +gst_rtp_stream_depay_class_init (GstRtpStreamDepayClass * klass) +{ + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GstBaseParseClass *parse_class = GST_BASE_PARSE_CLASS (klass); + + GST_DEBUG_CATEGORY_INIT (gst_rtp_stream_depay_debug, "rtpstreamdepay", 0, + "RTP stream depayloader"); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Stream Depayloading", "Codec/Depayloader/Network", + "Depayloads RTP/RTCP packets for streaming protocols according to RFC4571", + "Sebastian Dröge <sebastian@centricular.com>"); + + parse_class->set_sink_caps = + GST_DEBUG_FUNCPTR (gst_rtp_stream_depay_set_sink_caps); + parse_class->get_sink_caps = + GST_DEBUG_FUNCPTR (gst_rtp_stream_depay_get_sink_caps); + parse_class->handle_frame = + GST_DEBUG_FUNCPTR (gst_rtp_stream_depay_handle_frame); +} + +static void +gst_rtp_stream_depay_init (GstRtpStreamDepay * self) +{ + gst_base_parse_set_min_frame_size (GST_BASE_PARSE (self), 2); +} + +static gboolean +gst_rtp_stream_depay_set_sink_caps (GstBaseParse * parse, GstCaps * caps) +{ + GstCaps *othercaps; + GstStructure *structure; + gboolean ret; + + othercaps = gst_caps_copy (caps); + structure = gst_caps_get_structure (othercaps, 0); + + if (gst_structure_has_name (structure, "application/x-rtp-stream")) + gst_structure_set_name (structure, "application/x-rtp"); + else if (gst_structure_has_name (structure, "application/x-rtcp-stream")) + gst_structure_set_name (structure, "application/x-rtcp"); + else if (gst_structure_has_name (structure, "application/x-srtp-stream")) + gst_structure_set_name (structure, "application/x-srtp"); + else + gst_structure_set_name (structure, "application/x-srtcp"); + + ret = gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), othercaps); + gst_caps_unref (othercaps); + + return ret; +} + +static GstCaps * +gst_rtp_stream_depay_get_sink_caps (GstBaseParse * parse, GstCaps * filter) +{ + GstCaps *peerfilter = NULL, *peercaps, *templ; + GstCaps *res; + GstStructure *structure; + guint i, n; + + if (filter) { + peerfilter = gst_caps_copy (filter); + n = gst_caps_get_size (peerfilter); + for (i = 0; i < n; i++) { + structure = gst_caps_get_structure (peerfilter, i); + + if (gst_structure_has_name (structure, "application/x-rtp-stream")) + gst_structure_set_name (structure, "application/x-rtp"); + else if (gst_structure_has_name (structure, "application/x-rtcp-stream")) + gst_structure_set_name (structure, "application/x-rtcp"); + else if (gst_structure_has_name (structure, "application/x-srtp-stream")) + gst_structure_set_name (structure, "application/x-srtp"); + else + gst_structure_set_name (structure, "application/x-srtcp"); + } + } + + templ = gst_pad_get_pad_template_caps (GST_BASE_PARSE_SINK_PAD (parse)); + peercaps = + gst_pad_peer_query_caps (GST_BASE_PARSE_SRC_PAD (parse), peerfilter); + + if (peercaps) { + /* Rename structure names */ + peercaps = gst_caps_make_writable (peercaps); + n = gst_caps_get_size (peercaps); + for (i = 0; i < n; i++) { + structure = gst_caps_get_structure (peercaps, i); + + if (gst_structure_has_name (structure, "application/x-rtp")) + gst_structure_set_name (structure, "application/x-rtp-stream"); + else if (gst_structure_has_name (structure, "application/x-rtcp")) + gst_structure_set_name (structure, "application/x-rtcp-stream"); + else if (gst_structure_has_name (structure, "application/x-srtp")) + gst_structure_set_name (structure, "application/x-srtp-stream"); + else + gst_structure_set_name (structure, "application/x-srtcp-stream"); + } + + res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (peercaps); + } else { + res = templ; + } + + if (filter) { + GstCaps *intersection; + + intersection = + gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (res); + res = intersection; + + gst_caps_unref (peerfilter); + } + + return res; +} + +static GstFlowReturn +gst_rtp_stream_depay_handle_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, gint * skipsize) +{ + gsize buf_size; + guint16 size; + + if (gst_buffer_extract (frame->buffer, 0, &size, 2) != 2) + return GST_FLOW_ERROR; + + size = GUINT16_FROM_BE (size); + buf_size = gst_buffer_get_size (frame->buffer); + + /* Need more data */ + if (size + 2 > buf_size) + return GST_FLOW_OK; + + frame->out_buffer = + gst_buffer_copy_region (frame->buffer, GST_BUFFER_COPY_ALL, 2, size); + + return gst_base_parse_finish_frame (parse, frame, size + 2); +} + +gboolean +gst_rtp_stream_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpstreamdepay", + GST_RANK_NONE, GST_TYPE_RTP_STREAM_DEPAY); +} diff --git a/gst/rtp/gstrtpstreamdepay.h b/gst/rtp/gstrtpstreamdepay.h new file mode 100755 index 0000000..b6011cb --- /dev/null +++ b/gst/rtp/gstrtpstreamdepay.h @@ -0,0 +1,57 @@ +/* GStreamer + * Copyright (C) 2013 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_STREAM_DEPAY_H__ +#define __GST_RTP_STREAM_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstbaseparse.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_STREAM_DEPAY \ + (gst_rtp_stream_depay_get_type()) +#define GST_RTP_STREAM_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_STREAM_DEPAY,GstRtpStreamDepay)) +#define GST_RTP_STREAM_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_STREAM_DEPAY,GstRtpStreamDepayClass)) +#define GST_IS_RTP_STREAM_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_STREAM_DEPAY)) +#define GST_IS_RTP_STREAM_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_STREAM_DEPAY)) + +typedef struct _GstRtpStreamDepay GstRtpStreamDepay; +typedef struct _GstRtpStreamDepayClass GstRtpStreamDepayClass; + +struct _GstRtpStreamDepay +{ + GstBaseParse parent; +}; + +struct _GstRtpStreamDepayClass +{ + GstBaseParseClass parent_class; +}; + +GType gst_rtp_stream_depay_get_type (void); +gboolean gst_rtp_stream_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif diff --git a/gst/rtp/gstrtpstreampay.c b/gst/rtp/gstrtpstreampay.c new file mode 100755 index 0000000..117f678 --- /dev/null +++ b/gst/rtp/gstrtpstreampay.c @@ -0,0 +1,287 @@ +/* + * GStreamer + * Copyright (C) 2013 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +/** + * SECTION:element-rtpstreampay + * + * Implements stream payloading of RTP and RTCP packets for connection-oriented + * transport protocols according to RFC4571. + * <refsect2> + * <title>Example launch line</title> + * |[ + * gst-launch-1.0 audiotestsrc ! "audio/x-raw,rate=48000" ! vorbisenc ! rtpvorbispay config-interval=1 ! rtpstreampay ! tcpserversink port=5678 + * gst-launch-1.0 tcpclientsrc port=5678 host=127.0.0.1 do-timestamp=true ! "application/x-rtp-stream,media=audio,clock-rate=48000,encoding-name=VORBIS" ! rtpstreamdepay ! rtpvorbisdepay ! decodebin ! audioconvert ! audioresample ! autoaudiosink + * ]| + * </refsect2> + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "gstrtpstreampay.h" + +#define GST_CAT_DEFAULT gst_rtp_stream_pay_debug +GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); + +static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp; application/x-rtcp; " + "application/x-srtp; application/x-srtcp") + ); + +static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp-stream; application/x-rtcp-stream; " + "application/x-srtp-stream; application/x-srtcp-stream") + ); + +#define parent_class gst_rtp_stream_pay_parent_class +G_DEFINE_TYPE (GstRtpStreamPay, gst_rtp_stream_pay, GST_TYPE_ELEMENT); + +static gboolean gst_rtp_stream_pay_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query); +static GstFlowReturn gst_rtp_stream_pay_sink_chain (GstPad * pad, + GstObject * parent, GstBuffer * inbuf); +static gboolean gst_rtp_stream_pay_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event); + +static void +gst_rtp_stream_pay_class_init (GstRtpStreamPayClass * klass) +{ + GstElementClass *gstelement_class; + + GST_DEBUG_CATEGORY_INIT (gst_rtp_stream_pay_debug, "rtpstreampay", 0, + "RTP stream payloader"); + + gstelement_class = (GstElementClass *) klass; + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Stream Payloading", "Codec/Payloader/Network", + "Payloads RTP/RTCP packets for streaming protocols according to RFC4571", + "Sebastian Dröge <sebastian@centricular.com>"); + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&sink_template)); +} + +static void +gst_rtp_stream_pay_init (GstRtpStreamPay * self) +{ + self->sinkpad = gst_pad_new_from_static_template (&sink_template, "sink"); + gst_pad_set_chain_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rtp_stream_pay_sink_chain)); + gst_pad_set_event_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rtp_stream_pay_sink_event)); + gst_pad_set_query_function (self->sinkpad, + GST_DEBUG_FUNCPTR (gst_rtp_stream_pay_sink_query)); + gst_element_add_pad (GST_ELEMENT (self), self->sinkpad); + + self->srcpad = gst_pad_new_from_static_template (&src_template, "src"); + gst_pad_use_fixed_caps (self->srcpad); + gst_element_add_pad (GST_ELEMENT (self), self->srcpad); +} + +static GstCaps * +gst_rtp_stream_pay_sink_get_caps (GstRtpStreamPay * self, GstCaps * filter) +{ + GstCaps *peerfilter = NULL, *peercaps, *templ; + GstCaps *res; + GstStructure *structure; + guint i, n; + + if (filter) { + peerfilter = gst_caps_copy (filter); + n = gst_caps_get_size (peerfilter); + for (i = 0; i < n; i++) { + structure = gst_caps_get_structure (peerfilter, i); + + if (gst_structure_has_name (structure, "application/x-rtp")) + gst_structure_set_name (structure, "application/x-rtp-stream"); + else if (gst_structure_has_name (structure, "application/x-rtcp")) + gst_structure_set_name (structure, "application/x-rtcp-stream"); + else if (gst_structure_has_name (structure, "application/x-srtp")) + gst_structure_set_name (structure, "application/x-srtp-stream"); + else + gst_structure_set_name (structure, "application/x-srtcp-stream"); + } + } + + templ = gst_pad_get_pad_template_caps (self->sinkpad); + peercaps = gst_pad_peer_query_caps (self->srcpad, peerfilter); + + if (peercaps) { + /* Rename structure names */ + peercaps = gst_caps_make_writable (peercaps); + n = gst_caps_get_size (peercaps); + for (i = 0; i < n; i++) { + structure = gst_caps_get_structure (peercaps, i); + + if (gst_structure_has_name (structure, "application/x-rtp-stream")) + gst_structure_set_name (structure, "application/x-rtp"); + else if (gst_structure_has_name (structure, "application/x-rtcp-stream")) + gst_structure_set_name (structure, "application/x-rtcp"); + else if (gst_structure_has_name (structure, "application/x-srtp-stream")) + gst_structure_set_name (structure, "application/x-srtp"); + else + gst_structure_set_name (structure, "application/x-srtcp"); + } + + res = gst_caps_intersect_full (peercaps, templ, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (peercaps); + } else { + res = templ; + } + + if (filter) { + GstCaps *intersection; + + intersection = + gst_caps_intersect_full (filter, res, GST_CAPS_INTERSECT_FIRST); + gst_caps_unref (res); + res = intersection; + + gst_caps_unref (peerfilter); + } + + return res; +} + +static gboolean +gst_rtp_stream_pay_sink_query (GstPad * pad, GstObject * parent, + GstQuery * query) +{ + GstRtpStreamPay *self = GST_RTP_STREAM_PAY (parent); + gboolean ret; + + GST_LOG_OBJECT (pad, "Handling query of type '%s'", + gst_query_type_get_name (GST_QUERY_TYPE (query))); + + switch (GST_QUERY_TYPE (query)) { + case GST_QUERY_CAPS: + { + GstCaps *caps; + + gst_query_parse_caps (query, &caps); + caps = gst_rtp_stream_pay_sink_get_caps (self, caps); + gst_query_set_caps_result (query, caps); + gst_caps_unref (caps); + ret = TRUE; + break; + } + default: + ret = gst_pad_query_default (pad, parent, query); + } + + return ret; +} + +static gboolean +gst_rtp_stream_pay_sink_set_caps (GstRtpStreamPay * self, GstCaps * caps) +{ + GstCaps *othercaps; + GstStructure *structure; + gboolean ret; + + othercaps = gst_caps_copy (caps); + structure = gst_caps_get_structure (othercaps, 0); + + if (gst_structure_has_name (structure, "application/x-rtp")) + gst_structure_set_name (structure, "application/x-rtp-stream"); + else if (gst_structure_has_name (structure, "application/x-rtcp")) + gst_structure_set_name (structure, "application/x-rtcp-stream"); + else if (gst_structure_has_name (structure, "application/x-srtp")) + gst_structure_set_name (structure, "application/x-srtp-stream"); + else + gst_structure_set_name (structure, "application/x-srtcp-stream"); + + ret = gst_pad_set_caps (self->srcpad, othercaps); + gst_caps_unref (othercaps); + + return ret; +} + +static gboolean +gst_rtp_stream_pay_sink_event (GstPad * pad, GstObject * parent, + GstEvent * event) +{ + GstRtpStreamPay *self = GST_RTP_STREAM_PAY (parent); + gboolean ret; + + GST_LOG_OBJECT (pad, "Got %s event", GST_EVENT_TYPE_NAME (event)); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_CAPS: + { + GstCaps *caps; + + gst_event_parse_caps (event, &caps); + ret = gst_rtp_stream_pay_sink_set_caps (self, caps); + gst_event_unref (event); + break; + } + default: + ret = gst_pad_event_default (pad, parent, event); + break; + } + + return ret; +} + +static GstFlowReturn +gst_rtp_stream_pay_sink_chain (GstPad * pad, GstObject * parent, + GstBuffer * inbuf) +{ + GstRtpStreamPay *self = GST_RTP_STREAM_PAY (parent); + GstBuffer *outbuf; + gsize size; + guint8 size16[2]; + + size = gst_buffer_get_size (inbuf); + if (size > G_MAXUINT16) { + GST_ELEMENT_ERROR (self, CORE, FAILED, (NULL), + ("Only buffers up to %d bytes supported, got %" G_GSIZE_FORMAT, + G_MAXUINT16, size)); + gst_buffer_unref (inbuf); + return GST_FLOW_ERROR; + } + + outbuf = gst_buffer_new_and_alloc (2); + + GST_WRITE_UINT16_BE (size16, size); + gst_buffer_fill (outbuf, 0, size16, 2); + + gst_buffer_copy_into (outbuf, inbuf, GST_BUFFER_COPY_ALL, 0, -1); + + gst_buffer_unref (inbuf); + + return gst_pad_push (self->srcpad, outbuf); +} + +gboolean +gst_rtp_stream_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpstreampay", + GST_RANK_NONE, GST_TYPE_RTP_STREAM_PAY); +} diff --git a/gst/rtp/gstrtpstreampay.h b/gst/rtp/gstrtpstreampay.h new file mode 100755 index 0000000..a9436a8 --- /dev/null +++ b/gst/rtp/gstrtpstreampay.h @@ -0,0 +1,54 @@ +/* + * GStreamer + * Copyright (C) 2013 Sebastian Dröge <sebastian@centricular.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_STREAM_PAY_H__ +#define __GST_RTP_STREAM_PAY_H__ + +#include <gst/gst.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_STREAM_PAY (gst_rtp_stream_pay_get_type()) +#define GST_RTP_STREAM_PAY(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_STREAM_PAY,GstRtpStreamPay)) +#define GST_IS_RTP_STREAM_PAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_STREAM_PAY)) +#define GST_RTP_STREAM_PAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass) ,GST_TYPE_RTP_STREAM_PAY,GstRtpStreamPayClass)) +#define GST_IS_RTP_STREAM_PAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass) ,GST_TYPE_RTP_STREAM_PAY)) +#define GST_RTP_STREAM_PAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj) ,GST_TYPE_RTP_STREAM_PAY,GstRtpStreamPayClass)) + +typedef struct _GstRtpStreamPay GstRtpStreamPay; +typedef struct _GstRtpStreamPayClass GstRtpStreamPayClass; + +struct _GstRtpStreamPay { + GstElement parent; + + GstPad *srcpad, *sinkpad; +}; + +struct _GstRtpStreamPayClass { + GstElementClass parent_class; +}; + +GType gst_rtp_stream_pay_get_type (void); + +gboolean gst_rtp_stream_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_STREAM_PAY_H__ */ diff --git a/gst/rtp/gstrtpsv3vdepay.c b/gst/rtp/gstrtpsv3vdepay.c new file mode 100755 index 0000000..7e3bc89 --- /dev/null +++ b/gst/rtp/gstrtpsv3vdepay.c @@ -0,0 +1,323 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> +#include "gstrtpsv3vdepay.h" + +GST_DEBUG_CATEGORY (rtpsv3vdepay_debug); +#define GST_CAT_DEFAULT rtpsv3vdepay_debug + +static GstStaticPadTemplate gst_rtp_sv3v_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-svq, " "svqversion = (int) 3") + ); + +static GstStaticPadTemplate gst_rtp_sv3v_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " + "encoding-name = (string) { \"X-SV3V-ES\", \"X-SORENSON-VIDEO\" , \"X-SORENSONVIDEO\" , \"X-SorensonVideo\" }") + ); + +#define gst_rtp_sv3v_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpSV3VDepay, gst_rtp_sv3v_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static void gst_rtp_sv3v_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_sv3v_depay_change_state (GstElement * + element, GstStateChange transition); + +static GstBuffer *gst_rtp_sv3v_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +gboolean gst_rtp_sv3v_depay_setcaps (GstRTPBaseDepayload * filter, + GstCaps * caps); + +static void +gst_rtp_sv3v_depay_class_init (GstRtpSV3VDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstrtpbasedepayload_class->process = gst_rtp_sv3v_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_sv3v_depay_setcaps; + + gobject_class->finalize = gst_rtp_sv3v_depay_finalize; + + gstelement_class->change_state = gst_rtp_sv3v_depay_change_state; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_sv3v_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_sv3v_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP SVQ3 depayloader", "Codec/Depayloader/Network/RTP", + "Extracts SVQ3 video from RTP packets (no RFC)", + "Wim Taymans <wim.taymans@gmail.com>"); +} + +static void +gst_rtp_sv3v_depay_init (GstRtpSV3VDepay * rtpsv3vdepay) +{ + rtpsv3vdepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_sv3v_depay_finalize (GObject * object) +{ + GstRtpSV3VDepay *rtpsv3vdepay; + + rtpsv3vdepay = GST_RTP_SV3V_DEPAY (object); + + g_object_unref (rtpsv3vdepay->adapter); + rtpsv3vdepay->adapter = NULL; + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* only on the sink */ +gboolean +gst_rtp_sv3v_depay_setcaps (GstRTPBaseDepayload * filter, GstCaps * caps) +{ + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint clock_rate; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + filter->clock_rate = clock_rate; + + /* will set caps later */ + + return TRUE; +} + +static GstBuffer * +gst_rtp_sv3v_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpSV3VDepay *rtpsv3vdepay; + static struct + { + guint width, height; + } resolutions[7] = { + { + 160, 128}, { + 128, 96}, { + 176, 144}, { + 352, 288}, { + 704, 576}, { + 240, 180}, { + 320, 240} + }; + gint payload_len; + guint8 *payload; + gboolean M; + gboolean C, S, E; + GstBuffer *outbuf = NULL; + guint16 seq; + GstRTPBuffer rtp = { NULL }; + + rtpsv3vdepay = GST_RTP_SV3V_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + /* flush on sequence number gaps */ + seq = gst_rtp_buffer_get_seq (&rtp); + + GST_DEBUG ("timestamp %" GST_TIME_FORMAT ", sequence number:%d", + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), seq); + + if (seq != rtpsv3vdepay->nextseq) { + GST_DEBUG ("Sequence discontinuity, clearing adapter"); + gst_adapter_clear (rtpsv3vdepay->adapter); + } + rtpsv3vdepay->nextseq = seq + 1; + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + if (payload_len < 3) + goto bad_packet; + + payload = gst_rtp_buffer_get_payload (&rtp); + + M = gst_rtp_buffer_get_marker (&rtp); + + /* This is all a guess: + * 1 1 1 1 1 1 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0|C|S|E|0|0|0|0|0|0|0|0|0|0|0|0| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * C: config, packet contains config info + * S: start, packet contains start of frame + * E: end, packet contains end of frame + */ + /* this seems to indicate a packet with a config string sent before each + * keyframe */ + C = (payload[0] & 0x40) == 0x40; + + /* redundant with the RTP marker bit */ + S = (payload[0] & 0x20) == 0x20; + E = (payload[0] & 0x10) == 0x10; + + GST_DEBUG ("M:%d, C:%d, S:%d, E:%d", M, C, S, E); + + GST_MEMDUMP ("incoming buffer", payload, payload_len); + + if (G_UNLIKELY (C)) { + GstCaps *caps; + GstBuffer *codec_data; + GstMapInfo cmap; + guint8 res; + + GST_DEBUG ("Configuration packet"); + + /* if we already have caps, we don't need to do anything. FIXME, check if + * something changed. */ + if (G_UNLIKELY (gst_pad_has_current_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD + (depayload)))) { + GST_DEBUG ("Already configured, skipping config parsing"); + goto beach; + } + + res = payload[2] >> 5; + + /* width and height, according to http://wiki.multimedia.cx/index.php?title=Sorenson_Video_1#Stream_Format_And_Header */ + if (G_LIKELY (res < 7)) { + rtpsv3vdepay->width = resolutions[res].width; + rtpsv3vdepay->height = resolutions[res].height; + } else { + /* extended width/height, they're contained in the following 24bit */ + rtpsv3vdepay->width = ((payload[2] & 0x1f) << 7) | (payload[3] >> 1); + rtpsv3vdepay->height = + (payload[3] & 0x1) << 11 | payload[4] << 3 | (payload[5] >> 5); + } + + /* CodecData needs to be 'SEQH' + len (32bit) + data according to + * ffmpeg's libavcodec/svq3.c:svq3_decode_init */ + codec_data = gst_buffer_new_and_alloc (payload_len + 6); + gst_buffer_map (codec_data, &cmap, GST_MAP_WRITE); + memcpy (cmap.data, "SEQH", 4); + GST_WRITE_UINT32_LE (cmap.data + 4, payload_len - 2); + memcpy (cmap.data + 8, payload + 2, payload_len - 2); + GST_MEMDUMP ("codec_data", cmap.data, gst_buffer_get_size (codec_data)); + gst_buffer_unmap (codec_data, &cmap); + + caps = gst_caps_new_simple ("video/x-svq", + "svqversion", G_TYPE_INT, 3, + "width", G_TYPE_INT, rtpsv3vdepay->width, + "height", G_TYPE_INT, rtpsv3vdepay->height, + "codec_data", GST_TYPE_BUFFER, codec_data, NULL); + gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), caps); + gst_caps_unref (caps); + + GST_DEBUG ("Depayloader now configured"); + + rtpsv3vdepay->configured = TRUE; + + goto beach; + } + + if (G_LIKELY (rtpsv3vdepay->configured)) { + GstBuffer *tmpbuf; + + GST_DEBUG ("Storing incoming payload"); + /* store data in adapter, stip off 2 bytes header */ + tmpbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, 2, -1); + gst_adapter_push (rtpsv3vdepay->adapter, tmpbuf); + + if (G_UNLIKELY (M)) { + /* frame is completed: push contents of adapter */ + guint avail; + + avail = gst_adapter_available (rtpsv3vdepay->adapter); + GST_DEBUG ("Returning completed output buffer [%d bytes]", avail); + outbuf = gst_adapter_take_buffer (rtpsv3vdepay->adapter, avail); + } + } + +beach: + gst_rtp_buffer_unmap (&rtp); + return outbuf; + + /* ERRORS */ +bad_packet: + { + GST_ELEMENT_WARNING (rtpsv3vdepay, STREAM, DECODE, + (NULL), ("Packet was too short")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_sv3v_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpSV3VDepay *rtpsv3vdepay; + GstStateChangeReturn ret; + + rtpsv3vdepay = GST_RTP_SV3V_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_adapter_clear (rtpsv3vdepay->adapter); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_sv3v_depay_plugin_init (GstPlugin * plugin) +{ + GST_DEBUG_CATEGORY_INIT (rtpsv3vdepay_debug, "rtpsv3vdepay", 0, + "RTP SV3V depayloader"); + + return gst_element_register (plugin, "rtpsv3vdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_SV3V_DEPAY); +} diff --git a/gst/rtp/gstrtpsv3vdepay.h b/gst/rtp/gstrtpsv3vdepay.h new file mode 100755 index 0000000..428684a --- /dev/null +++ b/gst/rtp/gstrtpsv3vdepay.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_SV3V_DEPAY_H__ +#define __GST_RTP_SV3V_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_SV3V_DEPAY \ + (gst_rtp_sv3v_depay_get_type()) +#define GST_RTP_SV3V_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_SV3V_DEPAY,GstRtpSV3VDepay)) +#define GST_RTP_SV3V_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_SV3V_DEPAY,GstRtpSV3VDepayClass)) +#define GST_IS_RTP_SV3V_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_SV3V_DEPAY)) +#define GST_IS_RTP_SV3V_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_SV3V_DEPAY)) + +typedef struct _GstRtpSV3VDepay GstRtpSV3VDepay; +typedef struct _GstRtpSV3VDepayClass GstRtpSV3VDepayClass; + +struct _GstRtpSV3VDepay +{ + GstRTPBaseDepayload depayload; + + GstAdapter *adapter; + + gboolean configured; + + guint16 nextseq; + guint width; + guint height; +}; + +struct _GstRtpSV3VDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_sv3v_depay_get_type (void); + +gboolean gst_rtp_sv3v_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_SV3V_DEPAY_H__ */ diff --git a/gst/rtp/gstrtptheoradepay.c b/gst/rtp/gstrtptheoradepay.c new file mode 100755 index 0000000..ccfe12d --- /dev/null +++ b/gst/rtp/gstrtptheoradepay.c @@ -0,0 +1,656 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/tag/tag.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtptheoradepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtptheoradepay_debug); +#define GST_CAT_DEFAULT (rtptheoradepay_debug) + +static GstStaticPadTemplate gst_rtp_theora_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\"" + /* All required parameters + * + * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } " + * "width = (string) [1, 1048561] (multiples of 16) " + * "height = (string) [1, 1048561] (multiples of 16) " + * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } " + * "configuration = (string) ANY" + */ + /* All optional parameters + * + * "configuration-uri =" + */ + ) + ); + +static GstStaticPadTemplate gst_rtp_theora_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-theora") + ); + +#define gst_rtp_theora_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpTheoraDepay, gst_rtp_theora_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static gboolean gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload * + depayload, GstEvent * event); + +static void gst_rtp_theora_depay_finalize (GObject * object); + +static void +gst_rtp_theora_depay_class_init (GstRtpTheoraDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_theora_depay_finalize; + + gstrtpbasedepayload_class->process = gst_rtp_theora_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_theora_depay_setcaps; + gstrtpbasedepayload_class->packet_lost = gst_rtp_theora_depay_packet_lost; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_theora_depay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_theora_depay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Theora depayloader", "Codec/Depayloader/Network/RTP", + "Extracts Theora video from RTP packets (draft-01 of RFC XXXX)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtptheoradepay_debug, "rtptheoradepay", 0, + "Theora RTP Depayloader"); +} + +static void +gst_rtp_theora_depay_init (GstRtpTheoraDepay * rtptheoradepay) +{ + rtptheoradepay->adapter = gst_adapter_new (); +} + +static void +gst_rtp_theora_depay_finalize (GObject * object) +{ + GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (object); + + g_object_unref (rtptheoradepay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +static gboolean +gst_rtp_theora_depay_parse_configuration (GstRtpTheoraDepay * rtptheoradepay, + GstBuffer * confbuf) +{ + GstBuffer *buf; + guint32 num_headers; + GstMapInfo map; + guint8 *data; + gsize size; + gint i, j; + + gst_buffer_map (confbuf, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + GST_DEBUG_OBJECT (rtptheoradepay, "config size %" G_GSIZE_FORMAT, size); + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of packed headers | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (size < 4) + goto too_small; + + num_headers = GST_READ_UINT32_BE (data); + size -= 4; + data += 4; + + GST_DEBUG_OBJECT (rtptheoradepay, "have %u headers", num_headers); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | length .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | n. of headers | length1 | length2 .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Comment Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Comment Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Setup Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Setup Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + for (i = 0; i < num_headers; i++) { + guint32 ident; + guint16 length; + guint8 n_headers, b; + GstRtpTheoraConfig *conf; + guint *h_sizes; + guint extra = 1; + + if (size < 6) + goto too_small; + + ident = (data[0] << 16) | (data[1] << 8) | data[2]; + length = (data[3] << 8) | data[4]; + n_headers = data[5]; + size -= 6; + data += 6; + + GST_DEBUG_OBJECT (rtptheoradepay, + "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident, + length, size); + + /* FIXME check if we already got this ident */ + + /* length might also include count of following size fields */ + if (size < length && size + 1 != length) + goto too_small; + + /* read header sizes we read 2 sizes, the third size (for which we allocate + * space) must be derived from the total packed header length. */ + h_sizes = g_newa (guint, n_headers + 1); + for (j = 0; j < n_headers; j++) { + guint h_size; + + h_size = 0; + do { + if (size < 1) + goto too_small; + b = *data++; + size--; + extra++; + h_size = (h_size << 7) | (b & 0x7f); + } while (b & 0x80); + GST_DEBUG_OBJECT (rtptheoradepay, "headers %d: size: %u", j, h_size); + h_sizes[j] = h_size; + length -= h_size; + } + /* last header length is the remaining space */ + GST_DEBUG_OBJECT (rtptheoradepay, "last header size: %u", length); + h_sizes[j] = length; + + GST_DEBUG_OBJECT (rtptheoradepay, "preparing headers"); + conf = g_new0 (GstRtpTheoraConfig, 1); + conf->ident = ident; + + for (j = 0; j <= n_headers; j++) { + guint h_size; + + h_size = h_sizes[j]; + if (size < h_size) { + if (j != n_headers || size + extra != h_size) { + goto too_small; + } else { + /* otherwise means that overall length field contained total length, + * including extra fields */ + h_size -= extra; + } + } + + GST_DEBUG_OBJECT (rtptheoradepay, "reading header %d, size %u", j, + h_size); + + buf = gst_buffer_new_and_alloc (h_size); + gst_buffer_fill (buf, 0, data, h_size); + conf->headers = g_list_append (conf->headers, buf); + data += h_size; + size -= h_size; + } + rtptheoradepay->configs = g_list_append (rtptheoradepay->configs, conf); + } + + gst_buffer_unmap (confbuf, &map); + return TRUE; + + /* ERRORS */ +too_small: + { + GST_DEBUG_OBJECT (rtptheoradepay, "configuration too small"); + gst_buffer_unmap (confbuf, &map); + return FALSE; + } +} + +static gboolean +gst_rtp_theora_depay_parse_inband_configuration (GstRtpTheoraDepay * + rtptheoradepay, guint ident, guint8 * configuration, guint size, + guint length) +{ + GstBuffer *confbuf; + GstMapInfo map; + + if (G_UNLIKELY (size < 4)) + return FALSE; + + /* transform inline to out-of-band and parse that one */ + confbuf = gst_buffer_new_and_alloc (size + 9); + gst_buffer_map (confbuf, &map, GST_MAP_WRITE); + /* 1 header */ + GST_WRITE_UINT32_BE (map.data, 1); + /* write Ident */ + GST_WRITE_UINT24_BE (map.data + 4, ident); + /* write sort-of-length */ + GST_WRITE_UINT16_BE (map.data + 7, length); + /* copy remainder */ + memcpy (map.data + 9, configuration, size); + gst_buffer_unmap (confbuf, &map); + + return gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf); +} + +static gboolean +gst_rtp_theora_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpTheoraDepay *rtptheoradepay; + GstCaps *srccaps; + const gchar *configuration; + gboolean res; + + rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload); + + rtptheoradepay->needs_keyframe = FALSE; + + structure = gst_caps_get_structure (caps, 0); + + /* read and parse configuration string */ + configuration = gst_structure_get_string (structure, "configuration"); + if (configuration) { + GstBuffer *confbuf; + guint8 *data; + gsize size; + + /* deserialize base64 to buffer */ + data = g_base64_decode (configuration, &size); + + confbuf = gst_buffer_new (); + gst_buffer_append_memory (confbuf, + gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); + + if (!gst_rtp_theora_depay_parse_configuration (rtptheoradepay, confbuf)) + goto invalid_configuration; + } + + /* set caps on pad and on header */ + srccaps = gst_caps_new_empty_simple ("video/x-theora"); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + /* Clock rate is always 90000 according to draft-barbato-avt-rtp-theora-01 */ + depayload->clock_rate = 90000; + + return res; + + /* ERRORS */ +invalid_configuration: + { + GST_ERROR_OBJECT (rtptheoradepay, "invalid configuration specified"); + return FALSE; + } +} + +static gboolean +gst_rtp_theora_depay_switch_codebook (GstRtpTheoraDepay * rtptheoradepay, + guint32 ident) +{ + GList *walk; + gboolean res = FALSE; + + for (walk = rtptheoradepay->configs; walk; walk = g_list_next (walk)) { + GstRtpTheoraConfig *conf = (GstRtpTheoraConfig *) walk->data; + + if (conf->ident == ident) { + GList *headers; + + /* FIXME, remove pads, create new pad.. */ + + /* push out all the headers */ + for (headers = conf->headers; headers; headers = g_list_next (headers)) { + GstBuffer *header = GST_BUFFER_CAST (headers->data); + + gst_buffer_ref (header); + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtptheoradepay), + header); + } + /* remember the current config */ + rtptheoradepay->config = conf; + res = TRUE; + } + } + if (!res) { + /* we don't know about the headers, figure out an alternative method for + * getting the codebooks. FIXME, fail for now. */ + } + return res; +} + +static GstBuffer * +gst_rtp_theora_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpTheoraDepay *rtptheoradepay; + GstBuffer *outbuf; + GstFlowReturn ret; + gint payload_len; + guint8 *payload, *to_free = NULL; + guint32 header, ident; + guint8 F, TDT, packets; + GstRTPBuffer rtp = { NULL }; + guint length; + + rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len); + + /* we need at least 4 bytes for the packet header */ + if (G_UNLIKELY (payload_len < 4)) + goto packet_short; + + payload = gst_rtp_buffer_get_payload (&rtp); + + header = GST_READ_UINT32_BE (payload); + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | F |TDT|# pkts.| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * F: Fragment type (0=none, 1=start, 2=cont, 3=end) + * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved) + * pkts: number of packets. + */ + TDT = (header & 0x30) >> 4; + if (G_UNLIKELY (TDT == 3)) + goto ignore_reserved; + + ident = (header >> 8) & 0xffffff; + F = (header & 0xc0) >> 6; + packets = (header & 0xf); + + GST_DEBUG_OBJECT (depayload, "ident: 0x%08x, F: %d, TDT: %d, packets: %d", + ident, F, TDT, packets); + + if (TDT == 0) { + gboolean do_switch = FALSE; + + /* we have a raw payload, find the codebook for the ident */ + if (!rtptheoradepay->config) { + /* we don't have an active codebook, find the codebook and + * activate it */ + do_switch = TRUE; + } else if (rtptheoradepay->config->ident != ident) { + /* codebook changed */ + do_switch = TRUE; + } + if (do_switch) { + if (!gst_rtp_theora_depay_switch_codebook (rtptheoradepay, ident)) + goto switch_failed; + } + } + + /* skip header */ + payload += 4; + payload_len -= 4; + + /* fragmented packets, assemble */ + if (F != 0) { + GstBuffer *vdata; + guint headerskip; + + if (F == 1) { + /* if we start a packet, clear adapter and start assembling. */ + gst_adapter_clear (rtptheoradepay->adapter); + GST_DEBUG_OBJECT (depayload, "start assemble"); + rtptheoradepay->assembling = TRUE; + } + + if (!rtptheoradepay->assembling) + goto no_output; + + /* first assembled packet, reuse 2 bytes to store the length */ + headerskip = (F == 1 ? 4 : 6); + /* skip header and length. */ + vdata = gst_rtp_buffer_get_payload_subbuffer (&rtp, headerskip, -1); + + GST_DEBUG_OBJECT (depayload, "assemble theora packet"); + gst_adapter_push (rtptheoradepay->adapter, vdata); + + /* packet is not complete, we are done */ + if (F != 3) + goto no_output; + + /* construct assembled buffer */ + payload_len = gst_adapter_available (rtptheoradepay->adapter); + payload = gst_adapter_take (rtptheoradepay->adapter, payload_len); + + /* use this length */ + length = payload_len - 2; + + to_free = payload; + } else { + /* read length from data */ + length = 0; + } + + GST_DEBUG_OBJECT (depayload, "assemble done, payload_len %d", payload_len); + + /* we not assembling anymore now */ + rtptheoradepay->assembling = FALSE; + gst_adapter_clear (rtptheoradepay->adapter); + + /* payload now points to a length with that many theora data bytes. + * Iterate over the packets and send them out. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | length | theora data .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. theora data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | length | next theora packet data .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. theora data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* + */ + while (payload_len >= 2) { + if (length == 0) + length = GST_READ_UINT16_BE (payload); + + payload += 2; + payload_len -= 2; + + GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length, + payload_len); + + /* skip packet if something odd happens */ + if (G_UNLIKELY (length > payload_len)) + goto length_short; + + /* handle in-band configuration */ + if (G_UNLIKELY (TDT == 1)) { + GST_DEBUG_OBJECT (rtptheoradepay, "in-band configuration"); + if (!gst_rtp_theora_depay_parse_inband_configuration (rtptheoradepay, + ident, payload, payload_len, length)) + goto invalid_configuration; + goto no_output; + } + + /* create buffer for packet */ + if (G_UNLIKELY (to_free)) { + outbuf = + gst_buffer_new_wrapped_full (0, to_free, (payload - to_free) + length, + payload - to_free, length, to_free, g_free); + to_free = NULL; + } else { + outbuf = gst_buffer_new_and_alloc (length); + gst_buffer_fill (outbuf, 0, payload, length); + } + + if (payload_len > 0 && (payload[0] & 0xC0) == 0x0) { + rtptheoradepay->needs_keyframe = FALSE; + } else { + GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); + } + + payload += length; + payload_len -= length; + /* make sure to read next length */ + length = 0; + + ret = gst_rtp_base_depayload_push (depayload, outbuf); + if (ret != GST_FLOW_OK) + break; + } + + if (rtptheoradepay->needs_keyframe) + goto request_keyframe; + +out: +no_output: + + gst_rtp_buffer_unmap (&rtp); + g_free (to_free); + return NULL; + + /* ERORRS */ +switch_failed: + { + GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, + (NULL), ("Could not switch codebooks")); + goto request_config; + } +packet_short: + { + GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, + (NULL), ("Packet was too short (%d < 4)", payload_len)); + goto request_keyframe; + } +ignore_reserved: + { + GST_WARNING_OBJECT (rtptheoradepay, "reserved TDT ignored"); + goto out; + } +length_short: + { + GST_ELEMENT_WARNING (rtptheoradepay, STREAM, DECODE, + (NULL), ("Packet contains invalid data")); + goto request_keyframe; + } +invalid_configuration: + { + /* fatal, as we otherwise risk carrying on without output */ + GST_ELEMENT_ERROR (rtptheoradepay, STREAM, DECODE, + (NULL), ("Packet contains invalid configuration")); + goto request_config; + } +request_config: + { + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new ("GstForceKeyUnit", + "all-headers", G_TYPE_BOOLEAN, TRUE, NULL))); + goto out; + } +request_keyframe: + { + rtptheoradepay->needs_keyframe = TRUE; + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new_empty ("GstForceKeyUnit"))); + goto out; + } +} + +gboolean +gst_rtp_theora_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtptheoradepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_DEPAY); +} + +static gboolean +gst_rtp_theora_depay_packet_lost (GstRTPBaseDepayload * depayload, + GstEvent * event) +{ + GstRtpTheoraDepay *rtptheoradepay = GST_RTP_THEORA_DEPAY (depayload); + guint seqnum = 0; + + gst_structure_get_uint (gst_event_get_structure (event), "seqnum", &seqnum); + GST_LOG_OBJECT (depayload, "Requested keyframe because frame with seqnum %u" + " is missing", seqnum); + rtptheoradepay->needs_keyframe = TRUE; + + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload), + gst_event_new_custom (GST_EVENT_CUSTOM_UPSTREAM, + gst_structure_new_empty ("GstForceKeyUnit"))); + + return TRUE; +} diff --git a/gst/rtp/gstrtptheoradepay.h b/gst/rtp/gstrtptheoradepay.h new file mode 100755 index 0000000..2b0b260 --- /dev/null +++ b/gst/rtp/gstrtptheoradepay.h @@ -0,0 +1,72 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_THEORA_DEPAY_H__ +#define __GST_RTP_THEORA_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_THEORA_DEPAY \ + (gst_rtp_theora_depay_get_type()) +#define GST_RTP_THEORA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_THEORA_DEPAY,GstRtpTheoraDepay)) +#define GST_RTP_THEORA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_THEORA_DEPAY,GstRtpTheoraDepayClass)) +#define GST_IS_RTP_THEORA_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_THEORA_DEPAY)) +#define GST_IS_RTP_THEORA_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_THEORA_DEPAY)) + +typedef struct _GstRtpTheoraDepay GstRtpTheoraDepay; +typedef struct _GstRtpTheoraDepayClass GstRtpTheoraDepayClass; + +typedef struct _GstRtpTheoraConfig { + guint32 ident; + GList *headers; +} GstRtpTheoraConfig; + +struct _GstRtpTheoraDepay +{ + GstRTPBaseDepayload parent; + + GList *configs; + GstRtpTheoraConfig *config; + + GstAdapter *adapter; + gboolean assembling; + + gboolean needs_keyframe; +}; + +struct _GstRtpTheoraDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_theora_depay_get_type (void); + +gboolean gst_rtp_theora_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_THEORA_DEPAY_H__ */ diff --git a/gst/rtp/gstrtptheorapay.c b/gst/rtp/gstrtptheorapay.c new file mode 100755 index 0000000..10055c0 --- /dev/null +++ b/gst/rtp/gstrtptheorapay.c @@ -0,0 +1,947 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "fnv1hash.h" +#include "gstrtptheorapay.h" + +#define THEORA_ID_LEN 42 + +GST_DEBUG_CATEGORY_STATIC (rtptheorapay_debug); +#define GST_CAT_DEFAULT (rtptheorapay_debug) + +/* references: + * http://svn.xiph.org/trunk/theora/doc/draft-ietf-avt-rtp-theora-01.txt + */ + +static GstStaticPadTemplate gst_rtp_theora_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " "encoding-name = (string) \"THEORA\"" + /* All required parameters + * + * "sampling = (string) { "YCbCr-4:2:0", "YCbCr-4:2:2", "YCbCr-4:4:4" } " + * "width = (string) [1, 1048561] (multiples of 16) " + * "height = (string) [1, 1048561] (multiples of 16) " + * "configuration = (string) ANY" + */ + /* All optional parameters + * + * "configuration-uri =" + * "delivery-method = (string) { inline, in_band, out_band/<specific_name> } " + */ + ) + ); + +static GstStaticPadTemplate gst_rtp_theora_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-theora") + ); + +#define DEFAULT_CONFIG_INTERVAL 0 + +enum +{ + PROP_0, + PROP_CONFIG_INTERVAL +}; + +#define gst_rtp_theora_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpTheoraPay, gst_rtp_theora_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static gboolean gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstStateChangeReturn gst_rtp_theora_pay_change_state (GstElement * + element, GstStateChange transition); +static GstFlowReturn gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); +static gboolean gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); + +static gboolean gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload, + guint8 * data, guint size); +static gboolean gst_rtp_theora_pay_finish_headers (GstRTPBasePayload * + basepayload); + +static void gst_rtp_theora_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_theora_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_rtp_theora_pay_class_init (GstRtpTheoraPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstelement_class->change_state = gst_rtp_theora_pay_change_state; + + gstrtpbasepayload_class->set_caps = gst_rtp_theora_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_theora_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_theora_pay_sink_event; + + gobject_class->set_property = gst_rtp_theora_pay_set_property; + gobject_class->get_property = gst_rtp_theora_pay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_theora_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_theora_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Theora payloader", "Codec/Payloader/Network/RTP", + "Payload-encode Theora video into RTP packets (draft-01 RFC XXXX)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtptheorapay_debug, "rtptheorapay", 0, + "Theora RTP Payloader"); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL, + g_param_spec_uint ("config-interval", "Config Send Interval", + "Send Config Insertion Interval in seconds (configuration headers " + "will be multiplexed in the data stream when detected.) (0 = disabled)", + 0, 3600, DEFAULT_CONFIG_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); +} + +static void +gst_rtp_theora_pay_init (GstRtpTheoraPay * rtptheorapay) +{ + rtptheorapay->last_config = GST_CLOCK_TIME_NONE; +} + +static void +gst_rtp_theora_pay_clear_packet (GstRtpTheoraPay * rtptheorapay) +{ + if (rtptheorapay->packet) + gst_buffer_unref (rtptheorapay->packet); + rtptheorapay->packet = NULL; +} + +static void +gst_rtp_theora_pay_cleanup (GstRtpTheoraPay * rtptheorapay) +{ + g_list_foreach (rtptheorapay->headers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (rtptheorapay->headers); + rtptheorapay->headers = NULL; + + gst_rtp_theora_pay_clear_packet (rtptheorapay); + + if (rtptheorapay->config_data) + g_free (rtptheorapay->config_data); + rtptheorapay->config_data = NULL; + rtptheorapay->last_config = GST_CLOCK_TIME_NONE; +} + +static gboolean +gst_rtp_theora_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpTheoraPay *rtptheorapay; + GstStructure *s; + const GValue *array; + gint asize, i; + GstBuffer *buf; + GstMapInfo map; + + rtptheorapay = GST_RTP_THEORA_PAY (basepayload); + + s = gst_caps_get_structure (caps, 0); + + rtptheorapay->need_headers = TRUE; + + if ((array = gst_structure_get_value (s, "streamheader")) == NULL) + goto done; + + if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY) + goto done; + + if ((asize = gst_value_array_get_size (array)) < 3) + goto done; + + for (i = 0; i < asize; i++) { + const GValue *value; + + value = gst_value_array_get_value (array, i); + if ((buf = gst_value_get_buffer (value)) == NULL) + goto null_buffer; + + gst_buffer_map (buf, &map, GST_MAP_READ); + /* no data packets allowed */ + if (map.size < 1) + goto invalid_streamheader; + + /* we need packets with id 0x80, 0x81, 0x82 */ + if (map.data[0] != 0x80 + i) + goto invalid_streamheader; + + if (i == 0) { + /* identification, we need to parse this in order to get the clock rate. */ + if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, map.data, + map.size))) + goto parse_id_failed; + } + GST_DEBUG_OBJECT (rtptheorapay, "collecting header %d", i); + rtptheorapay->headers = + g_list_append (rtptheorapay->headers, gst_buffer_ref (buf)); + gst_buffer_unmap (buf, &map); + } + if (!gst_rtp_theora_pay_finish_headers (basepayload)) + goto finish_failed; + +done: + return TRUE; + + /* ERRORS */ +null_buffer: + { + GST_WARNING_OBJECT (rtptheorapay, "streamheader with null buffer received"); + return FALSE; + } +invalid_streamheader: + { + GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header"); + gst_buffer_unmap (buf, &map); + return FALSE; + } +parse_id_failed: + { + GST_WARNING_OBJECT (rtptheorapay, "unable to parse initial header"); + gst_buffer_unmap (buf, &map); + return FALSE; + } +finish_failed: + { + GST_WARNING_OBJECT (rtptheorapay, "unable to finish headers"); + return FALSE; + } +} + +static void +gst_rtp_theora_pay_reset_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT) +{ + guint payload_len; + GstRTPBuffer rtp = { NULL }; + + GST_DEBUG_OBJECT (rtptheorapay, "reset packet"); + + rtptheorapay->payload_pos = 4; + gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + gst_rtp_buffer_unmap (&rtp); + rtptheorapay->payload_left = payload_len - 4; + rtptheorapay->payload_duration = 0; + rtptheorapay->payload_F = 0; + rtptheorapay->payload_TDT = TDT; + rtptheorapay->payload_pkts = 0; +} + +static void +gst_rtp_theora_pay_init_packet (GstRtpTheoraPay * rtptheorapay, guint8 TDT, + GstClockTime timestamp) +{ + GST_DEBUG_OBJECT (rtptheorapay, "starting new packet, TDT: %d", TDT); + + if (rtptheorapay->packet) + gst_buffer_unref (rtptheorapay->packet); + + /* new packet allocate max packet size */ + rtptheorapay->packet = + gst_rtp_buffer_new_allocate_len (GST_RTP_BASE_PAYLOAD_MTU + (rtptheorapay), 0, 0); + gst_rtp_theora_pay_reset_packet (rtptheorapay, TDT); + + GST_BUFFER_TIMESTAMP (rtptheorapay->packet) = timestamp; +} + +static GstFlowReturn +gst_rtp_theora_pay_flush_packet (GstRtpTheoraPay * rtptheorapay) +{ + GstFlowReturn ret; + guint8 *payload; + guint hlen; + GstRTPBuffer rtp = { NULL }; + + /* check for empty packet */ + if (!rtptheorapay->packet || rtptheorapay->payload_pos <= 4) + return GST_FLOW_OK; + + GST_DEBUG_OBJECT (rtptheorapay, "flushing packet"); + + gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp); + + /* fix header */ + payload = gst_rtp_buffer_get_payload (&rtp); + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | F |TDT|# pkts.| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * F: Fragment type (0=none, 1=start, 2=cont, 3=end) + * TDT: Theora data type (0=theora, 1=config, 2=comment, 3=reserved) + * pkts: number of packets. + */ + payload[0] = (rtptheorapay->payload_ident >> 16) & 0xff; + payload[1] = (rtptheorapay->payload_ident >> 8) & 0xff; + payload[2] = (rtptheorapay->payload_ident) & 0xff; + payload[3] = (rtptheorapay->payload_F & 0x3) << 6 | + (rtptheorapay->payload_TDT & 0x3) << 4 | + (rtptheorapay->payload_pkts & 0xf); + + gst_rtp_buffer_unmap (&rtp); + + /* shrink the buffer size to the last written byte */ + hlen = gst_rtp_buffer_calc_header_len (0); + gst_buffer_resize (rtptheorapay->packet, 0, hlen + rtptheorapay->payload_pos); + + GST_BUFFER_DURATION (rtptheorapay->packet) = rtptheorapay->payload_duration; + + /* push, this gives away our ref to the packet, so clear it. */ + ret = + gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtptheorapay), + rtptheorapay->packet); + rtptheorapay->packet = NULL; + + return ret; +} + +static gboolean +gst_rtp_theora_pay_finish_headers (GstRTPBasePayload * basepayload) +{ + GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (basepayload); + GList *walk; + guint length, size, n_headers, configlen, extralen; + gchar *wstr, *hstr, *configuration; + guint8 *data, *config; + guint32 ident; + gboolean res; + + GST_DEBUG_OBJECT (rtptheorapay, "finish headers"); + + if (!rtptheorapay->headers) { + GST_DEBUG_OBJECT (rtptheorapay, "We need 2 headers but have none"); + goto no_headers; + } + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of packed headers | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * We only construct a config containing 1 packed header like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | length .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | n. of headers | length1 | length2 .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Comment Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Comment Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Setup Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Setup Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for + * the ident, 2 bytes for length, 1 byte for n. of headers. */ + size = 4 + 3 + 2 + 1; + + /* count the size of the headers first and update the hash */ + length = 0; + n_headers = 0; + ident = fnv1_hash_32_new (); + extralen = 1; + for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + GstMapInfo map; + guint bsize; + + bsize = gst_buffer_get_size (buf); + length += bsize; + n_headers++; + + /* count number of bytes needed for length fields, we don't need this for + * the last header. */ + if (g_list_next (walk)) { + do { + size++; + extralen++; + bsize >>= 7; + } while (bsize); + } + /* update hash */ + gst_buffer_map (buf, &map, GST_MAP_READ); + ident = fnv1_hash_32_update (ident, map.data, map.size); + gst_buffer_unmap (buf, &map); + } + + /* packet length is header size + packet length */ + configlen = size + length; + config = data = g_malloc (configlen); + + /* number of packed headers, we only pack 1 header */ + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 1; + + ident = fnv1_hash_32_to_24 (ident); + rtptheorapay->payload_ident = ident; + GST_DEBUG_OBJECT (rtptheorapay, "ident 0x%08x", ident); + + /* take lower 3 bytes */ + data[4] = (ident >> 16) & 0xff; + data[5] = (ident >> 8) & 0xff; + data[6] = ident & 0xff; + + /* store length of all theora headers */ + data[7] = ((length) >> 8) & 0xff; + data[8] = (length) & 0xff; + + /* store number of headers minus one. */ + data[9] = n_headers - 1; + data += 10; + + /* store length for each header */ + for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + guint bsize, size, temp; + guint flag; + + /* only need to store the length when it's not the last header */ + if (!g_list_next (walk)) + break; + + bsize = gst_buffer_get_size (buf); + + /* calc size */ + size = 0; + do { + size++; + bsize >>= 7; + } while (bsize); + temp = size; + + bsize = gst_buffer_get_size (buf); + /* write the size backwards */ + flag = 0; + while (size) { + size--; + data[size] = (bsize & 0x7f) | flag; + bsize >>= 7; + flag = 0x80; /* Flag bit on all bytes of the length except the last */ + } + data += temp; + } + + /* copy header data */ + for (walk = rtptheorapay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + + gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf)); + data += gst_buffer_get_size (buf); + gst_buffer_unref (buf); + } + g_list_free (rtptheorapay->headers); + rtptheorapay->headers = NULL; + rtptheorapay->need_headers = FALSE; + + /* serialize to base64 */ + configuration = g_base64_encode (config, configlen); + + /* store for later re-sending */ + if (rtptheorapay->config_data) + g_free (rtptheorapay->config_data); + rtptheorapay->config_size = configlen - 4 - 3 - 2; + rtptheorapay->config_data = g_malloc (rtptheorapay->config_size); + rtptheorapay->config_extra_len = extralen; + memcpy (rtptheorapay->config_data, config + 4 + 3 + 2, + rtptheorapay->config_size); + + g_free (config); + + /* configure payloader settings */ + wstr = g_strdup_printf ("%d", rtptheorapay->width); + hstr = g_strdup_printf ("%d", rtptheorapay->height); + gst_rtp_base_payload_set_options (basepayload, "video", TRUE, "THEORA", + 90000); + res = + gst_rtp_base_payload_set_outcaps (basepayload, "sampling", G_TYPE_STRING, + "YCbCr-4:2:0", "width", G_TYPE_STRING, wstr, "height", G_TYPE_STRING, + hstr, "configuration", G_TYPE_STRING, configuration, "delivery-method", + G_TYPE_STRING, "inline", + /* don't set the other defaults + */ + NULL); + g_free (wstr); + g_free (hstr); + g_free (configuration); + + return res; + + /* ERRORS */ +no_headers: + { + GST_DEBUG_OBJECT (rtptheorapay, "finish headers"); + return FALSE; + } +} + +static gboolean +gst_rtp_theora_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data, + guint size) +{ + GstRtpTheoraPay *rtptheorapay; + gint width, height; + + rtptheorapay = GST_RTP_THEORA_PAY (basepayload); + + if (G_UNLIKELY (size < 42)) + goto too_short; + + if (G_UNLIKELY (memcmp (data, "\200theora", 7))) + goto invalid_start; + data += 7; + + if (G_UNLIKELY (data[0] != 3)) + goto invalid_version; + if (G_UNLIKELY (data[1] != 2)) + goto invalid_version; + data += 3; + + width = GST_READ_UINT16_BE (data) << 4; + data += 2; + height = GST_READ_UINT16_BE (data) << 4; + data += 2; + + /* FIXME, parse pixel format */ + + /* store values */ + rtptheorapay->width = width; + rtptheorapay->height = height; + + return TRUE; + + /* ERRORS */ +too_short: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + (NULL), + ("Identification packet is too short, need at least 42, got %d", size)); + return FALSE; + } +invalid_start: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + (NULL), ("Invalid header start in identification packet")); + return FALSE; + } +invalid_version: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + (NULL), ("Invalid version")); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_theora_pay_payload_buffer (GstRtpTheoraPay * rtptheorapay, guint8 TDT, + guint8 * data, guint size, GstClockTime timestamp, GstClockTime duration, + guint not_in_length) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint newsize; + guint packet_len; + GstClockTime newduration; + gboolean flush; + guint plen; + guint8 *ppos, *payload; + gboolean fragmented; + GstRTPBuffer rtp = { NULL }; + + /* size increases with packet length and 2 bytes size eader. */ + newduration = rtptheorapay->payload_duration; + if (duration != GST_CLOCK_TIME_NONE) + newduration += duration; + + newsize = rtptheorapay->payload_pos + 2 + size; + packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0); + + /* check buffer filled against length and max latency */ + flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtptheorapay), + packet_len, newduration); + /* we can store up to 15 theora packets in one RTP packet. */ + flush |= (rtptheorapay->payload_pkts == 15); + /* flush if we have a new TDT */ + if (rtptheorapay->packet) + flush |= (rtptheorapay->payload_TDT != TDT); + if (flush) + ret = gst_rtp_theora_pay_flush_packet (rtptheorapay); + + /* create new packet if we must */ + if (!rtptheorapay->packet) { + gst_rtp_theora_pay_init_packet (rtptheorapay, TDT, timestamp); + } + + gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + ppos = payload + rtptheorapay->payload_pos; + fragmented = FALSE; + + /* put buffer in packet, it either fits completely or needs to be fragmented + * over multiple RTP packets. */ + do { + plen = MIN (rtptheorapay->payload_left - 2, size); + + GST_DEBUG_OBJECT (rtptheorapay, "append %u bytes", plen); + + /* data is copied in the payload with a 2 byte length header */ + ppos[0] = ((plen - not_in_length) >> 8) & 0xff; + ppos[1] = ((plen - not_in_length) & 0xff); + if (plen) + memcpy (&ppos[2], data, plen); + + /* only first (only) configuration cuts length field */ + /* NOTE: spec (if any) is not clear on this ... */ + not_in_length = 0; + + size -= plen; + data += plen; + + rtptheorapay->payload_pos += plen + 2; + rtptheorapay->payload_left -= plen + 2; + + if (fragmented) { + if (size == 0) + /* last fragment, set F to 0x3. */ + rtptheorapay->payload_F = 0x3; + else + /* fragment continues, set F to 0x2. */ + rtptheorapay->payload_F = 0x2; + } else { + if (size > 0) { + /* fragmented packet starts, set F to 0x1, mark ourselves as + * fragmented. */ + rtptheorapay->payload_F = 0x1; + fragmented = TRUE; + } + } + if (fragmented) { + gst_rtp_buffer_unmap (&rtp); + /* fragmented packets are always flushed and have ptks of 0 */ + rtptheorapay->payload_pkts = 0; + ret = gst_rtp_theora_pay_flush_packet (rtptheorapay); + + if (size > 0) { + /* start new packet and get pointers. TDT stays the same. */ + gst_rtp_theora_pay_init_packet (rtptheorapay, + rtptheorapay->payload_TDT, timestamp); + gst_rtp_buffer_map (rtptheorapay->packet, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + ppos = payload + rtptheorapay->payload_pos; + } + } else { + /* unfragmented packet, update stats for next packet, size == 0 and we + * exit the while loop */ + rtptheorapay->payload_pkts++; + if (duration != GST_CLOCK_TIME_NONE) + rtptheorapay->payload_duration += duration; + } + } while (size); + + if (rtp.buffer) + gst_rtp_buffer_unmap (&rtp); + + return ret; +} + +static GstFlowReturn +gst_rtp_theora_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpTheoraPay *rtptheorapay; + GstFlowReturn ret; + GstMapInfo map; + gsize size; + guint8 *data; + GstClockTime duration, timestamp; + guint8 TDT; + gboolean keyframe = FALSE; + + rtptheorapay = GST_RTP_THEORA_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + GST_DEBUG_OBJECT (rtptheorapay, "size %" G_GSIZE_FORMAT + ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration)); + + /* find packet type */ + if (size == 0) { + TDT = 0; + keyframe = FALSE; + } else if (data[0] & 0x80) { + /* header */ + if (data[0] == 0x80) { + /* identification, we need to parse this in order to get the clock rate. + */ + if (G_UNLIKELY (!gst_rtp_theora_pay_parse_id (basepayload, data, size))) + goto parse_id_failed; + TDT = 1; + } else if (data[0] == 0x81) { + /* comment */ + TDT = 2; + } else if (data[0] == 0x82) { + /* setup */ + TDT = 1; + } else + goto unknown_header; + } else { + /* data */ + TDT = 0; + keyframe = ((data[0] & 0x40) == 0); + } + + /* we need to collect the headers and construct a config string from them */ + if (TDT != 0) { + GST_DEBUG_OBJECT (rtptheorapay, "collecting header, buffer %p", buffer); + /* append header to the list of headers */ + gst_buffer_unmap (buffer, &map); + rtptheorapay->headers = g_list_append (rtptheorapay->headers, buffer); + ret = GST_FLOW_OK; + goto done; + } else if (rtptheorapay->headers) { + if (rtptheorapay->need_headers) { + if (!gst_rtp_theora_pay_finish_headers (basepayload)) + goto header_error; + } else { + g_list_free_full (rtptheorapay->headers, + (GDestroyNotify) gst_buffer_unref); + rtptheorapay->headers = NULL; + } + } + + /* there is a config request, see if we need to insert it */ + if (keyframe && (rtptheorapay->config_interval > 0) && + rtptheorapay->config_data) { + gboolean send_config = FALSE; + + if (rtptheorapay->last_config != -1) { + guint64 diff; + + GST_LOG_OBJECT (rtptheorapay, + "now %" GST_TIME_FORMAT ", last VOP-I %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtptheorapay->last_config)); + + /* calculate diff between last config in milliseconds */ + if (timestamp > rtptheorapay->last_config) { + diff = timestamp - rtptheorapay->last_config; + } else { + diff = 0; + } + + GST_DEBUG_OBJECT (rtptheorapay, + "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); + + /* bigger than interval, queue config */ + /* FIXME should convert timestamps to running time */ + if (GST_TIME_AS_SECONDS (diff) >= rtptheorapay->config_interval) { + GST_DEBUG_OBJECT (rtptheorapay, "time to send config"); + send_config = TRUE; + } + } else { + /* no known previous config time, send now */ + GST_DEBUG_OBJECT (rtptheorapay, "no previous config time, send now"); + send_config = TRUE; + } + + if (send_config) { + /* we need to send config now first */ + /* different TDT type forces flush */ + gst_rtp_theora_pay_payload_buffer (rtptheorapay, 1, + rtptheorapay->config_data, rtptheorapay->config_size, + timestamp, GST_CLOCK_TIME_NONE, rtptheorapay->config_extra_len); + + if (timestamp != -1) { + rtptheorapay->last_config = timestamp; + } + } + } + + ret = gst_rtp_theora_pay_payload_buffer (rtptheorapay, TDT, data, size, + timestamp, duration, 0); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + +done: + return ret; + + /* ERRORS */ +parse_id_failed: + { + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } +unknown_header: + { + GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE, + (NULL), ("Ignoring unknown header received")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +header_error: + { + GST_ELEMENT_WARNING (rtptheorapay, STREAM, DECODE, + (NULL), ("Error initializing header config")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +} + +static gboolean +gst_rtp_theora_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + GstRtpTheoraPay *rtptheorapay = GST_RTP_THEORA_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_theora_pay_clear_packet (rtptheorapay); + break; + default: + break; + } + /* false to let parent handle event as well */ + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); +} + +static GstStateChangeReturn +gst_rtp_theora_pay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpTheoraPay *rtptheorapay; + + GstStateChangeReturn ret; + + rtptheorapay = GST_RTP_THEORA_PAY (element); + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_theora_pay_cleanup (rtptheorapay); + break; + default: + break; + } + return ret; +} + +static void +gst_rtp_theora_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpTheoraPay *rtptheorapay; + + rtptheorapay = GST_RTP_THEORA_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + rtptheorapay->config_interval = g_value_get_uint (value); + break; + default: + break; + } +} + +static void +gst_rtp_theora_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpTheoraPay *rtptheorapay; + + rtptheorapay = GST_RTP_THEORA_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + g_value_set_uint (value, rtptheorapay->config_interval); + break; + default: + break; + } +} + +gboolean +gst_rtp_theora_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtptheorapay", + GST_RANK_SECONDARY, GST_TYPE_RTP_THEORA_PAY); +} diff --git a/gst/rtp/gstrtptheorapay.h b/gst/rtp/gstrtptheorapay.h new file mode 100755 index 0000000..0a1d472 --- /dev/null +++ b/gst/rtp/gstrtptheorapay.h @@ -0,0 +1,84 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_THEORA_PAY_H__ +#define __GST_RTP_THEORA_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_THEORA_PAY \ + (gst_rtp_theora_pay_get_type()) +#define GST_RTP_THEORA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_THEORA_PAY,GstRtpTheoraPay)) +#define GST_RTP_THEORA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_THEORA_PAY,GstRtpTheoraPayClass)) +#define GST_IS_RTP_THEORA_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_THEORA_PAY)) +#define GST_IS_RTP_THEORA_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_THEORA_PAY)) + +typedef struct _GstRtpTheoraPay GstRtpTheoraPay; +typedef struct _GstRtpTheoraPayClass GstRtpTheoraPayClass; + +struct _GstRtpTheoraPay +{ + GstRTPBasePayload payload; + + /* the headers */ + gboolean need_headers; + GList *headers; + + /* queues of buffers along with some stats. */ + GstBuffer *packet; + guint payload_pos; + guint payload_left; + guint32 payload_ident; + guint8 payload_F; + guint8 payload_TDT; + guint payload_pkts; + GstClockTime payload_timestamp; + GstClockTime payload_duration; + + /* config (re-sending) */ + guint8 *config_data; + guint config_size; + guint config_extra_len; + guint config_interval; + GstClockTime last_config; + + gint width; + gint height; +}; + +struct _GstRtpTheoraPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_theora_pay_get_type (void); + +gboolean gst_rtp_theora_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_THEORA_PAY_H__ */ diff --git a/gst/rtp/gstrtpvorbisdepay.c b/gst/rtp/gstrtpvorbisdepay.c new file mode 100755 index 0000000..313a6ed --- /dev/null +++ b/gst/rtp/gstrtpvorbisdepay.c @@ -0,0 +1,706 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/tag/tag.h> +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include "gstrtpvorbisdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpvorbisdepay_debug); +#define GST_CAT_DEFAULT (rtpvorbisdepay_debug) + +/* references: + * http://www.rfc-editor.org/rfc/rfc5215.txt + */ + +static GstStaticPadTemplate gst_rtp_vorbis_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"VORBIS\"" + /* All required parameters + * + * "encoding-params = (string) <num channels>" + * "configuration = (string) ANY" + */ + ) + ); + +static GstStaticPadTemplate gst_rtp_vorbis_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-vorbis") + ); + +#define gst_rtp_vorbis_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpVorbisDepay, gst_rtp_vorbis_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_vorbis_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static void gst_rtp_vorbis_depay_finalize (GObject * object); + +static GstStateChangeReturn gst_rtp_vorbis_depay_change_state (GstElement * + element, GstStateChange transition); + +static void +gst_rtp_vorbis_depay_class_init (GstRtpVorbisDepayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gobject_class->finalize = gst_rtp_vorbis_depay_finalize; + + gstelement_class->change_state = gst_rtp_vorbis_depay_change_state; + + gstrtpbasedepayload_class->process = gst_rtp_vorbis_depay_process; + gstrtpbasedepayload_class->set_caps = gst_rtp_vorbis_depay_setcaps; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vorbis_depay_sink_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vorbis_depay_src_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Vorbis depayloader", "Codec/Depayloader/Network/RTP", + "Extracts Vorbis Audio from RTP packets (RFC 5215)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpvorbisdepay_debug, "rtpvorbisdepay", 0, + "Vorbis RTP Depayloader"); +} + +static void +gst_rtp_vorbis_depay_init (GstRtpVorbisDepay * rtpvorbisdepay) +{ + rtpvorbisdepay->adapter = gst_adapter_new (); +} + +static void +free_config (GstRtpVorbisConfig * conf) +{ + GList *headers; + + for (headers = conf->headers; headers; headers = g_list_next (headers)) { + GstBuffer *header = GST_BUFFER_CAST (headers->data); + + gst_buffer_unref (header); + } + g_list_free (conf->headers); + g_free (conf); +} + +static void +free_indents (GstRtpVorbisDepay * rtpvorbisdepay) +{ + GList *walk; + + for (walk = rtpvorbisdepay->configs; walk; walk = g_list_next (walk)) { + free_config ((GstRtpVorbisConfig *) walk->data); + } + g_list_free (rtpvorbisdepay->configs); + rtpvorbisdepay->configs = NULL; +} + +static void +gst_rtp_vorbis_depay_finalize (GObject * object) +{ + GstRtpVorbisDepay *rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (object); + + g_object_unref (rtpvorbisdepay->adapter); + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +/* takes ownership of confbuf */ +static gboolean +gst_rtp_vorbis_depay_parse_configuration (GstRtpVorbisDepay * rtpvorbisdepay, + GstBuffer * confbuf) +{ + GstBuffer *buf; + guint32 num_headers; + GstMapInfo map; + guint8 *data; + gsize size; + guint offset; + gint i, j; + + gst_buffer_map (confbuf, &map, GST_MAP_READ); + data = map.data; + size = map.size; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "config size %" G_GSIZE_FORMAT, size); + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of packed headers | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + if (size < 4) + goto too_small; + + num_headers = GST_READ_UINT32_BE (data); + size -= 4; + data += 4; + offset = 4; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "have %u headers", num_headers); + + /* 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | length .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | n. of headers | length1 | length2 .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Comment Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Comment Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Setup Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Setup Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + for (i = 0; i < num_headers; i++) { + guint32 ident; + guint16 length; + guint8 n_headers, b; + GstRtpVorbisConfig *conf; + guint *h_sizes; + guint extra = 1; + + if (size < 6) + goto too_small; + + ident = (data[0] << 16) | (data[1] << 8) | data[2]; + length = (data[3] << 8) | data[4]; + n_headers = data[5]; + size -= 6; + data += 6; + offset += 6; + + GST_DEBUG_OBJECT (rtpvorbisdepay, + "header %d, ident 0x%08x, length %u, left %" G_GSIZE_FORMAT, i, ident, + length, size); + + /* FIXME check if we already got this ident */ + + /* length might also include count of following size fields */ + if (size < length && size + 1 != length) + goto too_small; + + /* read header sizes we read 2 sizes, the third size (for which we allocate + * space) must be derived from the total packed header length. */ + h_sizes = g_newa (guint, n_headers + 1); + for (j = 0; j < n_headers; j++) { + guint h_size; + + h_size = 0; + do { + if (size < 1) + goto too_small; + b = *data++; + offset++; + extra++; + size--; + h_size = (h_size << 7) | (b & 0x7f); + } while (b & 0x80); + GST_DEBUG_OBJECT (rtpvorbisdepay, "headers %d: size: %u", j, h_size); + + if (length < h_size) + goto too_small; + + h_sizes[j] = h_size; + length -= h_size; + } + /* last header length is the remaining space */ + GST_DEBUG_OBJECT (rtpvorbisdepay, "last header size: %u", length); + h_sizes[j] = length; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "preparing headers"); + conf = g_new0 (GstRtpVorbisConfig, 1); + conf->ident = ident; + + for (j = 0; j <= n_headers; j++) { + guint h_size; + + h_size = h_sizes[j]; + if (size < h_size) { + if (j != n_headers || size + extra != h_size) { + free_config (conf); + goto too_small; + } else { + /* otherwise means that overall length field contained total length, + * including extra fields */ + h_size -= extra; + } + } + + GST_DEBUG_OBJECT (rtpvorbisdepay, "reading header %d, size %u", j, + h_size); + + buf = gst_buffer_copy_region (confbuf, GST_BUFFER_COPY_MEMORY, offset, + h_size); + conf->headers = g_list_append (conf->headers, buf); + offset += h_size; + size -= h_size; + } + rtpvorbisdepay->configs = g_list_append (rtpvorbisdepay->configs, conf); + } + + gst_buffer_unmap (confbuf, &map); + gst_buffer_unref (confbuf); + + return TRUE; + + /* ERRORS */ +too_small: + { + GST_DEBUG_OBJECT (rtpvorbisdepay, "configuration too small"); + gst_buffer_unmap (confbuf, &map); + gst_buffer_unref (confbuf); + return FALSE; + } +} + +static gboolean +gst_rtp_vorbis_depay_parse_inband_configuration (GstRtpVorbisDepay * + rtpvorbisdepay, guint ident, guint8 * configuration, guint size, + guint length) +{ + GstBuffer *confbuf; + GstMapInfo map; + + if (G_UNLIKELY (size < 4)) + return FALSE; + + /* transform inline to out-of-band and parse that one */ + confbuf = gst_buffer_new_and_alloc (size + 9); + gst_buffer_map (confbuf, &map, GST_MAP_WRITE); + /* 1 header */ + GST_WRITE_UINT32_BE (map.data, 1); + /* write Ident */ + GST_WRITE_UINT24_BE (map.data + 4, ident); + /* write sort-of-length */ + GST_WRITE_UINT16_BE (map.data + 7, length); + /* copy remainder */ + memcpy (map.data + 9, configuration, size); + gst_buffer_unmap (confbuf, &map); + + return gst_rtp_vorbis_depay_parse_configuration (rtpvorbisdepay, confbuf); +} + +static gboolean +gst_rtp_vorbis_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpVorbisDepay *rtpvorbisdepay; + GstCaps *srccaps; + const gchar *configuration; + gint clock_rate; + gboolean res; + + rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + /* get clockrate */ + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + goto no_rate; + + /* read and parse configuration string */ + configuration = gst_structure_get_string (structure, "configuration"); + if (configuration) { + GstBuffer *confbuf; + guint8 *data; + gsize size; + + /* deserialize base64 to buffer */ + data = g_base64_decode (configuration, &size); + + confbuf = gst_buffer_new (); + gst_buffer_append_memory (confbuf, + gst_memory_new_wrapped (0, data, size, 0, size, data, g_free)); + if (!gst_rtp_vorbis_depay_parse_configuration (rtpvorbisdepay, confbuf)) + goto invalid_configuration; + } else { + GST_WARNING_OBJECT (rtpvorbisdepay, "no configuration specified"); + } + + /* caps seem good, configure element */ + depayload->clock_rate = clock_rate; + + /* set caps on pad and on header */ + srccaps = gst_caps_new_empty_simple ("audio/x-vorbis"); + res = gst_pad_set_caps (depayload->srcpad, srccaps); + gst_caps_unref (srccaps); + + return res; + + /* ERRORS */ +invalid_configuration: + { + GST_ERROR_OBJECT (rtpvorbisdepay, "invalid configuration specified"); + return FALSE; + } +no_rate: + { + GST_ERROR_OBJECT (rtpvorbisdepay, "no clock-rate specified"); + return FALSE; + } +} + +static gboolean +gst_rtp_vorbis_depay_switch_codebook (GstRtpVorbisDepay * rtpvorbisdepay, + guint32 ident) +{ + GList *walk; + gboolean res = FALSE; + + GST_DEBUG_OBJECT (rtpvorbisdepay, "Looking up code book ident 0x%08x", ident); + for (walk = rtpvorbisdepay->configs; walk; walk = g_list_next (walk)) { + GstRtpVorbisConfig *conf = (GstRtpVorbisConfig *) walk->data; + + if (conf->ident == ident) { + GList *headers; + + /* FIXME, remove pads, create new pad.. */ + + /* push out all the headers */ + for (headers = conf->headers; headers; headers = g_list_next (headers)) { + GstBuffer *header = GST_BUFFER_CAST (headers->data); + + gst_buffer_ref (header); + gst_rtp_base_depayload_push (GST_RTP_BASE_DEPAYLOAD (rtpvorbisdepay), + header); + } + /* remember the current config */ + rtpvorbisdepay->config = conf; + res = TRUE; + } + } + if (!res) { + /* we don't know about the headers, figure out an alternative method for + * getting the codebooks. FIXME, fail for now. */ + } + return res; +} + +static GstBuffer * +gst_rtp_vorbis_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpVorbisDepay *rtpvorbisdepay; + GstBuffer *outbuf; + GstFlowReturn ret; + gint payload_len; + guint8 *payload, *to_free = NULL; + guint32 header, ident; + guint8 F, VDT, packets; + GstRTPBuffer rtp = { NULL }; + guint length; + + rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + GST_DEBUG_OBJECT (depayload, "got RTP packet of size %d", payload_len); + + /* we need at least 4 bytes for the packet header */ + if (G_UNLIKELY (payload_len < 4)) + goto packet_short; + + payload = gst_rtp_buffer_get_payload (&rtp); + + header = GST_READ_UINT32_BE (payload); + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | F |VDT|# pkts.| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * F: Fragment type (0=none, 1=start, 2=cont, 3=end) + * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved) + * pkts: number of packets. + */ + VDT = (header & 0x30) >> 4; + if (G_UNLIKELY (VDT == 3)) + goto ignore_reserved; + + GST_DEBUG_OBJECT (depayload, "header: 0x%08x", header); + ident = (header >> 8) & 0xffffff; + F = (header & 0xc0) >> 6; + packets = (header & 0xf); + + if (VDT == 0) { + gboolean do_switch = FALSE; + + /* we have a raw payload, find the codebook for the ident */ + if (!rtpvorbisdepay->config) { + /* we don't have an active codebook, find the codebook and + * activate it */ + GST_DEBUG_OBJECT (rtpvorbisdepay, "No active codebook, switching"); + do_switch = TRUE; + } else if (rtpvorbisdepay->config->ident != ident) { + /* codebook changed */ + GST_DEBUG_OBJECT (rtpvorbisdepay, "codebook changed, switching"); + do_switch = TRUE; + } + if (do_switch) { + if (!gst_rtp_vorbis_depay_switch_codebook (rtpvorbisdepay, ident)) + goto switch_failed; + } + } + + /* skip header */ + payload += 4; + payload_len -= 4; + + GST_DEBUG_OBJECT (depayload, "ident: %u, F: %d, VDT: %d, packets: %d", ident, + F, VDT, packets); + + /* fragmented packets, assemble */ + if (F != 0) { + GstBuffer *vdata; + guint headerskip; + + if (F == 1) { + /* if we start a packet, clear adapter and start assembling. */ + gst_adapter_clear (rtpvorbisdepay->adapter); + GST_DEBUG_OBJECT (depayload, "start assemble"); + rtpvorbisdepay->assembling = TRUE; + } + + if (!rtpvorbisdepay->assembling) + goto no_output; + + /* first assembled packet, reuse 2 bytes to store the length */ + headerskip = (F == 1 ? 4 : 6); + /* skip header and length. */ + vdata = gst_rtp_buffer_get_payload_subbuffer (&rtp, headerskip, -1); + + GST_DEBUG_OBJECT (depayload, "assemble vorbis packet"); + gst_adapter_push (rtpvorbisdepay->adapter, vdata); + + /* packet is not complete, we are done */ + if (F != 3) + goto no_output; + + /* construct assembled buffer */ + payload_len = gst_adapter_available (rtpvorbisdepay->adapter); + payload = gst_adapter_take (rtpvorbisdepay->adapter, payload_len); + + /* use this length */ + length = payload_len - 2; + + to_free = payload; + } else { + /* read length from data */ + length = 0; + } + + GST_DEBUG_OBJECT (depayload, "assemble done"); + + /* we not assembling anymore now */ + rtpvorbisdepay->assembling = FALSE; + gst_adapter_clear (rtpvorbisdepay->adapter); + + /* payload now points to a length with that many vorbis data bytes. + * Iterate over the packets and send them out. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | length | vorbis data .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. vorbis data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | length | next vorbis packet data .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. vorbis data | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+* + */ + while (payload_len > 2) { + if (length == 0) + length = GST_READ_UINT16_BE (payload); + + payload += 2; + payload_len -= 2; + + GST_DEBUG_OBJECT (depayload, "read length %u, avail: %d", length, + payload_len); + + /* skip packet if something odd happens */ + if (G_UNLIKELY (length > payload_len)) + goto length_short; + + /* handle in-band configuration */ + if (G_UNLIKELY (VDT == 1)) { + GST_DEBUG_OBJECT (rtpvorbisdepay, "in-band configuration"); + if (!gst_rtp_vorbis_depay_parse_inband_configuration (rtpvorbisdepay, + ident, payload, payload_len, length)) + goto invalid_configuration; + goto no_output; + } + + /* create buffer for packet */ + if (G_UNLIKELY (to_free)) { + outbuf = gst_buffer_new (); + gst_buffer_append_memory (outbuf, + gst_memory_new_wrapped (0, to_free, + (payload - to_free) + length, payload - to_free, length, to_free, + g_free)); + to_free = NULL; + } else { + outbuf = gst_buffer_new_and_alloc (length); + gst_buffer_fill (outbuf, 0, payload, length); + } + + payload += length; + payload_len -= length; + /* make sure to read next length */ + length = 0; + + ret = gst_rtp_base_depayload_push (depayload, outbuf); + if (ret != GST_FLOW_OK) + break; + } + + g_free (to_free); + + gst_rtp_buffer_unmap (&rtp); + + return NULL; + +no_output: + { + gst_rtp_buffer_unmap (&rtp); + return NULL; + } + /* ERORRS */ +switch_failed: + { + GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, + (NULL), ("Could not switch codebooks")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +packet_short: + { + GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, + (NULL), ("Packet was too short (%d < 4)", payload_len)); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +ignore_reserved: + { + GST_WARNING_OBJECT (rtpvorbisdepay, "reserved VDT ignored"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +length_short: + { + GST_ELEMENT_WARNING (rtpvorbisdepay, STREAM, DECODE, + (NULL), ("Packet contains invalid data")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_configuration: + { + /* fatal, as we otherwise risk carrying on without output */ + GST_ELEMENT_ERROR (rtpvorbisdepay, STREAM, DECODE, + (NULL), ("Packet contains invalid configuration")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static GstStateChangeReturn +gst_rtp_vorbis_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpVorbisDepay *rtpvorbisdepay; + GstStateChangeReturn ret; + + rtpvorbisdepay = GST_RTP_VORBIS_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + free_indents (rtpvorbisdepay); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_vorbis_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvorbisdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_DEPAY); +} diff --git a/gst/rtp/gstrtpvorbisdepay.h b/gst/rtp/gstrtpvorbisdepay.h new file mode 100755 index 0000000..a343d04 --- /dev/null +++ b/gst/rtp/gstrtpvorbisdepay.h @@ -0,0 +1,70 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_VORBIS_DEPAY_H__ +#define __GST_RTP_VORBIS_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VORBIS_DEPAY \ + (gst_rtp_vorbis_depay_get_type()) +#define GST_RTP_VORBIS_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_VORBIS_DEPAY,GstRtpVorbisDepay)) +#define GST_RTP_VORBIS_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_VORBIS_DEPAY,GstRtpVorbisDepayClass)) +#define GST_IS_RTP_VORBIS_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_VORBIS_DEPAY)) +#define GST_IS_RTP_VORBIS_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_VORBIS_DEPAY)) + +typedef struct _GstRtpVorbisDepay GstRtpVorbisDepay; +typedef struct _GstRtpVorbisDepayClass GstRtpVorbisDepayClass; + +typedef struct _GstRtpVorbisConfig { + guint32 ident; + GList *headers; +} GstRtpVorbisConfig; + +struct _GstRtpVorbisDepay +{ + GstRTPBaseDepayload parent; + + GList *configs; + GstRtpVorbisConfig *config; + + GstAdapter *adapter; + gboolean assembling; +}; + +struct _GstRtpVorbisDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_vorbis_depay_get_type (void); + +gboolean gst_rtp_vorbis_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_VORBIS_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpvorbispay.c b/gst/rtp/gstrtpvorbispay.c new file mode 100755 index 0000000..a89b3a6 --- /dev/null +++ b/gst/rtp/gstrtpvorbispay.c @@ -0,0 +1,943 @@ +/* GStreamer + * Copyright (C) <2006> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "fnv1hash.h" +#include "gstrtpvorbispay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpvorbispay_debug); +#define GST_CAT_DEFAULT (rtpvorbispay_debug) + +/* references: + * http://www.rfc-editor.org/rfc/rfc5215.txt + */ + +static GstStaticPadTemplate gst_rtp_vorbis_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"audio\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) [1, MAX ], " "encoding-name = (string) \"VORBIS\"" + /* All required parameters + * + * "encoding-params = (string) <num channels>" + * "configuration = (string) ANY" + */ + ) + ); + +static GstStaticPadTemplate gst_rtp_vorbis_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-vorbis") + ); + +#define DEFAULT_CONFIG_INTERVAL 0 + +enum +{ + PROP_0, + PROP_CONFIG_INTERVAL +}; + +#define gst_rtp_vorbis_pay_parent_class parent_class +G_DEFINE_TYPE (GstRtpVorbisPay, gst_rtp_vorbis_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static gboolean gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload, + GstCaps * caps); +static GstStateChangeReturn gst_rtp_vorbis_pay_change_state (GstElement * + element, GstStateChange transition); +static GstFlowReturn gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * pad, + GstBuffer * buffer); +static gboolean gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); + +static gboolean gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload, + guint8 * data, guint size); +static gboolean gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload * + basepayload); + +static void gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); +static void gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); + +static void +gst_rtp_vorbis_pay_class_init (GstRtpVorbisPayClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + GstRTPBasePayloadClass *gstrtpbasepayload_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gstelement_class->change_state = gst_rtp_vorbis_pay_change_state; + + gstrtpbasepayload_class->set_caps = gst_rtp_vorbis_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_vorbis_pay_handle_buffer; + gstrtpbasepayload_class->sink_event = gst_rtp_vorbis_pay_sink_event; + + gobject_class->set_property = gst_rtp_vorbis_pay_set_property; + gobject_class->get_property = gst_rtp_vorbis_pay_get_property; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vorbis_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vorbis_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Vorbis payloader", + "Codec/Payloader/Network/RTP", + "Payload-encode Vorbis audio into RTP packets (RFC 5215)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpvorbispay_debug, "rtpvorbispay", 0, + "Vorbis RTP Payloader"); + + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_CONFIG_INTERVAL, + g_param_spec_uint ("config-interval", "Config Send Interval", + "Send Config Insertion Interval in seconds (configuration headers " + "will be multiplexed in the data stream when detected.) (0 = disabled)", + 0, 3600, DEFAULT_CONFIG_INTERVAL, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); +} + +static void +gst_rtp_vorbis_pay_init (GstRtpVorbisPay * rtpvorbispay) +{ + rtpvorbispay->last_config = GST_CLOCK_TIME_NONE; +} + +static void +gst_rtp_vorbis_pay_clear_packet (GstRtpVorbisPay * rtpvorbispay) +{ + if (rtpvorbispay->packet) + gst_buffer_unref (rtpvorbispay->packet); + rtpvorbispay->packet = NULL; +} + +static void +gst_rtp_vorbis_pay_cleanup (GstRtpVorbisPay * rtpvorbispay) +{ + g_list_foreach (rtpvorbispay->headers, (GFunc) gst_mini_object_unref, NULL); + g_list_free (rtpvorbispay->headers); + rtpvorbispay->headers = NULL; + + gst_rtp_vorbis_pay_clear_packet (rtpvorbispay); + + if (rtpvorbispay->config_data) + g_free (rtpvorbispay->config_data); + rtpvorbispay->config_data = NULL; + rtpvorbispay->last_config = GST_CLOCK_TIME_NONE; +} + +static gboolean +gst_rtp_vorbis_pay_setcaps (GstRTPBasePayload * basepayload, GstCaps * caps) +{ + GstRtpVorbisPay *rtpvorbispay; + GstStructure *s; + const GValue *array; + gint asize, i; + GstBuffer *buf; + GstMapInfo map; + + rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload); + + s = gst_caps_get_structure (caps, 0); + + rtpvorbispay->need_headers = TRUE; + + if ((array = gst_structure_get_value (s, "streamheader")) == NULL) + goto done; + + if (G_VALUE_TYPE (array) != GST_TYPE_ARRAY) + goto done; + + if ((asize = gst_value_array_get_size (array)) < 3) + goto done; + + for (i = 0; i < asize; i++) { + const GValue *value; + + value = gst_value_array_get_value (array, i); + if ((buf = gst_value_get_buffer (value)) == NULL) + goto null_buffer; + + gst_buffer_map (buf, &map, GST_MAP_READ); + if (map.size < 1) + goto invalid_streamheader; + + /* no data packets allowed */ + if ((map.data[0] & 1) == 0) + goto invalid_streamheader; + + /* we need packets with id 1, 3, 5 */ + if (map.data[0] != (i * 2) + 1) + goto invalid_streamheader; + + if (i == 0) { + /* identification, we need to parse this in order to get the clock rate. */ + if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, map.data, + map.size))) + goto parse_id_failed; + } + GST_DEBUG_OBJECT (rtpvorbispay, "collecting header %d", i); + rtpvorbispay->headers = + g_list_append (rtpvorbispay->headers, gst_buffer_ref (buf)); + gst_buffer_unmap (buf, &map); + } + if (!gst_rtp_vorbis_pay_finish_headers (basepayload)) + goto finish_failed; + +done: + return TRUE; + + /* ERRORS */ +null_buffer: + { + GST_WARNING_OBJECT (rtpvorbispay, "streamheader with null buffer received"); + return FALSE; + } +invalid_streamheader: + { + GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header"); + gst_buffer_unmap (buf, &map); + return FALSE; + } +parse_id_failed: + { + GST_WARNING_OBJECT (rtpvorbispay, "unable to parse initial header"); + gst_buffer_unmap (buf, &map); + return FALSE; + } +finish_failed: + { + GST_WARNING_OBJECT (rtpvorbispay, "unable to finish headers"); + return FALSE; + } +} + +static void +gst_rtp_vorbis_pay_reset_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT) +{ + guint payload_len; + GstRTPBuffer rtp = { NULL }; + + GST_LOG_OBJECT (rtpvorbispay, "reset packet"); + + rtpvorbispay->payload_pos = 4; + gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_READ, &rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + gst_rtp_buffer_unmap (&rtp); + rtpvorbispay->payload_left = payload_len - 4; + rtpvorbispay->payload_duration = 0; + rtpvorbispay->payload_F = 0; + rtpvorbispay->payload_VDT = VDT; + rtpvorbispay->payload_pkts = 0; +} + +static void +gst_rtp_vorbis_pay_init_packet (GstRtpVorbisPay * rtpvorbispay, guint8 VDT, + GstClockTime timestamp) +{ + GST_LOG_OBJECT (rtpvorbispay, "starting new packet, VDT: %d", VDT); + + if (rtpvorbispay->packet) + gst_buffer_unref (rtpvorbispay->packet); + + /* new packet allocate max packet size */ + rtpvorbispay->packet = + gst_rtp_buffer_new_allocate_len (GST_RTP_BASE_PAYLOAD_MTU + (rtpvorbispay), 0, 0); + gst_rtp_vorbis_pay_reset_packet (rtpvorbispay, VDT); + + GST_BUFFER_TIMESTAMP (rtpvorbispay->packet) = timestamp; +} + +static GstFlowReturn +gst_rtp_vorbis_pay_flush_packet (GstRtpVorbisPay * rtpvorbispay) +{ + GstFlowReturn ret; + guint8 *payload; + guint hlen; + GstRTPBuffer rtp = { NULL }; + + /* check for empty packet */ + if (!rtpvorbispay->packet || rtpvorbispay->payload_pos <= 4) + return GST_FLOW_OK; + + GST_LOG_OBJECT (rtpvorbispay, "flushing packet"); + + gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp); + + /* fix header */ + payload = gst_rtp_buffer_get_payload (&rtp); + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | F |VDT|# pkts.| + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * F: Fragment type (0=none, 1=start, 2=cont, 3=end) + * VDT: Vorbis data type (0=vorbis, 1=config, 2=comment, 3=reserved) + * pkts: number of packets. + */ + payload[0] = (rtpvorbispay->payload_ident >> 16) & 0xff; + payload[1] = (rtpvorbispay->payload_ident >> 8) & 0xff; + payload[2] = (rtpvorbispay->payload_ident) & 0xff; + payload[3] = (rtpvorbispay->payload_F & 0x3) << 6 | + (rtpvorbispay->payload_VDT & 0x3) << 4 | + (rtpvorbispay->payload_pkts & 0xf); + + gst_rtp_buffer_unmap (&rtp); + + /* shrink the buffer size to the last written byte */ + hlen = gst_rtp_buffer_calc_header_len (0); + gst_buffer_resize (rtpvorbispay->packet, 0, hlen + rtpvorbispay->payload_pos); + + GST_BUFFER_DURATION (rtpvorbispay->packet) = rtpvorbispay->payload_duration; + + /* push, this gives away our ref to the packet, so clear it. */ + ret = + gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtpvorbispay), + rtpvorbispay->packet); + rtpvorbispay->packet = NULL; + + return ret; +} + +static gboolean +gst_rtp_vorbis_pay_finish_headers (GstRTPBasePayload * basepayload) +{ + GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload); + GList *walk; + guint length, size, n_headers, configlen, extralen; + gchar *cstr, *configuration; + guint8 *data, *config; + guint32 ident; + gboolean res; + + GST_DEBUG_OBJECT (rtpvorbispay, "finish headers"); + + if (!rtpvorbispay->headers) + goto no_headers; + + /* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Number of packed headers | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Packed header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | .... | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * We only construct a config containing 1 packed header like this: + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ident | length .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | n. of headers | length1 | length2 .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Identification Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. | Comment Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Comment Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Setup Header .. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ................................................................. + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * .. Setup Header | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + /* we need 4 bytes for the number of headers (which is always 1), 3 bytes for + * the ident, 2 bytes for length, 1 byte for n. of headers. */ + size = 4 + 3 + 2 + 1; + + /* count the size of the headers first and update the hash */ + length = 0; + n_headers = 0; + ident = fnv1_hash_32_new (); + extralen = 1; + for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + GstMapInfo map; + guint bsize; + + bsize = gst_buffer_get_size (buf); + length += bsize; + n_headers++; + + /* count number of bytes needed for length fields, we don't need this for + * the last header. */ + if (g_list_next (walk)) { + do { + size++; + extralen++; + bsize >>= 7; + } while (bsize); + } + /* update hash */ + gst_buffer_map (buf, &map, GST_MAP_READ); + ident = fnv1_hash_32_update (ident, map.data, map.size); + gst_buffer_unmap (buf, &map); + } + + /* packet length is header size + packet length */ + configlen = size + length; + config = data = g_malloc (configlen); + + /* number of packed headers, we only pack 1 header */ + data[0] = 0; + data[1] = 0; + data[2] = 0; + data[3] = 1; + + ident = fnv1_hash_32_to_24 (ident); + rtpvorbispay->payload_ident = ident; + GST_DEBUG_OBJECT (rtpvorbispay, "ident 0x%08x", ident); + + /* take lower 3 bytes */ + data[4] = (ident >> 16) & 0xff; + data[5] = (ident >> 8) & 0xff; + data[6] = ident & 0xff; + + /* store length of all vorbis headers */ + data[7] = ((length) >> 8) & 0xff; + data[8] = (length) & 0xff; + + /* store number of headers minus one. */ + data[9] = n_headers - 1; + data += 10; + + /* store length for each header */ + for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + guint bsize, size, temp; + guint flag; + + /* only need to store the length when it's not the last header */ + if (!g_list_next (walk)) + break; + + bsize = gst_buffer_get_size (buf); + + /* calc size */ + size = 0; + do { + size++; + bsize >>= 7; + } while (bsize); + temp = size; + + bsize = gst_buffer_get_size (buf); + /* write the size backwards */ + flag = 0; + while (size) { + size--; + data[size] = (bsize & 0x7f) | flag; + bsize >>= 7; + flag = 0x80; /* Flag bit on all bytes of the length except the last */ + } + data += temp; + } + + /* copy header data */ + for (walk = rtpvorbispay->headers; walk; walk = g_list_next (walk)) { + GstBuffer *buf = GST_BUFFER_CAST (walk->data); + + gst_buffer_extract (buf, 0, data, gst_buffer_get_size (buf)); + data += gst_buffer_get_size (buf); + gst_buffer_unref (buf); + } + g_list_free (rtpvorbispay->headers); + rtpvorbispay->headers = NULL; + rtpvorbispay->need_headers = FALSE; + + /* serialize to base64 */ + configuration = g_base64_encode (config, configlen); + + /* store for later re-sending */ + if (rtpvorbispay->config_data) + g_free (rtpvorbispay->config_data); + rtpvorbispay->config_size = configlen - 4 - 3 - 2; + rtpvorbispay->config_data = g_malloc (rtpvorbispay->config_size); + rtpvorbispay->config_extra_len = extralen; + memcpy (rtpvorbispay->config_data, config + 4 + 3 + 2, + rtpvorbispay->config_size); + + g_free (config); + + /* configure payloader settings */ + cstr = g_strdup_printf ("%d", rtpvorbispay->channels); + gst_rtp_base_payload_set_options (basepayload, "audio", TRUE, "VORBIS", + rtpvorbispay->rate); + res = + gst_rtp_base_payload_set_outcaps (basepayload, "encoding-params", + G_TYPE_STRING, cstr, "configuration", G_TYPE_STRING, configuration, NULL); + g_free (cstr); + g_free (configuration); + + return res; + + /* ERRORS */ +no_headers: + { + GST_DEBUG_OBJECT (rtpvorbispay, "finish headers"); + return FALSE; + } +} + +static gboolean +gst_rtp_vorbis_pay_parse_id (GstRTPBasePayload * basepayload, guint8 * data, + guint size) +{ + GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload); + guint8 channels; + gint32 rate, version; + + if (G_UNLIKELY (size < 16)) + goto too_short; + + if (G_UNLIKELY (memcmp (data, "\001vorbis", 7))) + goto invalid_start; + data += 7; + + if (G_UNLIKELY ((version = GST_READ_UINT32_LE (data)) != 0)) + goto invalid_version; + data += 4; + + if (G_UNLIKELY ((channels = *data++) < 1)) + goto invalid_channels; + + if (G_UNLIKELY ((rate = GST_READ_UINT32_LE (data)) < 1)) + goto invalid_rate; + + /* all fine, store the values */ + rtpvorbispay->channels = channels; + rtpvorbispay->rate = rate; + + return TRUE; + + /* ERRORS */ +too_short: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + ("Identification packet is too short, need at least 16, got %d", size), + (NULL)); + return FALSE; + } +invalid_start: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + ("Invalid header start in identification packet"), (NULL)); + return FALSE; + } +invalid_version: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + ("Invalid version, expected 0, got %d", version), (NULL)); + return FALSE; + } +invalid_rate: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + ("Invalid rate %d", rate), (NULL)); + return FALSE; + } +invalid_channels: + { + GST_ELEMENT_ERROR (basepayload, STREAM, DECODE, + ("Invalid channels %d", channels), (NULL)); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_vorbis_pay_payload_buffer (GstRtpVorbisPay * rtpvorbispay, guint8 VDT, + guint8 * data, guint size, GstClockTime timestamp, GstClockTime duration, + guint not_in_length) +{ + GstFlowReturn ret = GST_FLOW_OK; + guint newsize; + guint packet_len; + GstClockTime newduration; + gboolean flush; + guint plen; + guint8 *ppos, *payload; + gboolean fragmented; + GstRTPBuffer rtp = { NULL }; + + /* size increases with packet length and 2 bytes size eader. */ + newduration = rtpvorbispay->payload_duration; + if (duration != GST_CLOCK_TIME_NONE) + newduration += duration; + + newsize = rtpvorbispay->payload_pos + 2 + size; + packet_len = gst_rtp_buffer_calc_packet_len (newsize, 0, 0); + + /* check buffer filled against length and max latency */ + flush = gst_rtp_base_payload_is_filled (GST_RTP_BASE_PAYLOAD (rtpvorbispay), + packet_len, newduration); + /* we can store up to 15 vorbis packets in one RTP packet. */ + flush |= (rtpvorbispay->payload_pkts == 15); + /* flush if we have a new VDT */ + if (rtpvorbispay->packet) + flush |= (rtpvorbispay->payload_VDT != VDT); + if (flush) + ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay); + + /* create new packet if we must */ + if (!rtpvorbispay->packet) { + gst_rtp_vorbis_pay_init_packet (rtpvorbispay, VDT, timestamp); + } + + gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + ppos = payload + rtpvorbispay->payload_pos; + fragmented = FALSE; + + /* put buffer in packet, it either fits completely or needs to be fragmented + * over multiple RTP packets. */ + do { + plen = MIN (rtpvorbispay->payload_left - 2, size); + + GST_LOG_OBJECT (rtpvorbispay, "append %u bytes", plen); + + /* data is copied in the payload with a 2 byte length header */ + ppos[0] = ((plen - not_in_length) >> 8) & 0xff; + ppos[1] = ((plen - not_in_length) & 0xff); + if (plen) + memcpy (&ppos[2], data, plen); + + /* only first (only) configuration cuts length field */ + /* NOTE: spec (if any) is not clear on this ... */ + not_in_length = 0; + + size -= plen; + data += plen; + + rtpvorbispay->payload_pos += plen + 2; + rtpvorbispay->payload_left -= plen + 2; + + if (fragmented) { + if (size == 0) + /* last fragment, set F to 0x3. */ + rtpvorbispay->payload_F = 0x3; + else + /* fragment continues, set F to 0x2. */ + rtpvorbispay->payload_F = 0x2; + } else { + if (size > 0) { + /* fragmented packet starts, set F to 0x1, mark ourselves as + * fragmented. */ + rtpvorbispay->payload_F = 0x1; + fragmented = TRUE; + } + } + if (fragmented) { + gst_rtp_buffer_unmap (&rtp); + /* fragmented packets are always flushed and have ptks of 0 */ + rtpvorbispay->payload_pkts = 0; + ret = gst_rtp_vorbis_pay_flush_packet (rtpvorbispay); + + if (size > 0) { + /* start new packet and get pointers. VDT stays the same. */ + gst_rtp_vorbis_pay_init_packet (rtpvorbispay, + rtpvorbispay->payload_VDT, timestamp); + gst_rtp_buffer_map (rtpvorbispay->packet, GST_MAP_WRITE, &rtp); + payload = gst_rtp_buffer_get_payload (&rtp); + ppos = payload + rtpvorbispay->payload_pos; + } + } else { + /* unfragmented packet, update stats for next packet, size == 0 and we + * exit the while loop */ + rtpvorbispay->payload_pkts++; + if (duration != GST_CLOCK_TIME_NONE) + rtpvorbispay->payload_duration += duration; + } + } while (size); + + if (rtp.buffer) + gst_rtp_buffer_unmap (&rtp); + + return ret; +} + +static GstFlowReturn +gst_rtp_vorbis_pay_handle_buffer (GstRTPBasePayload * basepayload, + GstBuffer * buffer) +{ + GstRtpVorbisPay *rtpvorbispay; + GstFlowReturn ret; + GstMapInfo map; + gsize size; + guint8 *data; + GstClockTime duration, timestamp; + guint8 VDT; + + rtpvorbispay = GST_RTP_VORBIS_PAY (basepayload); + + gst_buffer_map (buffer, &map, GST_MAP_READ); + data = map.data; + size = map.size; + duration = GST_BUFFER_DURATION (buffer); + timestamp = GST_BUFFER_TIMESTAMP (buffer); + + GST_LOG_OBJECT (rtpvorbispay, "size %" G_GSIZE_FORMAT + ", duration %" GST_TIME_FORMAT, size, GST_TIME_ARGS (duration)); + + if (G_UNLIKELY (size < 1)) + goto wrong_size; + + /* find packet type */ + if (data[0] & 1) { + /* header */ + if (data[0] == 1) { + /* identification, we need to parse this in order to get the clock rate. */ + if (G_UNLIKELY (!gst_rtp_vorbis_pay_parse_id (basepayload, data, size))) + goto parse_id_failed; + VDT = 1; + } else if (data[0] == 3) { + /* comment */ + VDT = 2; + } else if (data[0] == 5) { + /* setup */ + VDT = 1; + } else + goto unknown_header; + } else + /* data */ + VDT = 0; + + /* we need to collect the headers and construct a config string from them */ + if (VDT != 0) { + GST_DEBUG_OBJECT (rtpvorbispay, "collecting header"); + /* append header to the list of headers */ + gst_buffer_unmap (buffer, &map); + rtpvorbispay->headers = g_list_append (rtpvorbispay->headers, buffer); + ret = GST_FLOW_OK; + goto done; + } else if (rtpvorbispay->headers) { + if (rtpvorbispay->need_headers) { + if (!gst_rtp_vorbis_pay_finish_headers (basepayload)) + goto header_error; + } else { + g_list_free_full (rtpvorbispay->headers, + (GDestroyNotify) gst_buffer_unref); + rtpvorbispay->headers = NULL; + } + } + + /* there is a config request, see if we need to insert it */ + if (rtpvorbispay->config_interval > 0 && rtpvorbispay->config_data) { + gboolean send_config = FALSE; + + if (rtpvorbispay->last_config != -1) { + guint64 diff; + + GST_LOG_OBJECT (rtpvorbispay, + "now %" GST_TIME_FORMAT ", last config %" GST_TIME_FORMAT, + GST_TIME_ARGS (timestamp), GST_TIME_ARGS (rtpvorbispay->last_config)); + + /* calculate diff between last config in milliseconds */ + if (timestamp > rtpvorbispay->last_config) { + diff = timestamp - rtpvorbispay->last_config; + } else { + diff = 0; + } + + GST_DEBUG_OBJECT (rtpvorbispay, + "interval since last config %" GST_TIME_FORMAT, GST_TIME_ARGS (diff)); + + /* bigger than interval, queue config */ + /* FIXME should convert timestamps to running time */ + if (GST_TIME_AS_SECONDS (diff) >= rtpvorbispay->config_interval) { + GST_DEBUG_OBJECT (rtpvorbispay, "time to send config"); + send_config = TRUE; + } + } else { + /* no known previous config time, send now */ + GST_DEBUG_OBJECT (rtpvorbispay, "no previous config time, send now"); + send_config = TRUE; + } + + if (send_config) { + /* we need to send config now first */ + /* different TDT type forces flush */ + gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, 1, + rtpvorbispay->config_data, rtpvorbispay->config_size, + timestamp, GST_CLOCK_TIME_NONE, rtpvorbispay->config_extra_len); + + if (timestamp != -1) { + rtpvorbispay->last_config = timestamp; + } + } + } + + ret = gst_rtp_vorbis_pay_payload_buffer (rtpvorbispay, VDT, data, size, + timestamp, duration, 0); + + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + +done: + return ret; + + /* ERRORS */ +wrong_size: + { + GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE, + ("Invalid packet size (1 < %" G_GSIZE_FORMAT ")", size), (NULL)); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +parse_id_failed: + { + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_ERROR; + } +unknown_header: + { + GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE, + (NULL), ("Ignoring unknown header received")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +header_error: + { + GST_ELEMENT_WARNING (rtpvorbispay, STREAM, DECODE, + (NULL), ("Error initializing header config")); + gst_buffer_unmap (buffer, &map); + gst_buffer_unref (buffer); + return GST_FLOW_OK; + } +} + +static gboolean +gst_rtp_vorbis_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + GstRtpVorbisPay *rtpvorbispay = GST_RTP_VORBIS_PAY (payload); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_vorbis_pay_clear_packet (rtpvorbispay); + break; + default: + break; + } + /* false to let parent handle event as well */ + return GST_RTP_BASE_PAYLOAD_CLASS (parent_class)->sink_event (payload, event); +} + +static GstStateChangeReturn +gst_rtp_vorbis_pay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpVorbisPay *rtpvorbispay; + GstStateChangeReturn ret; + + rtpvorbispay = GST_RTP_VORBIS_PAY (element); + + switch (transition) { + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_vorbis_pay_cleanup (rtpvorbispay); + break; + default: + break; + } + return ret; +} + +static void +gst_rtp_vorbis_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpVorbisPay *rtpvorbispay; + + rtpvorbispay = GST_RTP_VORBIS_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + rtpvorbispay->config_interval = g_value_get_uint (value); + break; + default: + break; + } +} + +static void +gst_rtp_vorbis_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpVorbisPay *rtpvorbispay; + + rtpvorbispay = GST_RTP_VORBIS_PAY (object); + + switch (prop_id) { + case PROP_CONFIG_INTERVAL: + g_value_set_uint (value, rtpvorbispay->config_interval); + break; + default: + break; + } +} + +gboolean +gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvorbispay", + GST_RANK_SECONDARY, GST_TYPE_RTP_VORBIS_PAY); +} diff --git a/gst/rtp/gstrtpvorbispay.h b/gst/rtp/gstrtpvorbispay.h new file mode 100755 index 0000000..8f0974a --- /dev/null +++ b/gst/rtp/gstrtpvorbispay.h @@ -0,0 +1,84 @@ +/* GStreamer + * Copyright (C) <2005> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_VORBIS_PAY_H__ +#define __GST_RTP_VORBIS_PAY_H__ + +#include <gst/gst.h> +#include <gst/rtp/gstrtpbasepayload.h> +#include <gst/base/gstadapter.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VORBIS_PAY \ + (gst_rtp_vorbis_pay_get_type()) +#define GST_RTP_VORBIS_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_VORBIS_PAY,GstRtpVorbisPay)) +#define GST_RTP_VORBIS_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_VORBIS_PAY,GstRtpVorbisPayClass)) +#define GST_IS_RTP_VORBIS_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_VORBIS_PAY)) +#define GST_IS_RTP_VORBIS_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_VORBIS_PAY)) + +typedef struct _GstRtpVorbisPay GstRtpVorbisPay; +typedef struct _GstRtpVorbisPayClass GstRtpVorbisPayClass; + +struct _GstRtpVorbisPay +{ + GstRTPBasePayload payload; + + /* the headers */ + gboolean need_headers; + GList *headers; + + /* queues of buffers along with some stats. */ + GstBuffer *packet; + guint payload_pos; + guint payload_left; + guint32 payload_ident; + guint8 payload_F; + guint8 payload_VDT; + guint payload_pkts; + GstClockTime payload_timestamp; + GstClockTime payload_duration; + + /* config (re-sending) */ + guint8 *config_data; + guint config_size; + guint config_extra_len; + guint config_interval; + GstClockTime last_config; + + gint rate; + gint channels; +}; + +struct _GstRtpVorbisPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_vorbis_pay_get_type (void); + +gboolean gst_rtp_vorbis_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_VORBIS_PAY_H__ */ diff --git a/gst/rtp/gstrtpvp8depay.c b/gst/rtp/gstrtpvp8depay.c new file mode 100755 index 0000000..cafdad5 --- /dev/null +++ b/gst/rtp/gstrtpvp8depay.c @@ -0,0 +1,296 @@ +/* gstrtpvp8depay.c - Source for GstRtpVP8Depay + * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> + * Copyright (C) 2011 Collabora Ltd. + * Contact: Youness Alaoui <youness.alaoui@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstrtpvp8depay.h" + +#include <gst/video/video.h> + +#include <stdio.h> + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp8_depay_debug); +#define GST_CAT_DEFAULT gst_rtp_vp8_depay_debug + +static void gst_rtp_vp8_depay_dispose (GObject * object); +static GstBuffer *gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); +static GstStateChangeReturn gst_rtp_vp8_depay_change_state (GstElement * + element, GstStateChange transition); +static gboolean gst_rtp_vp8_depay_handle_event (GstRTPBaseDepayload * depay, + GstEvent * event); + +G_DEFINE_TYPE (GstRtpVP8Depay, gst_rtp_vp8_depay, GST_TYPE_RTP_BASE_DEPAYLOAD); + +static GstStaticPadTemplate gst_rtp_vp8_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-vp8")); + +static GstStaticPadTemplate gst_rtp_vp8_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "clock-rate = (int) 90000," + "media = (string) \"video\"," + "encoding-name = (string) { \"VP8\", \"VP8-DRAFT-IETF-01\" }")); + +static void +gst_rtp_vp8_depay_init (GstRtpVP8Depay * self) +{ + self->adapter = gst_adapter_new (); + self->started = FALSE; +} + +static void +gst_rtp_vp8_depay_class_init (GstRtpVP8DepayClass * gst_rtp_vp8_depay_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (gst_rtp_vp8_depay_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (gst_rtp_vp8_depay_class); + GstRTPBaseDepayloadClass *depay_class = + (GstRTPBaseDepayloadClass *) (gst_rtp_vp8_depay_class); + + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_vp8_depay_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_vp8_depay_src_template)); + + gst_element_class_set_static_metadata (element_class, "RTP VP8 depayloader", + "Codec/Depayloader/Network/RTP", + "Extracts VP8 video from RTP packets)", + "Sjoerd Simons <sjoerd@luon.net>"); + + object_class->dispose = gst_rtp_vp8_depay_dispose; + + element_class->change_state = gst_rtp_vp8_depay_change_state; + + depay_class->process = gst_rtp_vp8_depay_process; + depay_class->handle_event = gst_rtp_vp8_depay_handle_event; + + GST_DEBUG_CATEGORY_INIT (gst_rtp_vp8_depay_debug, "rtpvp8depay", 0, + "VP8 Video RTP Depayloader"); +} + +static void +gst_rtp_vp8_depay_dispose (GObject * object) +{ + GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (object); + + if (self->adapter != NULL) + g_object_unref (self->adapter); + self->adapter = NULL; + + /* release any references held by the object here */ + + if (G_OBJECT_CLASS (gst_rtp_vp8_depay_parent_class)->dispose) + G_OBJECT_CLASS (gst_rtp_vp8_depay_parent_class)->dispose (object); +} + +static GstBuffer * +gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstBuffer * buf) +{ + GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (depay); + GstBuffer *payload; + guint8 *data; + guint hdrsize; + guint size; + GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT; + + if (G_UNLIKELY (GST_BUFFER_IS_DISCONT (buf))) { + GST_LOG_OBJECT (self, "Discontinuity, flushing adapter"); + gst_adapter_clear (self->adapter); + self->started = FALSE; + } + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuffer); + size = gst_rtp_buffer_get_payload_len (&rtpbuffer); + + /* At least one header and one vp8 byte */ + if (G_UNLIKELY (size < 2)) + goto too_small; + + data = gst_rtp_buffer_get_payload (&rtpbuffer); + + if (G_UNLIKELY (!self->started)) { + /* Check if this is the start of a VP8 frame, otherwise bail */ + /* S=1 and PartID= 0 */ + if ((data[0] & 0x1F) != 0x10) + goto done; + + self->started = TRUE; + } + + hdrsize = 1; + /* Check X optional header */ + if ((data[0] & 0x80) != 0) { + hdrsize++; + /* Check I optional header */ + if ((data[1] & 0x80) != 0) { + if (G_UNLIKELY (size < 3)) + goto too_small; + hdrsize++; + /* Check for 16 bits PictureID */ + if ((data[2] & 0x80) != 0) + hdrsize++; + } + /* Check L optional header */ + if ((data[1] & 0x40) != 0) + hdrsize++; + /* Check T or K optional headers */ + if ((data[1] & 0x20) != 0 || (data[1] & 0x10) != 0) + hdrsize++; + } + GST_DEBUG_OBJECT (depay, "hdrsize %u, size %u", hdrsize, size); + + if (G_UNLIKELY (hdrsize >= size)) + goto too_small; + + payload = gst_rtp_buffer_get_payload_subbuffer (&rtpbuffer, hdrsize, -1); + gst_adapter_push (self->adapter, payload); + + /* Marker indicates that it was the last rtp packet for this frame */ + if (gst_rtp_buffer_get_marker (&rtpbuffer)) { + GstBuffer *out; + guint8 flag0; + + gst_adapter_copy (self->adapter, &flag0, 0, 1); + + out = gst_adapter_take_buffer (self->adapter, + gst_adapter_available (self->adapter)); + + self->started = FALSE; + gst_rtp_buffer_unmap (&rtpbuffer); + + /* mark keyframes */ + out = gst_buffer_make_writable (out); + if ((flag0 & 0x01)) { + GST_BUFFER_FLAG_SET (out, GST_BUFFER_FLAG_DELTA_UNIT); + + if (!self->caps_sent) { + gst_buffer_unref (out); + out = NULL; + GST_INFO_OBJECT (self, "Dropping inter-frame before intra-frame"); + gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depay), + gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, + TRUE, 0)); + } + } else { + GstMapInfo info; + + GST_BUFFER_FLAG_UNSET (out, GST_BUFFER_FLAG_DELTA_UNIT); + + if (gst_buffer_map (out, &info, GST_MAP_READ)) { + guint profile, width, height; + + profile = (flag0 & 0x0e) >> 1; + width = GST_READ_UINT16_LE (info.data + 6) & 0x3fff; + height = GST_READ_UINT16_LE (info.data + 8) & 0x3fff; + gst_buffer_unmap (out, &info); + + if (G_UNLIKELY (self->last_width != width || + self->last_height != height || self->last_profile != profile)) { + gchar profile_str[3]; + GstCaps *srccaps; + + snprintf (profile_str, 3, "%u", profile); + srccaps = gst_caps_new_simple ("video/x-vp8", + "framerate", GST_TYPE_FRACTION, 0, 1, + "height", G_TYPE_INT, height, + "width", G_TYPE_INT, width, + "profile", G_TYPE_STRING, profile_str, NULL); + + gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depay), srccaps); + gst_caps_unref (srccaps); + + self->caps_sent = TRUE; + self->last_width = width; + self->last_height = height; + self->last_profile = profile; + } + } + } + + return out; + } + +done: + gst_rtp_buffer_unmap (&rtpbuffer); + return NULL; + +too_small: + GST_LOG_OBJECT (self, "Invalid rtp packet (too small), ignoring"); + gst_adapter_clear (self->adapter); + self->started = FALSE; + + goto done; +} + +static GstStateChangeReturn +gst_rtp_vp8_depay_change_state (GstElement * element, GstStateChange transition) +{ + GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_READY_TO_PAUSED: + self->last_profile = -1; + self->last_height = -1; + self->last_width = -1; + self->caps_sent = FALSE; + break; + default: + break; + } + + return + GST_ELEMENT_CLASS (gst_rtp_vp8_depay_parent_class)->change_state (element, + transition); +} + +static gboolean +gst_rtp_vp8_depay_handle_event (GstRTPBaseDepayload * depay, GstEvent * event) +{ + GstRtpVP8Depay *self = GST_RTP_VP8_DEPAY (depay); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + self->last_profile = -1; + self->last_height = -1; + self->last_width = -1; + break; + default: + break; + } + + return + GST_RTP_BASE_DEPAYLOAD_CLASS + (gst_rtp_vp8_depay_parent_class)->handle_event (depay, event); +} + +gboolean +gst_rtp_vp8_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvp8depay", + GST_RANK_MARGINAL, GST_TYPE_RTP_VP8_DEPAY); +} diff --git a/gst/rtp/gstrtpvp8depay.h b/gst/rtp/gstrtpvp8depay.h new file mode 100755 index 0000000..258546a --- /dev/null +++ b/gst/rtp/gstrtpvp8depay.h @@ -0,0 +1,69 @@ +/* + * gstrtpvp8depay.h - Header for GstRtpVP8Depay + * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GST_RTP_VP8_DEPAY_H__ +#define __GST_RTP_VP8_DEPAY_H__ + +#include <gst/base/gstadapter.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VP8_DEPAY \ + (gst_rtp_vp8_depay_get_type()) +#define GST_RTP_VP8_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RTP_VP8_DEPAY, GstRtpVP8Depay)) +#define GST_RTP_VP8_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RTP_VP8_DEPAY, \ + GstRtpVP8DepayClass)) +#define GST_IS_RTP_VP8_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RTP_VP8_DEPAY)) +#define GST_IS_RTP_VP8_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RTP_VP8_DEPAY)) +#define GST_RTP_VP8_DEPAY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_VP8_DEPAY, \ + GstRtpVP8DepayClass)) + +typedef struct _GstRtpVP8Depay GstRtpVP8Depay; +typedef struct _GstRtpVP8DepayClass GstRtpVP8DepayClass; + +struct _GstRtpVP8DepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +struct _GstRtpVP8Depay +{ + GstRTPBaseDepayload parent; + GstAdapter *adapter; + gboolean started; + + gboolean caps_sent; + gint last_profile; + gint last_width; + gint last_height; +}; + +GType gst_rtp_vp8_depay_get_type (void); + +gboolean gst_rtp_vp8_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* #ifndef __GST_RTP_VP8_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpvp8pay.c b/gst/rtp/gstrtpvp8pay.c new file mode 100755 index 0000000..133c2fd --- /dev/null +++ b/gst/rtp/gstrtpvp8pay.c @@ -0,0 +1,539 @@ +/* + * gstrtpvp8pay.c - Source for GstRtpVP8Pay + * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> + * Copyright (C) 2011 Collabora Ltd. + * Contact: Youness Alaoui <youness.alaoui@collabora.co.uk> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <gst/base/gstbitreader.h> +#include <gst/rtp/gstrtppayloads.h> +#include <gst/rtp/gstrtpbuffer.h> +#include "dboolhuff.h" +#include "gstrtpvp8pay.h" + +GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp8_pay_debug); +#define GST_CAT_DEFAULT gst_rtp_vp8_pay_debug + +#define DEFAULT_PICTURE_ID_MODE VP8_PAY_NO_PICTURE_ID + +enum +{ + PROP_0, + PROP_PICTURE_ID_MODE +}; + +#define GST_TYPE_RTP_VP8_PAY_PICTURE_ID_MODE (gst_rtp_vp8_pay_picture_id_mode_get_type()) +static GType +gst_rtp_vp8_pay_picture_id_mode_get_type (void) +{ + static GType mode_type = 0; + static const GEnumValue modes[] = { + {VP8_PAY_NO_PICTURE_ID, "No Picture ID", "none"}, + {VP8_PAY_PICTURE_ID_7BITS, "7-bit Picture ID", "7-bit"}, + {VP8_PAY_PICTURE_ID_15BITS, "15-bit Picture ID", "15-bit"}, + {0, NULL, NULL}, + }; + + if (!mode_type) { + mode_type = g_enum_register_static ("GstVP8RTPPayMode", modes); + } + return mode_type; +} + +static void gst_rtp_vp8_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_vp8_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +static GstFlowReturn gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, + GstBuffer * buffer); +static gboolean gst_rtp_vp8_pay_sink_event (GstRTPBasePayload * payload, + GstEvent * event); +static gboolean gst_rtp_vp8_pay_set_caps (GstRTPBasePayload * payload, + GstCaps * caps); + +G_DEFINE_TYPE (GstRtpVP8Pay, gst_rtp_vp8_pay, GST_TYPE_RTP_BASE_PAYLOAD); + +static GstStaticPadTemplate gst_rtp_vp8_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING "," + "clock-rate = (int) 90000, encoding-name = (string) { \"VP8\", \"VP8-DRAFT-IETF-01\" }")); + +static GstStaticPadTemplate gst_rtp_vp8_pay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-vp8")); + +static void +gst_rtp_vp8_pay_init (GstRtpVP8Pay * obj) +{ + obj->picture_id_mode = DEFAULT_PICTURE_ID_MODE; + if (obj->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS) + obj->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F; + else if (obj->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS) + obj->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF; +} + +static void +gst_rtp_vp8_pay_class_init (GstRtpVP8PayClass * gst_rtp_vp8_pay_class) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (gst_rtp_vp8_pay_class); + GstElementClass *element_class = GST_ELEMENT_CLASS (gst_rtp_vp8_pay_class); + GstRTPBasePayloadClass *pay_class = + GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_vp8_pay_class); + + gobject_class->set_property = gst_rtp_vp8_pay_set_property; + gobject_class->get_property = gst_rtp_vp8_pay_get_property; + + g_object_class_install_property (gobject_class, PROP_PICTURE_ID_MODE, + g_param_spec_enum ("picture-id-mode", "Picture ID Mode", + "The picture ID mode for payloading", + GST_TYPE_RTP_VP8_PAY_PICTURE_ID_MODE, DEFAULT_PICTURE_ID_MODE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_vp8_pay_sink_template)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_rtp_vp8_pay_src_template)); + + gst_element_class_set_static_metadata (element_class, "RTP VP8 payloader", + "Codec/Payloader/Network/RTP", + "Puts VP8 video in RTP packets)", "Sjoerd Simons <sjoerd@luon.net>"); + + pay_class->handle_buffer = gst_rtp_vp8_pay_handle_buffer; + pay_class->sink_event = gst_rtp_vp8_pay_sink_event; + pay_class->set_caps = gst_rtp_vp8_pay_set_caps; + + GST_DEBUG_CATEGORY_INIT (gst_rtp_vp8_pay_debug, "rtpvp8pay", 0, + "VP8 Video RTP Payloader"); +} + +static void +gst_rtp_vp8_pay_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) +{ + GstRtpVP8Pay *rtpvp8pay = GST_RTP_VP8_PAY (object); + + switch (prop_id) { + case PROP_PICTURE_ID_MODE: + rtpvp8pay->picture_id_mode = g_value_get_enum (value); + if (rtpvp8pay->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS) + rtpvp8pay->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F; + else if (rtpvp8pay->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS) + rtpvp8pay->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF; + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_vp8_pay_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstRtpVP8Pay *rtpvp8pay = GST_RTP_VP8_PAY (object); + + switch (prop_id) { + case PROP_PICTURE_ID_MODE: + g_value_set_enum (value, rtpvp8pay->picture_id_mode); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static gboolean +gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer, + gsize buffer_size) +{ + GstMapInfo map = GST_MAP_INFO_INIT; + GstBitReader reader; + guint8 *data; + gsize size; + int i; + gboolean keyframe; + guint32 partition0_size; + guint8 version; + guint8 tmp8 = 0; + guint8 partitions; + guint offset; + BOOL_DECODER bc; + guint8 *pdata; + + if (G_UNLIKELY (buffer_size < 3)) + goto error; + + if (!gst_buffer_map (buffer, &map, GST_MAP_READ) || !map.data) + goto error; + + data = map.data; + size = map.size; + + gst_bit_reader_init (&reader, data, size); + + self->is_keyframe = keyframe = ((data[0] & 0x1) == 0); + version = (data[0] >> 1) & 0x7; + + if (G_UNLIKELY (version > 3)) { + GST_ERROR_OBJECT (self, "Unknown VP8 version %u", version); + goto error; + } + + /* keyframe, version and show_frame use 5 bits */ + partition0_size = data[2] << 11 | data[1] << 3 | (data[0] >> 5); + + /* Include the uncompressed data blob in the first partition */ + offset = keyframe ? 10 : 3; + partition0_size += offset; + + if (!gst_bit_reader_skip (&reader, 24)) + goto error; + + if (keyframe) { + /* check start tag: 0x9d 0x01 0x2a */ + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x9d) + goto error; + + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x01) + goto error; + + if (!gst_bit_reader_get_bits_uint8 (&reader, &tmp8, 8) || tmp8 != 0x2a) + goto error; + + /* Skip horizontal size code (16 bits) vertical size code (16 bits) */ + if (!gst_bit_reader_skip (&reader, 32)) + goto error; + } + + offset = keyframe ? 10 : 3; + vp8dx_start_decode (&bc, data + offset, size - offset); + + if (keyframe) { + /* color space (1 bit) and clamping type (1 bit) */ + vp8dx_decode_bool (&bc, 0x80); + vp8dx_decode_bool (&bc, 0x80); + } + + /* segmentation_enabled */ + if (vp8dx_decode_bool (&bc, 0x80)) { + guint8 update_mb_segmentation_map = vp8dx_decode_bool (&bc, 0x80); + guint8 update_segment_feature_data = vp8dx_decode_bool (&bc, 0x80); + + if (update_segment_feature_data) { + /* skip segment feature mode */ + vp8dx_decode_bool (&bc, 0x80); + + /* quantizer update */ + for (i = 0; i < 4; i++) { + /* skip flagged quantizer value (7 bits) and sign (1 bit) */ + if (vp8dx_decode_bool (&bc, 0x80)) + vp8_decode_value (&bc, 8); + } + + /* loop filter update */ + for (i = 0; i < 4; i++) { + /* skip flagged lf update value (6 bits) and sign (1 bit) */ + if (vp8dx_decode_bool (&bc, 0x80)) + vp8_decode_value (&bc, 7); + } + } + + if (update_mb_segmentation_map) { + /* segment prob update */ + for (i = 0; i < 3; i++) { + /* skip flagged segment prob */ + if (vp8dx_decode_bool (&bc, 0x80)) + vp8_decode_value (&bc, 8); + } + } + } + + /* skip filter type (1 bit), loop filter level (6 bits) and + * sharpness level (3 bits) */ + vp8_decode_value (&bc, 1); + vp8_decode_value (&bc, 6); + vp8_decode_value (&bc, 3); + + /* loop_filter_adj_enabled */ + if (vp8dx_decode_bool (&bc, 0x80)) { + + /* delta update */ + if (vp8dx_decode_bool (&bc, 0x80)) { + + for (i = 0; i < 8; i++) { + /* 8 updates, 1 bit indicate whether there is one and if follow by a + * 7 bit update */ + if (vp8dx_decode_bool (&bc, 0x80)) + vp8_decode_value (&bc, 7); + } + } + } + + if (vp8dx_bool_error (&bc)) + goto error; + + tmp8 = vp8_decode_value (&bc, 2); + + partitions = 1 << tmp8; + + /* Check if things are still sensible */ + if (partition0_size + (partitions - 1) * 3 >= size) + goto error; + + /* partition data is right after the mode partition */ + pdata = data + partition0_size; + + /* Set up mapping */ + self->n_partitions = partitions + 1; + self->partition_offset[0] = 0; + self->partition_size[0] = partition0_size + (partitions - 1) * 3; + + self->partition_offset[1] = self->partition_size[0]; + for (i = 1; i < partitions; i++) { + guint psize = (pdata[2] << 16 | pdata[1] << 8 | pdata[0]); + + pdata += 3; + self->partition_size[i] = psize; + self->partition_offset[i + 1] = self->partition_offset[i] + psize; + } + + /* Check that our partition offsets and sizes don't go outsize the buffer + * size. */ + if (self->partition_offset[i] >= size) + goto error; + + self->partition_size[i] = size - self->partition_offset[i]; + + self->partition_offset[i + 1] = size; + + gst_buffer_unmap (buffer, &map); + return TRUE; + +error: + GST_DEBUG ("Failed to parse frame"); + if (map.memory != NULL) { + gst_buffer_unmap (buffer, &map); + } + return FALSE; +} + +static guint +gst_rtp_vp8_offset_to_partition (GstRtpVP8Pay * self, guint offset) +{ + int i; + + for (i = 0; i < self->n_partitions; i++) { + if (offset >= self->partition_offset[i] && + offset < self->partition_offset[i + 1]) + return i; + } + + return i; +} + +static gsize +gst_rtp_vp8_calc_header_len (GstRtpVP8Pay * self) +{ + switch (self->picture_id_mode) { + case VP8_PAY_PICTURE_ID_7BITS: + return 3; + case VP8_PAY_PICTURE_ID_15BITS: + return 4; + case VP8_PAY_NO_PICTURE_ID: + default: + return 1; + } +} + +/* When growing the vp8 header keep max payload len calculation in sync */ +static GstBuffer * +gst_rtp_vp8_create_header_buffer (GstRtpVP8Pay * self, guint8 partid, + gboolean start, gboolean mark, GstBuffer * in) +{ + GstBuffer *out; + guint8 *p; + GstRTPBuffer rtpbuffer = GST_RTP_BUFFER_INIT; + + out = gst_rtp_buffer_new_allocate (gst_rtp_vp8_calc_header_len (self), 0, 0); + gst_rtp_buffer_map (out, GST_MAP_READWRITE, &rtpbuffer); + p = gst_rtp_buffer_get_payload (&rtpbuffer); + /* X=0,R=0,N=0,S=start,PartID=partid */ + p[0] = (start << 4) | partid; + if (self->picture_id_mode != VP8_PAY_NO_PICTURE_ID) { + /* Enable X=1 */ + p[0] |= 0x80; + /* X: I=1,L=0,T=0,K=0,RSV=0 */ + p[1] = 0x80; + if (self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS) { + /* I: 7 bit picture_id */ + p[2] = self->picture_id & 0x7F; + } else { + /* I: 15 bit picture_id */ + p[2] = 0x80 | ((self->picture_id & 0x7FFF) >> 8); + p[3] = self->picture_id & 0xFF; + } + } + + gst_rtp_buffer_set_marker (&rtpbuffer, mark); + + gst_rtp_buffer_unmap (&rtpbuffer); + + GST_BUFFER_DURATION (out) = GST_BUFFER_DURATION (in); + GST_BUFFER_PTS (out) = GST_BUFFER_PTS (in); + + return out; +} + + +static guint +gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, GstBufferList * list, + guint offset, GstBuffer * buffer, gsize buffer_size, gsize max_payload_len) +{ + guint partition; + GstBuffer *header; + GstBuffer *sub; + GstBuffer *out; + gboolean mark; + gsize remaining; + gsize available; + + remaining = buffer_size - offset; + available = max_payload_len; + if (available > remaining) + available = remaining; + + partition = gst_rtp_vp8_offset_to_partition (self, offset); + g_assert (partition < self->n_partitions); + + mark = (remaining == available); + /* whole set of partitions, payload them and done */ + header = gst_rtp_vp8_create_header_buffer (self, partition, + offset == self->partition_offset[partition], mark, buffer); + sub = gst_buffer_copy_region (buffer, GST_BUFFER_COPY_ALL, offset, available); + + out = gst_buffer_append (header, sub); + + gst_buffer_list_insert (list, -1, out); + + return available; +} + + +static GstFlowReturn +gst_rtp_vp8_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + GstRtpVP8Pay *self = GST_RTP_VP8_PAY (payload); + GstFlowReturn ret; + GstBufferList *list; + gsize size, max_paylen; + guint offset, mtu, vp8_hdr_len; + + size = gst_buffer_get_size (buffer); + + if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer, size))) { + GST_ELEMENT_ERROR (self, STREAM, ENCODE, (NULL), + ("Failed to parse VP8 frame")); + return GST_FLOW_ERROR; + } + + mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); + vp8_hdr_len = gst_rtp_vp8_calc_header_len (self); + max_paylen = gst_rtp_buffer_calc_payload_len (mtu - vp8_hdr_len, 0, 0); + + list = gst_buffer_list_new_sized ((size / max_paylen) + 1); + + offset = 0; + while (offset < size) { + offset += + gst_rtp_vp8_payload_next (self, list, offset, buffer, size, max_paylen); + } + + ret = gst_rtp_base_payload_push_list (payload, list); + + /* Incremenent and wrap the picture id if it overflows */ + if ((self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS && + ++self->picture_id >= 0x80) || + (self->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS && + ++self->picture_id >= 0x8000)) + self->picture_id = 0; + + gst_buffer_unref (buffer); + + return ret; +} + +static gboolean +gst_rtp_vp8_pay_sink_event (GstRTPBasePayload * payload, GstEvent * event) +{ + GstRtpVP8Pay *self = GST_RTP_VP8_PAY (payload); + + if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) { + if (self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS) + self->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F; + else if (self->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS) + self->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF; + } + + return GST_RTP_BASE_PAYLOAD_CLASS (gst_rtp_vp8_pay_parent_class)->sink_event + (payload, event); +} + +static gboolean +gst_rtp_vp8_pay_set_caps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstCaps *src_caps; + GstStructure *s; + char *encoding_name; + + src_caps = gst_pad_get_allowed_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload)); + if (src_caps) { + src_caps = gst_caps_make_writable (src_caps); + src_caps = gst_caps_truncate (src_caps); + s = gst_caps_get_structure (src_caps, 0); + gst_structure_fixate_field_string (s, "encoding-name", "VP8"); + encoding_name = g_strdup (gst_structure_get_string (s, "encoding-name")); + gst_caps_unref (src_caps); + } else { + encoding_name = g_strdup ("VP8-DRAFT-IETF-01"); + } + + gst_rtp_base_payload_set_options (payload, "video", TRUE, + encoding_name, 90000); + g_free (encoding_name); + return gst_rtp_base_payload_set_outcaps (payload, NULL); +} + +gboolean +gst_rtp_vp8_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvp8pay", + GST_RANK_MARGINAL, GST_TYPE_RTP_VP8_PAY); +} diff --git a/gst/rtp/gstrtpvp8pay.h b/gst/rtp/gstrtpvp8pay.h new file mode 100755 index 0000000..2472060 --- /dev/null +++ b/gst/rtp/gstrtpvp8pay.h @@ -0,0 +1,74 @@ +/* + * gstrtpvp8pay.h - Header for GstRtpVP8Pay + * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __GST_RTP_VP8_PAY_H__ +#define __GST_RTP_VP8_PAY_H__ + +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VP8_PAY \ + (gst_rtp_vp8_pay_get_type()) +#define GST_RTP_VP8_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_RTP_VP8_PAY, GstRtpVP8Pay)) +#define GST_RTP_VP8_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_RTP_VP8_PAY, GstRtpVP8PayClass)) +#define GST_IS_RTP_VP8_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_RTP_VP8_PAY)) +#define GST_IS_RTP_VP8_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_RTP_VP8_PAY)) +#define GST_RTP_VP8_PAY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_VP8_PAY, GstRtpVP8PayClass)) + +typedef struct _GstRtpVP8Pay GstRtpVP8Pay; +typedef struct _GstRtpVP8PayClass GstRtpVP8PayClass; +typedef enum _PictureIDMode PictureIDMode; + +enum _PictureIDMode { + VP8_PAY_NO_PICTURE_ID, + VP8_PAY_PICTURE_ID_7BITS, + VP8_PAY_PICTURE_ID_15BITS, +}; + +struct _GstRtpVP8PayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +struct _GstRtpVP8Pay +{ + GstRTPBasePayload parent; + gboolean is_keyframe; + gint n_partitions; + /* Treat frame header & tag & partition size block as the first partition, + * folowed by max. 8 data partitions. last offset is the end of the buffer */ + guint partition_offset[10]; + guint partition_size[9]; + PictureIDMode picture_id_mode; + guint16 picture_id; +}; + +GType gst_rtp_vp8_pay_get_type (void); + +gboolean gst_rtp_vp8_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* #ifndef __GST_RTP_VP8_PAY_H__ */ diff --git a/gst/rtp/gstrtpvrawdepay.c b/gst/rtp/gstrtpvrawdepay.c new file mode 100755 index 0000000..ee106c5 --- /dev/null +++ b/gst/rtp/gstrtpvrawdepay.c @@ -0,0 +1,668 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <gst/rtp/gstrtpbuffer.h> + +#include <string.h> +#include <stdlib.h> +#include "gstrtpvrawdepay.h" + +GST_DEBUG_CATEGORY_STATIC (rtpvrawdepay_debug); +#define GST_CAT_DEFAULT (rtpvrawdepay_debug) + +static GstStaticPadTemplate gst_rtp_vraw_depay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw") + ); + +static GstStaticPadTemplate gst_rtp_vraw_depay_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"RAW\", " + "sampling = (string) { \"RGB\", \"RGBA\", \"BGR\", \"BGRA\", " + "\"YCbCr-4:4:4\", \"YCbCr-4:2:2\", \"YCbCr-4:2:0\", " + "\"YCbCr-4:1:1\" }," + /* we cannot express these as strings + * "width = (string) [1 32767]," + * "height = (string) [1 32767]," + */ + "depth = (string) { \"8\", \"10\", \"12\", \"16\" }") + ); + +#define gst_rtp_vraw_depay_parent_class parent_class +G_DEFINE_TYPE (GstRtpVRawDepay, gst_rtp_vraw_depay, + GST_TYPE_RTP_BASE_DEPAYLOAD); + +static gboolean gst_rtp_vraw_depay_setcaps (GstRTPBaseDepayload * depayload, + GstCaps * caps); +static GstBuffer *gst_rtp_vraw_depay_process (GstRTPBaseDepayload * depayload, + GstBuffer * buf); + +static GstStateChangeReturn gst_rtp_vraw_depay_change_state (GstElement * + element, GstStateChange transition); + +static gboolean gst_rtp_vraw_depay_handle_event (GstRTPBaseDepayload * filter, + GstEvent * event); + +static void +gst_rtp_vraw_depay_class_init (GstRtpVRawDepayClass * klass) +{ + GstElementClass *gstelement_class; + GstRTPBaseDepayloadClass *gstrtpbasedepayload_class; + + gstelement_class = (GstElementClass *) klass; + gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass; + + gstelement_class->change_state = gst_rtp_vraw_depay_change_state; + + gstrtpbasedepayload_class->set_caps = gst_rtp_vraw_depay_setcaps; + gstrtpbasedepayload_class->process = gst_rtp_vraw_depay_process; + gstrtpbasedepayload_class->handle_event = gst_rtp_vraw_depay_handle_event; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vraw_depay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vraw_depay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Raw Video depayloader", "Codec/Depayloader/Network/RTP", + "Extracts raw video from RTP packets (RFC 4175)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpvrawdepay_debug, "rtpvrawdepay", 0, + "raw video RTP Depayloader"); +} + +static void +gst_rtp_vraw_depay_init (GstRtpVRawDepay * rtpvrawdepay) +{ +} + +static void +gst_rtp_vraw_depay_reset (GstRtpVRawDepay * rtpvrawdepay) +{ + if (rtpvrawdepay->outbuf) { + gst_video_frame_unmap (&rtpvrawdepay->frame); + gst_buffer_unref (rtpvrawdepay->outbuf); + rtpvrawdepay->outbuf = NULL; + } + rtpvrawdepay->timestamp = -1; + if (rtpvrawdepay->pool) { + gst_buffer_pool_set_active (rtpvrawdepay->pool, FALSE); + gst_object_unref (rtpvrawdepay->pool); + rtpvrawdepay->pool = NULL; + } +} + +static GstFlowReturn +gst_rtp_vraw_depay_negotiate_pool (GstRtpVRawDepay * depay, GstCaps * caps, + GstVideoInfo * info) +{ + GstQuery *query; + GstBufferPool *pool = NULL; + guint size, min, max; + GstStructure *config; + + /* find a pool for the negotiated caps now */ + query = gst_query_new_allocation (caps, TRUE); + + if (!gst_pad_peer_query (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depay), query)) { + /* not a problem, we use the defaults of query */ + GST_DEBUG_OBJECT (depay, "could not get downstream ALLOCATION hints"); + } + + if (gst_query_get_n_allocation_pools (query) > 0) { + /* we got configuration from our peer, parse them */ + gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max); + } else { + GST_DEBUG_OBJECT (depay, "didn't get downstream pool hints"); + size = info->size; + min = max = 0; + } + + if (pool == NULL) { + /* we did not get a pool, make one ourselves then */ + pool = gst_video_buffer_pool_new (); + } + + if (depay->pool) + gst_object_unref (depay->pool); + depay->pool = pool; + + config = gst_buffer_pool_get_config (pool); + gst_buffer_pool_config_set_params (config, caps, size, min, max); + if (gst_query_find_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL)) { + /* just set the metadata, if the pool can support it we will transparently use + * it through the video info API. We could also see if the pool support this + * metadata and only activate it then. */ + gst_buffer_pool_config_add_option (config, + GST_BUFFER_POOL_OPTION_VIDEO_META); + } + + gst_buffer_pool_set_config (pool, config); + /* and activate */ + gst_buffer_pool_set_active (pool, TRUE); + + gst_query_unref (query); + + return GST_FLOW_OK; +} + +static gboolean +gst_rtp_vraw_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps) +{ + GstStructure *structure; + GstRtpVRawDepay *rtpvrawdepay; + gint clock_rate; + const gchar *str; + gint format, width, height, depth, pgroup, xinc, yinc; + GstCaps *srccaps; + gboolean res; + GstFlowReturn ret; + + rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload); + + structure = gst_caps_get_structure (caps, 0); + + xinc = yinc = 1; + + if (!gst_structure_get_int (structure, "clock-rate", &clock_rate)) + clock_rate = 90000; /* default */ + depayload->clock_rate = clock_rate; + + if (!(str = gst_structure_get_string (structure, "width"))) + goto no_width; + width = atoi (str); + + if (!(str = gst_structure_get_string (structure, "height"))) + goto no_height; + height = atoi (str); + + if (!(str = gst_structure_get_string (structure, "depth"))) + goto no_depth; + depth = atoi (str); + + /* optional interlace value but we don't handle interlaced + * formats yet */ + if (gst_structure_get_string (structure, "interlace")) + goto interlaced; + + if (!(str = gst_structure_get_string (structure, "sampling"))) + goto no_sampling; + + if (!strcmp (str, "RGB")) { + format = GST_VIDEO_FORMAT_RGB; + pgroup = 3; + } else if (!strcmp (str, "RGBA")) { + format = GST_VIDEO_FORMAT_RGBA; + pgroup = 4; + } else if (!strcmp (str, "BGR")) { + format = GST_VIDEO_FORMAT_BGR; + pgroup = 3; + } else if (!strcmp (str, "BGRA")) { + format = GST_VIDEO_FORMAT_BGRA; + pgroup = 4; + } else if (!strcmp (str, "YCbCr-4:4:4")) { + format = GST_VIDEO_FORMAT_AYUV; + pgroup = 3; + } else if (!strcmp (str, "YCbCr-4:2:2")) { + if (depth == 8) { + format = GST_VIDEO_FORMAT_UYVY; + pgroup = 4; + } else if (depth == 10) { + format = GST_VIDEO_FORMAT_UYVP; + pgroup = 5; + } else + goto unknown_format; + xinc = 2; + } else if (!strcmp (str, "YCbCr-4:2:0")) { + format = GST_VIDEO_FORMAT_I420; + pgroup = 6; + xinc = yinc = 2; + } else if (!strcmp (str, "YCbCr-4:1:1")) { + format = GST_VIDEO_FORMAT_Y41B; + pgroup = 6; + xinc = 4; + } else { + goto unknown_format; + } + + gst_video_info_init (&rtpvrawdepay->vinfo); + gst_video_info_set_format (&rtpvrawdepay->vinfo, format, width, height); + GST_VIDEO_INFO_FPS_N (&rtpvrawdepay->vinfo) = 0; + GST_VIDEO_INFO_FPS_D (&rtpvrawdepay->vinfo) = 1; + + rtpvrawdepay->pgroup = pgroup; + rtpvrawdepay->xinc = xinc; + rtpvrawdepay->yinc = yinc; + + srccaps = gst_video_info_to_caps (&rtpvrawdepay->vinfo); + res = gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload), srccaps); + gst_caps_unref (srccaps); + + GST_DEBUG_OBJECT (depayload, "width %d, height %d, format %d", width, height, + format); + GST_DEBUG_OBJECT (depayload, "xinc %d, yinc %d, pgroup %d", + xinc, yinc, pgroup); + + /* negotiate a bufferpool */ + if ((ret = gst_rtp_vraw_depay_negotiate_pool (rtpvrawdepay, srccaps, + &rtpvrawdepay->vinfo)) != GST_FLOW_OK) + goto no_bufferpool; + + return res; + + /* ERRORS */ +no_width: + { + GST_ERROR_OBJECT (depayload, "no width specified"); + return FALSE; + } +no_height: + { + GST_ERROR_OBJECT (depayload, "no height specified"); + return FALSE; + } +no_depth: + { + GST_ERROR_OBJECT (depayload, "no depth specified"); + return FALSE; + } +interlaced: + { + GST_ERROR_OBJECT (depayload, "interlaced formats not supported yet"); + return FALSE; + } +no_sampling: + { + GST_ERROR_OBJECT (depayload, "no sampling specified"); + return FALSE; + } +unknown_format: + { + GST_ERROR_OBJECT (depayload, "unknown sampling format '%s'", str); + return FALSE; + } +no_bufferpool: + { + GST_DEBUG_OBJECT (depayload, "no bufferpool"); + return FALSE; + } +} + +static GstBuffer * +gst_rtp_vraw_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf) +{ + GstRtpVRawDepay *rtpvrawdepay; + guint8 *payload, *p0, *yp, *up, *vp, *headers; + guint32 timestamp; + guint cont, ystride, uvstride, pgroup, payload_len; + gint width, height, xinc, yinc; + GstRTPBuffer rtp = { NULL }; + GstVideoFrame *frame; + gboolean marker; + GstBuffer *outbuf = NULL; + + rtpvrawdepay = GST_RTP_VRAW_DEPAY (depayload); + + gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp); + + timestamp = gst_rtp_buffer_get_timestamp (&rtp); + + if (timestamp != rtpvrawdepay->timestamp || rtpvrawdepay->outbuf == NULL) { + GstBuffer *new_buffer; + GstFlowReturn ret; + + GST_LOG_OBJECT (depayload, "new frame with timestamp %u", timestamp); + /* new timestamp, flush old buffer and create new output buffer */ + if (rtpvrawdepay->outbuf) { + gst_video_frame_unmap (&rtpvrawdepay->frame); + gst_rtp_base_depayload_push (depayload, rtpvrawdepay->outbuf); + rtpvrawdepay->outbuf = NULL; + } + + if (gst_pad_check_reconfigure (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload))) { + GstCaps *caps; + + caps = + gst_pad_get_current_caps (GST_RTP_BASE_DEPAYLOAD_SRCPAD (depayload)); + gst_rtp_vraw_depay_negotiate_pool (rtpvrawdepay, caps, + &rtpvrawdepay->vinfo); + gst_caps_unref (caps); + } + + ret = + gst_buffer_pool_acquire_buffer (rtpvrawdepay->pool, &new_buffer, NULL); + + if (G_UNLIKELY (ret != GST_FLOW_OK)) + goto alloc_failed; + + /* clear timestamp from alloc... */ + GST_BUFFER_TIMESTAMP (new_buffer) = -1; + + if (!gst_video_frame_map (&rtpvrawdepay->frame, &rtpvrawdepay->vinfo, + new_buffer, GST_MAP_WRITE | GST_VIDEO_FRAME_MAP_FLAG_NO_REF)) { + gst_buffer_unref (new_buffer); + goto invalid_frame; + } + + rtpvrawdepay->outbuf = new_buffer; + rtpvrawdepay->timestamp = timestamp; + } + + frame = &rtpvrawdepay->frame; + + g_assert (frame->buffer != NULL); + + /* get pointer and strides of the planes */ + p0 = GST_VIDEO_FRAME_PLANE_DATA (frame, 0); + yp = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + up = GST_VIDEO_FRAME_COMP_DATA (frame, 1); + vp = GST_VIDEO_FRAME_COMP_DATA (frame, 2); + + ystride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + uvstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); + + pgroup = rtpvrawdepay->pgroup; + width = GST_VIDEO_INFO_WIDTH (&rtpvrawdepay->vinfo); + height = GST_VIDEO_INFO_HEIGHT (&rtpvrawdepay->vinfo); + xinc = rtpvrawdepay->xinc; + yinc = rtpvrawdepay->yinc; + + payload = gst_rtp_buffer_get_payload (&rtp); + payload_len = gst_rtp_buffer_get_payload_len (&rtp); + + if (payload_len < 3) + goto short_packet; + + /* skip extended seqnum */ + payload += 2; + payload_len -= 2; + + /* remember header position */ + headers = payload; + + /* find data start */ + do { + if (payload_len < 6) + goto short_packet; + + cont = payload[4] & 0x80; + + payload += 6; + payload_len -= 6; + } while (cont); + + while (TRUE) { + guint length, line, offs, plen; + guint8 *datap; + + /* stop when we run out of data */ + if (payload_len == 0) + break; + + /* read length and cont. This should work because we iterated the headers + * above. */ + length = (headers[0] << 8) | headers[1]; + line = ((headers[2] & 0x7f) << 8) | headers[3]; + offs = ((headers[4] & 0x7f) << 8) | headers[5]; + cont = headers[4] & 0x80; + headers += 6; + + /* length must be a multiple of pgroup */ + if (length % pgroup != 0) + goto wrong_length; + + if (length > payload_len) + length = payload_len; + + /* sanity check */ + if (line > (height - yinc)) { + GST_WARNING_OBJECT (depayload, "skipping line %d: out of range", line); + goto next; + } + if (offs > (width - xinc)) { + GST_WARNING_OBJECT (depayload, "skipping offset %d: out of range", offs); + goto next; + } + + /* calculate the maximim amount of bytes we can use per line */ + if (offs + ((length / pgroup) * xinc) > width) { + plen = ((width - offs) * pgroup) / xinc; + GST_WARNING_OBJECT (depayload, "clipping length %d, offset %d, plen %d", + length, offs, plen); + } else + plen = length; + + GST_LOG_OBJECT (depayload, + "writing length %u/%u, line %u, offset %u, remaining %u", plen, length, + line, offs, payload_len); + + switch (GST_VIDEO_INFO_FORMAT (&rtpvrawdepay->vinfo)) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_UYVP: + /* samples are packed just like gstreamer packs them */ + offs /= xinc; + datap = p0 + (line * ystride) + (offs * pgroup); + + memcpy (datap, payload, plen); + break; + case GST_VIDEO_FORMAT_AYUV: + { + gint i; + guint8 *p; + + datap = p0 + (line * ystride) + (offs * 4); + p = payload; + + /* samples are packed in order Cb-Y-Cr for both interlaced and + * progressive frames */ + for (i = 0; i < plen; i += pgroup) { + *datap++ = 0; + *datap++ = p[1]; + *datap++ = p[0]; + *datap++ = p[2]; + p += pgroup; + } + break; + } + case GST_VIDEO_FORMAT_I420: + { + gint i; + guint uvoff; + guint8 *yd1p, *yd2p, *udp, *vdp, *p; + + yd1p = yp + (line * ystride) + (offs); + yd2p = yd1p + ystride; + uvoff = (line / yinc * uvstride) + (offs / xinc); + + udp = up + uvoff; + vdp = vp + uvoff; + p = payload; + + /* line 0/1: Y00-Y01-Y10-Y11-Cb00-Cr00 Y02-Y03-Y12-Y13-Cb01-Cr01 ... */ + for (i = 0; i < plen; i += pgroup) { + *yd1p++ = p[0]; + *yd1p++ = p[1]; + *yd2p++ = p[2]; + *yd2p++ = p[3]; + *udp++ = p[4]; + *vdp++ = p[5]; + p += pgroup; + } + break; + } + case GST_VIDEO_FORMAT_Y41B: + { + gint i; + guint uvoff; + guint8 *ydp, *udp, *vdp, *p; + + ydp = yp + (line * ystride) + (offs); + uvoff = (line / yinc * uvstride) + (offs / xinc); + + udp = up + uvoff; + vdp = vp + uvoff; + p = payload; + + /* Samples are packed in order Cb0-Y0-Y1-Cr0-Y2-Y3 for both interlaced + * and progressive scan lines */ + for (i = 0; i < plen; i += pgroup) { + *udp++ = p[0]; + *ydp++ = p[1]; + *ydp++ = p[2]; + *vdp++ = p[3]; + *ydp++ = p[4]; + *ydp++ = p[5]; + p += pgroup; + } + break; + } + default: + goto unknown_sampling; + } + + next: + if (!cont) + break; + + payload += length; + payload_len -= length; + } + + marker = gst_rtp_buffer_get_marker (&rtp); + gst_rtp_buffer_unmap (&rtp); + + if (marker) { + GST_LOG_OBJECT (depayload, "marker, flushing frame"); + gst_video_frame_unmap (&rtpvrawdepay->frame); + outbuf = rtpvrawdepay->outbuf; + rtpvrawdepay->outbuf = NULL; + rtpvrawdepay->timestamp = -1; + } + return outbuf; + + /* ERRORS */ +unknown_sampling: + { + GST_ELEMENT_ERROR (depayload, STREAM, FORMAT, + (NULL), ("unimplemented sampling")); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +alloc_failed: + { + GST_WARNING_OBJECT (depayload, "failed to alloc output buffer"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +invalid_frame: + { + GST_ERROR_OBJECT (depayload, "could not map video frame"); + return NULL; + } +wrong_length: + { + GST_WARNING_OBJECT (depayload, "length not multiple of pgroup"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +short_packet: + { + GST_WARNING_OBJECT (depayload, "short packet"); + gst_rtp_buffer_unmap (&rtp); + return NULL; + } +} + +static gboolean +gst_rtp_vraw_depay_handle_event (GstRTPBaseDepayload * filter, GstEvent * event) +{ + gboolean ret; + GstRtpVRawDepay *rtpvrawdepay; + + rtpvrawdepay = GST_RTP_VRAW_DEPAY (filter); + + switch (GST_EVENT_TYPE (event)) { + case GST_EVENT_FLUSH_STOP: + gst_rtp_vraw_depay_reset (rtpvrawdepay); + break; + default: + break; + } + + ret = + GST_RTP_BASE_DEPAYLOAD_CLASS (parent_class)->handle_event (filter, event); + + return ret; +} + +static GstStateChangeReturn +gst_rtp_vraw_depay_change_state (GstElement * element, + GstStateChange transition) +{ + GstRtpVRawDepay *rtpvrawdepay; + GstStateChangeReturn ret; + + rtpvrawdepay = GST_RTP_VRAW_DEPAY (element); + + switch (transition) { + case GST_STATE_CHANGE_NULL_TO_READY: + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + gst_rtp_vraw_depay_reset (rtpvrawdepay); + break; + default: + break; + } + + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_rtp_vraw_depay_reset (rtpvrawdepay); + break; + case GST_STATE_CHANGE_READY_TO_NULL: + break; + default: + break; + } + return ret; +} + +gboolean +gst_rtp_vraw_depay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvrawdepay", + GST_RANK_SECONDARY, GST_TYPE_RTP_VRAW_DEPAY); +} diff --git a/gst/rtp/gstrtpvrawdepay.h b/gst/rtp/gstrtpvrawdepay.h new file mode 100755 index 0000000..18ca755 --- /dev/null +++ b/gst/rtp/gstrtpvrawdepay.h @@ -0,0 +1,71 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_VRAW_DEPAY_H__ +#define __GST_RTP_VRAW_DEPAY_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/video/gstvideometa.h> +#include <gst/video/gstvideopool.h> +#include <gst/rtp/gstrtpbasedepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VRAW_DEPAY \ + (gst_rtp_vraw_depay_get_type()) +#define GST_RTP_VRAW_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_VRAW_DEPAY,GstRtpVRawDepay)) +#define GST_RTP_VRAW_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_VRAW_DEPAY,GstRtpVRawDepayClass)) +#define GST_IS_RTP_VRAW_DEPAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_VRAW_DEPAY)) +#define GST_IS_RTP_VRAW_DEPAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_VRAW_DEPAY)) + +typedef struct _GstRtpVRawDepay GstRtpVRawDepay; +typedef struct _GstRtpVRawDepayClass GstRtpVRawDepayClass; + +struct _GstRtpVRawDepay +{ + GstRTPBaseDepayload payload; + + GstBufferPool *pool; + GstVideoInfo vinfo; + + GstVideoFrame frame; + GstBuffer *outbuf; + guint32 timestamp; + + gint pgroup; + gint xinc, yinc; +}; + +struct _GstRtpVRawDepayClass +{ + GstRTPBaseDepayloadClass parent_class; +}; + +GType gst_rtp_vraw_depay_get_type (void); + +gboolean gst_rtp_vraw_depay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_VRAW_DEPAY_H__ */ diff --git a/gst/rtp/gstrtpvrawpay.c b/gst/rtp/gstrtpvrawpay.c new file mode 100755 index 0000000..c03c8d2 --- /dev/null +++ b/gst/rtp/gstrtpvrawpay.c @@ -0,0 +1,647 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <string.h> + +#include <gst/rtp/gstrtpbuffer.h> + +#include "gstrtpvrawpay.h" + +enum +{ + PROP_CHUNKS_PER_FRAME = 1 +}; + +#define DEFAULT_CHUNKS_PER_FRAME 10 + +GST_DEBUG_CATEGORY_STATIC (rtpvrawpay_debug); +#define GST_CAT_DEFAULT (rtpvrawpay_debug) + +static GstStaticPadTemplate gst_rtp_vraw_pay_sink_template = + GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-raw, " + "format = (string) { RGB, RGBA, BGR, BGRA, AYUV, UYVY, I420, Y41B, UYVP }, " + "width = (int) [ 1, 32767 ], " "height = (int) [ 1, 32767 ]; ") + ); + +static GstStaticPadTemplate gst_rtp_vraw_pay_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("application/x-rtp, " + "media = (string) \"video\", " + "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", " + "clock-rate = (int) 90000, " + "encoding-name = (string) \"RAW\"," + "sampling = (string) { \"RGB\", \"RGBA\", \"BGR\", \"BGRA\", " + "\"YCbCr-4:4:4\", \"YCbCr-4:2:2\", \"YCbCr-4:2:0\", " + "\"YCbCr-4:1:1\" }," + /* we cannot express these as strings + * "width = (string) [1 32767]," + * "height = (string) [1 32767]," + */ + "depth = (string) { \"8\", \"10\", \"12\", \"16\" }," + "colorimetry = (string) { \"BT601-5\", \"BT709-2\", \"SMPTE240M\" }" + /* optional + * interlace = + * top-field-first = + * chroma-position = (string) + * gamma = (float) + */ + ) + ); + +static gboolean gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, + GstCaps * caps); +static GstFlowReturn gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * + payload, GstBuffer * buffer); +static void gst_rtp_vraw_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec); +static void gst_rtp_vraw_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec); + +G_DEFINE_TYPE (GstRtpVRawPay, gst_rtp_vraw_pay, GST_TYPE_RTP_BASE_PAYLOAD) + + static void gst_rtp_vraw_pay_class_init (GstRtpVRawPayClass * klass) +{ + GstRTPBasePayloadClass *gstrtpbasepayload_class; + GstElementClass *gstelement_class; + GObjectClass *gobject_class; + + gobject_class = (GObjectClass *) klass; + gstelement_class = (GstElementClass *) klass; + gstrtpbasepayload_class = (GstRTPBasePayloadClass *) klass; + + gobject_class->set_property = gst_rtp_vraw_pay_set_property; + gobject_class->get_property = gst_rtp_vraw_pay_get_property; + + g_object_class_install_property (gobject_class, + PROP_CHUNKS_PER_FRAME, + g_param_spec_int ("chunks-per-frame", "Chunks per Frame", + "Split and send out each frame in multiple chunks to reduce overhead", + 1, G_MAXINT, DEFAULT_CHUNKS_PER_FRAME, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS) + ); + + gstrtpbasepayload_class->set_caps = gst_rtp_vraw_pay_setcaps; + gstrtpbasepayload_class->handle_buffer = gst_rtp_vraw_pay_handle_buffer; + + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vraw_pay_src_template)); + gst_element_class_add_pad_template (gstelement_class, + gst_static_pad_template_get (&gst_rtp_vraw_pay_sink_template)); + + gst_element_class_set_static_metadata (gstelement_class, + "RTP Raw Video payloader", "Codec/Payloader/Network/RTP", + "Payload raw video as RTP packets (RFC 4175)", + "Wim Taymans <wim.taymans@gmail.com>"); + + GST_DEBUG_CATEGORY_INIT (rtpvrawpay_debug, "rtpvrawpay", 0, + "Raw video RTP Payloader"); +} + +static void +gst_rtp_vraw_pay_init (GstRtpVRawPay * rtpvrawpay) +{ + rtpvrawpay->chunks_per_frame = DEFAULT_CHUNKS_PER_FRAME; +} + +static gboolean +gst_rtp_vraw_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps) +{ + GstRtpVRawPay *rtpvrawpay; + gboolean res; + gint pgroup, xinc, yinc; + const gchar *depthstr, *samplingstr, *colorimetrystr; + gchar *wstr, *hstr; + GstVideoInfo info; + + rtpvrawpay = GST_RTP_VRAW_PAY (payload); + + if (!gst_video_info_from_caps (&info, caps)) + goto invalid_caps; + + rtpvrawpay->vinfo = info; + + if (gst_video_colorimetry_matches (&info.colorimetry, + GST_VIDEO_COLORIMETRY_BT601)) { + colorimetrystr = "BT601-5"; + } else if (gst_video_colorimetry_matches (&info.colorimetry, + GST_VIDEO_COLORIMETRY_BT709)) { + colorimetrystr = "BT709-2"; + } else if (gst_video_colorimetry_matches (&info.colorimetry, + GST_VIDEO_COLORIMETRY_SMPTE240M)) { + colorimetrystr = "SMPTE240M"; + } else { + colorimetrystr = "SMPTE240M"; + } + + xinc = yinc = 1; + + /* these values are the only thing we can do */ + depthstr = "8"; + + switch (GST_VIDEO_INFO_FORMAT (&info)) { + case GST_VIDEO_FORMAT_RGBA: + samplingstr = "RGBA"; + pgroup = 4; + break; + case GST_VIDEO_FORMAT_BGRA: + samplingstr = "BGRA"; + pgroup = 4; + break; + case GST_VIDEO_FORMAT_RGB: + samplingstr = "RGB"; + pgroup = 3; + break; + case GST_VIDEO_FORMAT_BGR: + samplingstr = "BGR"; + pgroup = 3; + break; + case GST_VIDEO_FORMAT_AYUV: + samplingstr = "YCbCr-4:4:4"; + pgroup = 3; + break; + case GST_VIDEO_FORMAT_UYVY: + samplingstr = "YCbCr-4:2:2"; + pgroup = 4; + xinc = 2; + break; + case GST_VIDEO_FORMAT_Y41B: + samplingstr = "YCbCr-4:1:1"; + pgroup = 6; + xinc = 4; + break; + case GST_VIDEO_FORMAT_I420: + samplingstr = "YCbCr-4:2:0"; + pgroup = 6; + xinc = yinc = 2; + break; + case GST_VIDEO_FORMAT_UYVP: + samplingstr = "YCbCr-4:2:2"; + pgroup = 5; + xinc = 2; + depthstr = "10"; + break; + default: + goto unknown_format; + break; + } + + if (GST_VIDEO_INFO_IS_INTERLACED (&info)) { + yinc *= 2; + } + + rtpvrawpay->pgroup = pgroup; + rtpvrawpay->xinc = xinc; + rtpvrawpay->yinc = yinc; + + GST_DEBUG_OBJECT (payload, "width %d, height %d, sampling %s", + GST_VIDEO_INFO_WIDTH (&info), GST_VIDEO_INFO_HEIGHT (&info), samplingstr); + GST_DEBUG_OBJECT (payload, "xinc %d, yinc %d, pgroup %d", xinc, yinc, pgroup); + + wstr = g_strdup_printf ("%d", GST_VIDEO_INFO_WIDTH (&info)); + hstr = g_strdup_printf ("%d", GST_VIDEO_INFO_HEIGHT (&info)); + + gst_rtp_base_payload_set_options (payload, "video", TRUE, "RAW", 90000); + if (GST_VIDEO_INFO_IS_INTERLACED (&info)) { + res = gst_rtp_base_payload_set_outcaps (payload, "sampling", G_TYPE_STRING, + samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING, + wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING, + colorimetrystr, "interlace", G_TYPE_STRING, "true", NULL); + } else { + res = gst_rtp_base_payload_set_outcaps (payload, "sampling", G_TYPE_STRING, + samplingstr, "depth", G_TYPE_STRING, depthstr, "width", G_TYPE_STRING, + wstr, "height", G_TYPE_STRING, hstr, "colorimetry", G_TYPE_STRING, + colorimetrystr, NULL); + } + g_free (wstr); + g_free (hstr); + + return res; + + /* ERRORS */ +invalid_caps: + { + GST_ERROR_OBJECT (payload, "could not parse caps"); + return FALSE; + } +unknown_format: + { + GST_ERROR_OBJECT (payload, "unknown caps format"); + return FALSE; + } +} + +static GstFlowReturn +gst_rtp_vraw_pay_handle_buffer (GstRTPBasePayload * payload, GstBuffer * buffer) +{ + GstRtpVRawPay *rtpvrawpay; + GstFlowReturn ret = GST_FLOW_OK; + gfloat packets_per_packline; + guint pgroups_per_packet; + guint packlines_per_list, buffers_per_list; + guint lines_delay; /* after how many packed lines we push out a buffer list */ + guint last_line; /* last pack line number we pushed out a buffer list */ + guint line, offset; + guint8 *p0, *yp, *up, *vp; + guint ystride, uvstride; + guint xinc, yinc; + guint pgroup; + guint mtu; + guint width, height; + gint field, fields; + GstVideoFormat format; + GstVideoFrame frame; + gint interlaced; + gboolean use_buffer_lists; + GstBufferList *list = NULL; + GstRTPBuffer rtp = { NULL, }; + + rtpvrawpay = GST_RTP_VRAW_PAY (payload); + + gst_video_frame_map (&frame, &rtpvrawpay->vinfo, buffer, GST_MAP_READ); + + GST_LOG_OBJECT (rtpvrawpay, "new frame of %" G_GSIZE_FORMAT " bytes", + gst_buffer_get_size (buffer)); + + /* get pointer and strides of the planes */ + p0 = GST_VIDEO_FRAME_PLANE_DATA (&frame, 0); + yp = GST_VIDEO_FRAME_COMP_DATA (&frame, 0); + up = GST_VIDEO_FRAME_COMP_DATA (&frame, 1); + vp = GST_VIDEO_FRAME_COMP_DATA (&frame, 2); + + ystride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, 0); + uvstride = GST_VIDEO_FRAME_COMP_STRIDE (&frame, 1); + + mtu = GST_RTP_BASE_PAYLOAD_MTU (payload); + + /* amount of bytes for one pixel */ + pgroup = rtpvrawpay->pgroup; + width = GST_VIDEO_INFO_WIDTH (&rtpvrawpay->vinfo); + height = GST_VIDEO_INFO_HEIGHT (&rtpvrawpay->vinfo); + + interlaced = GST_VIDEO_INFO_IS_INTERLACED (&rtpvrawpay->vinfo); + + format = GST_VIDEO_INFO_FORMAT (&rtpvrawpay->vinfo); + + yinc = rtpvrawpay->yinc; + xinc = rtpvrawpay->xinc; + + /* after how many packed lines we push out a buffer list */ + lines_delay = GST_ROUND_UP_4 (height / rtpvrawpay->chunks_per_frame); + + /* calculate how many buffers we expect to store in a single buffer list */ + pgroups_per_packet = (mtu - (12 + 14)) / pgroup; + packets_per_packline = width / (xinc * pgroups_per_packet * 1.0); + packlines_per_list = height / (yinc * rtpvrawpay->chunks_per_frame); + buffers_per_list = packlines_per_list * packets_per_packline; + buffers_per_list = GST_ROUND_UP_8 (buffers_per_list); + + use_buffer_lists = (rtpvrawpay->chunks_per_frame < (height / yinc)); + + fields = 1 + interlaced; + + /* start with line 0, offset 0 */ + for (field = 0; field < fields; field++) { + line = field; + offset = 0; + last_line = 0; + + if (use_buffer_lists) + list = gst_buffer_list_new_sized (buffers_per_list); + + /* write all lines */ + while (line < height) { + guint left, pack_line; + GstBuffer *out; + guint8 *outdata, *headers; + gboolean next_line, complete = FALSE; + guint length, cont, pixels; + + /* get the max allowed payload length size, we try to fill the complete MTU */ + left = gst_rtp_buffer_calc_payload_len (mtu, 0, 0); + out = gst_rtp_buffer_new_allocate (left, 0, 0); + + if (field == 0) { + GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer); + } else { + GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer) / 2; + } + + gst_rtp_buffer_map (out, GST_MAP_WRITE, &rtp); + outdata = gst_rtp_buffer_get_payload (&rtp); + + GST_LOG_OBJECT (rtpvrawpay, "created buffer of size %u for MTU %u", left, + mtu); + + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Extended Sequence Number | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |F| Line No |C| Offset | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Length |F| Line No | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |C| Offset | . + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ . + * . . + * . Two (partial) lines of video data . + * . . + * +---------------------------------------------------------------+ + */ + + /* need 2 bytes for the extended sequence number */ + *outdata++ = 0; + *outdata++ = 0; + left -= 2; + + /* the headers start here */ + headers = outdata; + + /* make sure we can fit at least *one* header and pixel */ + if (!(left > (6 + pgroup))) { + gst_rtp_buffer_unmap (&rtp); + gst_buffer_unref (out); + goto too_small; + } + + /* while we can fit at least one header and one pixel */ + while (left > (6 + pgroup)) { + /* we need a 6 bytes header */ + left -= 6; + + /* get how may bytes we need for the remaining pixels */ + pixels = width - offset; + length = (pixels * pgroup) / xinc; + + if (left >= length) { + /* pixels and header fit completely, we will write them and skip to the + * next line. */ + next_line = TRUE; + } else { + /* line does not fit completely, see how many pixels fit */ + pixels = (left / pgroup) * xinc; + length = (pixels * pgroup) / xinc; + next_line = FALSE; + } + GST_LOG_OBJECT (rtpvrawpay, "filling %u bytes in %u pixels", length, + pixels); + left -= length; + + /* write length */ + *outdata++ = (length >> 8) & 0xff; + *outdata++ = length & 0xff; + + /* write line no */ + *outdata++ = ((line >> 8) & 0x7f) | ((field << 7) & 0x80); + *outdata++ = line & 0xff; + + if (next_line) { + /* go to next line we do this here to make the check below easier */ + line += yinc; + } + + /* calculate continuation marker */ + cont = (left > (6 + pgroup) && line < height) ? 0x80 : 0x00; + + /* write offset and continuation marker */ + *outdata++ = ((offset >> 8) & 0x7f) | cont; + *outdata++ = offset & 0xff; + + if (next_line) { + /* reset offset */ + offset = 0; + GST_LOG_OBJECT (rtpvrawpay, "go to next line %u", line); + } else { + offset += pixels; + GST_LOG_OBJECT (rtpvrawpay, "next offset %u", offset); + } + + if (!cont) + break; + } + GST_LOG_OBJECT (rtpvrawpay, "consumed %u bytes", + (guint) (outdata - headers)); + + /* second pass, read headers and write the data */ + while (TRUE) { + guint offs, lin; + + /* read length and cont */ + length = (headers[0] << 8) | headers[1]; + lin = ((headers[2] & 0x7f) << 8) | headers[3]; + offs = ((headers[4] & 0x7f) << 8) | headers[5]; + cont = headers[4] & 0x80; + pixels = length / pgroup; + headers += 6; + + GST_LOG_OBJECT (payload, + "writing length %u, line %u, offset %u, cont %d", length, lin, offs, + cont); + + switch (format) { + case GST_VIDEO_FORMAT_RGB: + case GST_VIDEO_FORMAT_RGBA: + case GST_VIDEO_FORMAT_BGR: + case GST_VIDEO_FORMAT_BGRA: + case GST_VIDEO_FORMAT_UYVY: + case GST_VIDEO_FORMAT_UYVP: + offs /= xinc; + memcpy (outdata, p0 + (lin * ystride) + (offs * pgroup), length); + outdata += length; + break; + case GST_VIDEO_FORMAT_AYUV: + { + gint i; + guint8 *datap; + + datap = p0 + (lin * ystride) + (offs * 4); + + for (i = 0; i < pixels; i++) { + *outdata++ = datap[2]; + *outdata++ = datap[1]; + *outdata++ = datap[3]; + datap += 4; + } + break; + } + case GST_VIDEO_FORMAT_I420: + { + gint i; + guint uvoff; + guint8 *yd1p, *yd2p, *udp, *vdp; + + yd1p = yp + (lin * ystride) + (offs); + yd2p = yd1p + ystride; + uvoff = (lin / yinc * uvstride) + (offs / xinc); + udp = up + uvoff; + vdp = vp + uvoff; + + for (i = 0; i < pixels; i++) { + *outdata++ = *yd1p++; + *outdata++ = *yd1p++; + *outdata++ = *yd2p++; + *outdata++ = *yd2p++; + *outdata++ = *udp++; + *outdata++ = *vdp++; + } + break; + } + case GST_VIDEO_FORMAT_Y41B: + { + gint i; + guint uvoff; + guint8 *ydp, *udp, *vdp; + + ydp = yp + (lin * ystride) + offs; + uvoff = (lin / yinc * uvstride) + (offs / xinc); + udp = up + uvoff; + vdp = vp + uvoff; + + for (i = 0; i < pixels; i++) { + *outdata++ = *udp++; + *outdata++ = *ydp++; + *outdata++ = *ydp++; + *outdata++ = *vdp++; + *outdata++ = *ydp++; + *outdata++ = *ydp++; + } + break; + } + default: + gst_rtp_buffer_unmap (&rtp); + gst_buffer_unref (out); + goto unknown_sampling; + } + + if (!cont) + break; + } + + if (line >= height) { + GST_LOG_OBJECT (rtpvrawpay, "field/frame complete, set marker"); + gst_rtp_buffer_set_marker (&rtp, TRUE); + complete = TRUE; + } + gst_rtp_buffer_unmap (&rtp); + if (left > 0) { + GST_LOG_OBJECT (rtpvrawpay, "we have %u bytes left", left); + gst_buffer_resize (out, 0, gst_buffer_get_size (out) - left); + } + + /* Now either push out the buffer directly */ + if (!use_buffer_lists) { + ret = gst_rtp_base_payload_push (payload, out); + continue; + } + + /* or add the buffer to buffer list ... */ + gst_buffer_list_add (list, out); + + /* .. and check if we need to push out the list */ + pack_line = (line - field) / fields; + if (complete || (pack_line > last_line && pack_line % lines_delay == 0)) { + GST_LOG_OBJECT (rtpvrawpay, "pushing list of %u buffers up to pack " + "line %u", gst_buffer_list_length (list), pack_line); + ret = gst_rtp_base_payload_push_list (payload, list); + list = NULL; + if (!complete) + list = gst_buffer_list_new_sized (buffers_per_list); + last_line = pack_line; + } + } + + } + + gst_video_frame_unmap (&frame); + gst_buffer_unref (buffer); + + return ret; + + /* ERRORS */ +unknown_sampling: + { + GST_ELEMENT_ERROR (payload, STREAM, FORMAT, + (NULL), ("unimplemented sampling")); + gst_video_frame_unmap (&frame); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_SUPPORTED; + } +too_small: + { + GST_ELEMENT_ERROR (payload, RESOURCE, NO_SPACE_LEFT, + (NULL), ("not enough space to send at least one pixel")); + gst_video_frame_unmap (&frame); + gst_buffer_unref (buffer); + return GST_FLOW_NOT_SUPPORTED; + } +} + +static void +gst_rtp_vraw_pay_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstRtpVRawPay *rtpvrawpay; + + rtpvrawpay = GST_RTP_VRAW_PAY (object); + + switch (prop_id) { + case PROP_CHUNKS_PER_FRAME: + rtpvrawpay->chunks_per_frame = g_value_get_int (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gst_rtp_vraw_pay_get_property (GObject * object, guint prop_id, + GValue * value, GParamSpec * pspec) +{ + GstRtpVRawPay *rtpvrawpay; + + rtpvrawpay = GST_RTP_VRAW_PAY (object); + + switch (prop_id) { + case PROP_CHUNKS_PER_FRAME: + g_value_set_int (value, rtpvrawpay->chunks_per_frame); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +gboolean +gst_rtp_vraw_pay_plugin_init (GstPlugin * plugin) +{ + return gst_element_register (plugin, "rtpvrawpay", + GST_RANK_SECONDARY, GST_TYPE_RTP_VRAW_PAY); +} diff --git a/gst/rtp/gstrtpvrawpay.h b/gst/rtp/gstrtpvrawpay.h new file mode 100755 index 0000000..3fd2442 --- /dev/null +++ b/gst/rtp/gstrtpvrawpay.h @@ -0,0 +1,67 @@ +/* GStreamer + * Copyright (C) <2008> Wim Taymans <wim.taymans@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __GST_RTP_VRAW_PAY_H__ +#define __GST_RTP_VRAW_PAY_H__ + +#include <gst/gst.h> +#include <gst/video/video.h> +#include <gst/rtp/gstrtpbasepayload.h> + +G_BEGIN_DECLS + +#define GST_TYPE_RTP_VRAW_PAY \ + (gst_rtp_vraw_pay_get_type()) +#define GST_RTP_VRAW_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_VRAW_PAY,GstRtpVRawPay)) +#define GST_RTP_VRAW_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_VRAW_PAY,GstRtpVRawPayClass)) +#define GST_IS_RTP_VRAW_PAY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_VRAW_PAY)) +#define GST_IS_RTP_VRAW_PAY_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_VRAW_PAY)) + +typedef struct _GstRtpVRawPay GstRtpVRawPay; +typedef struct _GstRtpVRawPayClass GstRtpVRawPayClass; + +struct _GstRtpVRawPay +{ + GstRTPBasePayload payload; + + GstVideoInfo vinfo; + + gint pgroup; + gint xinc, yinc; + + /* properties */ + guint chunks_per_frame; +}; + +struct _GstRtpVRawPayClass +{ + GstRTPBasePayloadClass parent_class; +}; + +GType gst_rtp_vraw_pay_get_type (void); + +gboolean gst_rtp_vraw_pay_plugin_init (GstPlugin * plugin); + +G_END_DECLS + +#endif /* __GST_RTP_VRAW_PAY_H__ */ |