From 6b4e797513de059617bdd59b9eb237b9a1579942 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Philipp=20M=C3=BCller?= Date: Tue, 12 Apr 2011 21:47:14 +0100 Subject: [PATCH] qtmux: remove qtmux plugin, it has moved to -good https://bugzilla.gnome.org/show_bug.cgi?id=636699 --- Android.mk | 2 - Makefile.am | 3 + configure.ac | 2 - docs/plugins/Makefile.am | 1 - .../plugins/gst-plugins-bad-plugins-docs.sgml | 2 - .../gst-plugins-bad-plugins-sections.txt | 14 - docs/plugins/inspect/plugin-qtmux.xml | 157 - gst-plugins-bad.spec.in | 2 - gst/qtmux/Makefile.am | 45 - gst/qtmux/atoms.c | 4423 ----------------- gst/qtmux/atoms.h | 956 ---- gst/qtmux/atomsrecovery.c | 1095 ---- gst/qtmux/atomsrecovery.h | 159 - gst/qtmux/descriptors.c | 458 -- gst/qtmux/descriptors.h | 151 - gst/qtmux/fourcc.h | 243 - gst/qtmux/ftypcc.h | 68 - gst/qtmux/gstqtmoovrecover.c | 390 -- gst/qtmux/gstqtmoovrecover.h | 88 - gst/qtmux/gstqtmux.c | 3498 ------------- gst/qtmux/gstqtmux.h | 228 - gst/qtmux/gstqtmuxmap.c | 368 -- gst/qtmux/gstqtmuxmap.h | 83 - gst/qtmux/gstqtmuxplugin.c | 67 - gst/qtmux/properties.c | 210 - gst/qtmux/properties.h | 87 - tests/check/Makefile.am | 2 - tests/check/elements/.gitignore | 1 - tests/check/elements/qtmux.c | 422 -- tests/check/pipelines/tagschecking.c | 350 -- 30 files changed, 3 insertions(+), 13572 deletions(-) delete mode 100644 docs/plugins/inspect/plugin-qtmux.xml delete mode 100644 gst/qtmux/Makefile.am delete mode 100644 gst/qtmux/atoms.c delete mode 100644 gst/qtmux/atoms.h delete mode 100644 gst/qtmux/atomsrecovery.c delete mode 100644 gst/qtmux/atomsrecovery.h delete mode 100644 gst/qtmux/descriptors.c delete mode 100644 gst/qtmux/descriptors.h delete mode 100644 gst/qtmux/fourcc.h delete mode 100644 gst/qtmux/ftypcc.h delete mode 100644 gst/qtmux/gstqtmoovrecover.c delete mode 100644 gst/qtmux/gstqtmoovrecover.h delete mode 100644 gst/qtmux/gstqtmux.c delete mode 100644 gst/qtmux/gstqtmux.h delete mode 100644 gst/qtmux/gstqtmuxmap.c delete mode 100644 gst/qtmux/gstqtmuxmap.h delete mode 100644 gst/qtmux/gstqtmuxplugin.c delete mode 100644 gst/qtmux/properties.c delete mode 100644 gst/qtmux/properties.h delete mode 100644 tests/check/elements/qtmux.c delete mode 100644 tests/check/pipelines/tagschecking.c diff --git a/Android.mk b/Android.mk index 35e93755bc..c3a5ff3468 100644 --- a/Android.mk +++ b/Android.mk @@ -10,7 +10,6 @@ GST_PLUGINS_BAD_BUILT_SOURCES := \ gst-libs/gst/basecamerabinsrc/Android.mk \ gst-libs/gst/interfaces/Android.mk \ gst/h264parse/Android.mk \ - gst/qtmux/Android.mk \ gst/videoparsers/Android.mk \ gst/audiobuffer/Android.mk \ gst/autoconvert/Android.mk \ @@ -102,7 +101,6 @@ CONFIGURE_TARGETS += gst-plugins-bad-configure -include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/basecamerabinsrc/Android.mk -include $(GST_PLUGINS_BAD_TOP)/gst-libs/gst/interfaces/Android.mk -include $(GST_PLUGINS_BAD_TOP)/gst/h264parse/Android.mk --include $(GST_PLUGINS_BAD_TOP)/gst/qtmux/Android.mk -include $(GST_PLUGINS_BAD_TOP)/gst/audiobuffer/Android.mk -include $(GST_PLUGINS_BAD_TOP)/gst/autoconvert/Android.mk -include $(GST_PLUGINS_BAD_TOP)/gst/bayer/Android.mk diff --git a/Makefile.am b/Makefile.am index daeff6fe26..1c36d7d84f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,6 +52,7 @@ CRUFT_FILES = \ $(top_builddir)/gst/audioparsers/.libs/*.{so,dll,DLL,dylib} \ $(top_builddir)/gst/flacparse/.libs/*.{so,dll,DLL,dylib} \ $(top_builddir)/gst/imagefreeze/.libs/*.{so,dll,DLL,dylib} \ + $(top_builddir)/gst/qtmux/.libs/*.{so,dll,DLL,dylib} \ $(top_builddir)/gst/selector/.libs/*.{so,dll,DLL,dylib} \ $(top_builddir)/gst/shapewipe/.libs/*.{so,dll,DLL,dylib} \ $(top_builddir)/gst/valve/.libs/*.{so,dll,DLL,dylib} \ @@ -62,6 +63,7 @@ CRUFT_FILES = \ $(top_builddir)/tests/check/elements/autocolorspace \ $(top_builddir)/tests/check/elements/capssetter \ $(top_builddir)/tests/check/elements/imagefreeze \ + $(top_builddir)/tests/check/elements/qtmux \ $(top_builddir)/tests/check/elements/selector \ $(top_builddir)/tests/check/elements/valve \ $(top_builddir)/tests/check/pipelines/metadata \ @@ -76,6 +78,7 @@ CRUFT_DIRS = \ $(top_srcdir)/gst/amrparse \ $(top_srcdir)/gst/flacparse \ $(top_srcdir)/gst/imagefreeze \ + $(top_srcdir)/gst/qtmux \ $(top_srcdir)/gst/selector \ $(top_srcdir)/gst/shapewipe \ $(top_srcdir)/gst/valve \ diff --git a/configure.ac b/configure.ac index f19a122ff5..d6d5a954a6 100644 --- a/configure.ac +++ b/configure.ac @@ -341,7 +341,6 @@ AG_GST_CHECK_PLUGIN(nuvdemux) AG_GST_CHECK_PLUGIN(patchdetect) AG_GST_CHECK_PLUGIN(pcapparse) AG_GST_CHECK_PLUGIN(pnm) -AG_GST_CHECK_PLUGIN(qtmux) AG_GST_CHECK_PLUGIN(rawparse) AG_GST_CHECK_PLUGIN(real) AG_GST_CHECK_PLUGIN(rtpmux) @@ -1784,7 +1783,6 @@ gst/nuvdemux/Makefile gst/patchdetect/Makefile gst/pcapparse/Makefile gst/pnm/Makefile -gst/qtmux/Makefile gst/rawparse/Makefile gst/real/Makefile gst/rtpmux/Makefile diff --git a/docs/plugins/Makefile.am b/docs/plugins/Makefile.am index a939357acc..7d83b0e521 100644 --- a/docs/plugins/Makefile.am +++ b/docs/plugins/Makefile.am @@ -182,7 +182,6 @@ EXTRA_HFILES = \ $(top_srcdir)/gst/mxf/mxfmux.h \ $(top_srcdir)/gst/nuvdemux/gstnuvdemux.h \ $(top_srcdir)/gst/pcapparse/gstpcapparse.h \ - $(top_srcdir)/gst/qtmux/gstqtmux.h \ $(top_srcdir)/gst/rawparse/gstaudioparse.h \ $(top_srcdir)/gst/rawparse/gstvideoparse.h \ $(top_srcdir)/gst/rtpmux/gstrtpmux.h \ diff --git a/docs/plugins/gst-plugins-bad-plugins-docs.sgml b/docs/plugins/gst-plugins-bad-plugins-docs.sgml index fd393a723e..42bf40cfbf 100644 --- a/docs/plugins/gst-plugins-bad-plugins-docs.sgml +++ b/docs/plugins/gst-plugins-bad-plugins-docs.sgml @@ -88,7 +88,6 @@ - @@ -188,7 +187,6 @@ - diff --git a/docs/plugins/gst-plugins-bad-plugins-sections.txt b/docs/plugins/gst-plugins-bad-plugins-sections.txt index aa7a81dc61..8595dd3b7e 100644 --- a/docs/plugins/gst-plugins-bad-plugins-sections.txt +++ b/docs/plugins/gst-plugins-bad-plugins-sections.txt @@ -1118,20 +1118,6 @@ gst_pyramidsegment_get_type gst_pyramidsegment_plugin_init -
-element-qtmux -qtmux -GstQTMux - -GstQTMuxClass -GST_QT_MUX -GST_QT_MUX_CLASS -GST_IS_QT_MUX -GST_IS_QT_MUX_CLASS -GST_TYPE_QT_MUX -gst_qt_mux_get_type -
-
element-rsvgoverlay rsvgoverlay diff --git a/docs/plugins/inspect/plugin-qtmux.xml b/docs/plugins/inspect/plugin-qtmux.xml deleted file mode 100644 index ea5a9ac81b..0000000000 --- a/docs/plugins/inspect/plugin-qtmux.xml +++ /dev/null @@ -1,157 +0,0 @@ - - qtmux - Quicktime Muxer plugin - ../../gst/qtmux/.libs/libgstqtmux.so - libgstqtmux.so - 0.10.21.1 - LGPL - gst-plugins-bad - GStreamer Bad Plug-ins git - Unknown package origin - - - gppmux - 3GPP Muxer - Codec/Muxer - Multiplex audio and video into a 3GPP file - Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br> - - - audio_%d - sink - request -
audio/AMR, rate=(int)8000, channels=(int)[ 1, 2 ]; audio/AMR-WB, rate=(int)16000, channels=(int)[ 1, 2 ]; audio/mpeg, mpegversion=(int)1, layer=(int)3, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int)4, stream-format=(string)raw, channels=(int)[ 1, 8 ], rate=(int)[ 1, 2147483647 ]
-
- - video_%d - sink - request -
video/x-h263, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/mpeg, mpegversion=(int)4, systemstream=(boolean)false, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-divx, divxversion=(int)5, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-h264, stream-format=(string)avc, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
-
- - src - source - always -
video/quicktime, variant=(string)3gpp
-
-
-
- - ismlmux - ISML Muxer - Codec/Muxer - Multiplex audio and video into a ISML file - Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br> - - - audio_%d - sink - request -
audio/mpeg, mpegversion=(int)1, layer=(int)3, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int)4, stream-format=(string)raw, channels=(int)[ 1, 8 ], rate=(int)[ 1, 2147483647 ]
-
- - video_%d - sink - request -
video/mpeg, mpegversion=(int)4, systemstream=(boolean)false, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-divx, divxversion=(int)5, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-h264, stream-format=(string)avc, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
-
- - src - source - always -
video/quicktime, variant=(string)iso
-
-
-
- - mj2mux - MJ2 Muxer - Codec/Muxer - Multiplex audio and video into a MJ2 file - Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br> - - - audio_%d - sink - request -
audio/x-raw-int, width=(int)8, depth=(int)8, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean){ true, false }; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 4321, 1234 }, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean)true
-
- - video_%d - sink - request -
image/x-j2c, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; image/x-jpc, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
-
- - src - source - always -
video/mj2
-
-
-
- - mp4mux - MP4 Muxer - Codec/Muxer - Multiplex audio and video into a MP4 file - Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br> - - - audio_%d - sink - request -
audio/mpeg, mpegversion=(int)1, layer=(int)3, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int)4, stream-format=(string)raw, channels=(int)[ 1, 8 ], rate=(int)[ 1, 2147483647 ]; audio/x-alac, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]
-
- - video_%d - sink - request -
video/mpeg, mpegversion=(int)4, systemstream=(boolean)false, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-divx, divxversion=(int)5, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-h264, stream-format=(string)avc, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-mp4-part, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
-
- - src - source - always -
video/quicktime, variant=(string)iso
-
-
-
- - qtmoovrecover - QT Moov Recover - Util - Recovers unfinished qtmux files - Thiago Santos <thiago.sousa.santos@collabora.co.uk> - - - - - qtmux - QuickTime Muxer - Codec/Muxer - Multiplex audio and video into a QuickTime file - Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br> - - - audio_%d - sink - request -
audio/x-raw-int, width=(int)8, depth=(int)8, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean){ true, false }; audio/x-raw-int, width=(int)16, depth=(int)16, endianness=(int){ 4321, 1234 }, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean)true; audio/x-raw-int, width=(int)24, depth=(int)24, endianness=(int){ 4321, 1234 }, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean)true; audio/x-raw-int, width=(int)32, depth=(int)32, endianness=(int){ 4321, 1234 }, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ], signed=(boolean)true; audio/mpeg, mpegversion=(int)1, layer=(int)3, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/mpeg, mpegversion=(int)4, stream-format=(string)raw, channels=(int)[ 1, 8 ], rate=(int)[ 1, 2147483647 ]; audio/x-adpcm, layout=(string)dvi, block_align=(int)[ 64, 8096 ], channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/x-alaw, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]; audio/AMR, rate=(int)8000, channels=(int)[ 1, 2 ]; audio/AMR-WB, rate=(int)16000, channels=(int)[ 1, 2 ]; audio/x-alac, channels=(int)[ 1, 2 ], rate=(int)[ 1, 2147483647 ]
-
- - video_%d - sink - request -
video/x-raw-rgb, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-raw-yuv, format=(fourcc)UYVY, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/mpeg, mpegversion=(int)4, systemstream=(boolean)false, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-divx, divxversion=(int)5, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-h263, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-h264, stream-format=(string)avc, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-svq, svqversion=(int)3, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-dv, systemstream=(boolean)false, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; image/jpeg, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ]; video/x-vp8, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]; video/x-qt-part, width=(int)[ 16, 4096 ], height=(int)[ 16, 4096 ], framerate=(fraction)[ 0/1, 2147483647/1 ]
-
- - src - source - always -
video/quicktime, variant=(string)apple
-
-
-
-
-
\ No newline at end of file diff --git a/gst-plugins-bad.spec.in b/gst-plugins-bad.spec.in index 3b4fa9e27b..6d780c5b3c 100644 --- a/gst-plugins-bad.spec.in +++ b/gst-plugins-bad.spec.in @@ -106,7 +106,6 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/gstreamer-%{majorminor}/libgstmpegtsdemux.so %{_libdir}/gstreamer-%{majorminor}/libgstjp2k.so %{_libdir}/gstreamer-%{majorminor}/libgstapexsink.so -%{_libdir}/gstreamer-%{majorminor}/libgstqtmux.so %{_libdir}/gstreamer-%{majorminor}/libgstlegacyresample.so %{_libdir}/gstreamer-%{majorminor}/libgstmxf.so %{_libdir}/gstreamer-%{majorminor}/libgstvmnc.so @@ -124,7 +123,6 @@ rm -rf $RPM_BUILD_ROOT %{_libdir}/gstreamer-%{majorminor}/libgstasfmux.so %{_libdir}/gstreamer-%{majorminor}/libgstpnm.so %{_libdir}/gstreamer-%{majorminor}/libgstvideomeasure.so -%{_libdir}/gstreamer-%{majorminor}/libgstaudioparsersbad.so %{_libdir}/gstreamer-%{majorminor}/libgstrsvg.so %{_includedir}/gstreamer-%{majorminor}/gst/video/gstbasevideocodec.h diff --git a/gst/qtmux/Makefile.am b/gst/qtmux/Makefile.am deleted file mode 100644 index eb405c7b02..0000000000 --- a/gst/qtmux/Makefile.am +++ /dev/null @@ -1,45 +0,0 @@ -# plugindir is set in configure - -plugin_LTLIBRARIES = libgstqtmux.la - -# sources used to compile this plug-in -libgstqtmux_la_SOURCES = gstqtmux.c \ - gstqtmoovrecover.c \ - gstqtmuxplugin.c \ - atoms.c \ - atomsrecovery.c \ - descriptors.c \ - properties.c \ - gstqtmuxmap.c - -# flags used to compile this plugin -# add other _CFLAGS and _LIBS as needed -libgstqtmux_la_CFLAGS = $(GST_CFLAGS) $(GST_BASE_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) -libgstqtmux_la_LIBADD = -lgstinterfaces-$(GST_MAJORMINOR) $(GST_LIBS) $(GST_BASE_LIBS) $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) -libgstqtmux_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -libgstqtmux_la_LIBTOOLFLAGS = --tag=disable-static - -# headers we need but don't want installed -noinst_HEADERS = gstqtmux.h \ - gstqtmoovrecover.h \ - atoms.h \ - atomsrecovery.h \ - descriptors.h \ - properties.h \ - fourcc.h \ - ftypcc.h \ - gstqtmuxmap.h - -Android.mk: Makefile.am $(BUILT_SOURCES) - androgenizer \ - -:PROJECT libgstqtmux -:SHARED libgstqtmux \ - -:TAGS eng debug \ - -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ - -:SOURCES $(libgstqtmux_la_SOURCES) \ - -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstqtmux_la_CFLAGS) \ - -:LDFLAGS $(libgstqtmux_la_LDFLAGS) \ - $(libgstqtmux_la_LIBADD) \ - -ldl \ - -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ - LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \ - > $@ diff --git a/gst/qtmux/atoms.c b/gst/qtmux/atoms.c deleted file mode 100644 index 5aebd6b69d..0000000000 --- a/gst/qtmux/atoms.c +++ /dev/null @@ -1,4423 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008-2010 Thiago Santos - * Copyright (C) 2008 Mark Nauwelaerts - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "atoms.h" -#include -#include - -#include -#include -#include - -/** - * Creates a new AtomsContext for the given flavor. - */ -AtomsContext * -atoms_context_new (AtomsTreeFlavor flavor) -{ - AtomsContext *context = g_new0 (AtomsContext, 1); - context->flavor = flavor; - return context; -} - -/** - * Frees an AtomsContext and all memory associated with it - */ -void -atoms_context_free (AtomsContext * context) -{ - g_free (context); -} - -/* -- creation, initialization, clear and free functions -- */ - -#define SECS_PER_DAY (24 * 60 * 60) -#define LEAP_YEARS_FROM_1904_TO_1970 17 - -static guint64 -get_current_qt_time (void) -{ - GTimeVal timeval; - - g_get_current_time (&timeval); - /* FIXME this should use UTC coordinated time */ - return timeval.tv_sec + (((1970 - 1904) * (guint64) 365) + - LEAP_YEARS_FROM_1904_TO_1970) * SECS_PER_DAY; -} - -static void -common_time_info_init (TimeInfo * ti) -{ - ti->creation_time = ti->modification_time = get_current_qt_time (); - ti->timescale = 0; - ti->duration = 0; -} - -static void -atom_header_set (Atom * header, guint32 fourcc, gint32 size, gint64 ext_size) -{ - header->type = fourcc; - header->size = size; - header->extended_size = ext_size; -} - -static void -atom_clear (Atom * atom) -{ -} - -static void -atom_full_init (AtomFull * full, guint32 fourcc, gint32 size, gint64 ext_size, - guint8 version, guint8 flags[3]) -{ - atom_header_set (&(full->header), fourcc, size, ext_size); - full->version = version; - full->flags[0] = flags[0]; - full->flags[1] = flags[1]; - full->flags[2] = flags[2]; -} - -static void -atom_full_clear (AtomFull * full) -{ - atom_clear (&full->header); -} - -static void -atom_full_free (AtomFull * full) -{ - atom_full_clear (full); - g_free (full); -} - -static guint32 -atom_full_get_flags_as_uint (AtomFull * full) -{ - return full->flags[0] << 16 | full->flags[1] << 8 | full->flags[2]; -} - -static void -atom_full_set_flags_as_uint (AtomFull * full, guint32 flags_as_uint) -{ - full->flags[2] = flags_as_uint & 0xFF; - full->flags[1] = (flags_as_uint & 0xFF00) >> 8; - full->flags[0] = (flags_as_uint & 0xFF0000) >> 16; -} - -static AtomInfo * -build_atom_info_wrapper (Atom * atom, gpointer copy_func, gpointer free_func) -{ - AtomInfo *info = NULL; - - if (atom) { - info = g_new0 (AtomInfo, 1); - - info->atom = atom; - info->copy_data_func = copy_func; - info->free_func = free_func; - } - - return info; -} - -static GList * -atom_info_list_prepend_atom (GList * ai, Atom * atom, - AtomCopyDataFunc copy_func, AtomFreeFunc free_func) -{ - if (atom) - return g_list_prepend (ai, - build_atom_info_wrapper (atom, copy_func, free_func)); - else - return ai; -} - -static void -atom_info_list_free (GList * ai) -{ - while (ai) { - AtomInfo *info = (AtomInfo *) ai->data; - - info->free_func (info->atom); - g_free (info); - ai = g_list_delete_link (ai, ai); - } -} - -static AtomData * -atom_data_new (guint32 fourcc) -{ - AtomData *data = g_new0 (AtomData, 1); - - atom_header_set (&data->header, fourcc, 0, 0); - return data; -} - -static void -atom_data_alloc_mem (AtomData * data, guint32 size) -{ - if (data->data) { - g_free (data->data); - } - data->data = g_new0 (guint8, size); - data->datalen = size; -} - -static AtomData * -atom_data_new_from_gst_buffer (guint32 fourcc, const GstBuffer * buf) -{ - AtomData *data = atom_data_new (fourcc); - - atom_data_alloc_mem (data, GST_BUFFER_SIZE (buf)); - g_memmove (data->data, GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf)); - return data; -} - -static void -atom_data_free (AtomData * data) -{ - atom_clear (&data->header); - g_free (data->data); - g_free (data); -} - -static AtomUUID * -atom_uuid_new (void) -{ - AtomUUID *uuid = g_new0 (AtomUUID, 1); - - atom_header_set (&uuid->header, FOURCC_uuid, 0, 0); - return uuid; -} - -static void -atom_uuid_free (AtomUUID * data) -{ - atom_clear (&data->header); - g_free (data->data); - g_free (data); -} - -static void -atom_ftyp_init (AtomFTYP * ftyp, guint32 major, guint32 version, GList * brands) -{ - gint index; - GList *it = NULL; - - atom_header_set (&ftyp->header, FOURCC_ftyp, 16, 0); - ftyp->major_brand = major; - ftyp->version = version; - - /* always include major brand as compatible brand */ - ftyp->compatible_brands_size = g_list_length (brands) + 1; - ftyp->compatible_brands = g_new (guint32, ftyp->compatible_brands_size); - - ftyp->compatible_brands[0] = major; - index = 1; - for (it = brands; it != NULL; it = g_list_next (it)) { - ftyp->compatible_brands[index++] = GPOINTER_TO_UINT (it->data); - } -} - -AtomFTYP * -atom_ftyp_new (AtomsContext * context, guint32 major, guint32 version, - GList * brands) -{ - AtomFTYP *ftyp = g_new0 (AtomFTYP, 1); - - atom_ftyp_init (ftyp, major, version, brands); - return ftyp; -} - -void -atom_ftyp_free (AtomFTYP * ftyp) -{ - atom_clear (&ftyp->header); - g_free (ftyp->compatible_brands); - ftyp->compatible_brands = NULL; - g_free (ftyp); -} - -static void -atom_esds_init (AtomESDS * esds) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&esds->header, FOURCC_esds, 0, 0, 0, flags); - desc_es_init (&esds->es); -} - -static AtomESDS * -atom_esds_new (void) -{ - AtomESDS *esds = g_new0 (AtomESDS, 1); - - atom_esds_init (esds); - return esds; -} - -static void -atom_esds_free (AtomESDS * esds) -{ - atom_full_clear (&esds->header); - desc_es_descriptor_clear (&esds->es); - g_free (esds); -} - -static AtomFRMA * -atom_frma_new (void) -{ - AtomFRMA *frma = g_new0 (AtomFRMA, 1); - - atom_header_set (&frma->header, FOURCC_frma, 0, 0); - return frma; -} - -static void -atom_frma_free (AtomFRMA * frma) -{ - atom_clear (&frma->header); - g_free (frma); -} - -static AtomWAVE * -atom_wave_new (void) -{ - AtomWAVE *wave = g_new0 (AtomWAVE, 1); - - atom_header_set (&wave->header, FOURCC_wave, 0, 0); - return wave; -} - -static void -atom_wave_free (AtomWAVE * wave) -{ - atom_clear (&wave->header); - atom_info_list_free (wave->extension_atoms); - g_free (wave); -} - -static void -atom_elst_init (AtomELST * elst) -{ - guint8 flags[3] = { 0, 0, 0 }; - atom_full_init (&elst->header, FOURCC_elst, 0, 0, 0, flags); - elst->entries = 0; -} - -static void -atom_elst_clear (AtomELST * elst) -{ - GSList *walker; - - atom_full_clear (&elst->header); - walker = elst->entries; - while (walker) { - g_free ((EditListEntry *) walker->data); - walker = g_slist_next (walker); - } - g_slist_free (elst->entries); -} - -static void -atom_edts_init (AtomEDTS * edts) -{ - atom_header_set (&edts->header, FOURCC_edts, 0, 0); - atom_elst_init (&edts->elst); -} - -static void -atom_edts_clear (AtomEDTS * edts) -{ - atom_clear (&edts->header); - atom_elst_clear (&edts->elst); -} - -static AtomEDTS * -atom_edts_new (void) -{ - AtomEDTS *edts = g_new0 (AtomEDTS, 1); - atom_edts_init (edts); - return edts; -} - -static void -atom_edts_free (AtomEDTS * edts) -{ - atom_edts_clear (edts); - g_free (edts); -} - -static void -atom_sample_entry_init (SampleTableEntry * se, guint32 type) -{ - atom_header_set (&se->header, type, 0, 0); - - memset (se->reserved, 0, sizeof (guint8) * 6); - se->data_reference_index = 0; -} - -static void -atom_sample_entry_free (SampleTableEntry * se) -{ - atom_clear (&se->header); -} - -static void -sample_entry_mp4a_init (SampleTableEntryMP4A * mp4a) -{ - atom_sample_entry_init (&mp4a->se, FOURCC_mp4a); - - mp4a->version = 0; - mp4a->revision_level = 0; - mp4a->vendor = 0; - mp4a->channels = 2; - mp4a->sample_size = 16; - mp4a->compression_id = 0; - mp4a->packet_size = 0; - mp4a->sample_rate = 0; - /* following only used if version is 1 */ - mp4a->samples_per_packet = 0; - mp4a->bytes_per_packet = 0; - mp4a->bytes_per_frame = 0; - mp4a->bytes_per_sample = 0; - - mp4a->extension_atoms = NULL; -} - -static SampleTableEntryMP4A * -sample_entry_mp4a_new (void) -{ - SampleTableEntryMP4A *mp4a = g_new0 (SampleTableEntryMP4A, 1); - - sample_entry_mp4a_init (mp4a); - return mp4a; -} - -static void -sample_entry_mp4a_free (SampleTableEntryMP4A * mp4a) -{ - atom_sample_entry_free (&mp4a->se); - atom_info_list_free (mp4a->extension_atoms); - g_free (mp4a); -} - -static void -sample_entry_mp4v_init (SampleTableEntryMP4V * mp4v, AtomsContext * context) -{ - atom_sample_entry_init (&mp4v->se, FOURCC_mp4v); - - mp4v->version = 0; - mp4v->revision_level = 0; - mp4v->vendor = 0; - - mp4v->temporal_quality = 0; - mp4v->spatial_quality = 0; - - /* qt and ISO base media do not contradict, and examples agree */ - mp4v->horizontal_resolution = 0x00480000; - mp4v->vertical_resolution = 0x00480000; - - mp4v->datasize = 0; - mp4v->frame_count = 1; - - memset (mp4v->compressor, 0, sizeof (guint8) * 32); - - mp4v->depth = 0; - mp4v->color_table_id = 0; - - mp4v->extension_atoms = NULL; -} - -static void -sample_entry_mp4v_free (SampleTableEntryMP4V * mp4v) -{ - atom_sample_entry_free (&mp4v->se); - atom_info_list_free (mp4v->extension_atoms); - g_free (mp4v); -} - -static SampleTableEntryMP4V * -sample_entry_mp4v_new (AtomsContext * context) -{ - SampleTableEntryMP4V *mp4v = g_new0 (SampleTableEntryMP4V, 1); - - sample_entry_mp4v_init (mp4v, context); - return mp4v; -} - -static void -atom_stsd_init (AtomSTSD * stsd) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&stsd->header, FOURCC_stsd, 0, 0, 0, flags); - stsd->entries = NULL; - stsd->n_entries = 0; -} - -static void -atom_stsd_remove_entries (AtomSTSD * stsd) -{ - GList *walker; - - walker = stsd->entries; - while (walker) { - GList *aux = walker; - SampleTableEntry *se = (SampleTableEntry *) aux->data; - - walker = g_list_next (walker); - stsd->entries = g_list_remove_link (stsd->entries, aux); - - switch (se->kind) { - case AUDIO: - sample_entry_mp4a_free ((SampleTableEntryMP4A *) se); - break; - case VIDEO: - sample_entry_mp4v_free ((SampleTableEntryMP4V *) se); - break; - default: - /* best possible cleanup */ - atom_sample_entry_free (se); - } - g_list_free (aux); - } - stsd->n_entries = 0; -} - -static void -atom_stsd_clear (AtomSTSD * stsd) -{ - atom_stsd_remove_entries (stsd); - atom_full_clear (&stsd->header); -} - -static void -atom_ctts_init (AtomCTTS * ctts) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&ctts->header, FOURCC_ctts, 0, 0, 0, flags); - atom_array_init (&ctts->entries, 128); - ctts->do_pts = FALSE; -} - -static AtomCTTS * -atom_ctts_new (void) -{ - AtomCTTS *ctts = g_new0 (AtomCTTS, 1); - - atom_ctts_init (ctts); - return ctts; -} - -static void -atom_ctts_free (AtomCTTS * ctts) -{ - atom_full_clear (&ctts->header); - atom_array_clear (&ctts->entries); - g_free (ctts); -} - -static void -atom_stts_init (AtomSTTS * stts) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&stts->header, FOURCC_stts, 0, 0, 0, flags); - atom_array_init (&stts->entries, 512); -} - -static void -atom_stts_clear (AtomSTTS * stts) -{ - atom_full_clear (&stts->header); - atom_array_clear (&stts->entries); -} - -static void -atom_stsz_init (AtomSTSZ * stsz) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&stsz->header, FOURCC_stsz, 0, 0, 0, flags); - atom_array_init (&stsz->entries, 1024); - stsz->sample_size = 0; - stsz->table_size = 0; -} - -static void -atom_stsz_clear (AtomSTSZ * stsz) -{ - atom_full_clear (&stsz->header); - atom_array_clear (&stsz->entries); - stsz->table_size = 0; -} - -static void -atom_stsc_init (AtomSTSC * stsc) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&stsc->header, FOURCC_stsc, 0, 0, 0, flags); - atom_array_init (&stsc->entries, 128); -} - -static void -atom_stsc_clear (AtomSTSC * stsc) -{ - atom_full_clear (&stsc->header); - atom_array_clear (&stsc->entries); -} - -static void -atom_co64_init (AtomSTCO64 * co64) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&co64->header, FOURCC_stco, 0, 0, 0, flags); - atom_array_init (&co64->entries, 256); -} - -static void -atom_stco64_clear (AtomSTCO64 * stco64) -{ - atom_full_clear (&stco64->header); - atom_array_clear (&stco64->entries); -} - -static void -atom_stss_init (AtomSTSS * stss) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&stss->header, FOURCC_stss, 0, 0, 0, flags); - atom_array_init (&stss->entries, 128); -} - -static void -atom_stss_clear (AtomSTSS * stss) -{ - atom_full_clear (&stss->header); - atom_array_clear (&stss->entries); -} - -void -atom_stbl_init (AtomSTBL * stbl) -{ - atom_header_set (&stbl->header, FOURCC_stbl, 0, 0); - - atom_stts_init (&stbl->stts); - atom_stss_init (&stbl->stss); - atom_stsd_init (&stbl->stsd); - atom_stsz_init (&stbl->stsz); - atom_stsc_init (&stbl->stsc); - stbl->ctts = NULL; - - atom_co64_init (&stbl->stco64); -} - -void -atom_stbl_clear (AtomSTBL * stbl) -{ - atom_clear (&stbl->header); - atom_stsd_clear (&stbl->stsd); - atom_stts_clear (&stbl->stts); - atom_stss_clear (&stbl->stss); - atom_stsc_clear (&stbl->stsc); - atom_stsz_clear (&stbl->stsz); - if (stbl->ctts) { - atom_ctts_free (stbl->ctts); - } - atom_stco64_clear (&stbl->stco64); -} - -static void -atom_vmhd_init (AtomVMHD * vmhd, AtomsContext * context) -{ - guint8 flags[3] = { 0, 0, 1 }; - - atom_full_init (&vmhd->header, FOURCC_vmhd, 0, 0, 0, flags); - vmhd->graphics_mode = 0x0; - memset (vmhd->opcolor, 0, sizeof (guint16) * 3); - - if (context->flavor == ATOMS_TREE_FLAVOR_MOV) { - vmhd->graphics_mode = 0x40; - vmhd->opcolor[0] = 32768; - vmhd->opcolor[1] = 32768; - vmhd->opcolor[2] = 32768; - } -} - -static AtomVMHD * -atom_vmhd_new (AtomsContext * context) -{ - AtomVMHD *vmhd = g_new0 (AtomVMHD, 1); - - atom_vmhd_init (vmhd, context); - return vmhd; -} - -static void -atom_vmhd_free (AtomVMHD * vmhd) -{ - atom_full_clear (&vmhd->header); - g_free (vmhd); -} - -static void -atom_smhd_init (AtomSMHD * smhd) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&smhd->header, FOURCC_smhd, 0, 0, 0, flags); - smhd->balance = 0; - smhd->reserved = 0; -} - -static AtomSMHD * -atom_smhd_new (void) -{ - AtomSMHD *smhd = g_new0 (AtomSMHD, 1); - - atom_smhd_init (smhd); - return smhd; -} - -static void -atom_smhd_free (AtomSMHD * smhd) -{ - atom_full_clear (&smhd->header); - g_free (smhd); -} - -static void -atom_hmhd_free (AtomHMHD * hmhd) -{ - atom_full_clear (&hmhd->header); - g_free (hmhd); -} - -static void -atom_hdlr_init (AtomHDLR * hdlr) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&hdlr->header, FOURCC_hdlr, 0, 0, 0, flags); - - hdlr->component_type = 0; - hdlr->handler_type = 0; - hdlr->manufacturer = 0; - hdlr->flags = 0; - hdlr->flags_mask = 0; - hdlr->name = g_strdup (""); -} - -static AtomHDLR * -atom_hdlr_new (void) -{ - AtomHDLR *hdlr = g_new0 (AtomHDLR, 1); - - atom_hdlr_init (hdlr); - return hdlr; -} - -static void -atom_hdlr_clear (AtomHDLR * hdlr) -{ - atom_full_clear (&hdlr->header); - if (hdlr->name) { - g_free (hdlr->name); - hdlr->name = NULL; - } -} - -static void -atom_hdlr_free (AtomHDLR * hdlr) -{ - atom_hdlr_clear (hdlr); - g_free (hdlr); -} - -static void -atom_url_init (AtomURL * url) -{ - guint8 flags[3] = { 0, 0, 1 }; - - atom_full_init (&url->header, FOURCC_url_, 0, 0, 0, flags); - url->location = NULL; -} - -static void -atom_url_free (AtomURL * url) -{ - atom_full_clear (&url->header); - if (url->location) { - g_free (url->location); - url->location = NULL; - } - g_free (url); -} - -static AtomURL * -atom_url_new (void) -{ - AtomURL *url = g_new0 (AtomURL, 1); - - atom_url_init (url); - return url; -} - -static AtomFull * -atom_alis_new (void) -{ - guint8 flags[3] = { 0, 0, 1 }; - AtomFull *alis = g_new0 (AtomFull, 1); - - atom_full_init (alis, FOURCC_alis, 0, 0, 0, flags); - return alis; -} - -static void -atom_dref_init (AtomDREF * dref, AtomsContext * context) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&dref->header, FOURCC_dref, 0, 0, 0, flags); - - /* in either case, alis or url init arranges to set self-contained flag */ - if (context->flavor == ATOMS_TREE_FLAVOR_MOV) { - /* alis dref for qt */ - AtomFull *alis = atom_alis_new (); - dref->entries = g_list_append (dref->entries, alis); - } else { - /* url for iso spec, as 'alis' not specified there */ - AtomURL *url = atom_url_new (); - dref->entries = g_list_append (dref->entries, url); - } -} - -static void -atom_dref_clear (AtomDREF * dref) -{ - GList *walker; - - atom_full_clear (&dref->header); - walker = dref->entries; - while (walker) { - GList *aux = walker; - Atom *atom = (Atom *) aux->data; - - walker = g_list_next (walker); - dref->entries = g_list_remove_link (dref->entries, aux); - switch (atom->type) { - case FOURCC_alis: - atom_full_free ((AtomFull *) atom); - break; - case FOURCC_url_: - atom_url_free ((AtomURL *) atom); - break; - default: - /* we do nothing, better leak than crash */ - break; - } - g_list_free (aux); - } -} - -static void -atom_dinf_init (AtomDINF * dinf, AtomsContext * context) -{ - atom_header_set (&dinf->header, FOURCC_dinf, 0, 0); - atom_dref_init (&dinf->dref, context); -} - -static void -atom_dinf_clear (AtomDINF * dinf) -{ - atom_clear (&dinf->header); - atom_dref_clear (&dinf->dref); -} - -static void -atom_minf_init (AtomMINF * minf, AtomsContext * context) -{ - atom_header_set (&minf->header, FOURCC_minf, 0, 0); - - minf->vmhd = NULL; - minf->smhd = NULL; - minf->hmhd = NULL; - - if (context->flavor == ATOMS_TREE_FLAVOR_MOV) { - minf->hdlr = atom_hdlr_new (); - minf->hdlr->component_type = FOURCC_dhlr; - minf->hdlr->handler_type = FOURCC_alis; - } else { - minf->hdlr = NULL; - } - atom_dinf_init (&minf->dinf, context); - atom_stbl_init (&minf->stbl); -} - -static void -atom_minf_clear_handlers (AtomMINF * minf) -{ - if (minf->vmhd) { - atom_vmhd_free (minf->vmhd); - minf->vmhd = NULL; - } - if (minf->smhd) { - atom_smhd_free (minf->smhd); - minf->smhd = NULL; - } - if (minf->hmhd) { - atom_hmhd_free (minf->hmhd); - minf->hmhd = NULL; - } -} - -static void -atom_minf_clear (AtomMINF * minf) -{ - atom_clear (&minf->header); - atom_minf_clear_handlers (minf); - if (minf->hdlr) { - atom_hdlr_free (minf->hdlr); - } - atom_dinf_clear (&minf->dinf); - atom_stbl_clear (&minf->stbl); -} - -static void -atom_mdhd_init (AtomMDHD * mdhd) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&mdhd->header, FOURCC_mdhd, 0, 0, 0, flags); - common_time_info_init (&mdhd->time_info); - mdhd->language_code = 0; - mdhd->quality = 0; -} - -static void -atom_mdhd_clear (AtomMDHD * mdhd) -{ - atom_full_clear (&mdhd->header); -} - -static void -atom_mdia_init (AtomMDIA * mdia, AtomsContext * context) -{ - atom_header_set (&mdia->header, FOURCC_mdia, 0, 0); - - atom_mdhd_init (&mdia->mdhd); - atom_hdlr_init (&mdia->hdlr); - atom_minf_init (&mdia->minf, context); -} - -static void -atom_mdia_clear (AtomMDIA * mdia) -{ - atom_clear (&mdia->header); - atom_mdhd_clear (&mdia->mdhd); - atom_hdlr_clear (&mdia->hdlr); - atom_minf_clear (&mdia->minf); -} - -static void -atom_tkhd_init (AtomTKHD * tkhd, AtomsContext * context) -{ - /* - * flags info - * 1 -> track enabled - * 2 -> track in movie - * 4 -> track in preview - */ - guint8 flags[3] = { 0, 0, 7 }; - - atom_full_init (&tkhd->header, FOURCC_tkhd, 0, 0, 0, flags); - - tkhd->creation_time = tkhd->modification_time = get_current_qt_time (); - tkhd->duration = 0; - tkhd->track_ID = 0; - tkhd->reserved = 0; - - tkhd->reserved2[0] = tkhd->reserved2[1] = 0; - tkhd->layer = 0; - tkhd->alternate_group = 0; - tkhd->volume = 0; - tkhd->reserved3 = 0; - memset (tkhd->matrix, 0, sizeof (guint32) * 9); - tkhd->matrix[0] = 1 << 16; - tkhd->matrix[4] = 1 << 16; - tkhd->matrix[8] = 16384 << 16; - tkhd->width = 0; - tkhd->height = 0; -} - -static void -atom_tkhd_clear (AtomTKHD * tkhd) -{ - atom_full_clear (&tkhd->header); -} - -static void -atom_trak_init (AtomTRAK * trak, AtomsContext * context) -{ - atom_header_set (&trak->header, FOURCC_trak, 0, 0); - - atom_tkhd_init (&trak->tkhd, context); - trak->edts = NULL; - atom_mdia_init (&trak->mdia, context); -} - -AtomTRAK * -atom_trak_new (AtomsContext * context) -{ - AtomTRAK *trak = g_new0 (AtomTRAK, 1); - - atom_trak_init (trak, context); - return trak; -} - -static void -atom_trak_clear (AtomTRAK * trak) -{ - atom_clear (&trak->header); - atom_tkhd_clear (&trak->tkhd); - if (trak->edts) - atom_edts_free (trak->edts); - atom_mdia_clear (&trak->mdia); -} - -static void -atom_trak_free (AtomTRAK * trak) -{ - atom_trak_clear (trak); - g_free (trak); -} - -static void -atom_ilst_init (AtomILST * ilst) -{ - atom_header_set (&ilst->header, FOURCC_ilst, 0, 0); - ilst->entries = NULL; -} - -static AtomILST * -atom_ilst_new (void) -{ - AtomILST *ilst = g_new0 (AtomILST, 1); - - atom_ilst_init (ilst); - return ilst; -} - -static void -atom_ilst_free (AtomILST * ilst) -{ - if (ilst->entries) - atom_info_list_free (ilst->entries); - atom_clear (&ilst->header); - g_free (ilst); -} - -static void -atom_meta_init (AtomMETA * meta) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&meta->header, FOURCC_meta, 0, 0, 0, flags); - atom_hdlr_init (&meta->hdlr); - /* FIXME (ISOM says this is always 0) */ - meta->hdlr.component_type = FOURCC_mhlr; - meta->hdlr.handler_type = FOURCC_mdir; - meta->ilst = NULL; -} - -static AtomMETA * -atom_meta_new (void) -{ - AtomMETA *meta = g_new0 (AtomMETA, 1); - - atom_meta_init (meta); - return meta; -} - -static void -atom_meta_free (AtomMETA * meta) -{ - atom_full_clear (&meta->header); - atom_hdlr_clear (&meta->hdlr); - if (meta->ilst) - atom_ilst_free (meta->ilst); - meta->ilst = NULL; - g_free (meta); -} - -static void -atom_udta_init (AtomUDTA * udta) -{ - atom_header_set (&udta->header, FOURCC_udta, 0, 0); - udta->meta = NULL; -} - -static AtomUDTA * -atom_udta_new (void) -{ - AtomUDTA *udta = g_new0 (AtomUDTA, 1); - - atom_udta_init (udta); - return udta; -} - -static void -atom_udta_free (AtomUDTA * udta) -{ - atom_clear (&udta->header); - if (udta->meta) - atom_meta_free (udta->meta); - udta->meta = NULL; - if (udta->entries) - atom_info_list_free (udta->entries); - g_free (udta); -} - -static void -atom_tag_data_init (AtomTagData * data) -{ - guint8 flags[] = { 0, 0, 0 }; - - atom_full_init (&data->header, FOURCC_data, 0, 0, 0, flags); -} - -static void -atom_tag_data_clear (AtomTagData * data) -{ - atom_full_clear (&data->header); - g_free (data->data); - data->datalen = 0; -} - -/* - * Fourcc is the tag fourcc - * flags will be truncated to 24bits - */ -static AtomTag * -atom_tag_new (guint32 fourcc, guint32 flags_as_uint) -{ - AtomTag *tag = g_new0 (AtomTag, 1); - - tag->header.type = fourcc; - atom_tag_data_init (&tag->data); - atom_full_set_flags_as_uint (&tag->data.header, flags_as_uint); - return tag; -} - -static void -atom_tag_free (AtomTag * tag) -{ - atom_clear (&tag->header); - atom_tag_data_clear (&tag->data); - g_free (tag); -} - -static void -atom_mvhd_init (AtomMVHD * mvhd) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&(mvhd->header), FOURCC_mvhd, sizeof (AtomMVHD), 0, 0, flags); - - common_time_info_init (&mvhd->time_info); - - mvhd->prefered_rate = 1 << 16; - mvhd->volume = 1 << 8; - mvhd->reserved3 = 0; - memset (mvhd->reserved4, 0, sizeof (guint32[2])); - - memset (mvhd->matrix, 0, sizeof (guint32[9])); - mvhd->matrix[0] = 1 << 16; - mvhd->matrix[4] = 1 << 16; - mvhd->matrix[8] = 16384 << 16; - - mvhd->preview_time = 0; - mvhd->preview_duration = 0; - mvhd->poster_time = 0; - mvhd->selection_time = 0; - mvhd->selection_duration = 0; - mvhd->current_time = 0; - - mvhd->next_track_id = 1; -} - -static void -atom_mvhd_clear (AtomMVHD * mvhd) -{ - atom_full_clear (&mvhd->header); -} - -static void -atom_mehd_init (AtomMEHD * mehd) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&mehd->header, FOURCC_mehd, 0, 0, 1, flags); - mehd->fragment_duration = 0; -} - -static void -atom_mvex_init (AtomMVEX * mvex) -{ - atom_header_set (&mvex->header, FOURCC_mvex, 0, 0); - atom_mehd_init (&mvex->mehd); - mvex->trexs = NULL; -} - -static void -atom_moov_init (AtomMOOV * moov, AtomsContext * context) -{ - atom_header_set (&(moov->header), FOURCC_moov, 0, 0); - atom_mvhd_init (&(moov->mvhd)); - atom_mvex_init (&(moov->mvex)); - moov->udta = NULL; - moov->traks = NULL; - moov->context = *context; -} - -AtomMOOV * -atom_moov_new (AtomsContext * context) -{ - AtomMOOV *moov = g_new0 (AtomMOOV, 1); - - atom_moov_init (moov, context); - return moov; -} - -static void -atom_trex_free (AtomTREX * trex) -{ - atom_full_clear (&trex->header); - g_free (trex); -} - -static void -atom_mvex_clear (AtomMVEX * mvex) -{ - GList *walker; - - atom_clear (&mvex->header); - walker = mvex->trexs; - while (walker) { - atom_trex_free ((AtomTREX *) walker->data); - walker = g_list_next (walker); - } - g_list_free (mvex->trexs); - mvex->trexs = NULL; -} - -void -atom_moov_free (AtomMOOV * moov) -{ - GList *walker; - - atom_clear (&moov->header); - atom_mvhd_clear (&moov->mvhd); - - walker = moov->traks; - while (walker) { - atom_trak_free ((AtomTRAK *) walker->data); - walker = g_list_next (walker); - } - g_list_free (moov->traks); - moov->traks = NULL; - - if (moov->udta) { - atom_udta_free (moov->udta); - moov->udta = NULL; - } - - atom_mvex_clear (&moov->mvex); - - g_free (moov); -} - -/* -- end of init / free -- */ - -/* -- copy data functions -- */ - -static guint8 -atom_full_get_version (AtomFull * full) -{ - return full->version; -} - -static guint64 -common_time_info_copy_data (TimeInfo * ti, gboolean trunc_to_32, - guint8 ** buffer, guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (trunc_to_32) { - prop_copy_uint32 ((guint32) ti->creation_time, buffer, size, offset); - prop_copy_uint32 ((guint32) ti->modification_time, buffer, size, offset); - prop_copy_uint32 (ti->timescale, buffer, size, offset); - prop_copy_uint32 ((guint32) ti->duration, buffer, size, offset); - } else { - prop_copy_uint64 (ti->creation_time, buffer, size, offset); - prop_copy_uint64 (ti->modification_time, buffer, size, offset); - prop_copy_uint32 (ti->timescale, buffer, size, offset); - prop_copy_uint64 (ti->duration, buffer, size, offset); - } - return *offset - original_offset; -} - -static void -atom_write_size (guint8 ** buffer, guint64 * size, guint64 * offset, - guint64 atom_pos) -{ - /* this only works for non-extended atom size, which is OK - * (though it could be made to do mem_move, etc and write extended size) */ - prop_copy_uint32 (*offset - atom_pos, buffer, size, &atom_pos); -} - -guint64 -atom_copy_data (Atom * atom, guint8 ** buffer, guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - /* copies type and size */ - prop_copy_uint32 (atom->size, buffer, size, offset); - prop_copy_fourcc (atom->type, buffer, size, offset); - - /* extended size needed */ - if (atom->size == 1) { - /* really should not happen other than with mdat atom; - * would be a problem for size (re)write code, not to mention memory */ - g_return_val_if_fail (atom->type == FOURCC_mdat, 0); - prop_copy_uint64 (atom->extended_size, buffer, size, offset); - } - - return *offset - original_offset; -} - -static guint64 -atom_full_copy_data (AtomFull * atom, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&atom->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint8 (atom->version, buffer, size, offset); - prop_copy_uint8_array (atom->flags, 3, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_info_list_copy_data (GList * ai, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - while (ai) { - AtomInfo *info = (AtomInfo *) ai->data; - - if (!info->copy_data_func (info->atom, buffer, size, offset)) { - return 0; - } - ai = g_list_next (ai); - } - - return *offset - original_offset; -} - -static guint64 -atom_data_copy_data (AtomData * data, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&data->header, buffer, size, offset)) { - return 0; - } - if (data->datalen) - prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_uuid_copy_data (AtomUUID * uuid, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&uuid->header, buffer, size, offset)) { - return 0; - } - prop_copy_uint8_array (uuid->uuid, 16, buffer, size, offset); - if (uuid->datalen) - prop_copy_uint8_array (uuid->data, uuid->datalen, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_ftyp_copy_data (AtomFTYP * ftyp, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&ftyp->header, buffer, size, offset)) { - return 0; - } - prop_copy_fourcc (ftyp->major_brand, buffer, size, offset); - prop_copy_uint32 (ftyp->version, buffer, size, offset); - - prop_copy_fourcc_array (ftyp->compatible_brands, ftyp->compatible_brands_size, - buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint8 version; - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&(atom->header), buffer, size, offset)) { - return 0; - } - - version = atom_full_get_version (&(atom->header)); - if (version == 0) { - common_time_info_copy_data (&atom->time_info, TRUE, buffer, size, offset); - } else if (version == 1) { - common_time_info_copy_data (&atom->time_info, FALSE, buffer, size, offset); - } else { - *offset = original_offset; - return 0; - } - - prop_copy_uint32 (atom->prefered_rate, buffer, size, offset); - prop_copy_uint16 (atom->volume, buffer, size, offset); - prop_copy_uint16 (atom->reserved3, buffer, size, offset); - prop_copy_uint32_array (atom->reserved4, 2, buffer, size, offset); - prop_copy_uint32_array (atom->matrix, 9, buffer, size, offset); - prop_copy_uint32 (atom->preview_time, buffer, size, offset); - prop_copy_uint32 (atom->preview_duration, buffer, size, offset); - prop_copy_uint32 (atom->poster_time, buffer, size, offset); - prop_copy_uint32 (atom->selection_time, buffer, size, offset); - prop_copy_uint32 (atom->selection_duration, buffer, size, offset); - prop_copy_uint32 (atom->current_time, buffer, size, offset); - - prop_copy_uint32 (atom->next_track_id, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_tkhd_copy_data (AtomTKHD * tkhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&tkhd->header, buffer, size, offset)) { - return 0; - } - - if (atom_full_get_version (&tkhd->header) == 0) { - prop_copy_uint32 ((guint32) tkhd->creation_time, buffer, size, offset); - prop_copy_uint32 ((guint32) tkhd->modification_time, buffer, size, offset); - prop_copy_uint32 (tkhd->track_ID, buffer, size, offset); - prop_copy_uint32 (tkhd->reserved, buffer, size, offset); - prop_copy_uint32 ((guint32) tkhd->duration, buffer, size, offset); - } else { - prop_copy_uint64 (tkhd->creation_time, buffer, size, offset); - prop_copy_uint64 (tkhd->modification_time, buffer, size, offset); - prop_copy_uint32 (tkhd->track_ID, buffer, size, offset); - prop_copy_uint32 (tkhd->reserved, buffer, size, offset); - prop_copy_uint64 (tkhd->duration, buffer, size, offset); - } - - prop_copy_uint32_array (tkhd->reserved2, 2, buffer, size, offset); - prop_copy_uint16 (tkhd->layer, buffer, size, offset); - prop_copy_uint16 (tkhd->alternate_group, buffer, size, offset); - prop_copy_uint16 (tkhd->volume, buffer, size, offset); - prop_copy_uint16 (tkhd->reserved3, buffer, size, offset); - prop_copy_uint32_array (tkhd->matrix, 9, buffer, size, offset); - - prop_copy_uint32 (tkhd->width, buffer, size, offset); - prop_copy_uint32 (tkhd->height, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_hdlr_copy_data (AtomHDLR * hdlr, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&hdlr->header, buffer, size, offset)) { - return 0; - } - - prop_copy_fourcc (hdlr->component_type, buffer, size, offset); - prop_copy_fourcc (hdlr->handler_type, buffer, size, offset); - prop_copy_fourcc (hdlr->manufacturer, buffer, size, offset); - prop_copy_uint32 (hdlr->flags, buffer, size, offset); - prop_copy_uint32 (hdlr->flags_mask, buffer, size, offset); - - prop_copy_size_string ((guint8 *) hdlr->name, strlen (hdlr->name), buffer, - size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_vmhd_copy_data (AtomVMHD * vmhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&vmhd->header, buffer, size, offset)) { - return 0; - } - prop_copy_uint16 (vmhd->graphics_mode, buffer, size, offset); - prop_copy_uint16_array (vmhd->opcolor, 3, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - -static guint64 -atom_smhd_copy_data (AtomSMHD * smhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&smhd->header, buffer, size, offset)) { - return 0; - } - prop_copy_uint16 (smhd->balance, buffer, size, offset); - prop_copy_uint16 (smhd->reserved, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - -static guint64 -atom_hmhd_copy_data (AtomHMHD * hmhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&hmhd->header, buffer, size, offset)) { - return 0; - } - prop_copy_uint16 (hmhd->max_pdu_size, buffer, size, offset); - prop_copy_uint16 (hmhd->avg_pdu_size, buffer, size, offset); - prop_copy_uint32 (hmhd->max_bitrate, buffer, size, offset); - prop_copy_uint32 (hmhd->avg_bitrate, buffer, size, offset); - prop_copy_uint32 (hmhd->sliding_avg_bitrate, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - -static gboolean -atom_url_same_file_flag (AtomURL * url) -{ - return (url->header.flags[2] & 0x1) == 1; -} - -static guint64 -atom_url_copy_data (AtomURL * url, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&url->header, buffer, size, offset)) { - return 0; - } - - if (!atom_url_same_file_flag (url)) { - prop_copy_null_terminated_string (url->location, buffer, size, offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - -guint64 -atom_stts_copy_data (AtomSTTS * stts, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - - if (!atom_full_copy_data (&stts->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (atom_array_get_len (&stts->entries), buffer, size, offset); - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, - 8 * atom_array_get_len (&stts->entries)); - for (i = 0; i < atom_array_get_len (&stts->entries); i++) { - STTSEntry *entry = &atom_array_index (&stts->entries, i); - - prop_copy_uint32 (entry->sample_count, buffer, size, offset); - prop_copy_int32 (entry->sample_delta, buffer, size, offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_sample_entry_copy_data (SampleTableEntry * se, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&se->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint8_array (se->reserved, 6, buffer, size, offset); - prop_copy_uint16 (se->data_reference_index, buffer, size, offset); - - return *offset - original_offset; -} - -static guint64 -atom_esds_copy_data (AtomESDS * esds, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&esds->header, buffer, size, offset)) { - return 0; - } - if (!desc_es_descriptor_copy_data (&esds->es, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_frma_copy_data (AtomFRMA * frma, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&(frma->header), buffer, size, offset)) - return 0; - - prop_copy_fourcc (frma->media_type, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mp4s_copy_data (SampleTableEntryMP4S * mp4s, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_sample_entry_copy_data (&mp4s->se, buffer, size, offset)) { - return 0; - } - if (!atom_esds_copy_data (&mp4s->es, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_hint_sample_entry_copy_data (AtomHintSampleEntry * hse, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_sample_entry_copy_data (&hse->se, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (hse->size, buffer, size, offset); - prop_copy_uint8_array (hse->data, hse->size, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -sample_entry_mp4a_copy_data (SampleTableEntryMP4A * mp4a, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_sample_entry_copy_data (&mp4a->se, buffer, size, offset)) { - return 0; - } - - prop_copy_uint16 (mp4a->version, buffer, size, offset); - prop_copy_uint16 (mp4a->revision_level, buffer, size, offset); - prop_copy_uint32 (mp4a->vendor, buffer, size, offset); - prop_copy_uint16 (mp4a->channels, buffer, size, offset); - prop_copy_uint16 (mp4a->sample_size, buffer, size, offset); - prop_copy_uint16 (mp4a->compression_id, buffer, size, offset); - prop_copy_uint16 (mp4a->packet_size, buffer, size, offset); - prop_copy_uint32 (mp4a->sample_rate, buffer, size, offset); - - /* this should always be 0 for mp4 flavor */ - if (mp4a->version == 1) { - prop_copy_uint32 (mp4a->samples_per_packet, buffer, size, offset); - prop_copy_uint32 (mp4a->bytes_per_packet, buffer, size, offset); - prop_copy_uint32 (mp4a->bytes_per_frame, buffer, size, offset); - prop_copy_uint32 (mp4a->bytes_per_sample, buffer, size, offset); - } - - if (mp4a->extension_atoms) { - if (!atom_info_list_copy_data (mp4a->extension_atoms, buffer, size, offset)) - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -sample_entry_mp4v_copy_data (SampleTableEntryMP4V * mp4v, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_sample_entry_copy_data (&mp4v->se, buffer, size, offset)) { - return 0; - } - - prop_copy_uint16 (mp4v->version, buffer, size, offset); - prop_copy_uint16 (mp4v->revision_level, buffer, size, offset); - prop_copy_fourcc (mp4v->vendor, buffer, size, offset); - prop_copy_uint32 (mp4v->temporal_quality, buffer, size, offset); - prop_copy_uint32 (mp4v->spatial_quality, buffer, size, offset); - - prop_copy_uint16 (mp4v->width, buffer, size, offset); - prop_copy_uint16 (mp4v->height, buffer, size, offset); - - prop_copy_uint32 (mp4v->horizontal_resolution, buffer, size, offset); - prop_copy_uint32 (mp4v->vertical_resolution, buffer, size, offset); - prop_copy_uint32 (mp4v->datasize, buffer, size, offset); - - prop_copy_uint16 (mp4v->frame_count, buffer, size, offset); - - prop_copy_fixed_size_string ((guint8 *) mp4v->compressor, 32, buffer, size, - offset); - - prop_copy_uint16 (mp4v->depth, buffer, size, offset); - prop_copy_uint16 (mp4v->color_table_id, buffer, size, offset); - - /* extra atoms */ - if (mp4v->extension_atoms && - !atom_info_list_copy_data (mp4v->extension_atoms, buffer, size, offset)) - return 0; - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_stsz_copy_data (AtomSTSZ * stsz, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - - if (!atom_full_copy_data (&stsz->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (stsz->sample_size, buffer, size, offset); - prop_copy_uint32 (stsz->table_size, buffer, size, offset); - if (stsz->sample_size == 0) { - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, 4 * stsz->table_size); - /* entry count must match sample count */ - g_assert (atom_array_get_len (&stsz->entries) == stsz->table_size); - for (i = 0; i < atom_array_get_len (&stsz->entries); i++) { - prop_copy_uint32 (atom_array_index (&stsz->entries, i), buffer, size, - offset); - } - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_stsc_copy_data (AtomSTSC * stsc, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - - if (!atom_full_copy_data (&stsc->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (atom_array_get_len (&stsc->entries), buffer, size, offset); - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, - 12 * atom_array_get_len (&stsc->entries)); - - for (i = 0; i < atom_array_get_len (&stsc->entries); i++) { - STSCEntry *entry = &atom_array_index (&stsc->entries, i); - - prop_copy_uint32 (entry->first_chunk, buffer, size, offset); - prop_copy_uint32 (entry->samples_per_chunk, buffer, size, offset); - prop_copy_uint32 (entry->sample_description_index, buffer, size, offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_ctts_copy_data (AtomCTTS * ctts, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - - if (!atom_full_copy_data (&ctts->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (atom_array_get_len (&ctts->entries), buffer, size, offset); - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, - 8 * atom_array_get_len (&ctts->entries)); - for (i = 0; i < atom_array_get_len (&ctts->entries); i++) { - CTTSEntry *entry = &atom_array_index (&ctts->entries, i); - - prop_copy_uint32 (entry->samplecount, buffer, size, offset); - prop_copy_uint32 (entry->sampleoffset, buffer, size, offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_stco64_copy_data (AtomSTCO64 * stco64, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - gboolean trunc_to_32 = stco64->header.header.type == FOURCC_stco; - - if (!atom_full_copy_data (&stco64->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (atom_array_get_len (&stco64->entries), buffer, size, - offset); - - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, - 8 * atom_array_get_len (&stco64->entries)); - for (i = 0; i < atom_array_get_len (&stco64->entries); i++) { - guint64 *value = &atom_array_index (&stco64->entries, i); - - if (trunc_to_32) { - prop_copy_uint32 ((guint32) * value, buffer, size, offset); - } else { - prop_copy_uint64 (*value, buffer, size, offset); - } - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_stss_copy_data (AtomSTSS * stss, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint i; - - if (atom_array_get_len (&stss->entries) == 0) { - /* FIXME not needing this atom might be confused with error while copying */ - return 0; - } - - if (!atom_full_copy_data (&stss->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (atom_array_get_len (&stss->entries), buffer, size, offset); - /* minimize realloc */ - prop_copy_ensure_buffer (buffer, size, offset, - 4 * atom_array_get_len (&stss->entries)); - for (i = 0; i < atom_array_get_len (&stss->entries); i++) { - prop_copy_uint32 (atom_array_index (&stss->entries, i), buffer, size, - offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_stsd_copy_data (AtomSTSD * stsd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_full_copy_data (&stsd->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (stsd->n_entries, buffer, size, offset); - - for (walker = g_list_last (stsd->entries); walker != NULL; - walker = g_list_previous (walker)) { - SampleTableEntry *se = (SampleTableEntry *) walker->data; - - switch (((Atom *) walker->data)->type) { - case FOURCC_mp4a: - if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) walker->data, - buffer, size, offset)) { - return 0; - } - break; - case FOURCC_mp4s: - if (!atom_mp4s_copy_data ((SampleTableEntryMP4S *) walker->data, - buffer, size, offset)) { - return 0; - } - break; - case FOURCC_mp4v: - if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) walker->data, - buffer, size, offset)) { - return 0; - } - break; - default: - if (se->kind == VIDEO) { - if (!sample_entry_mp4v_copy_data ((SampleTableEntryMP4V *) - walker->data, buffer, size, offset)) { - return 0; - } - } else if (se->kind == AUDIO) { - if (!sample_entry_mp4a_copy_data ((SampleTableEntryMP4A *) - walker->data, buffer, size, offset)) { - return 0; - } - } else { - if (!atom_hint_sample_entry_copy_data ( - (AtomHintSampleEntry *) walker->data, buffer, size, offset)) { - return 0; - } - } - break; - } - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_stbl_copy_data (AtomSTBL * stbl, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&stbl->header, buffer, size, offset)) { - return 0; - } - - if (!atom_stsd_copy_data (&stbl->stsd, buffer, size, offset)) { - return 0; - } - if (!atom_stts_copy_data (&stbl->stts, buffer, size, offset)) { - return 0; - } - /* this atom is optional, so let's check if we need it - * (to avoid false error) */ - if (atom_array_get_len (&stbl->stss.entries)) { - if (!atom_stss_copy_data (&stbl->stss, buffer, size, offset)) { - return 0; - } - } - - if (!atom_stsc_copy_data (&stbl->stsc, buffer, size, offset)) { - return 0; - } - if (!atom_stsz_copy_data (&stbl->stsz, buffer, size, offset)) { - return 0; - } - if (stbl->ctts && stbl->ctts->do_pts) { - if (!atom_ctts_copy_data (stbl->ctts, buffer, size, offset)) { - return 0; - } - } - if (!atom_stco64_copy_data (&stbl->stco64, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - - -static guint64 -atom_dref_copy_data (AtomDREF * dref, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_full_copy_data (&dref->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (g_list_length (dref->entries), buffer, size, offset); - - walker = dref->entries; - while (walker != NULL) { - Atom *atom = (Atom *) walker->data; - - if (atom->type == FOURCC_url_) { - atom_url_copy_data ((AtomURL *) atom, buffer, size, offset); - } else if (atom->type == FOURCC_alis) { - atom_full_copy_data ((AtomFull *) atom, buffer, size, offset); - } else { - g_error ("Unsupported atom used inside dref atom"); - } - walker = g_list_next (walker); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_dinf_copy_data (AtomDINF * dinf, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&dinf->header, buffer, size, offset)) { - return 0; - } - - if (!atom_dref_copy_data (&dinf->dref, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return original_offset - *offset; -} - -static guint64 -atom_minf_copy_data (AtomMINF * minf, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&minf->header, buffer, size, offset)) { - return 0; - } - - if (minf->vmhd) { - if (!atom_vmhd_copy_data (minf->vmhd, buffer, size, offset)) { - return 0; - } - } else if (minf->smhd) { - if (!atom_smhd_copy_data (minf->smhd, buffer, size, offset)) { - return 0; - } - } else if (minf->hmhd) { - if (!atom_hmhd_copy_data (minf->hmhd, buffer, size, offset)) { - return 0; - } - } - - if (minf->hdlr) { - if (!atom_hdlr_copy_data (minf->hdlr, buffer, size, offset)) { - return 0; - } - } - - if (!atom_dinf_copy_data (&minf->dinf, buffer, size, offset)) { - return 0; - } - if (!atom_stbl_copy_data (&minf->stbl, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mdhd_copy_data (AtomMDHD * mdhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&mdhd->header, buffer, size, offset)) { - return 0; - } - - if (!common_time_info_copy_data (&mdhd->time_info, - atom_full_get_version (&mdhd->header) == 0, buffer, size, offset)) { - return 0; - } - - prop_copy_uint16 (mdhd->language_code, buffer, size, offset); - prop_copy_uint16 (mdhd->quality, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mdia_copy_data (AtomMDIA * mdia, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&mdia->header, buffer, size, offset)) { - return 0; - } - if (!atom_mdhd_copy_data (&mdia->mdhd, buffer, size, offset)) { - return 0; - } - if (!atom_hdlr_copy_data (&mdia->hdlr, buffer, size, offset)) { - return 0; - } - - if (!atom_minf_copy_data (&mdia->minf, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_elst_copy_data (AtomELST * elst, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GSList *walker; - - if (!atom_full_copy_data (&elst->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (g_slist_length (elst->entries), buffer, size, offset); - - for (walker = elst->entries; walker != NULL; walker = g_slist_next (walker)) { - EditListEntry *entry = (EditListEntry *) walker->data; - prop_copy_uint32 (entry->duration, buffer, size, offset); - prop_copy_uint32 (entry->media_time, buffer, size, offset); - prop_copy_uint32 (entry->media_rate, buffer, size, offset); - } - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_edts_copy_data (AtomEDTS * edts, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&(edts->header), buffer, size, offset)) - return 0; - - if (!atom_elst_copy_data (&(edts->elst), buffer, size, offset)) - return 0; - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_trak_copy_data (AtomTRAK * trak, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&trak->header, buffer, size, offset)) { - return 0; - } - if (!atom_tkhd_copy_data (&trak->tkhd, buffer, size, offset)) { - return 0; - } - if (trak->edts) { - if (!atom_edts_copy_data (trak->edts, buffer, size, offset)) { - return 0; - } - } - - if (!atom_mdia_copy_data (&trak->mdia, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_tag_data_copy_data (AtomTagData * data, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&data->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (data->reserved, buffer, size, offset); - prop_copy_uint8_array (data->data, data->datalen, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_tag_copy_data (AtomTag * tag, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&tag->header, buffer, size, offset)) { - return 0; - } - - if (!atom_tag_data_copy_data (&tag->data, buffer, size, offset)) { - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_ilst_copy_data (AtomILST * ilst, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&ilst->header, buffer, size, offset)) { - return 0; - } - /* extra atoms */ - if (ilst->entries && - !atom_info_list_copy_data (ilst->entries, buffer, size, offset)) - return 0; - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_meta_copy_data (AtomMETA * meta, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&meta->header, buffer, size, offset)) { - return 0; - } - if (!atom_hdlr_copy_data (&meta->hdlr, buffer, size, offset)) { - return 0; - } - if (meta->ilst) { - if (!atom_ilst_copy_data (meta->ilst, buffer, size, offset)) { - return 0; - } - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_udta_copy_data (AtomUDTA * udta, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&udta->header, buffer, size, offset)) { - return 0; - } - if (udta->meta) { - if (!atom_meta_copy_data (udta->meta, buffer, size, offset)) { - return 0; - } - } - if (udta->entries) { - /* extra atoms */ - if (!atom_info_list_copy_data (udta->entries, buffer, size, offset)) - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mehd_copy_data (AtomMEHD * mehd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&mehd->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint64 (mehd->fragment_duration, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_trex_copy_data (AtomTREX * trex, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&trex->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (trex->track_ID, buffer, size, offset); - prop_copy_uint32 (trex->default_sample_description_index, buffer, size, - offset); - prop_copy_uint32 (trex->default_sample_duration, buffer, size, offset); - prop_copy_uint32 (trex->default_sample_size, buffer, size, offset); - prop_copy_uint32 (trex->default_sample_flags, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mvex_copy_data (AtomMVEX * mvex, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_copy_data (&mvex->header, buffer, size, offset)) { - return 0; - } - - if (!atom_mehd_copy_data (&mvex->mehd, buffer, size, offset)) { - return 0; - } - - walker = g_list_first (mvex->trexs); - while (walker != NULL) { - if (!atom_trex_copy_data ((AtomTREX *) walker->data, buffer, size, offset)) { - return 0; - } - walker = g_list_next (walker); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -guint64 -atom_moov_copy_data (AtomMOOV * atom, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_copy_data (&(atom->header), buffer, size, offset)) - return 0; - - if (!atom_mvhd_copy_data (&(atom->mvhd), buffer, size, offset)) - return 0; - - walker = g_list_first (atom->traks); - while (walker != NULL) { - if (!atom_trak_copy_data ((AtomTRAK *) walker->data, buffer, size, offset)) { - return 0; - } - walker = g_list_next (walker); - } - - if (atom->udta) { - if (!atom_udta_copy_data (atom->udta, buffer, size, offset)) { - return 0; - } - } - - if (atom->fragmented) { - if (!atom_mvex_copy_data (&atom->mvex, buffer, size, offset)) { - return 0; - } - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_wave_copy_data (AtomWAVE * wave, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_copy_data (&(wave->header), buffer, size, offset)) - return 0; - - if (wave->extension_atoms) { - if (!atom_info_list_copy_data (wave->extension_atoms, buffer, size, offset)) - return 0; - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -/* -- end of copy data functions -- */ - -/* -- general functions, API and support functions */ - -/* add samples to tables */ - -static void -atom_stsc_add_new_entry (AtomSTSC * stsc, guint32 first_chunk, guint32 nsamples) -{ - STSCEntry nentry; - gint len; - - if ((len = atom_array_get_len (&stsc->entries)) && - ((atom_array_index (&stsc->entries, len - 1)).samples_per_chunk == - nsamples)) - return; - - nentry.first_chunk = first_chunk; - nentry.samples_per_chunk = nsamples; - nentry.sample_description_index = 1; - atom_array_append (&stsc->entries, nentry, 128); -} - -static void -atom_stts_add_entry (AtomSTTS * stts, guint32 sample_count, gint32 sample_delta) -{ - STTSEntry *entry = NULL; - - if (G_LIKELY (atom_array_get_len (&stts->entries) != 0)) - entry = &atom_array_index (&stts->entries, - atom_array_get_len (&stts->entries) - 1); - - if (entry && entry->sample_delta == sample_delta) { - entry->sample_count += sample_count; - } else { - STTSEntry nentry; - - nentry.sample_count = sample_count; - nentry.sample_delta = sample_delta; - atom_array_append (&stts->entries, nentry, 256); - } -} - -static void -atom_stsz_add_entry (AtomSTSZ * stsz, guint32 nsamples, guint32 size) -{ - guint32 i; - - stsz->table_size += nsamples; - if (stsz->sample_size != 0) { - /* it is constant size, we don't need entries */ - return; - } - for (i = 0; i < nsamples; i++) { - atom_array_append (&stsz->entries, size, 1024); - } -} - -static guint32 -atom_stco64_get_entry_count (AtomSTCO64 * stco64) -{ - return atom_array_get_len (&stco64->entries); -} - -static void -atom_stco64_add_entry (AtomSTCO64 * stco64, guint64 entry) -{ - atom_array_append (&stco64->entries, entry, 256); - if (entry > G_MAXUINT32) - stco64->header.header.type = FOURCC_co64; -} - -static void -atom_stss_add_entry (AtomSTSS * stss, guint32 sample) -{ - atom_array_append (&stss->entries, sample, 512); -} - -static void -atom_stbl_add_stss_entry (AtomSTBL * stbl) -{ - guint32 sample_index = stbl->stsz.table_size; - - atom_stss_add_entry (&stbl->stss, sample_index); -} - -static void -atom_ctts_add_entry (AtomCTTS * ctts, guint32 nsamples, guint32 offset) -{ - CTTSEntry *entry = NULL; - - if (G_LIKELY (atom_array_get_len (&ctts->entries) != 0)) - entry = &atom_array_index (&ctts->entries, - atom_array_get_len (&ctts->entries) - 1); - - if (entry == NULL || entry->sampleoffset != offset) { - CTTSEntry nentry; - - nentry.samplecount = nsamples; - nentry.sampleoffset = offset; - atom_array_append (&ctts->entries, nentry, 256); - if (offset != 0) - ctts->do_pts = TRUE; - } else { - entry->samplecount += nsamples; - } -} - -static void -atom_stbl_add_ctts_entry (AtomSTBL * stbl, guint32 nsamples, guint32 offset) -{ - if (stbl->ctts == NULL) { - stbl->ctts = atom_ctts_new (); - } - atom_ctts_add_entry (stbl->ctts, nsamples, offset); -} - -void -atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples, guint32 delta, - guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset) -{ - atom_stts_add_entry (&stbl->stts, nsamples, delta); - atom_stsz_add_entry (&stbl->stsz, nsamples, size); - atom_stco64_add_entry (&stbl->stco64, chunk_offset); - atom_stsc_add_new_entry (&stbl->stsc, - atom_stco64_get_entry_count (&stbl->stco64), nsamples); - if (sync) - atom_stbl_add_stss_entry (stbl); - /* always store to arrange for consistent content */ - atom_stbl_add_ctts_entry (stbl, nsamples, pts_offset); -} - -void -atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta, - guint32 size, guint64 chunk_offset, gboolean sync, gint64 pts_offset) -{ - AtomSTBL *stbl = &trak->mdia.minf.stbl; - atom_stbl_add_samples (stbl, nsamples, delta, size, chunk_offset, sync, - pts_offset); -} - -/* trak and moov molding */ - -guint32 -atom_trak_get_timescale (AtomTRAK * trak) -{ - return trak->mdia.mdhd.time_info.timescale; -} - -guint32 -atom_trak_get_id (AtomTRAK * trak) -{ - return trak->tkhd.track_ID; -} - -static void -atom_trak_set_id (AtomTRAK * trak, guint32 id) -{ - trak->tkhd.track_ID = id; -} - -static void -atom_moov_add_trex (AtomMOOV * moov, AtomTREX * trex) -{ - moov->mvex.trexs = g_list_append (moov->mvex.trexs, trex); -} - -static AtomTREX * -atom_trex_new (AtomTRAK * trak) -{ - guint8 flags[3] = { 0, 0, 0 }; - AtomTREX *trex = g_new0 (AtomTREX, 1); - - atom_full_init (&trex->header, FOURCC_trex, 0, 0, 0, flags); - - trex->track_ID = trak->tkhd.track_ID; - trex->default_sample_description_index = 1; - trex->default_sample_duration = 0; - trex->default_sample_size = 0; - trex->default_sample_flags = 0; - - return trex; -} - -void -atom_moov_add_trak (AtomMOOV * moov, AtomTRAK * trak) -{ - atom_trak_set_id (trak, moov->mvhd.next_track_id++); - moov->traks = g_list_append (moov->traks, trak); - /* additional trak means also new trex */ - atom_moov_add_trex (moov, atom_trex_new (trak)); -} - -static guint64 -atom_trak_get_duration (AtomTRAK * trak) -{ - return trak->tkhd.duration; -} - -static guint64 -atom_stts_get_total_duration (AtomSTTS * stts) -{ - guint i; - guint64 sum = 0; - - for (i = 0; i < atom_array_get_len (&stts->entries); i++) { - STTSEntry *entry = &atom_array_index (&stts->entries, i); - - sum += (guint64) (entry->sample_count) * entry->sample_delta; - } - return sum; -} - -static void -atom_trak_update_duration (AtomTRAK * trak, guint64 moov_timescale) -{ - trak->mdia.mdhd.time_info.duration = - atom_stts_get_total_duration (&trak->mdia.minf.stbl.stts); - if (trak->mdia.mdhd.time_info.timescale != 0) { - trak->tkhd.duration = - gst_util_uint64_scale (trak->mdia.mdhd.time_info.duration, - moov_timescale, trak->mdia.mdhd.time_info.timescale); - } else { - trak->tkhd.duration = 0; - } -} - -static guint32 -atom_moov_get_timescale (AtomMOOV * moov) -{ - return moov->mvhd.time_info.timescale; -} - -void -atom_moov_update_timescale (AtomMOOV * moov, guint32 timescale) -{ - moov->mvhd.time_info.timescale = timescale; -} - -void -atom_moov_update_duration (AtomMOOV * moov) -{ - GList *traks = moov->traks; - guint64 dur, duration = 0; - - while (traks) { - AtomTRAK *trak = (AtomTRAK *) traks->data; - - atom_trak_update_duration (trak, atom_moov_get_timescale (moov)); - dur = atom_trak_get_duration (trak); - if (dur > duration) - duration = dur; - traks = g_list_next (traks); - } - moov->mvhd.time_info.duration = duration; - moov->mvex.mehd.fragment_duration = duration; -} - -void -atom_moov_set_fragmented (AtomMOOV * moov, gboolean fragmented) -{ - moov->fragmented = fragmented; -} - -void -atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset) -{ - guint i; - - for (i = 0; i < atom_array_get_len (&stco64->entries); i++) { - guint64 *value = &atom_array_index (&stco64->entries, i); - - *value += offset; - } -} - -void -atom_moov_chunks_add_offset (AtomMOOV * moov, guint32 offset) -{ - GList *traks = moov->traks; - - while (traks) { - AtomTRAK *trak = (AtomTRAK *) traks->data; - - atom_stco64_chunks_add_offset (&trak->mdia.minf.stbl.stco64, offset); - traks = g_list_next (traks); - } -} - -/* - * Meta tags functions - */ -static void -atom_moov_init_metatags (AtomMOOV * moov, AtomsContext * context) -{ - if (!moov->udta) { - moov->udta = atom_udta_new (); - } - if (context->flavor != ATOMS_TREE_FLAVOR_3GP) { - if (!moov->udta->meta) { - moov->udta->meta = atom_meta_new (); - } - if (!moov->udta->meta->ilst) { - moov->udta->meta->ilst = atom_ilst_new (); - } - } -} - -static void -atom_tag_data_alloc_data (AtomTagData * data, guint size) -{ - if (data->data != NULL) { - g_free (data->data); - } - data->data = g_new0 (guint8, size); - data->datalen = size; -} - -static void -atom_moov_append_tag (AtomMOOV * moov, AtomInfo * tag) -{ - GList **entries; - - atom_moov_init_metatags (moov, &moov->context); - if (moov->udta->meta) - entries = &moov->udta->meta->ilst->entries; - else - entries = &moov->udta->entries; - *entries = g_list_append (*entries, tag); -} - -void -atom_moov_add_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags, - const guint8 * data, guint size) -{ - AtomTag *tag; - AtomTagData *tdata; - - tag = atom_tag_new (fourcc, flags); - tdata = &tag->data; - atom_tag_data_alloc_data (tdata, size); - g_memmove (tdata->data, data, size); - - atom_moov_append_tag (moov, - build_atom_info_wrapper ((Atom *) tag, atom_tag_copy_data, - atom_tag_free)); -} - -void -atom_moov_add_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value) -{ - gint len = strlen (value); - - if (len > 0) - atom_moov_add_tag (moov, fourcc, METADATA_TEXT_FLAG, (guint8 *) value, len); -} - -void -atom_moov_add_uint_tag (AtomMOOV * moov, guint32 fourcc, guint32 flags, - guint32 value) -{ - guint8 data[8] = { 0, }; - - if (flags) { - GST_WRITE_UINT16_BE (data, value); - atom_moov_add_tag (moov, fourcc, flags, data, 2); - } else { - GST_WRITE_UINT32_BE (data + 2, value); - atom_moov_add_tag (moov, fourcc, flags, data, 8); - } -} - -void -atom_moov_add_blob_tag (AtomMOOV * moov, guint8 * data, guint size) -{ - AtomData *data_atom; - GstBuffer *buf; - guint len; - guint32 fourcc; - - if (size < 8) - return; - - /* blob is unparsed atom; - * extract size and fourcc, and wrap remainder in data atom */ - len = GST_READ_UINT32_BE (data); - fourcc = GST_READ_UINT32_LE (data + 4); - if (len > size) - return; - - buf = gst_buffer_new (); - GST_BUFFER_SIZE (buf) = len - 8; - GST_BUFFER_DATA (buf) = data + 8; - - data_atom = atom_data_new_from_gst_buffer (fourcc, buf); - gst_buffer_unref (buf); - - atom_moov_append_tag (moov, - build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data, - atom_data_free)); -} - -void -atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data, - guint size) -{ - AtomData *data_atom; - GstBuffer *buf; - guint8 *bdata; - - /* need full atom */ - buf = gst_buffer_new_and_alloc (size + 4); - bdata = GST_BUFFER_DATA (buf); - /* full atom: version and flags */ - GST_WRITE_UINT32_BE (bdata, 0); - memcpy (bdata + 4, data, size); - - data_atom = atom_data_new_from_gst_buffer (fourcc, buf); - gst_buffer_unref (buf); - - atom_moov_append_tag (moov, - build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data, - atom_data_free)); -} - -guint16 -language_code (const char *lang) -{ - g_return_val_if_fail (lang != NULL, 0); - g_return_val_if_fail (strlen (lang) == 3, 0); - - return (((lang[0] - 0x60) & 0x1F) << 10) + (((lang[1] - 0x60) & 0x1F) << 5) + - ((lang[2] - 0x60) & 0x1F); -} - -void -atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc, - const gchar * value, gint16 ivalue) -{ - gint len = 0, size = 0; - guint8 *data; - - if (value) { - len = strlen (value); - size = len + 3; - } - - if (ivalue >= 0) - size += 2; - - data = g_malloc (size + 3); - /* language tag and null-terminated UTF-8 string */ - if (value) { - GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE)); - /* include 0 terminator */ - memcpy (data + 2, value, len + 1); - } - /* 16-bit unsigned int if standalone, otherwise 8-bit */ - if (ivalue >= 0) { - if (size == 2) - GST_WRITE_UINT16_BE (data + size - 2, ivalue); - else { - GST_WRITE_UINT8 (data + size - 2, ivalue & 0xFF); - size--; - } - } - - atom_moov_add_3gp_tag (moov, fourcc, data, size); - g_free (data); -} - -void -atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value) -{ - atom_moov_add_3gp_str_int_tag (moov, fourcc, value, -1); -} - -void -atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value) -{ - atom_moov_add_3gp_str_int_tag (moov, fourcc, NULL, value); -} - -void -atom_moov_add_xmp_tags (AtomMOOV * moov, GstBuffer * xmpbuffer) -{ - AtomData *data_atom = NULL; - - if (moov->context.flavor == ATOMS_TREE_FLAVOR_MOV) { - if (xmpbuffer) { - data_atom = atom_data_new_from_gst_buffer (FOURCC_XMP_, xmpbuffer); - atom_moov_init_metatags (moov, &moov->context); - moov->udta->entries = g_list_append (moov->udta->entries, - build_atom_info_wrapper ((Atom *) data_atom, atom_data_copy_data, - atom_data_free)); - } - } else { - GST_DEBUG ("Not adding xmp to moov atom, it is only used in 'mov' format"); - } - -} - -/* - * Functions for specifying media types - */ - -static void -atom_minf_set_audio (AtomMINF * minf) -{ - atom_minf_clear_handlers (minf); - minf->smhd = atom_smhd_new (); -} - -static void -atom_minf_set_video (AtomMINF * minf, AtomsContext * context) -{ - atom_minf_clear_handlers (minf); - minf->vmhd = atom_vmhd_new (context); -} - -static void -atom_hdlr_set_type (AtomHDLR * hdlr, AtomsContext * context, guint32 comp_type, - guint32 hdlr_type) -{ - if (context->flavor == ATOMS_TREE_FLAVOR_MOV) { - hdlr->component_type = comp_type; - } - hdlr->handler_type = hdlr_type; -} - -static void -atom_hdlr_set_name (AtomHDLR * hdlr, const char *name) -{ - if (hdlr->name) - g_free (hdlr->name); - hdlr->name = g_strdup (name); -} - -static void -atom_mdia_set_hdlr_type_audio (AtomMDIA * mdia, AtomsContext * context) -{ - atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_soun); - /* Some players (low-end hardware) check for this name, which is what - * QuickTime itself sets */ - atom_hdlr_set_name (&mdia->hdlr, "SoundHandler"); -} - -static void -atom_mdia_set_hdlr_type_video (AtomMDIA * mdia, AtomsContext * context) -{ - atom_hdlr_set_type (&mdia->hdlr, context, FOURCC_mhlr, FOURCC_vide); - /* Some players (low-end hardware) check for this name, which is what - * QuickTime itself sets */ - atom_hdlr_set_name (&mdia->hdlr, "VideoHandler"); -} - -static void -atom_mdia_set_audio (AtomMDIA * mdia, AtomsContext * context) -{ - atom_mdia_set_hdlr_type_audio (mdia, context); - atom_minf_set_audio (&mdia->minf); -} - -static void -atom_mdia_set_video (AtomMDIA * mdia, AtomsContext * context) -{ - atom_mdia_set_hdlr_type_video (mdia, context); - atom_minf_set_video (&mdia->minf, context); -} - -static void -atom_tkhd_set_audio (AtomTKHD * tkhd) -{ - tkhd->volume = 0x0100; - tkhd->width = tkhd->height = 0; -} - -static void -atom_tkhd_set_video (AtomTKHD * tkhd, AtomsContext * context, guint32 width, - guint32 height) -{ - tkhd->volume = 0; - - /* qt and ISO base media do not contradict, and examples agree */ - tkhd->width = width; - tkhd->height = height; -} - -static void -atom_edts_add_entry (AtomEDTS * edts, EditListEntry * entry) -{ - edts->elst.entries = g_slist_append (edts->elst.entries, entry); -} - -/* - * Adds a new entry to this trak edits list - * duration is in the moov's timescale - * media_time is the offset in the media time to start from (media's timescale) - * rate is a 32 bits fixed-point - */ -void -atom_trak_add_elst_entry (AtomTRAK * trak, guint32 duration, guint32 media_time, - guint32 rate) -{ - EditListEntry *entry = g_new (EditListEntry, 1); - - entry->duration = duration; - entry->media_time = media_time; - entry->media_rate = rate; - - if (trak->edts == NULL) { - trak->edts = atom_edts_new (); - } - atom_edts_add_entry (trak->edts, entry); -} - -/* re-negotiation is prevented at top-level, so only 1 entry expected. - * Quite some more care here and elsewhere may be needed to - * support several entries */ -static SampleTableEntryMP4A * -atom_trak_add_audio_entry (AtomTRAK * trak, AtomsContext * context, - guint32 type) -{ - AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd; - SampleTableEntryMP4A *mp4a = sample_entry_mp4a_new (); - - mp4a->se.header.type = type; - mp4a->se.kind = AUDIO; - mp4a->compression_id = -1; - mp4a->se.data_reference_index = 1; - - stsd->entries = g_list_prepend (stsd->entries, mp4a); - stsd->n_entries++; - return mp4a; -} - -static SampleTableEntryMP4V * -atom_trak_add_video_entry (AtomTRAK * trak, AtomsContext * context, - guint32 type) -{ - SampleTableEntryMP4V *mp4v = sample_entry_mp4v_new (context); - AtomSTSD *stsd = &trak->mdia.minf.stbl.stsd; - - mp4v->se.header.type = type; - mp4v->se.kind = VIDEO; - mp4v->se.data_reference_index = 1; - mp4v->horizontal_resolution = 72 << 16; - mp4v->vertical_resolution = 72 << 16; - if (context->flavor == ATOMS_TREE_FLAVOR_MOV) { - mp4v->spatial_quality = 512; - mp4v->temporal_quality = 512; - } - - stsd->entries = g_list_prepend (stsd->entries, mp4v); - stsd->n_entries++; - return mp4v; -} - -static void -atom_trak_set_constant_size_samples (AtomTRAK * trak, guint32 sample_size) -{ - trak->mdia.minf.stbl.stsz.sample_size = sample_size; -} - -static void -atom_trak_set_audio (AtomTRAK * trak, AtomsContext * context) -{ - atom_tkhd_set_audio (&trak->tkhd); - atom_mdia_set_audio (&trak->mdia, context); -} - -static void -atom_trak_set_video (AtomTRAK * trak, AtomsContext * context, guint32 width, - guint32 height) -{ - atom_tkhd_set_video (&trak->tkhd, context, width, height); - atom_mdia_set_video (&trak->mdia, context); -} - -static void -atom_trak_set_audio_commons (AtomTRAK * trak, AtomsContext * context, - guint32 rate) -{ - atom_trak_set_audio (trak, context); - trak->mdia.mdhd.time_info.timescale = rate; -} - -static void -atom_trak_set_video_commons (AtomTRAK * trak, AtomsContext * context, - guint32 rate, guint32 width, guint32 height) -{ - atom_trak_set_video (trak, context, width, height); - trak->mdia.mdhd.time_info.timescale = rate; - trak->tkhd.width = width << 16; - trak->tkhd.height = height << 16; -} - -void -atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context, - AudioSampleEntry * entry, guint32 scale, AtomInfo * ext, gint sample_size) -{ - SampleTableEntryMP4A *ste; - - atom_trak_set_audio_commons (trak, context, scale); - atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd); - ste = atom_trak_add_audio_entry (trak, context, entry->fourcc); - - trak->is_video = FALSE; - trak->is_h264 = FALSE; - - ste->version = entry->version; - ste->compression_id = entry->compression_id; - ste->sample_size = entry->sample_size; - ste->sample_rate = entry->sample_rate << 16; - ste->channels = entry->channels; - - ste->samples_per_packet = entry->samples_per_packet; - ste->bytes_per_sample = entry->bytes_per_sample; - ste->bytes_per_packet = entry->bytes_per_packet; - ste->bytes_per_frame = entry->bytes_per_frame; - - if (ext) - ste->extension_atoms = g_list_prepend (ste->extension_atoms, ext); - - /* 0 size means variable size */ - atom_trak_set_constant_size_samples (trak, sample_size); -} - -static AtomInfo * -build_pasp_extension (AtomTRAK * trak, gint par_width, gint par_height) -{ - AtomData *atom_data; - GstBuffer *buf; - guint8 *data; - - buf = gst_buffer_new_and_alloc (8); - data = GST_BUFFER_DATA (buf); - - /* ihdr = image header box */ - GST_WRITE_UINT32_BE (data, par_width); - GST_WRITE_UINT32_BE (data + 4, par_height); - - atom_data = atom_data_new_from_gst_buffer (FOURCC_pasp, buf); - gst_buffer_unref (buf); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -void -atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context, - VisualSampleEntry * entry, guint32 scale, GList * ext_atoms_list) -{ - SampleTableEntryMP4V *ste; - gint dwidth, dheight; - gint par_n = 0, par_d = 0; - - if ((entry->par_n != 1 || entry->par_d != 1) && - (entry->par_n != entry->par_d)) { - par_n = entry->par_n; - par_d = entry->par_d; - } - - dwidth = entry->width; - dheight = entry->height; - /* ISO file spec says track header w/h indicates track's visual presentation - * (so this together with pixels w/h implicitly defines PAR) */ - if (par_n && (context->flavor != ATOMS_TREE_FLAVOR_MOV)) { - if (par_n > par_d) { - dwidth = entry->width * par_n / par_d; - dheight = entry->height; - } else { - dwidth = entry->width * par_n / par_d; - dheight = entry->height; - } - } - - atom_trak_set_video_commons (trak, context, scale, dwidth, dheight); - atom_stsd_remove_entries (&trak->mdia.minf.stbl.stsd); - ste = atom_trak_add_video_entry (trak, context, entry->fourcc); - - trak->is_video = TRUE; - trak->is_h264 = (entry->fourcc == FOURCC_avc1); - - ste->version = entry->version; - ste->width = entry->width; - ste->height = entry->height; - ste->depth = entry->depth; - ste->color_table_id = entry->color_table_id; - ste->frame_count = entry->frame_count; - - if (ext_atoms_list) - ste->extension_atoms = g_list_concat (ste->extension_atoms, ext_atoms_list); - - /* QT spec has a pasp extension atom in stsd that can hold PAR */ - if (par_n && (context->flavor == ATOMS_TREE_FLAVOR_MOV)) { - ste->extension_atoms = g_list_append (ste->extension_atoms, - build_pasp_extension (trak, par_n, par_d)); - } -} - -static void -atom_mfhd_init (AtomMFHD * mfhd, guint32 sequence_number) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&(mfhd->header), FOURCC_mfhd, 0, 0, 0, flags); - mfhd->sequence_number = sequence_number; -} - -static void -atom_moof_init (AtomMOOF * moof, AtomsContext * context, - guint32 sequence_number) -{ - atom_header_set (&moof->header, FOURCC_moof, 0, 0); - atom_mfhd_init (&moof->mfhd, sequence_number); - moof->trafs = NULL; -} - -AtomMOOF * -atom_moof_new (AtomsContext * context, guint32 sequence_number) -{ - AtomMOOF *moof = g_new0 (AtomMOOF, 1); - - atom_moof_init (moof, context, sequence_number); - return moof; -} - -static void -atom_trun_free (AtomTRUN * trun) -{ - atom_full_clear (&trun->header); - atom_array_clear (&trun->entries); - g_free (trun); -} - -static void -atom_sdtp_free (AtomSDTP * sdtp) -{ - atom_full_clear (&sdtp->header); - atom_array_clear (&sdtp->entries); - g_free (sdtp); -} - -void -atom_traf_free (AtomTRAF * traf) -{ - GList *walker; - - walker = traf->truns; - while (walker) { - atom_trun_free ((AtomTRUN *) walker->data); - walker = g_list_next (walker); - } - g_list_free (traf->truns); - traf->truns = NULL; - - walker = traf->sdtps; - while (walker) { - atom_sdtp_free ((AtomSDTP *) walker->data); - walker = g_list_next (walker); - } - g_list_free (traf->sdtps); - traf->sdtps = NULL; - - g_free (traf); -} - -void -atom_moof_free (AtomMOOF * moof) -{ - GList *walker; - - walker = moof->trafs; - while (walker) { - atom_traf_free ((AtomTRAF *) walker->data); - walker = g_list_next (walker); - } - g_list_free (moof->trafs); - moof->trafs = NULL; - - g_free (moof); -} - -static guint64 -atom_mfhd_copy_data (AtomMFHD * mfhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&mfhd->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (mfhd->sequence_number, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_tfhd_copy_data (AtomTFHD * tfhd, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint32 flags; - - if (!atom_full_copy_data (&tfhd->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (tfhd->track_ID, buffer, size, offset); - - flags = atom_full_get_flags_as_uint (&tfhd->header); - - if (flags & TF_BASE_DATA_OFFSET) - prop_copy_uint64 (tfhd->base_data_offset, buffer, size, offset); - if (flags & TF_SAMPLE_DESCRIPTION_INDEX) - prop_copy_uint32 (tfhd->sample_description_index, buffer, size, offset); - if (flags & TF_DEFAULT_SAMPLE_DURATION) - prop_copy_uint32 (tfhd->default_sample_duration, buffer, size, offset); - if (flags & TF_DEFAULT_SAMPLE_SIZE) - prop_copy_uint32 (tfhd->default_sample_size, buffer, size, offset); - if (flags & TF_DEFAULT_SAMPLE_FLAGS) - prop_copy_uint32 (tfhd->default_sample_flags, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_trun_copy_data (AtomTRUN * trun, guint8 ** buffer, guint64 * size, - guint64 * offset, guint32 * data_offset) -{ - guint64 original_offset = *offset; - guint32 flags, i; - - flags = atom_full_get_flags_as_uint (&trun->header); - - /* if first trun in moof, forcibly add data_offset and record - * where it must be written later on */ - if (data_offset && !*data_offset) { - flags |= TR_DATA_OFFSET; - } else { - flags &= ~TR_DATA_OFFSET; - } - - atom_full_set_flags_as_uint (&trun->header, flags); - - if (!atom_full_copy_data (&trun->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (trun->sample_count, buffer, size, offset); - - if (flags & TR_DATA_OFFSET) { - *data_offset = *offset; - prop_copy_int32 (trun->data_offset, buffer, size, offset); - } - if (flags & TR_FIRST_SAMPLE_FLAGS) - prop_copy_uint32 (trun->first_sample_flags, buffer, size, offset); - - for (i = 0; i < atom_array_get_len (&trun->entries); i++) { - TRUNSampleEntry *entry = &atom_array_index (&trun->entries, i); - - if (flags & TR_SAMPLE_DURATION) - prop_copy_uint32 (entry->sample_duration, buffer, size, offset); - if (flags & TR_SAMPLE_SIZE) - prop_copy_uint32 (entry->sample_size, buffer, size, offset); - if (flags & TR_SAMPLE_FLAGS) - prop_copy_uint32 (entry->sample_flags, buffer, size, offset); - if (flags & TR_COMPOSITION_TIME_OFFSETS) - prop_copy_uint32 (entry->sample_composition_time_offset, - buffer, size, offset); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_sdtp_copy_data (AtomSDTP * sdtp, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!atom_full_copy_data (&sdtp->header, buffer, size, offset)) { - return 0; - } - - /* all entries at once */ - prop_copy_fixed_size_string (&atom_array_index (&sdtp->entries, 0), - atom_array_get_len (&sdtp->entries), buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_traf_copy_data (AtomTRAF * traf, guint8 ** buffer, guint64 * size, - guint64 * offset, guint32 * data_offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_copy_data (&traf->header, buffer, size, offset)) { - return 0; - } - if (!atom_tfhd_copy_data (&traf->tfhd, buffer, size, offset)) { - return 0; - } - - walker = g_list_first (traf->truns); - while (walker != NULL) { - if (!atom_trun_copy_data ((AtomTRUN *) walker->data, buffer, size, offset, - data_offset)) { - return 0; - } - walker = g_list_next (walker); - } - - walker = g_list_first (traf->sdtps); - while (walker != NULL) { - if (!atom_sdtp_copy_data ((AtomSDTP *) walker->data, buffer, size, offset)) { - return 0; - } - walker = g_list_next (walker); - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -/* creates moof atom; metadata is written expecting actual buffer data - * is in mdata directly after moof, and is consecutively written per trak */ -guint64 -atom_moof_copy_data (AtomMOOF * moof, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - guint32 data_offset = 0; - - if (!atom_copy_data (&moof->header, buffer, size, offset)) - return 0; - - if (!atom_mfhd_copy_data (&moof->mfhd, buffer, size, offset)) - return 0; - - walker = g_list_first (moof->trafs); - while (walker != NULL) { - if (!atom_traf_copy_data ((AtomTRAF *) walker->data, buffer, size, offset, - &data_offset)) { - return 0; - } - walker = g_list_next (walker); - } - - atom_write_size (buffer, size, offset, original_offset); - - if (*buffer && data_offset) { - /* first trun needs a data-offset relative to moof start - * = moof size + mdat prefix */ - GST_WRITE_UINT32_BE (*buffer + data_offset, *offset - original_offset + 8); - } - - return *offset - original_offset; -} - -static void -atom_tfhd_init (AtomTFHD * tfhd, guint32 track_ID) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&tfhd->header, FOURCC_tfhd, 0, 0, 0, flags); - tfhd->track_ID = track_ID; - tfhd->base_data_offset = 0; - tfhd->sample_description_index = 1; - tfhd->default_sample_duration = 0; - tfhd->default_sample_size = 0; - tfhd->default_sample_flags = 0; -} - -static void -atom_trun_init (AtomTRUN * trun) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&trun->header, FOURCC_trun, 0, 0, 0, flags); - trun->sample_count = 0; - trun->data_offset = 0; - trun->first_sample_flags = 0; - atom_array_init (&trun->entries, 512); -} - -static AtomTRUN * -atom_trun_new (void) -{ - AtomTRUN *trun = g_new0 (AtomTRUN, 1); - - atom_trun_init (trun); - return trun; -} - -static void -atom_sdtp_init (AtomSDTP * sdtp) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&sdtp->header, FOURCC_sdtp, 0, 0, 0, flags); - atom_array_init (&sdtp->entries, 512); -} - -static AtomSDTP * -atom_sdtp_new (AtomsContext * context) -{ - AtomSDTP *sdtp = g_new0 (AtomSDTP, 1); - - atom_sdtp_init (sdtp); - return sdtp; -} - -static void -atom_traf_add_sdtp (AtomTRAF * traf, AtomSDTP * sdtp) -{ - traf->sdtps = g_list_append (traf->sdtps, sdtp); -} - -static void -atom_sdtp_add_samples (AtomSDTP * sdtp, guint8 val) -{ - /* it does not make much/any sense according to specs, - * but that's how MS isml samples seem to do it */ - atom_array_append (&sdtp->entries, val, 256); -} - -static void -atom_trun_add_samples (AtomTRUN * trun, guint32 delta, guint32 size, - guint32 flags, gint64 pts_offset) -{ - TRUNSampleEntry nentry; - - if (pts_offset != 0) - trun->header.flags[1] |= TR_COMPOSITION_TIME_OFFSETS; - - nentry.sample_duration = delta; - nentry.sample_size = size; - nentry.sample_flags = flags; - nentry.sample_composition_time_offset = pts_offset; - atom_array_append (&trun->entries, nentry, 256); - trun->sample_count++; -} - -static void -atom_traf_init (AtomTRAF * traf, AtomsContext * context, guint32 track_ID) -{ - atom_header_set (&traf->header, FOURCC_traf, 0, 0); - atom_tfhd_init (&traf->tfhd, track_ID); - traf->truns = NULL; - - if (context->flavor == ATOMS_TREE_FLAVOR_ISML) - atom_traf_add_sdtp (traf, atom_sdtp_new (context)); -} - -AtomTRAF * -atom_traf_new (AtomsContext * context, guint32 track_ID) -{ - AtomTRAF *traf = g_new0 (AtomTRAF, 1); - - atom_traf_init (traf, context, track_ID); - return traf; -} - -static void -atom_traf_add_trun (AtomTRAF * traf, AtomTRUN * trun) -{ - traf->truns = g_list_append (traf->truns, trun); -} - -void -atom_traf_add_samples (AtomTRAF * traf, guint32 delta, guint32 size, - gboolean sync, gint64 pts_offset, gboolean sdtp_sync) -{ - AtomTRUN *trun; - guint32 flags; - - /* 0x10000 is sample-is-difference-sample flag - * low byte stuff is what ismv uses */ - flags = (sync ? 0x0 : 0x10000) | (sdtp_sync ? 0x40 : 0xc0); - - if (G_UNLIKELY (!traf->truns)) { - trun = atom_trun_new (); - atom_traf_add_trun (traf, trun); - /* optimistic; indicate all defaults present in tfhd */ - traf->tfhd.header.flags[2] = TF_DEFAULT_SAMPLE_DURATION | - TF_DEFAULT_SAMPLE_SIZE | TF_DEFAULT_SAMPLE_FLAGS; - traf->tfhd.default_sample_duration = delta; - traf->tfhd.default_sample_size = size; - traf->tfhd.default_sample_flags = flags; - trun->first_sample_flags = flags; - } - - trun = traf->truns->data; - - /* check if still matching defaults, - * if not, abandon default and need entry for each sample */ - if (traf->tfhd.default_sample_duration != delta) { - traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_DURATION; - trun->header.flags[1] |= (TR_SAMPLE_DURATION >> 8); - } - if (traf->tfhd.default_sample_size != size) { - traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_SIZE; - trun->header.flags[1] |= (TR_SAMPLE_SIZE >> 8); - } - if (traf->tfhd.default_sample_flags != flags) { - if (trun->sample_count == 1) { - /* at least will need first sample flag */ - traf->tfhd.default_sample_flags = flags; - trun->header.flags[2] |= TR_FIRST_SAMPLE_FLAGS; - } else { - /* now we need sample flags for each sample */ - traf->tfhd.header.flags[2] &= ~TF_DEFAULT_SAMPLE_FLAGS; - trun->header.flags[1] |= (TR_SAMPLE_FLAGS >> 8); - trun->header.flags[2] &= ~TR_FIRST_SAMPLE_FLAGS; - } - } - - atom_trun_add_samples (traf->truns->data, delta, size, flags, pts_offset); - - if (traf->sdtps) - atom_sdtp_add_samples (traf->sdtps->data, 0x10 | ((flags & 0xff) >> 4)); -} - -guint32 -atom_traf_get_sample_num (AtomTRAF * traf) -{ - AtomTRUN *trun; - - if (G_UNLIKELY (!traf->truns)) - return 0; - - trun = traf->truns->data; - return atom_array_get_len (&trun->entries); -} - -void -atom_moof_add_traf (AtomMOOF * moof, AtomTRAF * traf) -{ - moof->trafs = g_list_append (moof->trafs, traf); -} - -static void -atom_tfra_free (AtomTFRA * tfra) -{ - atom_full_clear (&tfra->header); - atom_array_clear (&tfra->entries); - g_free (tfra); -} - -AtomMFRA * -atom_mfra_new (AtomsContext * context) -{ - AtomMFRA *mfra = g_new0 (AtomMFRA, 1); - - atom_header_set (&mfra->header, FOURCC_mfra, 0, 0); - return mfra; -} - -void -atom_mfra_add_tfra (AtomMFRA * mfra, AtomTFRA * tfra) -{ - mfra->tfras = g_list_append (mfra->tfras, tfra); -} - -void -atom_mfra_free (AtomMFRA * mfra) -{ - GList *walker; - - walker = mfra->tfras; - while (walker) { - atom_tfra_free ((AtomTFRA *) walker->data); - walker = g_list_next (walker); - } - g_list_free (mfra->tfras); - mfra->tfras = NULL; - - atom_clear (&mfra->header); - g_free (mfra); -} - -static void -atom_tfra_init (AtomTFRA * tfra, guint32 track_ID) -{ - guint8 flags[3] = { 0, 0, 0 }; - - atom_full_init (&tfra->header, FOURCC_tfra, 0, 0, 0, flags); - tfra->track_ID = track_ID; - atom_array_init (&tfra->entries, 512); -} - -AtomTFRA * -atom_tfra_new (AtomsContext * context, guint32 track_ID) -{ - AtomTFRA *tfra = g_new0 (AtomTFRA, 1); - - atom_tfra_init (tfra, track_ID); - return tfra; - -} - -static inline gint -need_bytes (guint32 num) -{ - gint n = 0; - - while (num >>= 8) - n++; - - return n; -} - -void -atom_tfra_add_entry (AtomTFRA * tfra, guint64 dts, guint32 sample_num) -{ - TFRAEntry entry; - - entry.time = dts; - /* fill in later */ - entry.moof_offset = 0; - /* always write a single trun in a single traf */ - entry.traf_number = 1; - entry.trun_number = 1; - entry.sample_number = sample_num; - - /* auto-use 64 bits if needed */ - if (dts > G_MAXUINT32) - tfra->header.version = 1; - - /* 1 byte will always do for traf and trun number, - * check how much sample_num needs */ - tfra->lengths = (tfra->lengths & 0xfc) || - MAX (tfra->lengths, need_bytes (sample_num)); - - atom_array_append (&tfra->entries, entry, 256); -} - -void -atom_tfra_update_offset (AtomTFRA * tfra, guint64 offset) -{ - gint i; - - /* auto-use 64 bits if needed */ - if (offset > G_MAXUINT32) - tfra->header.version = 1; - - for (i = atom_array_get_len (&tfra->entries) - 1; i >= 0; i--) { - TFRAEntry *entry = &atom_array_index (&tfra->entries, i); - - if (entry->moof_offset) - break; - entry->moof_offset = offset; - } -} - -static guint64 -atom_tfra_copy_data (AtomTFRA * tfra, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint32 i; - TFRAEntry *entry; - guint32 data; - guint bytes; - guint version; - - if (!atom_full_copy_data (&tfra->header, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (tfra->track_ID, buffer, size, offset); - prop_copy_uint32 (tfra->lengths, buffer, size, offset); - prop_copy_uint32 (atom_array_get_len (&tfra->entries), buffer, size, offset); - - version = tfra->header.version; - for (i = 0; i < atom_array_get_len (&tfra->entries); ++i) { - entry = &atom_array_index (&tfra->entries, i); - if (version) { - prop_copy_uint64 (entry->time, buffer, size, offset); - prop_copy_uint64 (entry->moof_offset, buffer, size, offset); - } else { - prop_copy_uint32 (entry->time, buffer, size, offset); - prop_copy_uint32 (entry->moof_offset, buffer, size, offset); - } - - bytes = (tfra->lengths & (0x3 << 4)) + 1; - data = GUINT32_TO_BE (entry->traf_number); - prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes, - buffer, size, offset); - - bytes = (tfra->lengths & (0x3 << 2)) + 1; - data = GUINT32_TO_BE (entry->trun_number); - prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes, - buffer, size, offset); - - bytes = (tfra->lengths & (0x3)) + 1; - data = GUINT32_TO_BE (entry->sample_number); - prop_copy_fixed_size_string (((guint8 *) & data) + 4 - bytes, bytes, - buffer, size, offset); - - } - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -static guint64 -atom_mfro_copy_data (guint32 s, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - guint8 flags[3] = { 0, 0, 0 }; - AtomFull mfro; - - atom_full_init (&mfro, FOURCC_mfro, 0, 0, 0, flags); - - if (!atom_full_copy_data (&mfro, buffer, size, offset)) { - return 0; - } - - prop_copy_uint32 (s, buffer, size, offset); - - atom_write_size (buffer, size, offset, original_offset); - - return *offset - original_offset; -} - - -guint64 -atom_mfra_copy_data (AtomMFRA * mfra, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - guint64 original_offset = *offset; - GList *walker; - - if (!atom_copy_data (&mfra->header, buffer, size, offset)) - return 0; - - walker = g_list_first (mfra->tfras); - while (walker != NULL) { - if (!atom_tfra_copy_data ((AtomTFRA *) walker->data, buffer, size, offset)) { - return 0; - } - walker = g_list_next (walker); - } - - /* 16 is the size of the mfro atom */ - if (!atom_mfro_copy_data (*offset - original_offset + 16, buffer, - size, offset)) - return 0; - - atom_write_size (buffer, size, offset, original_offset); - return *offset - original_offset; -} - -/* some sample description construction helpers */ - -AtomInfo * -build_esds_extension (AtomTRAK * trak, guint8 object_type, guint8 stream_type, - const GstBuffer * codec_data, guint32 avg_bitrate, guint32 max_bitrate) -{ - guint32 track_id; - AtomESDS *esds; - - track_id = trak->tkhd.track_ID; - - esds = atom_esds_new (); - esds->es.id = track_id & 0xFFFF; - esds->es.dec_conf_desc.object_type = object_type; - esds->es.dec_conf_desc.stream_type = stream_type << 2 | 0x01; - - if (avg_bitrate > 0) - esds->es.dec_conf_desc.avg_bitrate = avg_bitrate; - if (max_bitrate > 0) - esds->es.dec_conf_desc.max_bitrate = max_bitrate; - - /* optional DecoderSpecificInfo */ - if (codec_data) { - DecoderSpecificInfoDescriptor *desc; - - esds->es.dec_conf_desc.dec_specific_info = desc = - desc_dec_specific_info_new (); - desc_dec_specific_info_alloc_data (desc, GST_BUFFER_SIZE (codec_data)); - - memcpy (desc->data, GST_BUFFER_DATA (codec_data), - GST_BUFFER_SIZE (codec_data)); - } - - return build_atom_info_wrapper ((Atom *) esds, atom_esds_copy_data, - atom_esds_free); -} - -AtomInfo * -build_btrt_extension (guint32 buffer_size_db, guint32 avg_bitrate, - guint32 max_bitrate) -{ - AtomData *atom_data; - GstBuffer *buf; - - if (buffer_size_db == 0 && avg_bitrate == 0 && max_bitrate == 0) - return 0; - - buf = gst_buffer_new_and_alloc (12); - - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), buffer_size_db); - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf) + 4, max_bitrate); - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf) + 8, avg_bitrate); - - atom_data = - atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('b', 't', 'r', 't'), buf); - gst_buffer_unref (buf); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -static AtomInfo * -build_mov_wave_extension (AtomTRAK * trak, guint32 fourcc, AtomInfo * atom1, - AtomInfo * atom2, gboolean terminator) -{ - AtomWAVE *wave; - AtomFRMA *frma; - Atom *ext_atom; - - /* Build WAVE atom for sample table entry */ - wave = atom_wave_new (); - - /* Prepend Terminator atom to the WAVE list first, so it ends up last */ - if (terminator) { - ext_atom = (Atom *) atom_data_new (FOURCC_null); - wave->extension_atoms = - atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom, - (AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free); - } - - /* Add supplied atoms to WAVE */ - if (atom2) - wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom2); - if (atom1) - wave->extension_atoms = g_list_prepend (wave->extension_atoms, atom1); - - /* Add FRMA to the WAVE */ - frma = atom_frma_new (); - frma->media_type = fourcc; - - wave->extension_atoms = - atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma, - (AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free); - - return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data, - atom_wave_free); -} - -AtomInfo * -build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data, - guint32 avg_bitrate, guint32 max_bitrate) -{ - AtomInfo *esds, *mp4a; - GstBuffer *buf; - - /* Add ESDS atom to WAVE */ - esds = build_esds_extension (trak, ESDS_OBJECT_TYPE_MPEG4_P3, - ESDS_STREAM_TYPE_AUDIO, codec_data, avg_bitrate, max_bitrate); - - /* Add MP4A atom to the WAVE: - * not really in spec, but makes offset based players happy */ - buf = gst_buffer_new_and_alloc (4); - *((guint32 *) GST_BUFFER_DATA (buf)) = 0; - mp4a = build_codec_data_extension (FOURCC_mp4a, buf); - gst_buffer_unref (buf); - - return build_mov_wave_extension (trak, FOURCC_mp4a, mp4a, esds, TRUE); -} - -AtomInfo * -build_mov_alac_extension (AtomTRAK * trak, const GstBuffer * codec_data) -{ - AtomInfo *alac; - - alac = build_codec_data_extension (FOURCC_alac, codec_data); - - return build_mov_wave_extension (trak, FOURCC_alac, NULL, alac, TRUE); -} - -AtomInfo * -build_fiel_extension (gint fields) -{ - AtomData *atom_data; - GstBuffer *buf; - - if (fields == 1) { - return NULL; - } - - buf = gst_buffer_new_and_alloc (1); - GST_BUFFER_DATA (buf)[0] = (guint8) fields; - - atom_data = - atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('f', 'i', 'e', 'l'), buf); - gst_buffer_unref (buf); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -AtomInfo * -build_jp2x_extension (const GstBuffer * prefix) -{ - AtomData *atom_data; - - if (!prefix) { - return NULL; - } - - atom_data = - atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('j', 'p', '2', 'x'), - prefix); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -AtomInfo * -build_jp2h_extension (AtomTRAK * trak, gint width, gint height, guint32 fourcc, - gint ncomp, const GValue * cmap_array, const GValue * cdef_array) -{ - AtomData *atom_data; - GstBuffer *buf; - guint8 cenum; - gint i; - gint idhr_size = 22; - gint colr_size = 15; - gint cmap_size = 0, cdef_size = 0; - gint cmap_array_size = 0; - gint cdef_array_size = 0; - GstByteWriter writer; - - g_return_val_if_fail (cmap_array == NULL || - GST_VALUE_HOLDS_ARRAY (cmap_array), NULL); - g_return_val_if_fail (cdef_array == NULL || - GST_VALUE_HOLDS_ARRAY (cdef_array), NULL); - - if (fourcc == GST_MAKE_FOURCC ('s', 'R', 'G', 'B')) { - cenum = 0x10; - if (ncomp == 0) - ncomp = 3; - } else if (fourcc == GST_MAKE_FOURCC ('G', 'R', 'A', 'Y')) { - cenum = 0x11; - if (ncomp == 0) - ncomp = 1; - } else if (fourcc == GST_MAKE_FOURCC ('s', 'Y', 'U', 'V')) { - cenum = 0x12; - if (ncomp == 0) - ncomp = 3; - } else - return NULL; - - if (cmap_array) { - cmap_array_size = gst_value_array_get_size (cmap_array); - cmap_size = 8 + cmap_array_size * 4; - } - if (cdef_array) { - cdef_array_size = gst_value_array_get_size (cdef_array); - cdef_size = 8 + 2 + cdef_array_size * 6; - } - - buf = gst_buffer_new_and_alloc (idhr_size + colr_size + cmap_size + - cdef_size); - gst_byte_writer_init_with_buffer (&writer, buf, FALSE); - - /* ihdr = image header box */ - gst_byte_writer_put_uint32_be (&writer, 22); - gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('i', 'h', 'd', 'r')); - gst_byte_writer_put_uint32_be (&writer, height); - gst_byte_writer_put_uint32_be (&writer, width); - gst_byte_writer_put_uint16_be (&writer, ncomp); - /* 8 bits per component, unsigned */ - gst_byte_writer_put_uint8 (&writer, 0x7); - /* compression type; reserved */ - gst_byte_writer_put_uint8 (&writer, 0x7); - /* colour space (un)known */ - gst_byte_writer_put_uint8 (&writer, 0x0); - /* intellectual property right (box present) */ - gst_byte_writer_put_uint8 (&writer, 0x0); - - /* colour specification box */ - gst_byte_writer_put_uint32_be (&writer, 15); - gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('c', 'o', 'l', 'r')); - - /* specification method: enumerated */ - gst_byte_writer_put_uint8 (&writer, 0x1); - /* precedence; reserved */ - gst_byte_writer_put_uint8 (&writer, 0x0); - /* approximation; reserved */ - gst_byte_writer_put_uint8 (&writer, 0x0); - /* enumerated colourspace */ - gst_byte_writer_put_uint32_be (&writer, cenum); - - if (cmap_array) { - gst_byte_writer_put_uint32_be (&writer, cmap_size); - gst_byte_writer_put_uint32_le (&writer, - GST_MAKE_FOURCC ('c', 'm', 'a', 'p')); - for (i = 0; i < cmap_array_size; i++) { - const GValue *item; - gint value; - guint16 cmp; - guint8 mtyp; - guint8 pcol; - item = gst_value_array_get_value (cmap_array, i); - value = g_value_get_int (item); - - /* value is '(mtyp << 24) | (pcol << 16) | cmp' */ - cmp = value & 0xFFFF; - mtyp = value >> 24; - pcol = (value >> 16) & 0xFF; - - if (mtyp == 1) - GST_WARNING ("MTYP of cmap atom signals Pallete Mapping, but we don't " - "handle Pallete mapping atoms yet"); - - gst_byte_writer_put_uint16_be (&writer, cmp); - gst_byte_writer_put_uint8 (&writer, mtyp); - gst_byte_writer_put_uint8 (&writer, pcol); - } - } - - if (cdef_array) { - gst_byte_writer_put_uint32_be (&writer, cdef_size); - gst_byte_writer_put_uint32_le (&writer, - GST_MAKE_FOURCC ('c', 'd', 'e', 'f')); - gst_byte_writer_put_uint16_be (&writer, cdef_array_size); - for (i = 0; i < cdef_array_size; i++) { - const GValue *item; - gint value; - item = gst_value_array_get_value (cdef_array, i); - value = g_value_get_int (item); - - gst_byte_writer_put_uint16_be (&writer, i); - if (value > 0) { - gst_byte_writer_put_uint16_be (&writer, 0); - gst_byte_writer_put_uint16_be (&writer, value); - } else if (value < 0) { - gst_byte_writer_put_uint16_be (&writer, -value); - gst_byte_writer_put_uint16_be (&writer, 0); /* TODO what here? */ - } else { - gst_byte_writer_put_uint16_be (&writer, 1); - gst_byte_writer_put_uint16_be (&writer, 0); - } - } - } - - g_assert (gst_byte_writer_get_remaining (&writer) == 0); - - atom_data = atom_data_new_from_gst_buffer (FOURCC_jp2h, buf); - gst_buffer_unref (buf); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -AtomInfo * -build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data) -{ - AtomData *data; - AtomInfo *result = NULL; - - if (codec_data) { - data = atom_data_new_from_gst_buffer (fourcc, codec_data); - result = build_atom_info_wrapper ((Atom *) data, atom_data_copy_data, - atom_data_free); - } - - return result; -} - -AtomInfo * -build_amr_extension (void) -{ - guint8 ext[9]; - GstBuffer *buf; - AtomInfo *res; - - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = ext; - GST_BUFFER_SIZE (buf) = sizeof (ext); - - /* vendor */ - GST_WRITE_UINT32_LE (ext, 0); - /* decoder version */ - GST_WRITE_UINT8 (ext + 4, 0); - /* mode set (all modes) */ - GST_WRITE_UINT16_BE (ext + 5, 0x81FF); - /* mode change period (no restriction) */ - GST_WRITE_UINT8 (ext + 7, 0); - /* frames per sample */ - GST_WRITE_UINT8 (ext + 8, 1); - - res = build_codec_data_extension (GST_MAKE_FOURCC ('d', 'a', 'm', 'r'), buf); - gst_buffer_unref (buf); - return res; -} - -AtomInfo * -build_h263_extension (void) -{ - guint8 ext[7]; - GstBuffer *buf; - AtomInfo *res; - - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = ext; - GST_BUFFER_SIZE (buf) = sizeof (ext); - - /* vendor */ - GST_WRITE_UINT32_LE (ext, 0); - /* decoder version */ - GST_WRITE_UINT8 (ext + 4, 0); - /* level / profile */ - /* FIXME ? maybe ? obtain somewhere; baseline for now */ - GST_WRITE_UINT8 (ext + 5, 10); - GST_WRITE_UINT8 (ext + 6, 0); - - res = build_codec_data_extension (GST_MAKE_FOURCC ('d', '2', '6', '3'), buf); - gst_buffer_unref (buf); - return res; -} - -AtomInfo * -build_gama_atom (gdouble gamma) -{ - AtomInfo *res; - guint32 gamma_fp; - GstBuffer *buf; - - /* convert to uint32 from fixed point */ - gamma_fp = (guint32) 65536 *gamma; - - buf = gst_buffer_new_and_alloc (4); - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), gamma_fp); - res = build_codec_data_extension (FOURCC_gama, buf); - gst_buffer_unref (buf); - return res; -} - -AtomInfo * -build_SMI_atom (const GstBuffer * seqh) -{ - AtomInfo *res; - GstBuffer *buf; - - /* the seqh plus its size and fourcc */ - buf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (seqh) + 8); - - GST_WRITE_UINT32_LE (GST_BUFFER_DATA (buf), FOURCC_SEQH); - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf) + 4, GST_BUFFER_SIZE (seqh)); - memcpy (GST_BUFFER_DATA (buf) + 8, GST_BUFFER_DATA (seqh), - GST_BUFFER_SIZE (seqh)); - res = build_codec_data_extension (FOURCC_SMI_, buf); - gst_buffer_unref (buf); - return res; -} - -static AtomInfo * -build_ima_adpcm_atom (gint channels, gint rate, gint blocksize) -{ - AtomData *atom_data; - GstBuffer *buf; - guint8 *data; - const gint ima_adpcm_atom_size = 20; - guint32 fourcc; - gint samplesperblock; - gint bytespersec; - - /* The FOURCC for WAV codecs in QT is 'ms' followed by the 16 bit wave codec - identifier. Note that the identifier here is big-endian, but when used - within the WAVE header (below), it's little endian. */ - fourcc = MS_WAVE_FOURCC (0x11); - - buf = gst_buffer_new_and_alloc (ima_adpcm_atom_size); - data = GST_BUFFER_DATA (buf); - - /* This atom's content is a WAVE header, including 2 bytes of extra data. - Note that all of this is little-endian, unlike most stuff in qt. */ - /* 4 bytes header per channel (including 1 sample). Then 2 samples per byte - for the rest. Simplifies to this. */ - samplesperblock = 2 * blocksize / channels - 7; - bytespersec = rate * blocksize / samplesperblock; - GST_WRITE_UINT16_LE (data, 0x11); - GST_WRITE_UINT16_LE (data + 2, channels); - GST_WRITE_UINT32_LE (data + 4, rate); - GST_WRITE_UINT32_LE (data + 8, bytespersec); - GST_WRITE_UINT16_LE (data + 12, blocksize); - GST_WRITE_UINT16_LE (data + 14, 4); - GST_WRITE_UINT16_LE (data + 16, 2); /* Two extra bytes */ - GST_WRITE_UINT16_LE (data + 18, samplesperblock); - - atom_data = atom_data_new_from_gst_buffer (fourcc, buf); - gst_buffer_unref (buf); - - return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data, - atom_data_free); -} - -AtomInfo * -build_ima_adpcm_extension (gint channels, gint rate, gint blocksize) -{ - AtomWAVE *wave; - AtomFRMA *frma; - Atom *ext_atom; - - /* Add WAVE atom */ - wave = atom_wave_new (); - - /* Prepend Terminator atom to the WAVE list first, so it ends up last */ - ext_atom = (Atom *) atom_data_new (FOURCC_null); - wave->extension_atoms = - atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) ext_atom, - (AtomCopyDataFunc) atom_data_copy_data, (AtomFreeFunc) atom_data_free); - - /* Add wave ima adpcm atom to WAVE */ - wave->extension_atoms = g_list_prepend (wave->extension_atoms, - build_ima_adpcm_atom (channels, rate, blocksize)); - - /* Add FRMA to the WAVE */ - frma = atom_frma_new (); - frma->media_type = MS_WAVE_FOURCC (0x11); - - wave->extension_atoms = - atom_info_list_prepend_atom (wave->extension_atoms, (Atom *) frma, - (AtomCopyDataFunc) atom_frma_copy_data, (AtomFreeFunc) atom_frma_free); - - return build_atom_info_wrapper ((Atom *) wave, atom_wave_copy_data, - atom_wave_free); -} - -AtomInfo * -build_uuid_xmp_atom (GstBuffer * xmp_data) -{ - AtomUUID *uuid; - static guint8 xmp_uuid[] = { 0xBE, 0x7A, 0xCF, 0xCB, - 0x97, 0xA9, 0x42, 0xE8, - 0x9C, 0x71, 0x99, 0x94, - 0x91, 0xE3, 0xAF, 0xAC - }; - - if (xmp_data == NULL) - return NULL; - - uuid = atom_uuid_new (); - memcpy (uuid->uuid, xmp_uuid, 16); - - uuid->data = g_malloc (GST_BUFFER_SIZE (xmp_data)); - uuid->datalen = GST_BUFFER_SIZE (xmp_data); - memcpy (uuid->data, GST_BUFFER_DATA (xmp_data), GST_BUFFER_SIZE (xmp_data)); - - return build_atom_info_wrapper ((Atom *) uuid, atom_uuid_copy_data, - atom_uuid_free); -} diff --git a/gst/qtmux/atoms.h b/gst/qtmux/atoms.h deleted file mode 100644 index 5aeb5f8f65..0000000000 --- a/gst/qtmux/atoms.h +++ /dev/null @@ -1,956 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008-2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __ATOMS_H__ -#define __ATOMS_H__ - -#include -#include - -#include "descriptors.h" -#include "properties.h" -#include "fourcc.h" -#include "ftypcc.h" - -/* helper storage struct */ -#define ATOM_ARRAY(struct_type) \ -struct { \ - guint size; \ - guint len; \ - struct_type *data; \ -} - -/* storage helpers */ - -#define atom_array_init(array, reserve) \ -G_STMT_START { \ - (array)->len = 0; \ - (array)->size = reserve; \ - (array)->data = g_malloc (sizeof (*(array)->data) * reserve); \ -} G_STMT_END - -#define atom_array_append(array, elmt, inc) \ -G_STMT_START { \ - g_assert ((array)->data); \ - g_assert (inc > 0); \ - if (G_UNLIKELY ((array)->len == (array)->size)) { \ - (array)->size += inc; \ - (array)->data = \ - g_realloc ((array)->data, sizeof (*((array)->data)) * (array)->size); \ - } \ - (array)->data[(array)->len] = elmt; \ - (array)->len++; \ -} G_STMT_END - -#define atom_array_get_len(array) ((array)->len) -#define atom_array_index(array, index) ((array)->data[index]) - -#define atom_array_clear(array) \ -G_STMT_START { \ - (array)->size = (array)->len = 0; \ - g_free ((array)->data); \ - (array)->data = NULL; \ -} G_STMT_END - -/* light-weight context that may influence header atom tree construction */ -typedef enum _AtomsTreeFlavor -{ - ATOMS_TREE_FLAVOR_MOV, - ATOMS_TREE_FLAVOR_ISOM, - ATOMS_TREE_FLAVOR_3GP, - ATOMS_TREE_FLAVOR_ISML -} AtomsTreeFlavor; - -typedef struct _AtomsContext -{ - AtomsTreeFlavor flavor; -} AtomsContext; - -AtomsContext* atoms_context_new (AtomsTreeFlavor flavor); -void atoms_context_free (AtomsContext *context); - -#define METADATA_DATA_FLAG 0x0 -#define METADATA_TEXT_FLAG 0x1 - -/* atom defs and functions */ - -/** - * Used for storing time related values for some atoms. - */ -typedef struct _TimeInfo -{ - guint64 creation_time; - guint64 modification_time; - guint32 timescale; - guint64 duration; -} TimeInfo; - -typedef struct _Atom -{ - guint32 size; - guint32 type; - guint64 extended_size; -} Atom; - -typedef struct _AtomFull -{ - Atom header; - - guint8 version; - guint8 flags[3]; -} AtomFull; - -/* - * Generic extension atom - */ -typedef struct _AtomData -{ - Atom header; - - /* not written */ - guint32 datalen; - - guint8 *data; -} AtomData; - -typedef struct _AtomUUID -{ - Atom header; - - guint8 uuid[16]; - - /* not written */ - guint32 datalen; - - guint8 *data; -} AtomUUID; - -typedef struct _AtomFTYP -{ - Atom header; - guint32 major_brand; - guint32 version; - guint32 *compatible_brands; - - /* not written */ - guint32 compatible_brands_size; -} AtomFTYP; - -typedef struct _AtomMVHD -{ - AtomFull header; - - /* version 0: 32 bits */ - TimeInfo time_info; - - guint32 prefered_rate; /* ISO: 0x00010000 */ - guint16 volume; /* ISO: 0x0100 */ - guint16 reserved3; /* ISO: 0x0 */ - guint32 reserved4[2]; /* ISO: 0, 0 */ - /* ISO: identity matrix = - * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ - guint32 matrix[9]; - - /* ISO: all 0 */ - guint32 preview_time; - guint32 preview_duration; - guint32 poster_time; - guint32 selection_time; - guint32 selection_duration; - guint32 current_time; - - guint32 next_track_id; -} AtomMVHD; - -typedef struct _AtomTKHD -{ - AtomFull header; - - /* version 0: 32 bits */ - /* like the TimeInfo struct, but it has this track_ID inside */ - guint64 creation_time; - guint64 modification_time; - guint32 track_ID; - guint32 reserved; - guint64 duration; - - guint32 reserved2[2]; - guint16 layer; - guint16 alternate_group; - guint16 volume; - guint16 reserved3; - - /* ISO: identity matrix = - * { 0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000 } */ - guint32 matrix[9]; - guint32 width; - guint32 height; -} AtomTKHD; - -typedef struct _AtomMDHD -{ - AtomFull header; - - /* version 0: 32 bits */ - TimeInfo time_info; - - /* ISO: packed ISO-639-2/T language code (first bit must be 0) */ - guint16 language_code; - /* ISO: 0 */ - guint16 quality; -} AtomMDHD; - -typedef struct _AtomHDLR -{ - AtomFull header; - - /* ISO: 0 */ - guint32 component_type; - guint32 handler_type; - guint32 manufacturer; - guint32 flags; - guint32 flags_mask; - gchar *name; -} AtomHDLR; - -typedef struct _AtomVMHD -{ - AtomFull header; /* ISO: flags = 1 */ - - guint16 graphics_mode; - /* RGB */ - guint16 opcolor[3]; -} AtomVMHD; - -typedef struct _AtomSMHD -{ - AtomFull header; - - guint16 balance; - guint16 reserved; -} AtomSMHD; - -typedef struct _AtomHMHD -{ - AtomFull header; - - guint16 max_pdu_size; - guint16 avg_pdu_size; - guint32 max_bitrate; - guint32 avg_bitrate; - guint32 sliding_avg_bitrate; -} AtomHMHD; - -typedef struct _AtomURL -{ - AtomFull header; - - gchar *location; -} AtomURL; - -typedef struct _AtomDREF -{ - AtomFull header; - - GList *entries; -} AtomDREF; - -typedef struct _AtomDINF -{ - Atom header; - - AtomDREF dref; -} AtomDINF; - -typedef struct _STTSEntry -{ - guint32 sample_count; - gint32 sample_delta; -} STTSEntry; - -typedef struct _AtomSTTS -{ - AtomFull header; - - ATOM_ARRAY (STTSEntry) entries; -} AtomSTTS; - -typedef struct _AtomSTSS -{ - AtomFull header; - - ATOM_ARRAY (guint32) entries; -} AtomSTSS; - -typedef struct _AtomESDS -{ - AtomFull header; - - ESDescriptor es; -} AtomESDS; - -typedef struct _AtomFRMA -{ - Atom header; - - guint32 media_type; -} AtomFRMA; - -typedef enum _SampleEntryKind -{ - UNKNOWN, - AUDIO, - VIDEO -} SampleEntryKind; - -typedef struct _SampleTableEntry -{ - Atom header; - - guint8 reserved[6]; - guint16 data_reference_index; - - /* sort of entry */ - SampleEntryKind kind; -} SampleTableEntry; - -typedef struct _AtomHintSampleEntry -{ - SampleTableEntry se; - guint32 size; - guint8 *data; -} AtomHintSampleEntry; - -typedef struct _SampleTableEntryMP4V -{ - SampleTableEntry se; - - guint16 version; - guint16 revision_level; - - guint32 vendor; /* fourcc code */ - guint32 temporal_quality; - guint32 spatial_quality; - - guint16 width; - guint16 height; - - guint32 horizontal_resolution; - guint32 vertical_resolution; - guint32 datasize; - - guint16 frame_count; /* usually 1 */ - - guint8 compressor[32]; /* pascal string, i.e. first byte = length */ - - guint16 depth; - guint16 color_table_id; - - /* (optional) list of AtomInfo */ - GList *extension_atoms; -} SampleTableEntryMP4V; - -typedef struct _SampleTableEntryMP4A -{ - SampleTableEntry se; - - guint16 version; - guint16 revision_level; - guint32 vendor; - - guint16 channels; - guint16 sample_size; - guint16 compression_id; - guint16 packet_size; - - guint32 sample_rate; /* fixed point 16.16 */ - - guint32 samples_per_packet; - guint32 bytes_per_packet; - guint32 bytes_per_frame; - guint32 bytes_per_sample; - - /* (optional) list of AtomInfo */ - GList *extension_atoms; -} SampleTableEntryMP4A; - -typedef struct _SampleTableEntryMP4S -{ - SampleTableEntry se; - - AtomESDS es; -} SampleTableEntryMP4S; - -typedef struct _AtomSTSD -{ - AtomFull header; - - guint n_entries; - /* list of subclasses of SampleTableEntry */ - GList *entries; -} AtomSTSD; - -typedef struct _AtomSTSZ -{ - AtomFull header; - - guint32 sample_size; - - /* need the size here because when sample_size is constant, - * the list is empty */ - guint32 table_size; - ATOM_ARRAY (guint32) entries; -} AtomSTSZ; - -typedef struct _STSCEntry -{ - guint32 first_chunk; - guint32 samples_per_chunk; - guint32 sample_description_index; -} STSCEntry; - -typedef struct _AtomSTSC -{ - AtomFull header; - - ATOM_ARRAY (STSCEntry) entries; -} AtomSTSC; - - -/* - * used for both STCO and CO64 - * if used as STCO, entries should be truncated to use only 32bits - */ -typedef struct _AtomSTCO64 -{ - AtomFull header; - - ATOM_ARRAY (guint64) entries; -} AtomSTCO64; - -typedef struct _CTTSEntry -{ - guint32 samplecount; - guint32 sampleoffset; -} CTTSEntry; - -typedef struct _AtomCTTS -{ - AtomFull header; - - /* also entry count here */ - ATOM_ARRAY (CTTSEntry) entries; - gboolean do_pts; -} AtomCTTS; - -typedef struct _AtomSTBL -{ - Atom header; - - AtomSTSD stsd; - AtomSTTS stts; - AtomSTSS stss; - AtomSTSC stsc; - AtomSTSZ stsz; - /* NULL if not present */ - AtomCTTS *ctts; - - AtomSTCO64 stco64; -} AtomSTBL; - -typedef struct _AtomMINF -{ - Atom header; - - /* only (exactly) one of those must be present */ - AtomVMHD *vmhd; - AtomSMHD *smhd; - AtomHMHD *hmhd; - - AtomHDLR *hdlr; - AtomDINF dinf; - AtomSTBL stbl; -} AtomMINF; - -typedef struct _EditListEntry -{ - /* duration in movie's timescale */ - guint32 duration; - /* start time in media's timescale, -1 for empty */ - guint32 media_time; - guint32 media_rate; /* fixed point 32 bit */ -} EditListEntry; - -typedef struct _AtomELST -{ - AtomFull header; - - /* number of entries is implicit */ - GSList *entries; -} AtomELST; - -typedef struct _AtomEDTS -{ - Atom header; - AtomELST elst; -} AtomEDTS; - -typedef struct _AtomMDIA -{ - Atom header; - - AtomMDHD mdhd; - AtomHDLR hdlr; - AtomMINF minf; -} AtomMDIA; - -typedef struct _AtomILST -{ - Atom header; - - /* list of AtomInfo */ - GList* entries; -} AtomILST; - -typedef struct _AtomTagData -{ - AtomFull header; - guint32 reserved; - - guint32 datalen; - guint8* data; -} AtomTagData; - -typedef struct _AtomTag -{ - Atom header; - - AtomTagData data; -} AtomTag; - -typedef struct _AtomMETA -{ - AtomFull header; - AtomHDLR hdlr; - AtomILST *ilst; -} AtomMETA; - -typedef struct _AtomUDTA -{ - Atom header; - - /* list of AtomInfo */ - GList* entries; - /* or list is further down */ - AtomMETA *meta; -} AtomUDTA; - -enum TrFlags -{ - TR_DATA_OFFSET = 0x01, /* data-offset-present */ - TR_FIRST_SAMPLE_FLAGS = 0x04, /* first-sample-flags-present */ - TR_SAMPLE_DURATION = 0x0100, /* sample-duration-present */ - TR_SAMPLE_SIZE = 0x0200, /* sample-size-present */ - TR_SAMPLE_FLAGS = 0x0400, /* sample-flags-present */ - TR_COMPOSITION_TIME_OFFSETS = 0x0800 /* sample-composition-time-offsets-presents */ -}; - -enum TfFlags -{ - TF_BASE_DATA_OFFSET = 0x01, /* base-data-offset-present */ - TF_SAMPLE_DESCRIPTION_INDEX = 0x02, /* sample-description-index-present */ - TF_DEFAULT_SAMPLE_DURATION = 0x08, /* default-sample-duration-present */ - TF_DEFAULT_SAMPLE_SIZE = 0x010, /* default-sample-size-present */ - TF_DEFAULT_SAMPLE_FLAGS = 0x020, /* default-sample-flags-present */ - TF_DURATION_IS_EMPTY = 0x010000 /* sample-composition-time-offsets-presents */ -}; - -typedef struct _AtomTRAK -{ - Atom header; - - AtomTKHD tkhd; - AtomEDTS *edts; - AtomMDIA mdia; - - /* some helper info for structural conformity checks */ - gboolean is_video; - gboolean is_h264; -} AtomTRAK; - -typedef struct _AtomTREX -{ - AtomFull header; - - guint32 track_ID; - guint32 default_sample_description_index; - guint32 default_sample_duration; - guint32 default_sample_size; - guint32 default_sample_flags; -} AtomTREX; - -typedef struct _AtomMEHD -{ - AtomFull header; - - guint64 fragment_duration; -} AtomMEHD; - - -typedef struct _AtomMVEX -{ - Atom header; - - AtomMEHD mehd; - - /* list of AtomTREX */ - GList *trexs; -} AtomMVEX; - -typedef struct _AtomMFHD -{ - AtomFull header; - - guint32 sequence_number; -} AtomMFHD; - -typedef struct _AtomTFHD -{ - AtomFull header; - - guint32 track_ID; - guint64 base_data_offset; - guint32 sample_description_index; - guint32 default_sample_duration; - guint32 default_sample_size; - guint32 default_sample_flags; -} AtomTFHD; - -typedef struct _TRUNSampleEntry -{ - guint32 sample_duration; - guint32 sample_size; - guint32 sample_flags; - guint32 sample_composition_time_offset; -} TRUNSampleEntry; - -typedef struct _AtomTRUN -{ - AtomFull header; - - guint32 sample_count; - gint32 data_offset; - guint32 first_sample_flags; - - /* array of fields */ - ATOM_ARRAY (TRUNSampleEntry) entries; -} AtomTRUN; - -typedef struct _AtomSDTP -{ - AtomFull header; - - /* not serialized */ - guint32 sample_count; - - /* array of fields */ - ATOM_ARRAY (guint8) entries; -} AtomSDTP; - -typedef struct _AtomTRAF -{ - Atom header; - - AtomTFHD tfhd; - - /* list of AtomTRUN */ - GList *truns; - /* list of AtomSDTP */ - GList *sdtps; -} AtomTRAF; - -typedef struct _AtomMOOF -{ - Atom header; - - AtomMFHD mfhd; - - /* list of AtomTRAF */ - GList *trafs; -} AtomMOOF; - - -typedef struct _AtomMOOV -{ - /* style */ - AtomsContext context; - - Atom header; - - AtomMVHD mvhd; - AtomMVEX mvex; - - /* list of AtomTRAK */ - GList *traks; - AtomUDTA *udta; - - gboolean fragmented; -} AtomMOOV; - -typedef struct _AtomWAVE -{ - Atom header; - - /* list of AtomInfo */ - GList *extension_atoms; -} AtomWAVE; - -typedef struct _TFRAEntry -{ - guint64 time; - guint64 moof_offset; - guint32 traf_number; - guint32 trun_number; - guint32 sample_number; -} TFRAEntry; - -typedef struct _AtomTFRA -{ - AtomFull header; - - guint32 track_ID; - guint32 lengths; - /* array of entries */ - ATOM_ARRAY (TFRAEntry) entries; -} AtomTFRA; - -typedef struct _AtomMFRA -{ - Atom header; - - /* list of tfra */ - GList *tfras; -} AtomMFRA; - -/* - * Function to serialize an atom - */ -typedef guint64 (*AtomCopyDataFunc) (Atom *atom, guint8 **buffer, guint64 *size, guint64 *offset); - -/* - * Releases memory allocated by an atom - */ -typedef guint64 (*AtomFreeFunc) (Atom *atom); - -/* - * Some atoms might have many optional different kinds of child atoms, so this - * is useful for enabling generic handling of any atom. - * All we need are the two functions (copying it to an array - * for serialization and the memory releasing function). - */ -typedef struct _AtomInfo -{ - Atom *atom; - AtomCopyDataFunc copy_data_func; - AtomFreeFunc free_func; -} AtomInfo; - - -guint64 atom_copy_data (Atom *atom, guint8 **buffer, - guint64 *size, guint64* offset); - -AtomFTYP* atom_ftyp_new (AtomsContext *context, guint32 major, - guint32 version, GList *brands); -guint64 atom_ftyp_copy_data (AtomFTYP *ftyp, guint8 **buffer, - guint64 *size, guint64 *offset); -void atom_ftyp_free (AtomFTYP *ftyp); - -AtomTRAK* atom_trak_new (AtomsContext *context); -void atom_trak_add_samples (AtomTRAK * trak, guint32 nsamples, guint32 delta, - guint32 size, guint64 chunk_offset, gboolean sync, - gint64 pts_offset); -void atom_trak_add_elst_entry (AtomTRAK * trak, guint32 duration, - guint32 media_time, guint32 rate); -guint32 atom_trak_get_timescale (AtomTRAK *trak); -guint32 atom_trak_get_id (AtomTRAK * trak); -void atom_stbl_add_samples (AtomSTBL * stbl, guint32 nsamples, - guint32 delta, guint32 size, - guint64 chunk_offset, gboolean sync, - gint64 pts_offset); - -AtomMOOV* atom_moov_new (AtomsContext *context); -void atom_moov_free (AtomMOOV *moov); -guint64 atom_moov_copy_data (AtomMOOV *atom, guint8 **buffer, guint64 *size, guint64* offset); -void atom_moov_update_timescale (AtomMOOV *moov, guint32 timescale); -void atom_moov_update_duration (AtomMOOV *moov); -void atom_moov_set_fragmented (AtomMOOV *moov, gboolean fragmented); -void atom_moov_chunks_add_offset (AtomMOOV *moov, guint32 offset); -void atom_moov_add_trak (AtomMOOV *moov, AtomTRAK *trak); - -guint64 atom_mvhd_copy_data (AtomMVHD * atom, guint8 ** buffer, - guint64 * size, guint64 * offset); -void atom_stco64_chunks_add_offset (AtomSTCO64 * stco64, guint32 offset); -guint64 atom_trak_copy_data (AtomTRAK * atom, guint8 ** buffer, - guint64 * size, guint64 * offset); -void atom_stbl_clear (AtomSTBL * stbl); -void atom_stbl_init (AtomSTBL * stbl); -guint64 atom_stss_copy_data (AtomSTSS *atom, guint8 **buffer, - guint64 *size, guint64* offset); -guint64 atom_stts_copy_data (AtomSTTS *atom, guint8 **buffer, - guint64 *size, guint64* offset); -guint64 atom_stsc_copy_data (AtomSTSC *atom, guint8 **buffer, - guint64 *size, guint64* offset); -guint64 atom_stsz_copy_data (AtomSTSZ *atom, guint8 **buffer, - guint64 *size, guint64* offset); -guint64 atom_ctts_copy_data (AtomCTTS *atom, guint8 **buffer, - guint64 *size, guint64* offset); -guint64 atom_stco64_copy_data (AtomSTCO64 *atom, guint8 **buffer, - guint64 *size, guint64* offset); -AtomMOOF* atom_moof_new (AtomsContext *context, guint32 sequence_number); -void atom_moof_free (AtomMOOF *moof); -guint64 atom_moof_copy_data (AtomMOOF *moof, guint8 **buffer, guint64 *size, guint64* offset); -AtomTRAF * atom_traf_new (AtomsContext * context, guint32 track_ID); -void atom_traf_free (AtomTRAF * traf); -void atom_traf_add_samples (AtomTRAF * traf, guint32 delta, - guint32 size, gboolean sync, gint64 pts_offset, - gboolean sdtp_sync); -guint32 atom_traf_get_sample_num (AtomTRAF * traf); -void atom_moof_add_traf (AtomMOOF *moof, AtomTRAF *traf); - -AtomMFRA* atom_mfra_new (AtomsContext *context); -void atom_mfra_free (AtomMFRA *mfra); -AtomTFRA* atom_tfra_new (AtomsContext *context, guint32 track_ID); -void atom_tfra_add_entry (AtomTFRA *tfra, guint64 dts, guint32 sample_num); -void atom_tfra_update_offset (AtomTFRA * tfra, guint64 offset); -void atom_mfra_add_tfra (AtomMFRA *mfra, AtomTFRA *tfra); -guint64 atom_mfra_copy_data (AtomMFRA *mfra, guint8 **buffer, guint64 *size, guint64* offset); - - -/* media sample description related helpers */ - -typedef struct -{ - guint16 version; - guint32 fourcc; - guint width; - guint height; - guint depth; - guint frame_count; - gint color_table_id; - guint par_n; - guint par_d; - - GstBuffer *codec_data; -} VisualSampleEntry; - -typedef struct -{ - guint32 fourcc; - guint version; - gint compression_id; - guint sample_rate; - guint channels; - guint sample_size; - guint bytes_per_packet; - guint samples_per_packet; - guint bytes_per_sample; - guint bytes_per_frame; - - GstBuffer *codec_data; -} AudioSampleEntry; - -void atom_trak_set_audio_type (AtomTRAK * trak, AtomsContext * context, - AudioSampleEntry * entry, guint32 scale, - AtomInfo * ext, gint sample_size); - -void atom_trak_set_video_type (AtomTRAK * trak, AtomsContext * context, - VisualSampleEntry * entry, guint32 rate, - GList * ext_atoms_list); - -AtomInfo * build_codec_data_extension (guint32 fourcc, const GstBuffer * codec_data); -AtomInfo * build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data, - guint32 avg_bitrate, guint32 max_bitrate); -AtomInfo * build_mov_alac_extension (AtomTRAK * trak, const GstBuffer * codec_data); -AtomInfo * build_esds_extension (AtomTRAK * trak, guint8 object_type, - guint8 stream_type, const GstBuffer * codec_data, - guint32 avg_bitrate, guint32 max_bitrate); -AtomInfo * build_btrt_extension (guint32 buffer_size_db, guint32 avg_bitrate, - guint32 max_bitrate); -AtomInfo * build_jp2h_extension (AtomTRAK * trak, gint width, gint height, - guint32 fourcc, gint ncomp, - const GValue * cmap_array, - const GValue * cdef_array); - -AtomInfo * build_jp2x_extension (const GstBuffer * prefix); -AtomInfo * build_fiel_extension (gint fields); -AtomInfo * build_amr_extension (void); -AtomInfo * build_h263_extension (void); -AtomInfo * build_gama_atom (gdouble gamma); -AtomInfo * build_SMI_atom (const GstBuffer *seqh); -AtomInfo * build_ima_adpcm_extension (gint channels, gint rate, - gint blocksize); -AtomInfo * build_uuid_xmp_atom (GstBuffer * xmp); - - -/* - * Meta tags functions - */ -void atom_moov_add_str_tag (AtomMOOV *moov, guint32 fourcc, const gchar *value); -void atom_moov_add_uint_tag (AtomMOOV *moov, guint32 fourcc, guint32 flags, - guint32 value); -void atom_moov_add_tag (AtomMOOV *moov, guint32 fourcc, guint32 flags, - const guint8 * data, guint size); -void atom_moov_add_blob_tag (AtomMOOV *moov, guint8 *data, guint size); - -void atom_moov_add_3gp_str_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value); -void atom_moov_add_3gp_uint_tag (AtomMOOV * moov, guint32 fourcc, guint16 value); -void atom_moov_add_3gp_str_int_tag (AtomMOOV * moov, guint32 fourcc, const gchar * value, - gint16 ivalue); -void atom_moov_add_3gp_tag (AtomMOOV * moov, guint32 fourcc, guint8 * data, - guint size); - -void atom_moov_add_xmp_tags (AtomMOOV * moov, GstBuffer * xmp); - -#define GST_QT_MUX_DEFAULT_TAG_LANGUAGE "eng" -guint16 language_code (const char * lang); - -#endif /* __ATOMS_H__ */ diff --git a/gst/qtmux/atomsrecovery.c b/gst/qtmux/atomsrecovery.c deleted file mode 100644 index 1f5a287d1d..0000000000 --- a/gst/qtmux/atomsrecovery.c +++ /dev/null @@ -1,1095 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/** - * This module contains functions for serializing partial information from - * a mux in progress (by qtmux elements). This enables reconstruction of the - * moov box if a crash happens and thus recovering the movie file. - * - * Usage: - * 1) pipeline: ...yourelements ! qtmux moov-recovery-file=path.mrf ! \ - * filesink location=moovie.mov - * - * 2) CRASH! - * - * 3) gst-launch qtmoovrecover recovery-input=path.mrf broken-input=moovie.mov \ - fixed-output=recovered.mov - * - * 4) (Hopefully) enjoy recovered.mov. - * - * --- Recovery file layout --- - * 1) Version (a guint16) - * 2) Prefix atom (if present) - * 3) ftyp atom - * 4) MVHD atom (without timescale/duration set) - * 5) moovie timescale - * 6) number of traks - * 7) list of trak atoms (stbl data is ignored, except for the stsd atom) - * 8) Buffers metadata (metadata that is relevant to the container) - * Buffers metadata are stored in the order they are added to the mdat, - * each entre has a fixed size and is stored in BE. booleans are stored - * as a single byte where 0 means false, otherwise is true. - * Metadata: - * - guint32 track_id; - * - guint32 nsamples; - * - guint32 delta; - * - guint32 size; - * - guint64 chunk_offset; - * - gboolean sync; - * - gboolean do_pts; - * - guint64 pts_offset; (always present, ignored if do_pts is false) - * - * The mdat file might contain ftyp and then mdat, in case this is the faststart - * temporary file there is no ftyp and no mdat header, only the buffers data. - * - * Notes about recovery file layout: We still don't store tags nor EDTS data. - * - * IMPORTANT: this is still at a experimental state. - */ - -#include "atomsrecovery.h" - -#define ATOMS_RECOV_OUTPUT_WRITE_ERROR(err) \ - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, \ - "Failed to write to output file: %s", g_strerror (errno)) - -static gboolean -atoms_recov_write_version (FILE * f) -{ - guint8 data[2]; - GST_WRITE_UINT16_BE (data, ATOMS_RECOV_FILE_VERSION); - return fwrite (data, 2, 1, f) == 1; -} - -static gboolean -atoms_recov_write_ftyp_info (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix) -{ - guint8 *data = NULL; - guint64 offset = 0; - guint64 size = 0; - - if (prefix) { - if (fwrite (GST_BUFFER_DATA (prefix), 1, GST_BUFFER_SIZE (prefix), f) != - GST_BUFFER_SIZE (prefix)) { - return FALSE; - } - } - if (!atom_ftyp_copy_data (ftyp, &data, &size, &offset)) { - return FALSE; - } - if (fwrite (data, 1, offset, f) != offset) { - return FALSE; - } - return TRUE; -} - -/** - * Writes important info on the 'moov' atom (non-trak related) - * to be able to recover the moov structure after a crash. - * - * Currently, it writes the MVHD atom. - */ -static gboolean -atoms_recov_write_moov_info (FILE * f, AtomMOOV * moov) -{ - guint8 *data; - guint64 size; - guint64 offset = 0; - guint64 atom_size = 0; - gint writen = 0; - - /* likely enough */ - size = 256; - data = g_malloc (size); - atom_size = atom_mvhd_copy_data (&moov->mvhd, &data, &size, &offset); - if (atom_size > 0) - writen = fwrite (data, 1, atom_size, f); - g_free (data); - return atom_size > 0 && writen == atom_size; -} - -/** - * Writes the number of traks to the file. - * This simply writes a guint32 in BE. - */ -static gboolean -atoms_recov_write_traks_number (FILE * f, guint32 traks) -{ - guint8 data[4]; - GST_WRITE_UINT32_BE (data, traks); - return fwrite (data, 4, 1, f) == 1; -} - -/** - * Writes the moov's timescale to the file - * This simply writes a guint32 in BE. - */ -static gboolean -atoms_recov_write_moov_timescale (FILE * f, guint32 timescale) -{ - guint8 data[4]; - GST_WRITE_UINT32_BE (data, timescale); - return fwrite (data, 4, 1, f) == 1; -} - -/** - * Writes the trak atom to the file. - */ -gboolean -atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak) -{ - guint8 *data; - guint64 size; - guint64 offset = 0; - guint64 atom_size = 0; - gint writen = 0; - - /* buffer is realloced to a larger size if needed */ - size = 4 * 1024; - data = g_malloc (size); - atom_size = atom_trak_copy_data (trak, &data, &size, &offset); - if (atom_size > 0) - writen = fwrite (data, atom_size, 1, f); - g_free (data); - return atom_size > 0 && writen == atom_size; -} - -gboolean -atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, guint32 nsamples, - guint32 delta, guint32 size, guint64 chunk_offset, gboolean sync, - gboolean do_pts, gint64 pts_offset) -{ - guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE]; - /* - * We have to write a TrakBufferEntryInfo - */ - GST_WRITE_UINT32_BE (data + 0, trak->tkhd.track_ID); - GST_WRITE_UINT32_BE (data + 4, nsamples); - GST_WRITE_UINT32_BE (data + 8, delta); - GST_WRITE_UINT32_BE (data + 12, size); - GST_WRITE_UINT64_BE (data + 16, chunk_offset); - if (sync) - GST_WRITE_UINT8 (data + 24, 1); - else - GST_WRITE_UINT8 (data + 24, 0); - if (do_pts) { - GST_WRITE_UINT8 (data + 25, 1); - GST_WRITE_UINT64_BE (data + 26, pts_offset); - } else { - GST_WRITE_UINT8 (data + 25, 0); - GST_WRITE_UINT64_BE (data + 26, 0); - } - - return fwrite (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, f) == - TRAK_BUFFER_ENTRY_INFO_SIZE; -} - -gboolean -atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, GstBuffer * prefix, - AtomMOOV * moov, guint32 timescale, guint32 traks_number) -{ - if (!atoms_recov_write_version (f)) { - return FALSE; - } - - if (!atoms_recov_write_ftyp_info (f, ftyp, prefix)) { - return FALSE; - } - - if (!atoms_recov_write_moov_info (f, moov)) { - return FALSE; - } - - if (!atoms_recov_write_moov_timescale (f, timescale)) { - return FALSE; - } - - if (!atoms_recov_write_traks_number (f, traks_number)) { - return FALSE; - } - - return TRUE; -} - -static gboolean -read_atom_header (FILE * f, guint32 * fourcc, guint32 * size) -{ - guint8 aux[8]; - - if (fread (aux, 1, 8, f) != 8) - return FALSE; - *size = GST_READ_UINT32_BE (aux); - *fourcc = GST_READ_UINT32_LE (aux + 4); - return TRUE; -} - -static gboolean -moov_recov_file_parse_prefix (MoovRecovFile * moovrf) -{ - guint32 fourcc; - guint32 size; - guint32 total_size = 0; - if (fseek (moovrf->file, 2, SEEK_SET) != 0) - return FALSE; - if (!read_atom_header (moovrf->file, &fourcc, &size)) { - return FALSE; - } - - if (fourcc != FOURCC_ftyp) { - /* we might have a prefix here */ - if (fseek (moovrf->file, size - 8, SEEK_CUR) != 0) - return FALSE; - - total_size += size; - - /* now read the ftyp */ - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - } - - /* this has to be the ftyp */ - if (fourcc != FOURCC_ftyp) - return FALSE; - total_size += size; - moovrf->prefix_size = total_size; - return fseek (moovrf->file, size - 8, SEEK_CUR) == 0; -} - -static gboolean -moov_recov_file_parse_mvhd (MoovRecovFile * moovrf) -{ - guint32 fourcc; - guint32 size; - if (!read_atom_header (moovrf->file, &fourcc, &size)) { - return FALSE; - } - /* check for sanity */ - if (fourcc != FOURCC_mvhd) - return FALSE; - - moovrf->mvhd_size = size; - moovrf->mvhd_pos = ftell (moovrf->file) - 8; - - /* skip the remaining of the mvhd in the file */ - return fseek (moovrf->file, size - 8, SEEK_CUR) == 0; -} - -static gboolean -mdat_recov_file_parse_mdat_start (MdatRecovFile * mdatrf) -{ - guint32 fourcc, size; - - if (!read_atom_header (mdatrf->file, &fourcc, &size)) { - return FALSE; - } - if (size == 1) { - mdatrf->mdat_header_size = 16; - mdatrf->mdat_size = 16; - } else { - mdatrf->mdat_header_size = 8; - mdatrf->mdat_size = 8; - } - mdatrf->mdat_start = ftell (mdatrf->file) - 8; - - return fourcc == FOURCC_mdat; -} - -MdatRecovFile * -mdat_recov_file_create (FILE * file, gboolean datafile, GError ** err) -{ - MdatRecovFile *mrf = g_new0 (MdatRecovFile, 1); - guint32 fourcc, size; - - g_return_val_if_fail (file != NULL, NULL); - - mrf->file = file; - mrf->rawfile = datafile; - - /* get the file/data length */ - if (fseek (file, 0, SEEK_END) != 0) - goto file_length_error; - /* still needs to deduce the mdat header and ftyp size */ - mrf->data_size = ftell (file); - if (mrf->data_size == -1L) - goto file_length_error; - - if (fseek (file, 0, SEEK_SET) != 0) - goto file_seek_error; - - if (datafile) { - /* this file contains no atoms, only raw data to be placed on the mdat - * this happens when faststart mode is used */ - mrf->mdat_start = 0; - mrf->mdat_header_size = 16; - mrf->mdat_size = 16; - return mrf; - } - - if (!read_atom_header (file, &fourcc, &size)) { - goto parse_error; - } - if (fourcc != FOURCC_ftyp) { - /* this could be a prefix atom, let's skip it and try again */ - if (fseek (file, size - 8, SEEK_CUR) != 0) { - goto file_seek_error; - } - if (!read_atom_header (file, &fourcc, &size)) { - goto parse_error; - } - } - - if (fourcc != FOURCC_ftyp) { - goto parse_error; - } - if (fseek (file, size - 8, SEEK_CUR) != 0) - goto file_seek_error; - - /* we don't parse this if we have a tmpdatafile */ - if (!mdat_recov_file_parse_mdat_start (mrf)) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing mdat atom"); - goto fail; - } - - return mrf; - -parse_error: - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to parse atom"); - goto fail; - -file_seek_error: - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to seek to start of the file"); - goto fail; - -file_length_error: - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to determine file size"); - goto fail; - -fail: - mdat_recov_file_free (mrf); - return NULL; -} - -void -mdat_recov_file_free (MdatRecovFile * mrf) -{ - fclose (mrf->file); - g_free (mrf); -} - -static gboolean -moov_recov_parse_num_traks (MoovRecovFile * moovrf) -{ - guint8 traks[4]; - if (fread (traks, 1, 4, moovrf->file) != 4) - return FALSE; - moovrf->num_traks = GST_READ_UINT32_BE (traks); - return TRUE; -} - -static gboolean -moov_recov_parse_moov_timescale (MoovRecovFile * moovrf) -{ - guint8 ts[4]; - if (fread (ts, 1, 4, moovrf->file) != 4) - return FALSE; - moovrf->timescale = GST_READ_UINT32_BE (ts); - return TRUE; -} - -static gboolean -skip_atom (MoovRecovFile * moovrf, guint32 expected_fourcc) -{ - guint32 size; - guint32 fourcc; - - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != expected_fourcc) - return FALSE; - - return (fseek (moovrf->file, size - 8, SEEK_CUR) == 0); -} - -static gboolean -moov_recov_parse_tkhd (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint32 size; - guint32 fourcc; - guint8 data[4]; - - /* make sure we are on a tkhd atom */ - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != FOURCC_tkhd) - return FALSE; - - trakrd->tkhd_file_offset = ftell (moovrf->file) - 8; - - /* move 8 bytes forward to the trak_id pos */ - if (fseek (moovrf->file, 12, SEEK_CUR) != 0) - return FALSE; - if (fread (data, 1, 4, moovrf->file) != 4) - return FALSE; - - /* advance the rest of tkhd */ - fseek (moovrf->file, 68, SEEK_CUR); - - trakrd->trak_id = GST_READ_UINT32_BE (data); - return TRUE; -} - -static gboolean -moov_recov_parse_stbl (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint32 size; - guint32 fourcc; - guint32 auxsize; - - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != FOURCC_stbl) - return FALSE; - - trakrd->stbl_file_offset = ftell (moovrf->file) - 8; - trakrd->stbl_size = size; - - /* skip the stsd */ - if (!read_atom_header (moovrf->file, &fourcc, &auxsize)) - return FALSE; - if (fourcc != FOURCC_stsd) - return FALSE; - if (fseek (moovrf->file, auxsize - 8, SEEK_CUR) != 0) - return FALSE; - - trakrd->stsd_size = auxsize; - trakrd->post_stsd_offset = ftell (moovrf->file); - - /* as this is the last atom we parse, we don't skip forward */ - - return TRUE; -} - -static gboolean -moov_recov_parse_minf (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint32 size; - guint32 fourcc; - guint32 auxsize; - - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != FOURCC_minf) - return FALSE; - - trakrd->minf_file_offset = ftell (moovrf->file) - 8; - trakrd->minf_size = size; - - /* skip either of vmhd, smhd, hmhd that might follow */ - if (!read_atom_header (moovrf->file, &fourcc, &auxsize)) - return FALSE; - if (fourcc != FOURCC_vmhd && fourcc != FOURCC_smhd && fourcc != FOURCC_hmhd && - fourcc != FOURCC_gmhd) - return FALSE; - if (fseek (moovrf->file, auxsize - 8, SEEK_CUR)) - return FALSE; - - /* skip a possible hdlr and the following dinf */ - if (!read_atom_header (moovrf->file, &fourcc, &auxsize)) - return FALSE; - if (fourcc == FOURCC_hdlr) { - if (fseek (moovrf->file, auxsize - 8, SEEK_CUR)) - return FALSE; - if (!read_atom_header (moovrf->file, &fourcc, &auxsize)) - return FALSE; - } - if (fourcc != FOURCC_dinf) - return FALSE; - if (fseek (moovrf->file, auxsize - 8, SEEK_CUR)) - return FALSE; - - /* now we are ready to read the stbl */ - if (!moov_recov_parse_stbl (moovrf, trakrd)) - return FALSE; - - return TRUE; -} - -static gboolean -moov_recov_parse_mdhd (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint32 size; - guint32 fourcc; - guint8 data[4]; - - /* make sure we are on a tkhd atom */ - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != FOURCC_mdhd) - return FALSE; - - trakrd->mdhd_file_offset = ftell (moovrf->file) - 8; - - /* get the timescale */ - if (fseek (moovrf->file, 12, SEEK_CUR) != 0) - return FALSE; - if (fread (data, 1, 4, moovrf->file) != 4) - return FALSE; - trakrd->timescale = GST_READ_UINT32_BE (data); - if (fseek (moovrf->file, 8, SEEK_CUR) != 0) - return FALSE; - return TRUE; -} - -static gboolean -moov_recov_parse_mdia (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint32 size; - guint32 fourcc; - - /* make sure we are on a tkhd atom */ - if (!read_atom_header (moovrf->file, &fourcc, &size)) - return FALSE; - if (fourcc != FOURCC_mdia) - return FALSE; - - trakrd->mdia_file_offset = ftell (moovrf->file) - 8; - trakrd->mdia_size = size; - - if (!moov_recov_parse_mdhd (moovrf, trakrd)) - return FALSE; - - if (!skip_atom (moovrf, FOURCC_hdlr)) - return FALSE; - if (!moov_recov_parse_minf (moovrf, trakrd)) - return FALSE; - return TRUE; -} - -static gboolean -moov_recov_parse_trak (MoovRecovFile * moovrf, TrakRecovData * trakrd) -{ - guint64 offset; - guint32 size; - guint32 fourcc; - - offset = ftell (moovrf->file); - if (offset == -1) { - return FALSE; - } - - /* make sure we are on a trak atom */ - if (!read_atom_header (moovrf->file, &fourcc, &size)) { - return FALSE; - } - if (fourcc != FOURCC_trak) { - return FALSE; - } - trakrd->trak_size = size; - - /* now we should have a trak header 'tkhd' */ - if (!moov_recov_parse_tkhd (moovrf, trakrd)) - return FALSE; - - /* FIXME add edts handling here and in qtmux, as this is only detected - * after buffers start flowing */ - - if (!moov_recov_parse_mdia (moovrf, trakrd)) - return FALSE; - - trakrd->file_offset = offset; - /* position after the trak */ - return fseek (moovrf->file, (long int) offset + size, SEEK_SET) == 0; -} - -MoovRecovFile * -moov_recov_file_create (FILE * file, GError ** err) -{ - gint i; - MoovRecovFile *moovrf = g_new0 (MoovRecovFile, 1); - - g_return_val_if_fail (file != NULL, NULL); - - moovrf->file = file; - - /* look for ftyp and prefix at the start */ - if (!moov_recov_file_parse_prefix (moovrf)) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing prefix atoms"); - goto fail; - } - - /* parse the mvhd */ - if (!moov_recov_file_parse_mvhd (moovrf)) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing mvhd atom"); - goto fail; - } - - if (!moov_recov_parse_moov_timescale (moovrf)) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing timescale"); - goto fail; - } - if (!moov_recov_parse_num_traks (moovrf)) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing parsing number of traks"); - goto fail; - } - - /* init the traks */ - moovrf->traks_rd = g_new0 (TrakRecovData, moovrf->num_traks); - for (i = 0; i < moovrf->num_traks; i++) { - atom_stbl_init (&(moovrf->traks_rd[i].stbl)); - } - for (i = 0; i < moovrf->num_traks; i++) { - if (!moov_recov_parse_trak (moovrf, &(moovrf->traks_rd[i]))) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Error while parsing trak atom"); - goto fail; - } - } - - return moovrf; - -fail: - moov_recov_file_free (moovrf); - return NULL; -} - -void -moov_recov_file_free (MoovRecovFile * moovrf) -{ - gint i; - fclose (moovrf->file); - if (moovrf->traks_rd) { - for (i = 0; i < moovrf->num_traks; i++) { - atom_stbl_clear (&(moovrf->traks_rd[i].stbl)); - } - g_free (moovrf->traks_rd); - } - g_free (moovrf); -} - -static gboolean -moov_recov_parse_buffer_entry (MoovRecovFile * moovrf, TrakBufferEntryInfo * b) -{ - guint8 data[TRAK_BUFFER_ENTRY_INFO_SIZE]; - gint read; - - read = fread (data, 1, TRAK_BUFFER_ENTRY_INFO_SIZE, moovrf->file); - if (read != TRAK_BUFFER_ENTRY_INFO_SIZE) - return FALSE; - - b->track_id = GST_READ_UINT32_BE (data); - b->nsamples = GST_READ_UINT32_BE (data + 4); - b->delta = GST_READ_UINT32_BE (data + 8); - b->size = GST_READ_UINT32_BE (data + 12); - b->chunk_offset = GST_READ_UINT64_BE (data + 16); - b->sync = data[24] != 0; - b->do_pts = data[25] != 0; - b->pts_offset = GST_READ_UINT64_BE (data + 26); - return TRUE; -} - -static gboolean -mdat_recov_add_sample (MdatRecovFile * mdatrf, guint32 size) -{ - /* test if this data exists */ - if (mdatrf->mdat_size - mdatrf->mdat_header_size + size > mdatrf->data_size) - return FALSE; - - mdatrf->mdat_size += size; - return TRUE; -} - -static TrakRecovData * -moov_recov_get_trak (MoovRecovFile * moovrf, guint32 id) -{ - gint i; - for (i = 0; i < moovrf->num_traks; i++) { - if (moovrf->traks_rd[i].trak_id == id) - return &(moovrf->traks_rd[i]); - } - return NULL; -} - -static void -trak_recov_data_add_sample (TrakRecovData * trak, TrakBufferEntryInfo * b) -{ - trak->duration += b->nsamples * b->delta; - atom_stbl_add_samples (&trak->stbl, b->nsamples, b->delta, b->size, - b->chunk_offset, b->sync, b->pts_offset); -} - -/** - * Parses the buffer entries in the MoovRecovFile and matches the inputs - * with the data in the MdatRecovFile. Whenever a buffer entry of that - * represents 'x' bytes of data, the same amount of data is 'validated' in - * the MdatRecovFile and will be inluded in the generated moovie file. - */ -gboolean -moov_recov_parse_buffers (MoovRecovFile * moovrf, MdatRecovFile * mdatrf, - GError ** err) -{ - TrakBufferEntryInfo entry; - TrakRecovData *trak; - - /* we assume both moovrf and mdatrf are at the starting points of their - * data reading */ - while (moov_recov_parse_buffer_entry (moovrf, &entry)) { - /* be sure we still have this data in mdat */ - trak = moov_recov_get_trak (moovrf, entry.track_id); - if (trak == NULL) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_PARSING, - "Invalid trak id found in buffer entry"); - return FALSE; - } - if (!mdat_recov_add_sample (mdatrf, entry.size)) - break; - trak_recov_data_add_sample (trak, &entry); - } - return TRUE; -} - -static guint32 -trak_recov_data_get_trak_atom_size (TrakRecovData * trak) -{ - AtomSTBL *stbl = &trak->stbl; - guint64 offset; - - /* write out our stbl child atoms */ - offset = 0; - - if (!atom_stts_copy_data (&stbl->stts, NULL, NULL, &offset)) { - goto fail; - } - if (atom_array_get_len (&stbl->stss.entries) > 0) { - if (!atom_stss_copy_data (&stbl->stss, NULL, NULL, &offset)) { - goto fail; - } - } - if (!atom_stsc_copy_data (&stbl->stsc, NULL, NULL, &offset)) { - goto fail; - } - if (!atom_stsz_copy_data (&stbl->stsz, NULL, NULL, &offset)) { - goto fail; - } - if (stbl->ctts) { - if (!atom_ctts_copy_data (stbl->ctts, NULL, NULL, &offset)) { - goto fail; - } - } - if (!atom_stco64_copy_data (&stbl->stco64, NULL, NULL, &offset)) { - goto fail; - } - - return trak->trak_size + ((trak->stsd_size + offset + 8) - trak->stbl_size); - -fail: - return 0; -} - -static guint8 * -moov_recov_get_stbl_children_data (MoovRecovFile * moovrf, TrakRecovData * trak, - guint64 * p_size) -{ - AtomSTBL *stbl = &trak->stbl; - guint8 *buffer; - guint64 size; - guint64 offset; - - /* write out our stbl child atoms - * - * Use 1MB as a starting size, *_copy_data functions - * will grow the buffer if needed. - */ - size = 1024 * 1024; - buffer = g_malloc0 (size); - offset = 0; - - if (!atom_stts_copy_data (&stbl->stts, &buffer, &size, &offset)) { - goto fail; - } - if (atom_array_get_len (&stbl->stss.entries) > 0) { - if (!atom_stss_copy_data (&stbl->stss, &buffer, &size, &offset)) { - goto fail; - } - } - if (!atom_stsc_copy_data (&stbl->stsc, &buffer, &size, &offset)) { - goto fail; - } - if (!atom_stsz_copy_data (&stbl->stsz, &buffer, &size, &offset)) { - goto fail; - } - if (stbl->ctts) { - if (!atom_ctts_copy_data (stbl->ctts, &buffer, &size, &offset)) { - goto fail; - } - } - if (!atom_stco64_copy_data (&stbl->stco64, &buffer, &size, &offset)) { - goto fail; - } - *p_size = offset; - return buffer; - -fail: - g_free (buffer); - return NULL; -} - -gboolean -moov_recov_write_file (MoovRecovFile * moovrf, MdatRecovFile * mdatrf, - FILE * outf, GError ** err) -{ - guint8 auxdata[16]; - guint8 *data = NULL; - guint8 *prefix_data = NULL; - guint8 *mvhd_data = NULL; - guint8 *trak_data = NULL; - guint32 moov_size = 0; - gint i; - guint64 stbl_children_size = 0; - guint8 *stbl_children = NULL; - guint32 longest_duration = 0; - guint16 version; - - /* check the version */ - if (fseek (moovrf->file, 0, SEEK_SET) != 0) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to seek to the start of the moov recovery file"); - goto fail; - } - if (fread (auxdata, 1, 2, moovrf->file) != 2) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to read version from file"); - } - - version = GST_READ_UINT16_BE (auxdata); - if (version != ATOMS_RECOV_FILE_VERSION) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_VERSION, - "Input file version (%u) is not supported in this version (%u)", - version, ATOMS_RECOV_FILE_VERSION); - return FALSE; - } - - /* write the ftyp */ - prefix_data = g_malloc (moovrf->prefix_size); - if (fread (prefix_data, 1, moovrf->prefix_size, - moovrf->file) != moovrf->prefix_size) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to read the ftyp atom from file"); - goto fail; - } - if (fwrite (prefix_data, 1, moovrf->prefix_size, outf) != moovrf->prefix_size) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - g_free (prefix_data); - prefix_data = NULL; - - /* need to calculate the moov size beforehand to add the offset to - * chunk offset entries */ - moov_size += moovrf->mvhd_size + 8; /* mvhd + moov size + fourcc */ - for (i = 0; i < moovrf->num_traks; i++) { - TrakRecovData *trak = &(moovrf->traks_rd[i]); - guint32 duration; /* in moov's timescale */ - guint32 trak_size; - - /* convert trak duration to moov's duration */ - duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale, - trak->timescale); - - if (duration > longest_duration) - longest_duration = duration; - trak_size = trak_recov_data_get_trak_atom_size (trak); - if (trak_size == 0) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_GENERIC, - "Failed to estimate trak atom size"); - goto fail; - } - moov_size += trak_size; - } - - /* add chunks offsets */ - for (i = 0; i < moovrf->num_traks; i++) { - TrakRecovData *trak = &(moovrf->traks_rd[i]); - /* 16 for the mdat header */ - gint64 offset = moov_size + ftell (outf) + 16; - atom_stco64_chunks_add_offset (&trak->stbl.stco64, offset); - } - - /* write the moov */ - GST_WRITE_UINT32_BE (auxdata, moov_size); - GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_moov); - if (fwrite (auxdata, 1, 8, outf) != 8) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - - /* write the mvhd */ - mvhd_data = g_malloc (moovrf->mvhd_size); - if (fseek (moovrf->file, moovrf->mvhd_pos, SEEK_SET) != 0) - goto fail; - if (fread (mvhd_data, 1, moovrf->mvhd_size, - moovrf->file) != moovrf->mvhd_size) - goto fail; - GST_WRITE_UINT32_BE (mvhd_data + 20, moovrf->timescale); - GST_WRITE_UINT32_BE (mvhd_data + 24, longest_duration); - if (fwrite (mvhd_data, 1, moovrf->mvhd_size, outf) != moovrf->mvhd_size) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - g_free (mvhd_data); - mvhd_data = NULL; - - /* write the traks, this is the tough part because we need to update: - * - stbl atom - * - sizes of atoms from stbl to trak - * - trak duration - */ - for (i = 0; i < moovrf->num_traks; i++) { - TrakRecovData *trak = &(moovrf->traks_rd[i]); - guint trak_data_size; - guint32 stbl_new_size; - guint32 minf_new_size; - guint32 mdia_new_size; - guint32 trak_new_size; - guint32 size_diff; - guint32 duration; /* in moov's timescale */ - - /* convert trak duration to moov's duration */ - duration = gst_util_uint64_scale_round (trak->duration, moovrf->timescale, - trak->timescale); - - stbl_children = moov_recov_get_stbl_children_data (moovrf, trak, - &stbl_children_size); - if (stbl_children == NULL) - goto fail; - - /* calc the new size of the atoms from stbl to trak in the atoms tree */ - stbl_new_size = trak->stsd_size + stbl_children_size + 8; - size_diff = stbl_new_size - trak->stbl_size; - minf_new_size = trak->minf_size + size_diff; - mdia_new_size = trak->mdia_size + size_diff; - trak_new_size = trak->trak_size + size_diff; - - if (fseek (moovrf->file, trak->file_offset, SEEK_SET) != 0) - goto fail; - trak_data_size = trak->post_stsd_offset - trak->file_offset; - trak_data = g_malloc (trak_data_size); - if (fread (trak_data, 1, trak_data_size, moovrf->file) != trak_data_size) { - goto fail; - } - /* update the size values in those read atoms before writing */ - GST_WRITE_UINT32_BE (trak_data, trak_new_size); - GST_WRITE_UINT32_BE (trak_data + (trak->mdia_file_offset - - trak->file_offset), mdia_new_size); - GST_WRITE_UINT32_BE (trak_data + (trak->minf_file_offset - - trak->file_offset), minf_new_size); - GST_WRITE_UINT32_BE (trak_data + (trak->stbl_file_offset - - trak->file_offset), stbl_new_size); - - /* update duration values in tkhd and mdhd */ - GST_WRITE_UINT32_BE (trak_data + (trak->tkhd_file_offset - - trak->file_offset) + 28, duration); - GST_WRITE_UINT32_BE (trak_data + (trak->mdhd_file_offset - - trak->file_offset) + 24, trak->duration); - - if (fwrite (trak_data, 1, trak_data_size, outf) != trak_data_size) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - if (fwrite (stbl_children, 1, stbl_children_size, outf) != - stbl_children_size) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - g_free (trak_data); - trak_data = NULL; - g_free (stbl_children); - stbl_children = NULL; - } - - /* write the mdat */ - /* write the header first */ - GST_WRITE_UINT32_BE (auxdata, 1); - GST_WRITE_UINT32_LE (auxdata + 4, FOURCC_mdat); - GST_WRITE_UINT64_BE (auxdata + 8, mdatrf->mdat_size); - if (fwrite (auxdata, 1, 16, outf) != 16) { - ATOMS_RECOV_OUTPUT_WRITE_ERROR (err); - goto fail; - } - - /* now read the mdat data and output to the file */ - if (fseek (mdatrf->file, mdatrf->mdat_start + - (mdatrf->rawfile ? 0 : mdatrf->mdat_header_size), SEEK_SET) != 0) - goto fail; - - data = g_malloc (4096); - while (!feof (mdatrf->file)) { - gint read, write; - - read = fread (data, 1, 4096, mdatrf->file); - write = fwrite (data, 1, read, outf); - - if (write != read) { - g_set_error (err, ATOMS_RECOV_QUARK, ATOMS_RECOV_ERR_FILE, - "Failed to copy data to output file: %s", g_strerror (errno)); - goto fail; - } - } - g_free (data); - - return TRUE; - -fail: - g_free (stbl_children); - g_free (mvhd_data); - g_free (prefix_data); - g_free (trak_data); - g_free (data); - return FALSE; -} diff --git a/gst/qtmux/atomsrecovery.h b/gst/qtmux/atomsrecovery.h deleted file mode 100644 index 4dffc48d33..0000000000 --- a/gst/qtmux/atomsrecovery.h +++ /dev/null @@ -1,159 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __ATOMS_RECOVERY_H__ -#define __ATOMS_RECOVERY_H__ - -#include -#include -#include -#include - -#include "atoms.h" - -/* Version to be incremented each time we decide - * to change the file layout */ -#define ATOMS_RECOV_FILE_VERSION 1 - -#define ATOMS_RECOV_QUARK (g_quark_from_string ("qtmux-atoms-recovery")) - -/* gerror error codes */ -#define ATOMS_RECOV_ERR_GENERIC 1 -#define ATOMS_RECOV_ERR_FILE 2 -#define ATOMS_RECOV_ERR_PARSING 3 -#define ATOMS_RECOV_ERR_VERSION 4 - -/* this struct represents each buffer in a moov file, containing the info - * that is placed in the stsd children atoms - * Fields should be writen in BE order, and booleans should be writen as - * 1byte with 0 for false, anything otherwise */ -#define TRAK_BUFFER_ENTRY_INFO_SIZE 34 -typedef struct -{ - guint32 track_id; - guint32 nsamples; - guint32 delta; - guint32 size; - guint64 chunk_offset; - guint64 pts_offset; - gboolean sync; - gboolean do_pts; -} TrakBufferEntryInfo; - -typedef struct -{ - guint32 trak_id; - guint32 duration; /* duration in trak timescale */ - guint32 timescale; /* trak's timescale */ - - guint64 file_offset; - - /* need for later updating duration */ - guint64 tkhd_file_offset; - guint64 mdhd_file_offset; - - /* need these offsets to update size */ - guint32 trak_size; - guint64 mdia_file_offset; - guint32 mdia_size; - guint64 minf_file_offset; - guint32 minf_size; - guint64 stbl_file_offset; - guint32 stbl_size; - - guint64 post_stsd_offset; - guint32 stsd_size; - - /* for storing the samples info */ - AtomSTBL stbl; -} TrakRecovData; - -typedef struct -{ - FILE * file; - gboolean rawfile; - - /* results from parsing the input file */ - guint64 data_size; - guint32 mdat_header_size; - guint mdat_start; - - guint64 mdat_size; -} MdatRecovFile; - -typedef struct -{ - FILE * file; - guint32 timescale; - - guint32 mvhd_pos; - guint32 mvhd_size; - guint32 prefix_size; /* prefix + ftyp total size */ - - gint num_traks; - TrakRecovData *traks_rd; -} MoovRecovFile; - -gboolean atoms_recov_write_trak_info (FILE * f, AtomTRAK * trak); -gboolean atoms_recov_write_headers (FILE * f, AtomFTYP * ftyp, - GstBuffer * prefix, AtomMOOV * moov, - guint32 timescale, - guint32 traks_number); -gboolean atoms_recov_write_trak_samples (FILE * f, AtomTRAK * trak, - guint32 nsamples, guint32 delta, - guint32 size, guint64 chunk_offset, - gboolean sync, gboolean do_pts, - gint64 pts_offset); - -MdatRecovFile * mdat_recov_file_create (FILE * file, gboolean datafile, - GError ** err); -void mdat_recov_file_free (MdatRecovFile * mrf); -MoovRecovFile * moov_recov_file_create (FILE * file, GError ** err); -void moov_recov_file_free (MoovRecovFile * moovrf); -gboolean moov_recov_parse_buffers (MoovRecovFile * moovrf, - MdatRecovFile * mdatrf, - GError ** err); -gboolean moov_recov_write_file (MoovRecovFile * moovrf, - MdatRecovFile * mdatrf, FILE * outf, - GError ** err); - -#endif /* __ATOMS_RECOVERY_H__ */ diff --git a/gst/qtmux/descriptors.c b/gst/qtmux/descriptors.c deleted file mode 100644 index d1e99c21e7..0000000000 --- a/gst/qtmux/descriptors.c +++ /dev/null @@ -1,458 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "descriptors.h" - -/* - * Some mp4 structures (descriptors) use a coding scheme for - * representing its size. - * It is grouped in bytes. The 1st bit set to 1 means we need another byte, - * 0 otherwise. The remaining 7 bits are the useful values. - * - * The next set of functions handle those values - */ - -/* - * Gets an unsigned integer and packs it into a 'expandable size' format - * (as used by mp4 descriptors) - * @size: the integer to be parsed - * @ptr: the array to place the result - * @array_size: the size of ptr array - */ -static void -expandable_size_parse (guint64 size, guint8 * ptr, guint32 array_size) -{ - int index = 0; - - memset (ptr, 0, sizeof (array_size)); - while (size > 0 && index < array_size) { - ptr[index++] = (size > 0x7F ? 0x80 : 0x0) | (size & 0x7F); - size = size >> 7; - } -} - -/* - * Gets how many positions in an array holding an 'expandable size' - * are really used - * - * @ptr: the array with the 'expandable size' - * @array_size: the size of ptr array - * - * Returns: the number of really used positions - */ -static guint64 -expandable_size_get_length (guint8 * ptr, guint32 array_size) -{ - gboolean next = TRUE; - guint32 index = 0; - - while (next && index < array_size) { - next = ((ptr[index] & 0x80) == 1); - index++; - } - return index; -} - -/* - * Initializers below - */ - -static void -desc_base_descriptor_init (BaseDescriptor * bd, guint8 tag, guint32 size) -{ - bd->tag = tag; - expandable_size_parse (size, bd->size, 4); -} - -static void -desc_dec_specific_info_init (DecoderSpecificInfoDescriptor * dsid) -{ - desc_base_descriptor_init (&dsid->base, DECODER_SPECIFIC_INFO_TAG, 0); - dsid->length = 0; - dsid->data = NULL; -} - -DecoderSpecificInfoDescriptor * -desc_dec_specific_info_new (void) -{ - DecoderSpecificInfoDescriptor *desc = - g_new0 (DecoderSpecificInfoDescriptor, 1); - desc_dec_specific_info_init (desc); - return desc; -} - -static void -desc_dec_conf_desc_init (DecoderConfigDescriptor * dcd) -{ - desc_base_descriptor_init (&dcd->base, DECODER_CONFIG_DESC_TAG, 0); - dcd->dec_specific_info = NULL; -} - -static void -desc_sl_conf_desc_init (SLConfigDescriptor * sl) -{ - desc_base_descriptor_init (&sl->base, SL_CONFIG_DESC_TAG, 0); - sl->predefined = 0x2; -} - -void -desc_es_init (ESDescriptor * es) -{ - desc_base_descriptor_init (&es->base, ES_DESCRIPTOR_TAG, 0); - - es->id = 0; - es->flags = 0; - es->depends_on_es_id = 0; - es->ocr_es_id = 0; - es->url_length = 0; - es->url_string = NULL; - - desc_dec_conf_desc_init (&es->dec_conf_desc); - desc_sl_conf_desc_init (&es->sl_conf_desc); -} - -ESDescriptor * -desc_es_descriptor_new (void) -{ - ESDescriptor *es = g_new0 (ESDescriptor, 1); - - desc_es_init (es); - return es; -} - -/* - * Deinitializers/Destructors below - */ - -static void -desc_base_descriptor_clear (BaseDescriptor * base) -{ -} - -void -desc_dec_specific_info_free (DecoderSpecificInfoDescriptor * dsid) -{ - desc_base_descriptor_clear (&dsid->base); - if (dsid->data) { - g_free (dsid->data); - dsid->data = NULL; - } - g_free (dsid); -} - -static void -desc_dec_conf_desc_clear (DecoderConfigDescriptor * dec) -{ - desc_base_descriptor_clear (&dec->base); - if (dec->dec_specific_info) { - desc_dec_specific_info_free (dec->dec_specific_info); - } -} - -static void -desc_sl_config_descriptor_clear (SLConfigDescriptor * sl) -{ - desc_base_descriptor_clear (&sl->base); -} - -void -desc_es_descriptor_clear (ESDescriptor * es) -{ - desc_base_descriptor_clear (&es->base); - if (es->url_string) { - g_free (es->url_string); - es->url_string = NULL; - } - desc_dec_conf_desc_clear (&es->dec_conf_desc); - desc_sl_config_descriptor_clear (&es->sl_conf_desc); -} - -/* - * Size handling functions below - */ - -void -desc_dec_specific_info_alloc_data (DecoderSpecificInfoDescriptor * dsid, - guint32 size) -{ - if (dsid->data) { - g_free (dsid->data); - } - dsid->data = g_new0 (guint8, size); - dsid->length = size; -} - -static void -desc_base_descriptor_set_size (BaseDescriptor * bd, guint32 size) -{ - expandable_size_parse (size, bd->size, 4); -} - -static guint64 -desc_base_descriptor_get_size (BaseDescriptor * bd) -{ - guint64 size = 0; - - size += sizeof (guint8); - size += expandable_size_get_length (bd->size, 4) * sizeof (guint8); - return size; -} - -static guint64 -desc_sl_config_descriptor_get_size (SLConfigDescriptor * sl_desc) -{ - guint64 size = 0; - guint64 extra_size = 0; - - size += desc_base_descriptor_get_size (&sl_desc->base); - /* predefined */ - extra_size += sizeof (guint8); - - desc_base_descriptor_set_size (&sl_desc->base, extra_size); - - return size + extra_size; -} - -static guint64 -desc_dec_specific_info_get_size (DecoderSpecificInfoDescriptor * dsid) -{ - guint64 size = 0; - guint64 extra_size = 0; - - size += desc_base_descriptor_get_size (&dsid->base); - extra_size += sizeof (guint8) * dsid->length; - desc_base_descriptor_set_size (&dsid->base, extra_size); - return size + extra_size; -} - -static guint64 -desc_dec_config_descriptor_get_size (DecoderConfigDescriptor * dec_desc) -{ - guint64 size = 0; - guint64 extra_size = 0; - - size += desc_base_descriptor_get_size (&dec_desc->base); - /* object type */ - extra_size += sizeof (guint8); - /* stream type */ - extra_size += sizeof (guint8); - /* buffer size */ - extra_size += sizeof (guint8) * 3; - /* max bitrate */ - extra_size += sizeof (guint32); - /* avg bitrate */ - extra_size += sizeof (guint32); - if (dec_desc->dec_specific_info) { - extra_size += desc_dec_specific_info_get_size (dec_desc->dec_specific_info); - } - - desc_base_descriptor_set_size (&dec_desc->base, extra_size); - return size + extra_size; -} - -static guint64 -desc_es_descriptor_get_size (ESDescriptor * es) -{ - guint64 size = 0; - guint64 extra_size = 0; - - size += desc_base_descriptor_get_size (&es->base); - /* id */ - extra_size += sizeof (guint16); - /* flags */ - extra_size += sizeof (guint8); - /* depends_on_es_id */ - if (es->flags & 0x80) { - extra_size += sizeof (guint16); - } - if (es->flags & 0x40) { - /* url_length */ - extra_size += sizeof (guint8); - /* url */ - extra_size += sizeof (gchar) * es->url_length; - } - if (es->flags & 0x20) { - /* ocr_es_id */ - extra_size += sizeof (guint16); - } - - extra_size += desc_dec_config_descriptor_get_size (&es->dec_conf_desc); - extra_size += desc_sl_config_descriptor_get_size (&es->sl_conf_desc); - - desc_base_descriptor_set_size (&es->base, extra_size); - - return size + extra_size; -} - -static gboolean -desc_es_descriptor_check_stream_dependency (ESDescriptor * es) -{ - return es->flags & 0x80; -} - -static gboolean -desc_es_descriptor_check_url_flag (ESDescriptor * es) -{ - return es->flags & 0x40; -} - -static gboolean -desc_es_descriptor_check_ocr (ESDescriptor * es) -{ - return es->flags & 0x20; -} - -/* Copy/Serializations Functions below */ - -static guint64 -desc_base_descriptor_copy_data (BaseDescriptor * desc, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - prop_copy_uint8 (desc->tag, buffer, size, offset); - prop_copy_uint8_array (desc->size, expandable_size_get_length (desc->size, 4), - buffer, size, offset); - return original_offset - *offset; -} - -static guint64 -desc_sl_config_descriptor_copy_data (SLConfigDescriptor * desc, - guint8 ** buffer, guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!desc_base_descriptor_copy_data (&desc->base, buffer, size, offset)) { - return 0; - } - /* predefined attribute */ - prop_copy_uint8 (desc->predefined, buffer, size, offset); - - return *offset - original_offset; -} - -static guint64 -desc_dec_specific_info_copy_data (DecoderSpecificInfoDescriptor * desc, - guint8 ** buffer, guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!desc_base_descriptor_copy_data (&desc->base, buffer, size, offset)) { - return 0; - } - prop_copy_uint8_array (desc->data, desc->length, buffer, size, offset); - - return *offset - original_offset; -} - -static guint64 -desc_dec_config_descriptor_copy_data (DecoderConfigDescriptor * desc, - guint8 ** buffer, guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - if (!desc_base_descriptor_copy_data (&desc->base, buffer, size, offset)) { - return 0; - } - - prop_copy_uint8 (desc->object_type, buffer, size, offset); - - prop_copy_uint8 (desc->stream_type, buffer, size, offset); - prop_copy_uint8_array (desc->buffer_size_DB, 3, buffer, size, offset); - - prop_copy_uint32 (desc->max_bitrate, buffer, size, offset); - prop_copy_uint32 (desc->avg_bitrate, buffer, size, offset); - - if (desc->dec_specific_info) { - if (!desc_dec_specific_info_copy_data (desc->dec_specific_info, buffer, - size, offset)) { - return 0; - } - } - - return *offset - original_offset; -} - -guint64 -desc_es_descriptor_copy_data (ESDescriptor * desc, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 desc_size; - guint64 original_offset = *offset; - - /* must call this twice to have size fields of all contained descriptors set - * correctly, and to have the size of the size fields taken into account */ - desc_size = desc_es_descriptor_get_size (desc); - desc_size = desc_es_descriptor_get_size (desc); - - if (!desc_base_descriptor_copy_data (&desc->base, buffer, size, offset)) { - return 0; - } - /* id and flags */ - prop_copy_uint16 (desc->id, buffer, size, offset); - prop_copy_uint8 (desc->flags, buffer, size, offset); - - if (desc_es_descriptor_check_stream_dependency (desc)) { - prop_copy_uint16 (desc->depends_on_es_id, buffer, size, offset); - } - - if (desc_es_descriptor_check_url_flag (desc)) { - prop_copy_size_string (desc->url_string, desc->url_length, buffer, size, - offset); - } - - if (desc_es_descriptor_check_ocr (desc)) { - prop_copy_uint16 (desc->ocr_es_id, buffer, size, offset); - } - - if (!desc_dec_config_descriptor_copy_data (&desc->dec_conf_desc, buffer, size, - offset)) { - return 0; - } - - if (!desc_sl_config_descriptor_copy_data (&desc->sl_conf_desc, buffer, size, - offset)) { - return 0; - } - - return *offset - original_offset; -} diff --git a/gst/qtmux/descriptors.h b/gst/qtmux/descriptors.h deleted file mode 100644 index cc633a305b..0000000000 --- a/gst/qtmux/descriptors.h +++ /dev/null @@ -1,151 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __DESCRIPTORS_H__ -#define __DESCRIPTORS_H__ - -#include -#include -#include "properties.h" - -/* - * Tags for descriptor (each kind is represented by a number, instead of fourcc as in atoms) - */ -#define OBJECT_DESC_TAG 0x01 -#define INIT_OBJECT_DESC_TAG 0x02 -#define ES_DESCRIPTOR_TAG 0x03 -#define DECODER_CONFIG_DESC_TAG 0x04 -#define DECODER_SPECIFIC_INFO_TAG 0x05 -#define SL_CONFIG_DESC_TAG 0x06 -#define ES_ID_INC_TAG 0x0E -#define MP4_INIT_OBJECT_DESC_TAG 0x10 - -#define ESDS_OBJECT_TYPE_MPEG1_P3 0x6B -#define ESDS_OBJECT_TYPE_MPEG2_P7_MAIN 0x66 -#define ESDS_OBJECT_TYPE_MPEG4_P7_LC 0x67 -#define ESDS_OBJECT_TYPE_MPEG4_P7_SSR 0x68 -#define ESDS_OBJECT_TYPE_MPEG4_P2 0x20 -#define ESDS_OBJECT_TYPE_MPEG4_P3 0x40 - -#define ESDS_STREAM_TYPE_VISUAL 0x04 -#define ESDS_STREAM_TYPE_AUDIO 0x05 - - -typedef struct _BaseDescriptor -{ - guint8 tag; - /* the first bit of each byte indicates if the next byte should be used */ - guint8 size[4]; -} BaseDescriptor; - -typedef struct _SLConfigDescriptor -{ - BaseDescriptor base; - - guint8 predefined; /* everything is supposed predefined */ -} SLConfigDescriptor; - -typedef struct _DecoderSpecificInfoDescriptor -{ - BaseDescriptor base; - guint32 length; - guint8 *data; -} DecoderSpecificInfoDescriptor; - -typedef struct _DecoderConfigDescriptor { - BaseDescriptor base; - - guint8 object_type; - - /* following are condensed into streamType: - * bit(6) streamType; - * bit(1) upStream; - * const bit(1) reserved=1; - */ - guint8 stream_type; - - guint8 buffer_size_DB[3]; - guint32 max_bitrate; - guint32 avg_bitrate; - - DecoderSpecificInfoDescriptor *dec_specific_info; -} DecoderConfigDescriptor; - -typedef struct _ESDescriptor -{ - BaseDescriptor base; - - guint16 id; - - /* flags contains the following: - * bit(1) streamDependenceFlag; - * bit(1) URL_Flag; - * bit(1) OCRstreamFlag; - * bit(5) streamPriority; - */ - guint8 flags; - - guint16 depends_on_es_id; - guint8 url_length; /* only if URL_flag is set */ - guint8 *url_string; /* size is url_length */ - - guint16 ocr_es_id; /* only if OCRstreamFlag is set */ - - DecoderConfigDescriptor dec_conf_desc; - SLConfigDescriptor sl_conf_desc; - - /* optional remainder of ESDescriptor is not used */ -} ESDescriptor; - -/* --- FUNCTIONS --- */ -void desc_es_init (ESDescriptor *es); -ESDescriptor *desc_es_descriptor_new (void); -guint64 desc_es_descriptor_copy_data (ESDescriptor *es, guint8 **buffer, - guint64 *size, guint64 *offset); -void desc_es_descriptor_clear (ESDescriptor *es); - -DecoderSpecificInfoDescriptor *desc_dec_specific_info_new(void); -void desc_dec_specific_info_free (DecoderSpecificInfoDescriptor *dsid); -void desc_dec_specific_info_alloc_data (DecoderSpecificInfoDescriptor *dsid, - guint32 size); - -#endif /* __DESCRIPTORS_H__ */ diff --git a/gst/qtmux/fourcc.h b/gst/qtmux/fourcc.h deleted file mode 100644 index 188e20275e..0000000000 --- a/gst/qtmux/fourcc.h +++ /dev/null @@ -1,243 +0,0 @@ -/* GStreamer - * Copyright (C) <1999> Erik Walthinsen - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - /* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -#ifndef __FOURCC_H__ -#define __FOURCC_H__ - -#include - -G_BEGIN_DECLS - -#define FOURCC_null 0x0 - -#define FOURCC_moov GST_MAKE_FOURCC('m','o','o','v') -#define FOURCC_mvhd GST_MAKE_FOURCC('m','v','h','d') -#define FOURCC_clip GST_MAKE_FOURCC('c','l','i','p') -#define FOURCC_trak GST_MAKE_FOURCC('t','r','a','k') -#define FOURCC_udta GST_MAKE_FOURCC('u','d','t','a') -#define FOURCC_ctab GST_MAKE_FOURCC('c','t','a','b') -#define FOURCC_tkhd GST_MAKE_FOURCC('t','k','h','d') -#define FOURCC_crgn GST_MAKE_FOURCC('c','r','g','n') -#define FOURCC_matt GST_MAKE_FOURCC('m','a','t','t') -#define FOURCC_kmat GST_MAKE_FOURCC('k','m','a','t') -#define FOURCC_edts GST_MAKE_FOURCC('e','d','t','s') -#define FOURCC_elst GST_MAKE_FOURCC('e','l','s','t') -#define FOURCC_load GST_MAKE_FOURCC('l','o','a','d') -#define FOURCC_tref GST_MAKE_FOURCC('t','r','e','f') -#define FOURCC_imap GST_MAKE_FOURCC('i','m','a','p') -#define FOURCC___in GST_MAKE_FOURCC(' ',' ','i','n') -#define FOURCC___ty GST_MAKE_FOURCC(' ',' ','t','y') -#define FOURCC_mdia GST_MAKE_FOURCC('m','d','i','a') -#define FOURCC_mdhd GST_MAKE_FOURCC('m','d','h','d') -#define FOURCC_hdlr GST_MAKE_FOURCC('h','d','l','r') -#define FOURCC_dhlr GST_MAKE_FOURCC('d','h','l','r') -#define FOURCC_mhlr GST_MAKE_FOURCC('m','h','l','r') -#define FOURCC_minf GST_MAKE_FOURCC('m','i','n','f') -#define FOURCC_mdir GST_MAKE_FOURCC('m','d','i','r') -#define FOURCC_vmhd GST_MAKE_FOURCC('v','m','h','d') -#define FOURCC_smhd GST_MAKE_FOURCC('s','m','h','d') -#define FOURCC_gmhd GST_MAKE_FOURCC('g','m','h','d') -#define FOURCC_hmhd GST_MAKE_FOURCC('h','m','h','d') -#define FOURCC_gmin GST_MAKE_FOURCC('g','m','i','n') -#define FOURCC_dinf GST_MAKE_FOURCC('d','i','n','f') -#define FOURCC_dref GST_MAKE_FOURCC('d','r','e','f') -#define FOURCC_stbl GST_MAKE_FOURCC('s','t','b','l') -#define FOURCC_stsd GST_MAKE_FOURCC('s','t','s','d') -#define FOURCC_stts GST_MAKE_FOURCC('s','t','t','s') -#define FOURCC_stss GST_MAKE_FOURCC('s','t','s','s') -#define FOURCC_stsc GST_MAKE_FOURCC('s','t','s','c') -#define FOURCC_stsz GST_MAKE_FOURCC('s','t','s','z') -#define FOURCC_stco GST_MAKE_FOURCC('s','t','c','o') -#define FOURCC_vide GST_MAKE_FOURCC('v','i','d','e') -#define FOURCC_soun GST_MAKE_FOURCC('s','o','u','n') -#define FOURCC_strm GST_MAKE_FOURCC('s','t','r','m') -#define FOURCC_rtsp GST_MAKE_FOURCC('r','t','s','p') -#define FOURCC_co64 GST_MAKE_FOURCC('c','o','6','4') -#define FOURCC_cmov GST_MAKE_FOURCC('c','m','o','v') -#define FOURCC_dcom GST_MAKE_FOURCC('d','c','o','m') -#define FOURCC_cmvd GST_MAKE_FOURCC('c','m','v','d') -#define FOURCC_hint GST_MAKE_FOURCC('h','i','n','t') -#define FOURCC_mp4a GST_MAKE_FOURCC('m','p','4','a') -#define FOURCC__mp3 GST_MAKE_FOURCC('.','m','p','3') -#define FOURCC_mp4s GST_MAKE_FOURCC('m','p','4','s') -#define FOURCC_mp4v GST_MAKE_FOURCC('m','p','4','v') -#define FOURCC_2vuy GST_MAKE_FOURCC('2','v','u','y') -#define FOURCC_wave GST_MAKE_FOURCC('w','a','v','e') -#define FOURCC_appl GST_MAKE_FOURCC('a','p','p','l') -#define FOURCC_esds GST_MAKE_FOURCC('e','s','d','s') -#define FOURCC_pasp GST_MAKE_FOURCC('p','a','s','p') -#define FOURCC_hnti GST_MAKE_FOURCC('h','n','t','i') -#define FOURCC_rtp_ GST_MAKE_FOURCC('r','t','p',' ') -#define FOURCC_sdp_ GST_MAKE_FOURCC('s','d','p',' ') -#define FOURCC_meta GST_MAKE_FOURCC('m','e','t','a') -#define FOURCC_ilst GST_MAKE_FOURCC('i','l','s','t') -#define FOURCC__nam GST_MAKE_FOURCC(0xa9,'n','a','m') -#define FOURCC__ART GST_MAKE_FOURCC(0xa9,'A','R','T') -#define FOURCC_aART GST_MAKE_FOURCC('a','A','R','T') -#define FOURCC__wrt GST_MAKE_FOURCC(0xa9,'w','r','t') -#define FOURCC__grp GST_MAKE_FOURCC(0xa9,'g','r','p') -#define FOURCC__alb GST_MAKE_FOURCC(0xa9,'a','l','b') -#define FOURCC__day GST_MAKE_FOURCC(0xa9,'d','a','y') -#define FOURCC__des GST_MAKE_FOURCC(0xa9,'d','e','s') -#define FOURCC__lyr GST_MAKE_FOURCC(0xa9,'l','y','r') -#define FOURCC_gnre GST_MAKE_FOURCC('g','n','r','e') -#define FOURCC_disc GST_MAKE_FOURCC('d','i','s','c') -#define FOURCC_disk GST_MAKE_FOURCC('d','i','s','k') -#define FOURCC_trkn GST_MAKE_FOURCC('t','r','k','n') -#define FOURCC_cprt GST_MAKE_FOURCC('c','p','r','t') -#define FOURCC_covr GST_MAKE_FOURCC('c','o','v','r') -#define FOURCC_cpil GST_MAKE_FOURCC('c','p','i','l') -#define FOURCC_tmpo GST_MAKE_FOURCC('t','m','p','o') -#define FOURCC__too GST_MAKE_FOURCC(0xa9,'t','o','o') -#define FOURCC_keyw GST_MAKE_FOURCC('k','e','y','w') -#define FOURCC_____ GST_MAKE_FOURCC('-','-','-','-') -#define FOURCC_free GST_MAKE_FOURCC('f','r','e','e') -#define FOURCC_data GST_MAKE_FOURCC('d','a','t','a') -#define FOURCC_SVQ3 GST_MAKE_FOURCC('S','V','Q','3') -#define FOURCC_rmra GST_MAKE_FOURCC('r','m','r','a') -#define FOURCC_rmda GST_MAKE_FOURCC('r','m','d','a') -#define FOURCC_rdrf GST_MAKE_FOURCC('r','d','r','f') -#define FOURCC__gen GST_MAKE_FOURCC(0xa9, 'g', 'e', 'n') -#define FOURCC_rmdr GST_MAKE_FOURCC('r','m','d','r') -#define FOURCC_rmvc GST_MAKE_FOURCC('r','m','v','c') -#define FOURCC_qtim GST_MAKE_FOURCC('q','t','i','m') -#define FOURCC_drms GST_MAKE_FOURCC('d','r','m','s') -#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') -#define FOURCC_h263 GST_MAKE_FOURCC('h','2','6','3') -#define FOURCC_s263 GST_MAKE_FOURCC('s','2','6','3') -#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C') -#define FOURCC_VP31 GST_MAKE_FOURCC('V','P','3','1') -#define FOURCC_VP80 GST_MAKE_FOURCC('V','P','8','0') -#define FOURCC_rle_ GST_MAKE_FOURCC('r','l','e',' ') -#define FOURCC_MAC6 GST_MAKE_FOURCC('M','A','C','6') -#define FOURCC_MAC3 GST_MAKE_FOURCC('M','A','C','3') -#define FOURCC_ima4 GST_MAKE_FOURCC('i','m','a','4') -#define FOURCC_ulaw GST_MAKE_FOURCC('u','l','a','w') -#define FOURCC_alaw GST_MAKE_FOURCC('a','l','a','w') -#define FOURCC_twos GST_MAKE_FOURCC('t','w','o','s') -#define FOURCC_sowt GST_MAKE_FOURCC('s','o','w','t') -#define FOURCC_raw_ GST_MAKE_FOURCC('r','a','w',' ') -#define FOURCC_QDM2 GST_MAKE_FOURCC('Q','D','M','2') -#define FOURCC_alac GST_MAKE_FOURCC('a','l','a','c') -#define FOURCC_samr GST_MAKE_FOURCC('s','a','m','r') -#define FOURCC_sawb GST_MAKE_FOURCC('s','a','w','b') -#define FOURCC_mdat GST_MAKE_FOURCC('m','d','a','t') -#define FOURCC_wide GST_MAKE_FOURCC('w','i','d','e') -#define FOURCC_PICT GST_MAKE_FOURCC('P','I','C','T') -#define FOURCC_pnot GST_MAKE_FOURCC('p','n','o','t') -#define FOURCC_zlib GST_MAKE_FOURCC('z','l','i','b') -#define FOURCC_alis GST_MAKE_FOURCC('a','l','i','s') -#define FOURCC_url_ GST_MAKE_FOURCC('u','r','l',' ') -#define FOURCC_frma GST_MAKE_FOURCC('f','r','m','a') -#define FOURCC_ctts GST_MAKE_FOURCC('c','t','t','s') -#define FOURCC_drac GST_MAKE_FOURCC('d','r','a','c') -#define FOURCC_jpeg GST_MAKE_FOURCC('j','p','e','g') -#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') -#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h') -#define FOURCC_jp2c GST_MAKE_FOURCC('j','p','2','c') -#define FOURCC_gama GST_MAKE_FOURCC('g','a','m','a') -#define FOURCC_tvsh GST_MAKE_FOURCC('t','v','s','h') -#define FOURCC_tven GST_MAKE_FOURCC('t','v','e','n') -#define FOURCC_tvsn GST_MAKE_FOURCC('t','v','s','n') -#define FOURCC_tves GST_MAKE_FOURCC('t','v','e','s') -#define FOURCC_sonm GST_MAKE_FOURCC('s','o','n','m') -#define FOURCC_soal GST_MAKE_FOURCC('s','o','a','l') -#define FOURCC_soar GST_MAKE_FOURCC('s','o','a','r') -#define FOURCC_soaa GST_MAKE_FOURCC('s','o','a','a') -#define FOURCC_soco GST_MAKE_FOURCC('s','o','c','o') -#define FOURCC_sosn GST_MAKE_FOURCC('s','o','s','n') -#define FOURCC_XMP_ GST_MAKE_FOURCC('X','M','P','_') -#define FOURCC_uuid GST_MAKE_FOURCC('u','u','i','d') - - -/* SVQ3 fourcc */ -#define FOURCC_SEQH GST_MAKE_FOURCC('S','E','Q','H') -#define FOURCC_SMI_ GST_MAKE_FOURCC('S','M','I',' ') - -/* fragmented mp4 */ -#define FOURCC_mvex GST_MAKE_FOURCC('m','v','e','x') -#define FOURCC_mehd GST_MAKE_FOURCC('m','e','h','d') -#define FOURCC_trex GST_MAKE_FOURCC('t','r','e','x') -#define FOURCC_mfra GST_MAKE_FOURCC('m','f','r','a') -#define FOURCC_moof GST_MAKE_FOURCC('m','o','o','f') -#define FOURCC_tfra GST_MAKE_FOURCC('t','f','r','a') -#define FOURCC_tfhd GST_MAKE_FOURCC('t','f','h','d') -#define FOURCC_trun GST_MAKE_FOURCC('t','r','u','n') -#define FOURCC_sdtp GST_MAKE_FOURCC('s','d','t','p') -#define FOURCC_mfro GST_MAKE_FOURCC('m','f','r','o') -#define FOURCC_mfhd GST_MAKE_FOURCC('m','f','h','d') -#define FOURCC_mvhd GST_MAKE_FOURCC('m','v','h','d') -#define FOURCC_traf GST_MAKE_FOURCC('t','r','a','f') - -/* Xiph fourcc */ -#define FOURCC_XiTh GST_MAKE_FOURCC('X','i','T','h') -#define FOURCC_XdxT GST_MAKE_FOURCC('X','d','x','T') -#define FOURCC_tCtH GST_MAKE_FOURCC('t','C','t','H') -#define FOURCC_tCt_ GST_MAKE_FOURCC('t','C','t','#') -#define FOURCC_tCtC GST_MAKE_FOURCC('t','C','t','C') - -/* ilst metatags */ -#define FOURCC_titl GST_MAKE_FOURCC('t','i','t','l') -#define FOURCC__cmt GST_MAKE_FOURCC(0xa9, 'c','m','t') - -/* 3gp tags */ -#define FOURCC_dscp GST_MAKE_FOURCC('d','s','c','p') -#define FOURCC_perf GST_MAKE_FOURCC('p','e','r','f') -#define FOURCC_auth GST_MAKE_FOURCC('a','u','t','h') -#define FOURCC_yrrc GST_MAKE_FOURCC('y','r','r','c') -#define FOURCC_albm GST_MAKE_FOURCC('a','l','b','m') -#define FOURCC_loci GST_MAKE_FOURCC('l','o','c','i') -#define FOURCC_kywd GST_MAKE_FOURCC('k','y','w','d') -#define FOURCC_clsf GST_MAKE_FOURCC('c','l','s','f') - -/* For Microsoft Wave formats embedded in quicktime, the FOURCC is - 'm', 's', then the 16 bit wave codec id */ -#define MS_WAVE_FOURCC(codecid) GST_MAKE_FOURCC( \ - 'm', 's', ((codecid)>>8)&0xff, ((codecid)&0xff)) - -#define FOURCC_owma GST_MAKE_FOURCC('o','w','m','a') -#define FOURCC_ovc1 GST_MAKE_FOURCC('o','v','c','1') - -G_END_DECLS - -#endif /* __FOURCC_H__ */ diff --git a/gst/qtmux/ftypcc.h b/gst/qtmux/ftypcc.h deleted file mode 100644 index 3d31b6dd0b..0000000000 --- a/gst/qtmux/ftypcc.h +++ /dev/null @@ -1,68 +0,0 @@ -/* GStreamer - * Copyright (C) <2008> Thiago Sousa Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __FTYP_CC_H__ -#define __FTYP_CC_H__ - -#include - -G_BEGIN_DECLS - -#define FOURCC_ftyp GST_MAKE_FOURCC('f','t','y','p') -#define FOURCC_isom GST_MAKE_FOURCC('i','s','o','m') -#define FOURCC_iso2 GST_MAKE_FOURCC('i','s','o','2') -#define FOURCC_mp41 GST_MAKE_FOURCC('m','p','4','1') -#define FOURCC_mp42 GST_MAKE_FOURCC('m','p','4','2') -#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2') -#define FOURCC_3gp4 GST_MAKE_FOURCC('3','g','p','4') -#define FOURCC_3gp6 GST_MAKE_FOURCC('3','g','p','6') -#define FOURCC_3gg6 GST_MAKE_FOURCC('3','g','g','6') -#define FOURCC_3gr6 GST_MAKE_FOURCC('3','g','r','6') -#define FOURCC_3gg7 GST_MAKE_FOURCC('3','g','g','7') -#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1') -#define FOURCC_qt__ GST_MAKE_FOURCC('q','t',' ',' ') -#define FOURCC_isml GST_MAKE_FOURCC('i','s','m','l') -#define FOURCC_piff GST_MAKE_FOURCC('p','i','f','f') - -G_END_DECLS - -#endif /* __FTYP_CC_H__ */ diff --git a/gst/qtmux/gstqtmoovrecover.c b/gst/qtmux/gstqtmoovrecover.c deleted file mode 100644 index 889b2cc4b5..0000000000 --- a/gst/qtmux/gstqtmoovrecover.c +++ /dev/null @@ -1,390 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -/** - * SECTION:gstqtmoovrecover - * @short_description: Utility element for recovering unfinished quicktime files - * - * - * - * This element recovers quicktime files created with qtmux using the moov recovery feature. - * - * Example pipelines - * - * - * TODO - * - * - * - * Last reviewed on 2010-02-01 - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "gstqtmoovrecover.h" - -GST_DEBUG_CATEGORY_STATIC (gst_qt_moov_recover_debug); -#define GST_CAT_DEFAULT gst_qt_moov_recover_debug - -/* QTMoovRecover signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_RECOVERY_INPUT, - PROP_BROKEN_INPUT, - PROP_FIXED_OUTPUT, - PROP_FAST_START_MODE -}; - -GST_BOILERPLATE (GstQTMoovRecover, gst_qt_moov_recover, GstPipeline, - GST_TYPE_PIPELINE); - -/* property functions */ -static void gst_qt_moov_recover_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_qt_moov_recover_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -static GstStateChangeReturn gst_qt_moov_recover_change_state (GstElement * - element, GstStateChange transition); - -static void gst_qt_moov_recover_finalize (GObject * object); - -static void -gst_qt_moov_recover_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); -#if 0 - GstQTMoovRecoverClass *klass = (GstQTMoovRecoverClass *) g_class; -#endif - gst_element_class_set_details_simple (element_class, "QT Moov Recover", - "Util", "Recovers unfinished qtmux files", - "Thiago Santos "); -} - -static void -gst_qt_moov_recover_class_init (GstQTMoovRecoverClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_qt_moov_recover_finalize; - gobject_class->get_property = gst_qt_moov_recover_get_property; - gobject_class->set_property = gst_qt_moov_recover_set_property; - - gstelement_class->change_state = gst_qt_moov_recover_change_state; - - g_object_class_install_property (gobject_class, PROP_FIXED_OUTPUT, - g_param_spec_string ("fixed-output", - "Path to write the fixed file", - "Path to write the fixed file to (used as output)", - NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_BROKEN_INPUT, - g_param_spec_string ("broken-input", - "Path to broken input file", - "Path to broken input file. (If qtmux was on faststart mode, this " - "file is the faststart file)", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_RECOVERY_INPUT, - g_param_spec_string ("recovery-input", - "Path to recovery file", - "Path to recovery file (used as input)", NULL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_FAST_START_MODE, - g_param_spec_boolean ("faststart-mode", - "If the broken input is from faststart mode", - "If the broken input is from faststart mode", - FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - - GST_DEBUG_CATEGORY_INIT (gst_qt_moov_recover_debug, "qtmoovrecover", 0, - "QT Moovie Recover"); -} - -static void -gst_qt_moov_recover_init (GstQTMoovRecover * qtmr, - GstQTMoovRecoverClass * qtmr_klass) -{ -} - -static void -gst_qt_moov_recover_finalize (GObject * object) -{ - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static void -gst_qt_moov_recover_run (void *data) -{ - FILE *moovrec = NULL; - FILE *mdatinput = NULL; - FILE *output = NULL; - MdatRecovFile *mdat_recov = NULL; - MoovRecovFile *moov_recov = NULL; - GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (data); - GError *err = NULL; - - GST_LOG_OBJECT (qtmr, "Starting task"); - - GST_DEBUG_OBJECT (qtmr, "Validating properties"); - GST_OBJECT_LOCK (qtmr); - /* validate properties */ - if (qtmr->broken_input == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS, - ("Please set broken-input property"), (NULL)); - goto end; - } - if (qtmr->recovery_input == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS, - ("Please set recovery-input property"), (NULL)); - goto end; - } - if (qtmr->fixed_output == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, SETTINGS, - ("Please set fixed-output property"), (NULL)); - goto end; - } - - GST_DEBUG_OBJECT (qtmr, "Opening input/output files"); - /* open files */ - moovrec = g_fopen (qtmr->recovery_input, "rb"); - if (moovrec == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ, - ("Failed to open recovery-input file"), (NULL)); - goto end; - } - - mdatinput = g_fopen (qtmr->broken_input, "rb"); - if (mdatinput == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ, - ("Failed to open broken-input file"), (NULL)); - goto end; - } - output = g_fopen (qtmr->fixed_output, "wb+"); - if (output == NULL) { - GST_OBJECT_UNLOCK (qtmr); - GST_ELEMENT_ERROR (qtmr, RESOURCE, OPEN_READ_WRITE, - ("Failed to open fixed-output file"), (NULL)); - goto end; - } - GST_OBJECT_UNLOCK (qtmr); - - GST_DEBUG_OBJECT (qtmr, "Parsing input files"); - /* now create our structures */ - mdat_recov = mdat_recov_file_create (mdatinput, qtmr->faststart_mode, &err); - mdatinput = NULL; - if (mdat_recov == NULL) { - GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED, - ("Broken file could not be parsed correctly"), (NULL)); - goto end; - } - moov_recov = moov_recov_file_create (moovrec, &err); - moovrec = NULL; - if (moov_recov == NULL) { - GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED, - ("Recovery file could not be parsed correctly"), (NULL)); - goto end; - } - - /* now parse the buffers data from moovrec */ - if (!moov_recov_parse_buffers (moov_recov, mdat_recov, &err)) { - goto end; - } - - GST_DEBUG_OBJECT (qtmr, "Writing fixed file to output"); - if (!moov_recov_write_file (moov_recov, mdat_recov, output, &err)) { - goto end; - } - - /* here means success */ - GST_DEBUG_OBJECT (qtmr, "Finished successfully, posting EOS"); - gst_element_post_message (GST_ELEMENT_CAST (qtmr), - gst_message_new_eos (GST_OBJECT_CAST (qtmr))); - -end: - GST_LOG_OBJECT (qtmr, "Finalizing task"); - if (err) { - GST_ELEMENT_ERROR (qtmr, RESOURCE, FAILED, ("%s", err->message), (NULL)); - g_error_free (err); - } - - if (moov_recov) - moov_recov_file_free (moov_recov); - if (moovrec) - fclose (moovrec); - - if (mdat_recov) - mdat_recov_file_free (mdat_recov); - if (mdatinput) - fclose (mdatinput); - - if (output) - fclose (output); - GST_LOG_OBJECT (qtmr, "Leaving task"); - gst_task_stop (qtmr->task); -} - -static void -gst_qt_moov_recover_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object); - - GST_OBJECT_LOCK (qtmr); - switch (prop_id) { - case PROP_FAST_START_MODE: - g_value_set_boolean (value, qtmr->faststart_mode); - break; - case PROP_BROKEN_INPUT: - g_value_set_string (value, qtmr->broken_input); - break; - case PROP_RECOVERY_INPUT: - g_value_set_string (value, qtmr->recovery_input); - break; - case PROP_FIXED_OUTPUT: - g_value_set_string (value, qtmr->fixed_output); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (qtmr); -} - -static void -gst_qt_moov_recover_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (object); - - GST_OBJECT_LOCK (qtmr); - switch (prop_id) { - case PROP_FAST_START_MODE: - qtmr->faststart_mode = g_value_get_boolean (value); - break; - case PROP_BROKEN_INPUT: - g_free (qtmr->broken_input); - qtmr->broken_input = g_value_dup_string (value); - break; - case PROP_RECOVERY_INPUT: - g_free (qtmr->recovery_input); - qtmr->recovery_input = g_value_dup_string (value); - break; - case PROP_FIXED_OUTPUT: - g_free (qtmr->fixed_output); - qtmr->fixed_output = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (qtmr); -} - -static GstStateChangeReturn -gst_qt_moov_recover_change_state (GstElement * element, - GstStateChange transition) -{ - GstStateChangeReturn ret; - GstQTMoovRecover *qtmr = GST_QT_MOOV_RECOVER_CAST (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - qtmr->task = gst_task_create (gst_qt_moov_recover_run, qtmr); - qtmr->task_mutex = g_new (GStaticRecMutex, 1); - g_static_rec_mutex_init (qtmr->task_mutex); - gst_task_set_lock (qtmr->task, qtmr->task_mutex); - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - gst_task_start (qtmr->task); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - gst_task_stop (qtmr->task); - gst_task_join (qtmr->task); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - g_assert (gst_task_get_state (qtmr->task) == GST_TASK_STOPPED); - gst_object_unref (qtmr->task); - qtmr->task = NULL; - g_static_rec_mutex_free (qtmr->task_mutex); - break; - default: - break; - } - return ret; -} - - -gboolean -gst_qt_moov_recover_register (GstPlugin * plugin) -{ - return gst_element_register (plugin, "qtmoovrecover", GST_RANK_NONE, - GST_TYPE_QT_MOOV_RECOVER); -} diff --git a/gst/qtmux/gstqtmoovrecover.h b/gst/qtmux/gstqtmoovrecover.h deleted file mode 100644 index 07dc9d9379..0000000000 --- a/gst/qtmux/gstqtmoovrecover.h +++ /dev/null @@ -1,88 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __GST_QT_MOOV_RECOVER_H__ -#define __GST_QT_MOOV_RECOVER_H__ - -#include - -#include "atoms.h" -#include "atomsrecovery.h" - -G_BEGIN_DECLS - -#define GST_TYPE_QT_MOOV_RECOVER (gst_qt_moov_recover_get_type()) -#define GST_QT_MOOV_RECOVER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_MOOV_RECOVER, GstQTMoovRecover)) -#define GST_QT_MOOV_RECOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_MOOV_RECOVER, GstQTMoovRecover)) -#define GST_IS_QT_MOOV_RECOVER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QT_MOOV_RECOVER)) -#define GST_IS_QT_MOOV_RECOVER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QT_MOOV_RECOVER)) -#define GST_QT_MOOV_RECOVER_CAST(obj) ((GstQTMoovRecover*)(obj)) - - -typedef struct _GstQTMoovRecover GstQTMoovRecover; -typedef struct _GstQTMoovRecoverClass GstQTMoovRecoverClass; - -struct _GstQTMoovRecover -{ - GstPipeline pipeline; - - GstTask *task; - GStaticRecMutex *task_mutex; - - /* properties */ - gboolean faststart_mode; - gchar *recovery_input; - gchar *fixed_output; - gchar *broken_input; -}; - -struct _GstQTMoovRecoverClass -{ - GstPipelineClass parent_class; -}; - -GType gst_qt_moov_recover_get_type (void); -gboolean gst_qt_moov_recover_register (GstPlugin * plugin); - -G_END_DECLS - -#endif /* __GST_QT_MOOV_RECOVER_H__ */ diff --git a/gst/qtmux/gstqtmux.c b/gst/qtmux/gstqtmux.c deleted file mode 100644 index 39110d846b..0000000000 --- a/gst/qtmux/gstqtmux.c +++ /dev/null @@ -1,3498 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008-2010 Thiago Santos - * Copyright (C) 2008 Mark Nauwelaerts - * Copyright (C) 2010 Nokia Corporation. All rights reserved. - * Contact: Stefan Kost - - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - - -/** - * SECTION:element-qtmux - * @short_description: Muxer for quicktime(.mov) files - * - * This element merges streams (audio and video) into QuickTime(.mov) files. - * - * The following background intends to explain why various similar muxers - * are present in this plugin. - * - * The - * QuickTime file format specification served as basis for the MP4 file - * format specification (mp4mux), and as such the QuickTime file structure is - * nearly identical to the so-called ISO Base Media file format defined in - * ISO 14496-12 (except for some media specific parts). - * In turn, the latter ISO Base Media format was further specialized as a - * Motion JPEG-2000 file format in ISO 15444-3 (mj2mux) - * and in various 3GPP(2) specs (gppmux). - * The fragmented file features defined (only) in ISO Base Media are used by - * ISMV files making up (a.o.) Smooth Streaming (ismlmux). - * - * A few properties (movie-timescale, - * trak-timescale) allow adjusting - * some technical parameters, which might be useful in (rare) cases to resolve - * compatibility issues in some situations. - * - * Some other properties influence the result more fundamentally. - * A typical mov/mp4 file's metadata (aka moov) is located at the end of the file, - * somewhat contrary to this usually being called "the header". - * However, a faststart file will - * (with some effort) arrange this to be located near start of the file, - * which then allows it e.g. to be played while downloading. - * Alternatively, rather than having one chunk of metadata at start (or end), - * there can be some metadata at start and most of the other data can be spread - * out into fragments of fragment-duration. - * If such fragmented layout is intended for streaming purposes, then - * streamable allows foregoing to add - * index metadata (at the end of file). - * - * dts-method allows selecting a - * method for managing input timestamps (stay tuned for 0.11 to have this - * automagically settled). The default delta/duration method should handle nice - * (aka perfect streams) just fine, but may experience problems otherwise - * (e.g. input stream with re-ordered B-frames and/or with frame dropping). - * The re-ordering approach re-assigns incoming timestamps in ascending order - * to incoming buffers and offers an alternative in such cases. In cases where - * that might fail, the remaining method can be tried, which is exact and - * according to specs, but might experience playback on not so spec-wise players. - * Note that this latter approach also requires one to enable - * presentation-timestamp. - * - * - * Example pipelines - * |[ - * gst-launch v4l2src num-buffers=500 ! video/x-raw-yuv,width=320,height=240 ! ffmpegcolorspace ! qtmux ! filesink location=video.mov - * ]| - * Records a video stream captured from a v4l2 device and muxes it into a qt file. - * - * - * Last reviewed on 2010-12-03 - */ - -/* - * Based on avimux - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include -#include -#include - -#include -#ifdef G_OS_WIN32 -#include /* lseek, open, close, read */ -#undef lseek -#define lseek _lseeki64 -#undef off_t -#define off_t guint64 -#endif - -#ifdef _MSC_VER -#define ftruncate g_win32_ftruncate -#endif - -#ifdef HAVE_UNISTD_H -# include -#endif - -#include "gstqtmux.h" - -GST_DEBUG_CATEGORY_STATIC (gst_qt_mux_debug); -#define GST_CAT_DEFAULT gst_qt_mux_debug - -enum -{ - DTS_METHOD_DD, - DTS_METHOD_REORDER, - DTS_METHOD_ASC -}; - -static GType -gst_qt_mux_dts_method_get_type (void) -{ - static GType gst_qt_mux_dts_method = 0; - - if (!gst_qt_mux_dts_method) { - static const GEnumValue dts_methods[] = { - {DTS_METHOD_DD, "delta/duration", "dd"}, - {DTS_METHOD_REORDER, "reorder", "reorder"}, - {DTS_METHOD_ASC, "ascending", "asc"}, - {0, NULL, NULL}, - }; - - gst_qt_mux_dts_method = - g_enum_register_static ("GstQTMuxDtsMethods", dts_methods); - } - - return gst_qt_mux_dts_method; -} - -#define GST_TYPE_QT_MUX_DTS_METHOD \ - (gst_qt_mux_dts_method_get_type ()) - -/* QTMux signals and args */ -enum -{ - /* FILL ME */ - LAST_SIGNAL -}; - -enum -{ - PROP_0, - PROP_MOVIE_TIMESCALE, - PROP_TRAK_TIMESCALE, - PROP_FAST_START, - PROP_FAST_START_TEMP_FILE, - PROP_MOOV_RECOV_FILE, - PROP_FRAGMENT_DURATION, - PROP_STREAMABLE, - PROP_DTS_METHOD, - PROP_DO_CTTS, -}; - -/* some spare for header size as well */ -#define MDAT_LARGE_FILE_LIMIT ((guint64) 1024 * 1024 * 1024 * 2) -#define MAX_TOLERATED_LATENESS (GST_SECOND / 10) - -#define DEFAULT_MOVIE_TIMESCALE 1000 -#define DEFAULT_TRAK_TIMESCALE 0 -#define DEFAULT_DO_CTTS FALSE -#define DEFAULT_FAST_START FALSE -#define DEFAULT_FAST_START_TEMP_FILE NULL -#define DEFAULT_MOOV_RECOV_FILE NULL -#define DEFAULT_FRAGMENT_DURATION 0 -#define DEFAULT_STREAMABLE FALSE -#define DEFAULT_DTS_METHOD DTS_METHOD_DD - - -static void gst_qt_mux_finalize (GObject * object); - -static GstStateChangeReturn gst_qt_mux_change_state (GstElement * element, - GstStateChange transition); - -/* property functions */ -static void gst_qt_mux_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_qt_mux_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec); - -/* pad functions */ -static GstPad *gst_qt_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * name); -static void gst_qt_mux_release_pad (GstElement * element, GstPad * pad); - -/* event */ -static gboolean gst_qt_mux_sink_event (GstPad * pad, GstEvent * event); - -static GstFlowReturn gst_qt_mux_collected (GstCollectPads * pads, - gpointer user_data); -static GstFlowReturn gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, - GstBuffer * buf); - -static GstElementClass *parent_class = NULL; - -static void -gst_qt_mux_base_init (gpointer g_class) -{ - GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - GstQTMuxClass *klass = (GstQTMuxClass *) g_class; - GstQTMuxClassParams *params; - GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl; - gchar *longname, *description; - - params = - (GstQTMuxClassParams *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (g_class), - GST_QT_MUX_PARAMS_QDATA); - g_assert (params != NULL); - - /* construct the element details struct */ - longname = g_strdup_printf ("%s Muxer", params->prop->long_name); - description = g_strdup_printf ("Multiplex audio and video into a %s file", - params->prop->long_name); - gst_element_class_set_details_simple (element_class, longname, - "Codec/Muxer", description, - "Thiago Sousa Santos "); - g_free (longname); - g_free (description); - - /* pad templates */ - srctempl = gst_pad_template_new ("src", GST_PAD_SRC, - GST_PAD_ALWAYS, params->src_caps); - gst_element_class_add_pad_template (element_class, srctempl); - - if (params->audio_sink_caps) { - audiosinktempl = gst_pad_template_new ("audio_%d", - GST_PAD_SINK, GST_PAD_REQUEST, params->audio_sink_caps); - gst_element_class_add_pad_template (element_class, audiosinktempl); - } - - if (params->video_sink_caps) { - videosinktempl = gst_pad_template_new ("video_%d", - GST_PAD_SINK, GST_PAD_REQUEST, params->video_sink_caps); - gst_element_class_add_pad_template (element_class, videosinktempl); - } - - klass->format = params->prop->format; -} - -static void -gst_qt_mux_class_init (GstQTMuxClass * klass) -{ - GObjectClass *gobject_class; - GstElementClass *gstelement_class; - - gobject_class = (GObjectClass *) klass; - gstelement_class = (GstElementClass *) klass; - - parent_class = g_type_class_peek_parent (klass); - - gobject_class->finalize = gst_qt_mux_finalize; - gobject_class->get_property = gst_qt_mux_get_property; - gobject_class->set_property = gst_qt_mux_set_property; - - g_object_class_install_property (gobject_class, PROP_MOVIE_TIMESCALE, - g_param_spec_uint ("movie-timescale", "Movie timescale", - "Timescale to use in the movie (units per second)", - 1, G_MAXUINT32, DEFAULT_MOVIE_TIMESCALE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_TRAK_TIMESCALE, - g_param_spec_uint ("trak-timescale", "Track timescale", - "Timescale to use for the tracks (units per second, 0 is automatic)", - 0, G_MAXUINT32, DEFAULT_TRAK_TIMESCALE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_DO_CTTS, - g_param_spec_boolean ("presentation-time", - "Include presentation-time info", - "Calculate and include presentation/composition time " - "(in addition to decoding time) (use with caution)", - DEFAULT_DO_CTTS, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_DTS_METHOD, - g_param_spec_enum ("dts-method", "dts-method", - "Method to determine DTS time", - GST_TYPE_QT_MUX_DTS_METHOD, DEFAULT_DTS_METHOD, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_FAST_START, - g_param_spec_boolean ("faststart", "Format file to faststart", - "If the file should be formated for faststart (headers first). ", - DEFAULT_FAST_START, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_FAST_START_TEMP_FILE, - g_param_spec_string ("faststart-file", "File to use for storing buffers", - "File that will be used temporarily to store data from the stream " - "when creating a faststart file. If null a filepath will be " - "created automatically", DEFAULT_FAST_START_TEMP_FILE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_MOOV_RECOV_FILE, - g_param_spec_string ("moov-recovery-file", - "File to store data for posterior moov atom recovery", - "File to be used to store " - "data for moov atom making movie file recovery possible in case " - "of a crash during muxing. Null for disabled. (Experimental)", - DEFAULT_MOOV_RECOV_FILE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_FRAGMENT_DURATION, - g_param_spec_uint ("fragment-duration", "Fragment duration", - "Fragment durations in ms (produce a fragmented file if > 0)", - 0, G_MAXUINT32, klass->format == GST_QT_MUX_FORMAT_ISML ? - 2000 : DEFAULT_FRAGMENT_DURATION, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (gobject_class, PROP_STREAMABLE, - g_param_spec_boolean ("streamable", "Streamable", - "If set to true, the output should be as if it is to be streamed " - "and hence no indexes written or duration written.", - DEFAULT_STREAMABLE, - G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); - - gstelement_class->request_new_pad = - GST_DEBUG_FUNCPTR (gst_qt_mux_request_new_pad); - gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qt_mux_change_state); - gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_qt_mux_release_pad); -} - -static void -gst_qt_mux_pad_reset (GstQTPad * qtpad) -{ - gint i; - - qtpad->fourcc = 0; - qtpad->is_out_of_order = FALSE; - qtpad->have_dts = FALSE; - qtpad->sample_size = 0; - qtpad->sync = FALSE; - qtpad->last_dts = 0; - qtpad->first_ts = GST_CLOCK_TIME_NONE; - qtpad->prepare_buf_func = NULL; - qtpad->avg_bitrate = 0; - qtpad->max_bitrate = 0; - qtpad->ts_n_entries = 0; - - qtpad->buf_head = 0; - qtpad->buf_tail = 0; - for (i = 0; i < G_N_ELEMENTS (qtpad->buf_entries); i++) { - if (qtpad->buf_entries[i]) { - gst_buffer_unref (qtpad->buf_entries[i]); - qtpad->buf_entries[i] = NULL; - } - } - - if (qtpad->last_buf) - gst_buffer_replace (&qtpad->last_buf, NULL); - - /* reference owned elsewhere */ - qtpad->trak = NULL; - - if (qtpad->traf) { - atom_traf_free (qtpad->traf); - qtpad->traf = NULL; - } - atom_array_clear (&qtpad->fragment_buffers); - - /* reference owned elsewhere */ - qtpad->tfra = NULL; -} - -/* - * Takes GstQTMux back to its initial state - */ -static void -gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc) -{ - GSList *walk; - - qtmux->state = GST_QT_MUX_STATE_NONE; - qtmux->header_size = 0; - qtmux->mdat_size = 0; - qtmux->mdat_pos = 0; - qtmux->longest_chunk = GST_CLOCK_TIME_NONE; - qtmux->video_pads = 0; - qtmux->audio_pads = 0; - qtmux->fragment_sequence = 0; - - if (qtmux->ftyp) { - atom_ftyp_free (qtmux->ftyp); - qtmux->ftyp = NULL; - } - if (qtmux->moov) { - atom_moov_free (qtmux->moov); - qtmux->moov = NULL; - } - if (qtmux->mfra) { - atom_mfra_free (qtmux->mfra); - qtmux->mfra = NULL; - } - if (qtmux->fast_start_file) { - fclose (qtmux->fast_start_file); - g_remove (qtmux->fast_start_file_path); - qtmux->fast_start_file = NULL; - } - if (qtmux->moov_recov_file) { - fclose (qtmux->moov_recov_file); - qtmux->moov_recov_file = NULL; - } - for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) { - AtomInfo *ainfo = (AtomInfo *) walk->data; - ainfo->free_func (ainfo->atom); - g_free (ainfo); - } - g_slist_free (qtmux->extra_atoms); - qtmux->extra_atoms = NULL; - - GST_OBJECT_LOCK (qtmux); - gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux)); - GST_OBJECT_UNLOCK (qtmux); - - /* reset pad data */ - for (walk = qtmux->sinkpads; walk; walk = g_slist_next (walk)) { - GstQTPad *qtpad = (GstQTPad *) walk->data; - gst_qt_mux_pad_reset (qtpad); - - /* hm, moov_free above yanked the traks away from us, - * so do not free, but do clear */ - qtpad->trak = NULL; - } - - if (alloc) { - qtmux->moov = atom_moov_new (qtmux->context); - /* ensure all is as nice and fresh as request_new_pad would provide it */ - for (walk = qtmux->sinkpads; walk; walk = g_slist_next (walk)) { - GstQTPad *qtpad = (GstQTPad *) walk->data; - - qtpad->trak = atom_trak_new (qtmux->context); - atom_moov_add_trak (qtmux->moov, qtpad->trak); - } - } -} - -static void -gst_qt_mux_init (GstQTMux * qtmux, GstQTMuxClass * qtmux_klass) -{ - GstElementClass *klass = GST_ELEMENT_CLASS (qtmux_klass); - GstPadTemplate *templ; - - templ = gst_element_class_get_pad_template (klass, "src"); - qtmux->srcpad = gst_pad_new_from_template (templ, "src"); - gst_pad_use_fixed_caps (qtmux->srcpad); - gst_element_add_pad (GST_ELEMENT (qtmux), qtmux->srcpad); - - qtmux->sinkpads = NULL; - qtmux->collect = gst_collect_pads_new (); - gst_collect_pads_set_function (qtmux->collect, - (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_qt_mux_collected), qtmux); - - /* properties set to default upon construction */ - - /* always need this */ - qtmux->context = - atoms_context_new (gst_qt_mux_map_format_to_flavor (qtmux_klass->format)); - - /* internals to initial state */ - gst_qt_mux_reset (qtmux, TRUE); -} - - -static void -gst_qt_mux_finalize (GObject * object) -{ - GstQTMux *qtmux = GST_QT_MUX_CAST (object); - - gst_qt_mux_reset (qtmux, FALSE); - - g_free (qtmux->fast_start_file_path); - g_free (qtmux->moov_recov_file_path); - - atoms_context_free (qtmux->context); - gst_object_unref (qtmux->collect); - - g_slist_free (qtmux->sinkpads); - - G_OBJECT_CLASS (parent_class)->finalize (object); -} - -static GstBuffer * -gst_qt_mux_prepare_jpc_buffer (GstQTPad * qtpad, GstBuffer * buf, - GstQTMux * qtmux) -{ - GstBuffer *newbuf; - - GST_LOG_OBJECT (qtmux, "Preparing jpc buffer"); - - if (buf == NULL) - return NULL; - - newbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 8); - gst_buffer_copy_metadata (newbuf, buf, GST_BUFFER_COPY_ALL); - - GST_WRITE_UINT32_BE (GST_BUFFER_DATA (newbuf), GST_BUFFER_SIZE (newbuf)); - GST_WRITE_UINT32_LE (GST_BUFFER_DATA (newbuf) + 4, FOURCC_jp2c); - - memcpy (GST_BUFFER_DATA (newbuf) + 8, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); - gst_buffer_unref (buf); - - return newbuf; -} - -static void -gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - switch (gst_tag_get_type (tag)) { - /* strings */ - case G_TYPE_STRING: - { - gchar *str = NULL; - - if (!gst_tag_list_get_string (list, tag, &str) || !str) - break; - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s", - GST_FOURCC_ARGS (fourcc), str); - atom_moov_add_str_tag (qtmux->moov, fourcc, str); - g_free (str); - break; - } - /* double */ - case G_TYPE_DOUBLE: - { - gdouble value; - - if (!gst_tag_list_get_double (list, tag, &value)) - break; - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u", - GST_FOURCC_ARGS (fourcc), (gint) value); - atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value); - break; - } - case G_TYPE_UINT: - { - guint value = 0; - if (tag2) { - /* paired unsigned integers */ - guint count = 0; - - if (!(gst_tag_list_get_uint (list, tag, &value) || - gst_tag_list_get_uint (list, tag2, &count))) - break; - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u", - GST_FOURCC_ARGS (fourcc), value, count); - atom_moov_add_uint_tag (qtmux->moov, fourcc, 0, - value << 16 | (count & 0xFFFF)); - } else { - /* unpaired unsigned integers */ - if (!gst_tag_list_get_uint (list, tag, &value)) - break; - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u", - GST_FOURCC_ARGS (fourcc), value); - atom_moov_add_uint_tag (qtmux->moov, fourcc, 1, value); - } - break; - } - default: - g_assert_not_reached (); - break; - } -} - -static void -gst_qt_mux_add_mp4_date (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - GDate *date = NULL; - GDateYear year; - GDateMonth month; - GDateDay day; - gchar *str; - - g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE); - - if (!gst_tag_list_get_date (list, tag, &date) || !date) - return; - - year = g_date_get_year (date); - month = g_date_get_month (date); - day = g_date_get_day (date); - - if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH && - day == G_DATE_BAD_DAY) { - GST_WARNING_OBJECT (qtmux, "invalid date in tag"); - return; - } - - str = g_strdup_printf ("%u-%u-%u", year, month, day); - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s", - GST_FOURCC_ARGS (fourcc), str); - atom_moov_add_str_tag (qtmux->moov, fourcc, str); - g_free (str); -} - -static void -gst_qt_mux_add_mp4_cover (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - GValue value = { 0, }; - GstBuffer *buf; - GstCaps *caps; - GstStructure *structure; - gint flags = 0; - - g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_BUFFER); - - if (!gst_tag_list_copy_value (&value, list, tag)) - return; - - buf = gst_value_get_buffer (&value); - if (!buf) - goto done; - - caps = gst_buffer_get_caps (buf); - if (!caps) { - GST_WARNING_OBJECT (qtmux, "preview image without caps"); - goto done; - } - - GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps); - - structure = gst_caps_get_structure (caps, 0); - if (gst_structure_has_name (structure, "image/jpeg")) - flags = 13; - else if (gst_structure_has_name (structure, "image/png")) - flags = 14; - gst_caps_unref (caps); - - if (!flags) { - GST_WARNING_OBJECT (qtmux, "preview image format not supported"); - goto done; - } - - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT - " -> image size %d", GST_FOURCC_ARGS (fourcc), GST_BUFFER_SIZE (buf)); - atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); -done: - g_value_unset (&value); -} - -static void -gst_qt_mux_add_3gp_str (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - gchar *str = NULL; - guint number; - - g_return_if_fail (gst_tag_get_type (tag) == G_TYPE_STRING); - g_return_if_fail (!tag2 || gst_tag_get_type (tag2) == G_TYPE_UINT); - - if (!gst_tag_list_get_string (list, tag, &str) || !str) - return; - - if (tag2) - if (!gst_tag_list_get_uint (list, tag2, &number)) - tag2 = NULL; - - if (!tag2) { - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s", - GST_FOURCC_ARGS (fourcc), str); - atom_moov_add_3gp_str_tag (qtmux->moov, fourcc, str); - } else { - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s/%d", - GST_FOURCC_ARGS (fourcc), str, number); - atom_moov_add_3gp_str_int_tag (qtmux->moov, fourcc, str, number); - } - - g_free (str); -} - -static void -gst_qt_mux_add_3gp_date (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - GDate *date = NULL; - GDateYear year; - - g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE); - - if (!gst_tag_list_get_date (list, tag, &date) || !date) - return; - - year = g_date_get_year (date); - - if (year == G_DATE_BAD_YEAR) { - GST_WARNING_OBJECT (qtmux, "invalid date in tag"); - return; - } - - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %d", - GST_FOURCC_ARGS (fourcc), year); - atom_moov_add_3gp_uint_tag (qtmux->moov, fourcc, year); -} - -static void -gst_qt_mux_add_3gp_location (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - gdouble latitude = -360, longitude = -360, altitude = 0; - gchar *location = NULL; - guint8 *data, *ddata; - gint size = 0, len = 0; - gboolean ret = FALSE; - - g_return_if_fail (strcmp (tag, GST_TAG_GEO_LOCATION_NAME) == 0); - - ret = gst_tag_list_get_string (list, tag, &location); - ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LONGITUDE, - &longitude); - ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LATITUDE, - &latitude); - ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_ELEVATION, - &altitude); - - if (!ret) - return; - - if (location) - len = strlen (location); - size += len + 1 + 2; - - /* role + (long, lat, alt) + body + notes */ - size += 1 + 3 * 4 + 1 + 1; - - data = ddata = g_malloc (size); - - /* language tag */ - GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE)); - /* location */ - if (location) - memcpy (data + 2, location, len); - GST_WRITE_UINT8 (data + 2 + len, 0); - data += len + 1 + 2; - /* role */ - GST_WRITE_UINT8 (data, 0); - /* long, lat, alt */ - GST_WRITE_UINT32_BE (data + 1, (guint32) (longitude * 65536.0)); - GST_WRITE_UINT32_BE (data + 5, (guint32) (latitude * 65536.0)); - GST_WRITE_UINT32_BE (data + 9, (guint32) (altitude * 65536.0)); - /* neither astronomical body nor notes */ - GST_WRITE_UINT16_BE (data + 13, 0); - - GST_DEBUG_OBJECT (qtmux, "Adding tag 'loci'"); - atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size); - g_free (ddata); -} - -static void -gst_qt_mux_add_3gp_keywords (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - gchar *keywords = NULL; - guint8 *data, *ddata; - gint size = 0, i; - gchar **kwds; - - g_return_if_fail (strcmp (tag, GST_TAG_KEYWORDS) == 0); - - if (!gst_tag_list_get_string (list, tag, &keywords) || !keywords) - return; - - kwds = g_strsplit (keywords, ",", 0); - g_free (keywords); - - size = 0; - for (i = 0; kwds[i]; i++) { - /* size byte + null-terminator */ - size += strlen (kwds[i]) + 1 + 1; - } - - /* language tag + count + keywords */ - size += 2 + 1; - - data = ddata = g_malloc (size); - - /* language tag */ - GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE)); - /* count */ - GST_WRITE_UINT8 (data + 2, i); - data += 3; - /* keywords */ - for (i = 0; kwds[i]; ++i) { - gint len = strlen (kwds[i]); - - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s", - GST_FOURCC_ARGS (fourcc), kwds[i]); - /* size */ - GST_WRITE_UINT8 (data, len + 1); - memcpy (data + 1, kwds[i], len + 1); - data += len + 2; - } - - g_strfreev (kwds); - - atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size); - g_free (ddata); -} - -static gboolean -gst_qt_mux_parse_classification_string (GstQTMux * qtmux, const gchar * input, - guint32 * p_fourcc, guint16 * p_table, gchar ** p_content) -{ - guint32 fourcc; - gint table; - gint size; - const gchar *data; - - data = input; - size = strlen (input); - - if (size < 4 + 3 + 1 + 1 + 1) { - /* at least the minimum xxxx://y/z */ - GST_WARNING_OBJECT (qtmux, "Classification tag input (%s) too short, " - "ignoring", input); - return FALSE; - } - - /* read the fourcc */ - memcpy (&fourcc, data, 4); - size -= 4; - data += 4; - - if (strncmp (data, "://", 3) != 0) { - goto mismatch; - } - data += 3; - size -= 3; - - /* read the table number */ - if (sscanf (data, "%d", &table) != 1) { - goto mismatch; - } - if (table < 0) { - GST_WARNING_OBJECT (qtmux, "Invalid table number in classification tag (%d)" - ", table numbers should be positive, ignoring tag", table); - return FALSE; - } - - /* find the next / */ - while (size > 0 && data[0] != '/') { - data += 1; - size -= 1; - } - if (size == 0) { - goto mismatch; - } - g_assert (data[0] == '/'); - - /* skip the '/' */ - data += 1; - size -= 1; - if (size == 0) { - goto mismatch; - } - - /* read up the rest of the string */ - *p_content = g_strdup (data); - *p_table = (guint16) table; - *p_fourcc = fourcc; - return TRUE; - -mismatch: - { - GST_WARNING_OBJECT (qtmux, "Ignoring classification tag as " - "input (%s) didn't match the expected entitycode://table/content", - input); - return FALSE; - } -} - -static void -gst_qt_mux_add_3gp_classification (GstQTMux * qtmux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc) -{ - gchar *clsf_data = NULL; - gint size = 0; - guint32 entity = 0; - guint16 table = 0; - gchar *content = NULL; - guint8 *data; - - g_return_if_fail (strcmp (tag, GST_TAG_3GP_CLASSIFICATION) == 0); - - if (!gst_tag_list_get_string (list, tag, &clsf_data) || !clsf_data) - return; - - GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s", - GST_FOURCC_ARGS (fourcc), clsf_data); - - /* parse the string, format is: - * entityfourcc://table/content - */ - gst_qt_mux_parse_classification_string (qtmux, clsf_data, &entity, &table, - &content); - g_free (clsf_data); - /* +1 for the \0 */ - size = strlen (content) + 1; - - /* now we have everything, build the atom - * atom description is at 3GPP TS 26.244 V8.2.0 (2009-09) */ - data = g_malloc (4 + 2 + 2 + size); - GST_WRITE_UINT32_LE (data, entity); - GST_WRITE_UINT16_BE (data + 4, (guint16) table); - GST_WRITE_UINT16_BE (data + 6, 0); - memcpy (data + 8, content, size); - g_free (content); - - atom_moov_add_3gp_tag (qtmux->moov, fourcc, data, 4 + 2 + 2 + size); - g_free (data); -} - -typedef void (*GstQTMuxAddTagFunc) (GstQTMux * mux, const GstTagList * list, - const char *tag, const char *tag2, guint32 fourcc); - -/* - * Struct to record mappings from gstreamer tags to fourcc codes - */ -typedef struct _GstTagToFourcc -{ - guint32 fourcc; - const gchar *gsttag; - const gchar *gsttag2; - const GstQTMuxAddTagFunc func; -} GstTagToFourcc; - -/* tag list tags to fourcc matching */ -static const GstTagToFourcc tag_matches_mp4[] = { - {FOURCC__alb, GST_TAG_ALBUM, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__ART, GST_TAG_ARTIST, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__cmt, GST_TAG_COMMENT, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__wrt, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__gen, GST_TAG_GENRE, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__nam, GST_TAG_TITLE, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_perf, GST_TAG_PERFORMER, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__grp, GST_TAG_GROUPING, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__des, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__lyr, GST_TAG_LYRICS, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__too, GST_TAG_ENCODER, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_keyw, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC__day, GST_TAG_DATE, NULL, gst_qt_mux_add_mp4_date}, - {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, gst_qt_mux_add_mp4_tag}, - {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT, - gst_qt_mux_add_mp4_tag}, - {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT, - gst_qt_mux_add_mp4_tag}, - {FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, gst_qt_mux_add_mp4_cover}, - {0, NULL,} -}; - -static const GstTagToFourcc tag_matches_3gp[] = { - {FOURCC_titl, GST_TAG_TITLE, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_perf, GST_TAG_ARTIST, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_auth, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_gnre, GST_TAG_GENRE, NULL, gst_qt_mux_add_3gp_str}, - {FOURCC_kywd, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_3gp_keywords}, - {FOURCC_yrrc, GST_TAG_DATE, NULL, gst_qt_mux_add_3gp_date}, - {FOURCC_albm, GST_TAG_ALBUM, GST_TAG_TRACK_NUMBER, gst_qt_mux_add_3gp_str}, - {FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, gst_qt_mux_add_3gp_location}, - {FOURCC_clsf, GST_TAG_3GP_CLASSIFICATION, NULL, - gst_qt_mux_add_3gp_classification}, - {0, NULL,} -}; - -/* qtdemux produces these for atoms it cannot parse */ -#define GST_QT_DEMUX_PRIVATE_TAG "private-qt-tag" - -static void -gst_qt_mux_add_xmp_tags (GstQTMux * qtmux, const GstTagList * list) -{ - GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); - GstBuffer *xmp = NULL; - - /* adobe specs only have 'quicktime' and 'mp4', - * but I guess we can extrapolate to gpp. - * Keep mj2 out for now as we don't add any tags for it yet. - * If you have further info about xmp on these formats, please share */ - if (qtmux_klass->format == GST_QT_MUX_FORMAT_MJ2) - return; - - GST_DEBUG_OBJECT (qtmux, "Adding xmp tags"); - - if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) { - xmp = gst_tag_xmp_writer_tag_list_to_xmp_buffer (GST_TAG_XMP_WRITER (qtmux), - list, TRUE); - if (xmp) - atom_moov_add_xmp_tags (qtmux->moov, xmp); - } else { - AtomInfo *ainfo; - /* for isom/mp4, it is a top level uuid atom */ - xmp = gst_tag_xmp_writer_tag_list_to_xmp_buffer (GST_TAG_XMP_WRITER (qtmux), - list, TRUE); - if (xmp) { - ainfo = build_uuid_xmp_atom (xmp); - if (ainfo) { - qtmux->extra_atoms = g_slist_prepend (qtmux->extra_atoms, ainfo); - } - } - } - if (xmp) - gst_buffer_unref (xmp); -} - -static void -gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list) -{ - GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); - guint32 fourcc; - gint i; - const gchar *tag, *tag2; - const GstTagToFourcc *tag_matches; - - switch (qtmux_klass->format) { - case GST_QT_MUX_FORMAT_3GP: - tag_matches = tag_matches_3gp; - break; - case GST_QT_MUX_FORMAT_MJ2: - tag_matches = NULL; - break; - default: - /* sort of iTunes style for mp4 and QT (?) */ - tag_matches = tag_matches_mp4; - break; - } - - if (!tag_matches) - return; - - for (i = 0; tag_matches[i].fourcc; i++) { - fourcc = tag_matches[i].fourcc; - tag = tag_matches[i].gsttag; - tag2 = tag_matches[i].gsttag2; - - g_assert (tag_matches[i].func); - tag_matches[i].func (qtmux, list, tag, tag2, fourcc); - } - - /* add unparsed blobs if present */ - if (gst_tag_exists (GST_QT_DEMUX_PRIVATE_TAG)) { - guint num_tags; - - num_tags = gst_tag_list_get_tag_size (list, GST_QT_DEMUX_PRIVATE_TAG); - for (i = 0; i < num_tags; ++i) { - const GValue *val; - GstBuffer *buf; - GstCaps *caps = NULL; - - val = gst_tag_list_get_value_index (list, GST_QT_DEMUX_PRIVATE_TAG, i); - buf = (GstBuffer *) gst_value_get_mini_object (val); - - if (buf && (caps = gst_buffer_get_caps (buf))) { - GstStructure *s; - const gchar *style = NULL; - - GST_DEBUG_OBJECT (qtmux, "Found private tag %d/%d; size %d, caps %" - GST_PTR_FORMAT, i, num_tags, GST_BUFFER_SIZE (buf), caps); - s = gst_caps_get_structure (caps, 0); - if (s && (style = gst_structure_get_string (s, "style"))) { - /* try to prevent some style tag ending up into another variant - * (todo: make into a list if more cases) */ - if ((strcmp (style, "itunes") == 0 && - qtmux_klass->format == GST_QT_MUX_FORMAT_MP4) || - (strcmp (style, "iso") == 0 && - qtmux_klass->format == GST_QT_MUX_FORMAT_3GP)) { - GST_DEBUG_OBJECT (qtmux, "Adding private tag"); - atom_moov_add_blob_tag (qtmux->moov, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); - } - } - gst_caps_unref (caps); - } - } - } - - return; -} - -/* - * Gets the tagsetter iface taglist and puts the known tags - * into the output stream - */ -static void -gst_qt_mux_setup_metadata (GstQTMux * qtmux) -{ - const GstTagList *tags; - - GST_OBJECT_LOCK (qtmux); - tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (qtmux)); - GST_OBJECT_UNLOCK (qtmux); - - GST_LOG_OBJECT (qtmux, "tags: %" GST_PTR_FORMAT, tags); - - if (tags && !gst_tag_list_is_empty (tags)) { - GstTagList *copy = gst_tag_list_copy (tags); - - GST_DEBUG_OBJECT (qtmux, "Removing bogus tags"); - gst_tag_list_remove_tag (copy, GST_TAG_VIDEO_CODEC); - gst_tag_list_remove_tag (copy, GST_TAG_AUDIO_CODEC); - gst_tag_list_remove_tag (copy, GST_TAG_CONTAINER_FORMAT); - - GST_DEBUG_OBJECT (qtmux, "Formatting tags"); - gst_qt_mux_add_metadata_tags (qtmux, copy); - gst_qt_mux_add_xmp_tags (qtmux, copy); - gst_tag_list_free (copy); - } else { - GST_DEBUG_OBJECT (qtmux, "No tags received"); - } -} - -static inline GstBuffer * -_gst_buffer_new_take_data (guint8 * data, guint size) -{ - GstBuffer *buf; - - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data; - GST_BUFFER_SIZE (buf) = size; - - return buf; -} - -static GstFlowReturn -gst_qt_mux_send_buffer (GstQTMux * qtmux, GstBuffer * buf, guint64 * offset, - gboolean mind_fast) -{ - GstFlowReturn res; - guint8 *data; - guint size; - - g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); - - data = GST_BUFFER_DATA (buf); - size = GST_BUFFER_SIZE (buf); - - GST_LOG_OBJECT (qtmux, "sending buffer size %d", size); - - if (mind_fast && qtmux->fast_start_file) { - gint ret; - - GST_LOG_OBJECT (qtmux, "to temporary file"); - ret = fwrite (data, sizeof (guint8), size, qtmux->fast_start_file); - gst_buffer_unref (buf); - if (ret != size) - goto write_error; - else - res = GST_FLOW_OK; - } else { - GST_LOG_OBJECT (qtmux, "downstream"); - - buf = gst_buffer_make_metadata_writable (buf); - gst_buffer_set_caps (buf, GST_PAD_CAPS (qtmux->srcpad)); - res = gst_pad_push (qtmux->srcpad, buf); - } - - if (G_LIKELY (offset)) - *offset += size; - - return res; - - /* ERRORS */ -write_error: - { - GST_ELEMENT_ERROR (qtmux, RESOURCE, WRITE, - ("Failed to write to temporary file"), GST_ERROR_SYSTEM); - return GST_FLOW_ERROR; - } -} - -static gboolean -gst_qt_mux_seek_to_beginning (FILE * f) -{ -#ifdef HAVE_FSEEKO - if (fseeko (f, (off_t) 0, SEEK_SET) != 0) - return FALSE; -#elif defined (G_OS_UNIX) || defined (G_OS_WIN32) - if (lseek (fileno (f), (off_t) 0, SEEK_SET) == (off_t) - 1) - return FALSE; -#else - if (fseek (f, (long) 0, SEEK_SET) != 0) - return FALSE; -#endif - return TRUE; -} - -static GstFlowReturn -gst_qt_mux_send_buffered_data (GstQTMux * qtmux, guint64 * offset) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *buf = NULL; - - if (fflush (qtmux->fast_start_file)) - goto flush_failed; - - if (!gst_qt_mux_seek_to_beginning (qtmux->fast_start_file)) - goto seek_failed; - - /* hm, this could all take a really really long time, - * but there may not be another way to get moov atom first - * (somehow optimize copy?) */ - GST_DEBUG_OBJECT (qtmux, "Sending buffered data"); - while (ret == GST_FLOW_OK) { - gint r; - const int bufsize = 4096; - - buf = gst_buffer_new_and_alloc (bufsize); - r = fread (GST_BUFFER_DATA (buf), sizeof (guint8), bufsize, - qtmux->fast_start_file); - if (r == 0) - break; - GST_BUFFER_SIZE (buf) = r; - GST_LOG_OBJECT (qtmux, "Pushing buffered buffer of size %d", r); - ret = gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE); - buf = NULL; - } - if (buf) - gst_buffer_unref (buf); - - if (ftruncate (fileno (qtmux->fast_start_file), 0)) - goto seek_failed; - if (!gst_qt_mux_seek_to_beginning (qtmux->fast_start_file)) - goto seek_failed; - - return ret; - - /* ERRORS */ -flush_failed: - { - GST_ELEMENT_ERROR (qtmux, RESOURCE, WRITE, - ("Failed to flush temporary file"), GST_ERROR_SYSTEM); - ret = GST_FLOW_ERROR; - goto fail; - } -seek_failed: - { - GST_ELEMENT_ERROR (qtmux, RESOURCE, SEEK, - ("Failed to seek temporary file"), GST_ERROR_SYSTEM); - ret = GST_FLOW_ERROR; - goto fail; - } -fail: - { - /* clear descriptor so we don't remove temp file later on, - * might be possible to recover */ - fclose (qtmux->fast_start_file); - qtmux->fast_start_file = NULL; - return ret; - } -} - -/* - * Sends the initial mdat atom fields (size fields and fourcc type), - * the subsequent buffers are considered part of it's data. - * As we can't predict the amount of data that we are going to place in mdat - * we need to record the position of the size field in the stream so we can - * seek back to it later and update when the streams have finished. - */ -static GstFlowReturn -gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size, - gboolean extended) -{ - Atom *node_header; - GstBuffer *buf; - guint8 *data = NULL; - guint64 offset = 0; - - GST_DEBUG_OBJECT (qtmux, "Sending mdat's atom header, " - "size %" G_GUINT64_FORMAT, size); - - node_header = g_malloc0 (sizeof (Atom)); - node_header->type = FOURCC_mdat; - if (extended) { - /* use extended size */ - node_header->size = 1; - node_header->extended_size = 0; - if (size) - node_header->extended_size = size + 16; - } else { - node_header->size = size + 8; - } - - size = offset = 0; - if (atom_copy_data (node_header, &data, &size, &offset) == 0) - goto serialize_error; - - buf = _gst_buffer_new_take_data (data, offset); - g_free (node_header); - - GST_LOG_OBJECT (qtmux, "Pushing mdat start"); - return gst_qt_mux_send_buffer (qtmux, buf, off, FALSE); - - /* ERRORS */ -serialize_error: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Failed to serialize mdat")); - return GST_FLOW_ERROR; - } -} - -/* - * We get the position of the mdat size field, seek back to it - * and overwrite with the real value - */ -static GstFlowReturn -gst_qt_mux_update_mdat_size (GstQTMux * qtmux, guint64 mdat_pos, - guint64 mdat_size, guint64 * offset) -{ - GstEvent *event; - GstBuffer *buf; - gboolean large_file; - - large_file = (mdat_size > MDAT_LARGE_FILE_LIMIT); - - if (large_file) - mdat_pos += 8; - - /* seek and rewrite the header */ - event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, - mdat_pos, GST_CLOCK_TIME_NONE, 0); - gst_pad_push_event (qtmux->srcpad, event); - - if (large_file) { - buf = gst_buffer_new_and_alloc (sizeof (guint64)); - GST_WRITE_UINT64_BE (GST_BUFFER_DATA (buf), mdat_size + 16); - } else { - guint8 *data; - - buf = gst_buffer_new_and_alloc (16); - data = GST_BUFFER_DATA (buf); - GST_WRITE_UINT32_BE (data, 8); - GST_WRITE_UINT32_LE (data + 4, FOURCC_free); - GST_WRITE_UINT32_BE (data + 8, mdat_size + 8); - GST_WRITE_UINT32_LE (data + 12, FOURCC_mdat); - } - - return gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE); -} - -static GstFlowReturn -gst_qt_mux_send_ftyp (GstQTMux * qtmux, guint64 * off) -{ - GstBuffer *buf; - guint64 size = 0, offset = 0; - guint8 *data = NULL; - - GST_DEBUG_OBJECT (qtmux, "Sending ftyp atom"); - - if (!atom_ftyp_copy_data (qtmux->ftyp, &data, &size, &offset)) - goto serialize_error; - - buf = _gst_buffer_new_take_data (data, offset); - - GST_LOG_OBJECT (qtmux, "Pushing ftyp"); - return gst_qt_mux_send_buffer (qtmux, buf, off, FALSE); - - /* ERRORS */ -serialize_error: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Failed to serialize ftyp")); - return GST_FLOW_ERROR; - } -} - -static void -gst_qt_mux_prepare_ftyp (GstQTMux * qtmux, AtomFTYP ** p_ftyp, - GstBuffer ** p_prefix) -{ - GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); - guint32 major, version; - GList *comp; - GstBuffer *prefix = NULL; - AtomFTYP *ftyp = NULL; - - GST_DEBUG_OBJECT (qtmux, "Preparing ftyp and possible prefix atom"); - - /* init and send context and ftyp based on current property state */ - gst_qt_mux_map_format_to_header (qtmux_klass->format, &prefix, &major, - &version, &comp, qtmux->moov, qtmux->longest_chunk, - qtmux->fast_start_file != NULL); - ftyp = atom_ftyp_new (qtmux->context, major, version, comp); - if (comp) - g_list_free (comp); - if (prefix) { - if (p_prefix) - *p_prefix = prefix; - else - gst_buffer_unref (prefix); - } - *p_ftyp = ftyp; -} - -static GstFlowReturn -gst_qt_mux_prepare_and_send_ftyp (GstQTMux * qtmux) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstBuffer *prefix = NULL; - - GST_DEBUG_OBJECT (qtmux, "Preparing to send ftyp atom"); - - /* init and send context and ftyp based on current property state */ - if (qtmux->ftyp) { - atom_ftyp_free (qtmux->ftyp); - qtmux->ftyp = NULL; - } - gst_qt_mux_prepare_ftyp (qtmux, &qtmux->ftyp, &prefix); - if (prefix) { - ret = gst_qt_mux_send_buffer (qtmux, prefix, &qtmux->header_size, FALSE); - if (ret != GST_FLOW_OK) - return ret; - } - return gst_qt_mux_send_ftyp (qtmux, &qtmux->header_size); -} - -static void -gst_qt_mux_set_header_on_caps (GstQTMux * mux, GstBuffer * buf) -{ - GstStructure *structure; - GValue array = { 0 }; - GValue value = { 0 }; - GstCaps *caps = GST_PAD_CAPS (mux->srcpad); - - caps = gst_caps_copy (GST_PAD_CAPS (mux->srcpad)); - structure = gst_caps_get_structure (caps, 0); - - g_value_init (&array, GST_TYPE_ARRAY); - - GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS); - g_value_init (&value, GST_TYPE_BUFFER); - gst_value_take_buffer (&value, gst_buffer_ref (buf)); - gst_value_array_append_value (&array, &value); - g_value_unset (&value); - - gst_structure_set_value (structure, "streamheader", &array); - g_value_unset (&array); - gst_pad_set_caps (mux->srcpad, caps); - gst_caps_unref (caps); -} - -static void -gst_qt_mux_configure_moov (GstQTMux * qtmux, guint32 * _timescale) -{ - gboolean fragmented; - guint32 timescale; - - GST_OBJECT_LOCK (qtmux); - timescale = qtmux->timescale; - fragmented = qtmux->fragment_sequence > 0; - GST_OBJECT_UNLOCK (qtmux); - - /* inform lower layers of our property wishes, and determine duration. - * Let moov take care of this using its list of traks; - * so that released pads are also included */ - GST_DEBUG_OBJECT (qtmux, "Updating timescale to %" G_GUINT32_FORMAT, - timescale); - atom_moov_update_timescale (qtmux->moov, timescale); - atom_moov_set_fragmented (qtmux->moov, fragmented); - - atom_moov_update_duration (qtmux->moov); - - if (_timescale) - *_timescale = timescale; -} - -static GstFlowReturn -gst_qt_mux_send_moov (GstQTMux * qtmux, guint64 * _offset, gboolean mind_fast) -{ - guint64 offset = 0, size = 0; - guint8 *data; - GstBuffer *buf; - GstFlowReturn ret = GST_FLOW_OK; - - /* serialize moov */ - offset = size = 0; - data = NULL; - GST_LOG_OBJECT (qtmux, "Copying movie header into buffer"); - if (!atom_moov_copy_data (qtmux->moov, &data, &size, &offset)) - goto serialize_error; - - buf = _gst_buffer_new_take_data (data, offset); - GST_DEBUG_OBJECT (qtmux, "Pushing moov atoms"); - gst_qt_mux_set_header_on_caps (qtmux, buf); - ret = gst_qt_mux_send_buffer (qtmux, buf, _offset, mind_fast); - - return ret; - -serialize_error: - { - g_free (data); - return GST_FLOW_ERROR; - } -} - -/* either calculates size of extra atoms or pushes them */ -static GstFlowReturn -gst_qt_mux_send_extra_atoms (GstQTMux * qtmux, gboolean send, guint64 * offset, - gboolean mind_fast) -{ - GSList *walk; - guint64 loffset = 0, size = 0; - guint8 *data; - GstFlowReturn ret = GST_FLOW_OK; - - for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) { - AtomInfo *ainfo = (AtomInfo *) walk->data; - - loffset = size = 0; - data = NULL; - if (!ainfo->copy_data_func (ainfo->atom, - send ? &data : NULL, &size, &loffset)) - goto serialize_error; - - if (send) { - GstBuffer *buf; - - GST_DEBUG_OBJECT (qtmux, - "Pushing extra top-level atom %" GST_FOURCC_FORMAT, - GST_FOURCC_ARGS (ainfo->atom->type)); - buf = _gst_buffer_new_take_data (data, loffset); - ret = gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE); - if (ret != GST_FLOW_OK) - break; - } else { - if (offset) - *offset += loffset; - } - } - - return ret; - -serialize_error: - { - g_free (data); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_qt_mux_start_file (GstQTMux * qtmux) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstCaps *caps; - - GST_DEBUG_OBJECT (qtmux, "starting file"); - - caps = gst_caps_copy (gst_pad_get_pad_template_caps (qtmux->srcpad)); - gst_pad_set_caps (qtmux->srcpad, caps); - gst_caps_unref (caps); - - /* let downstream know we think in BYTES and expect to do seeking later on */ - gst_pad_push_event (qtmux->srcpad, - gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); - - /* initialize our moov recovery file */ - GST_OBJECT_LOCK (qtmux); - if (qtmux->moov_recov_file_path) { - GST_DEBUG_OBJECT (qtmux, "Openning moov recovery file: %s", - qtmux->moov_recov_file_path); - qtmux->moov_recov_file = g_fopen (qtmux->moov_recov_file_path, "wb+"); - if (qtmux->moov_recov_file == NULL) { - GST_WARNING_OBJECT (qtmux, "Failed to open moov recovery file in %s", - qtmux->moov_recov_file_path); - } else { - GSList *walk; - gboolean fail = FALSE; - AtomFTYP *ftyp = NULL; - GstBuffer *prefix = NULL; - - gst_qt_mux_prepare_ftyp (qtmux, &ftyp, &prefix); - - if (!atoms_recov_write_headers (qtmux->moov_recov_file, ftyp, prefix, - qtmux->moov, qtmux->timescale, - g_slist_length (qtmux->sinkpads))) { - GST_WARNING_OBJECT (qtmux, "Failed to write moov recovery file " - "headers"); - fail = TRUE; - } - - atom_ftyp_free (ftyp); - if (prefix) - gst_buffer_unref (prefix); - - for (walk = qtmux->sinkpads; walk && !fail; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; - GstQTPad *qpad = (GstQTPad *) cdata; - /* write info for each stream */ - fail = atoms_recov_write_trak_info (qtmux->moov_recov_file, qpad->trak); - if (fail) { - GST_WARNING_OBJECT (qtmux, "Failed to write trak info to recovery " - "file"); - } - } - if (fail) { - /* cleanup */ - fclose (qtmux->moov_recov_file); - qtmux->moov_recov_file = NULL; - GST_WARNING_OBJECT (qtmux, "An error was detected while writing to " - "recover file, moov recovery won't work"); - } - } - } - GST_OBJECT_UNLOCK (qtmux); - - /* - * send mdat header if already needed, and mark position for later update. - * We don't send ftyp now if we are on fast start mode, because we can - * better fine tune using the information we gather to create the whole moov - * atom. - */ - if (qtmux->fast_start) { - GST_OBJECT_LOCK (qtmux); - qtmux->fast_start_file = g_fopen (qtmux->fast_start_file_path, "wb+"); - if (!qtmux->fast_start_file) - goto open_failed; - GST_OBJECT_UNLOCK (qtmux); - - /* send a dummy buffer for preroll */ - ret = gst_qt_mux_send_buffer (qtmux, gst_buffer_new (), NULL, FALSE); - if (ret != GST_FLOW_OK) - goto exit; - - } else { - ret = gst_qt_mux_prepare_and_send_ftyp (qtmux); - if (ret != GST_FLOW_OK) { - goto exit; - } - - /* well, it's moov pos if fragmented ... */ - qtmux->mdat_pos = qtmux->header_size; - - if (qtmux->fragment_duration) { - GST_DEBUG_OBJECT (qtmux, "fragment duration %d ms, writing headers", - qtmux->fragment_duration); - /* also used as snapshot marker to indicate fragmented file */ - qtmux->fragment_sequence = 1; - /* prepare moov and/or tags */ - gst_qt_mux_configure_moov (qtmux, NULL); - gst_qt_mux_setup_metadata (qtmux); - ret = gst_qt_mux_send_moov (qtmux, &qtmux->header_size, FALSE); - if (ret != GST_FLOW_OK) - return ret; - /* extra atoms */ - ret = - gst_qt_mux_send_extra_atoms (qtmux, TRUE, &qtmux->header_size, FALSE); - if (ret != GST_FLOW_OK) - return ret; - /* prepare index */ - if (!qtmux->streamable) - qtmux->mfra = atom_mfra_new (qtmux->context); - } else { - /* extended to ensure some spare space */ - ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, 0, TRUE); - } - } - -exit: - return ret; - - /* ERRORS */ -open_failed: - { - GST_ELEMENT_ERROR (qtmux, RESOURCE, OPEN_READ_WRITE, - (("Could not open temporary file \"%s\""), qtmux->fast_start_file_path), - GST_ERROR_SYSTEM); - GST_OBJECT_UNLOCK (qtmux); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_qt_mux_stop_file (GstQTMux * qtmux) -{ - gboolean ret = GST_FLOW_OK; - guint64 offset = 0, size = 0; - GSList *walk; - gboolean large_file; - guint32 timescale; - GstClockTime first_ts = GST_CLOCK_TIME_NONE; - - GST_DEBUG_OBJECT (qtmux, "Updating remaining values and sending last data"); - - /* pushing last buffers for each pad */ - for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; - GstQTPad *qtpad = (GstQTPad *) cdata; - - if (!qtpad->last_buf) { - GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers", - GST_PAD_NAME (qtpad->collect.pad)); - continue; - } - /* send last buffer */ - GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s", - GST_PAD_NAME (qtpad->collect.pad)); - ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL); - if (ret != GST_FLOW_OK) - GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, " - "flow return: %s", GST_PAD_NAME (qtpad->collect.pad), - gst_flow_get_name (ret)); - /* determine max stream duration */ - if (!GST_CLOCK_TIME_IS_VALID (first_ts) || - (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) && - qtpad->last_dts > first_ts)) { - first_ts = qtpad->last_dts; - } - } - - if (qtmux->fragment_sequence) { - GstEvent *event; - - if (qtmux->mfra) { - guint8 *data = NULL; - GstBuffer *buf; - - size = offset = 0; - GST_DEBUG_OBJECT (qtmux, "adding mfra"); - if (!atom_mfra_copy_data (qtmux->mfra, &data, &size, &offset)) - goto serialize_error; - buf = _gst_buffer_new_take_data (data, offset); - ret = gst_qt_mux_send_buffer (qtmux, buf, NULL, FALSE); - if (ret != GST_FLOW_OK) - return ret; - } else { - /* must have been streamable; no need to write duration */ - GST_DEBUG_OBJECT (qtmux, "streamable file; nothing to stop"); - return GST_FLOW_OK; - } - - - timescale = qtmux->timescale; - /* only mvex duration is updated, - * mvhd should be consistent with empty moov - * (but TODO maybe some clients do not handle that well ?) */ - qtmux->moov->mvex.mehd.fragment_duration = - gst_util_uint64_scale (first_ts, timescale, GST_SECOND); - GST_DEBUG_OBJECT (qtmux, "rewriting moov with mvex duration %" - GST_TIME_FORMAT, GST_TIME_ARGS (first_ts)); - /* seek and rewrite the header */ - event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, - qtmux->mdat_pos, GST_CLOCK_TIME_NONE, 0); - gst_pad_push_event (qtmux->srcpad, event); - /* no need to seek back */ - return gst_qt_mux_send_moov (qtmux, NULL, FALSE); - } - - gst_qt_mux_configure_moov (qtmux, ×cale); - - /* check for late streams */ - first_ts = GST_CLOCK_TIME_NONE; - for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; - GstQTPad *qtpad = (GstQTPad *) cdata; - - if (!GST_CLOCK_TIME_IS_VALID (first_ts) || - (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) && - qtpad->first_ts < first_ts)) { - first_ts = qtpad->first_ts; - } - } - GST_DEBUG_OBJECT (qtmux, "Media first ts selected: %" GST_TIME_FORMAT, - GST_TIME_ARGS (first_ts)); - /* add EDTSs for late streams */ - for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) { - GstCollectData *cdata = (GstCollectData *) walk->data; - GstQTPad *qtpad = (GstQTPad *) cdata; - guint32 lateness; - guint32 duration; - - if (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) && - qtpad->first_ts > first_ts + MAX_TOLERATED_LATENESS) { - GST_DEBUG_OBJECT (qtmux, "Pad %s is a late stream by %" GST_TIME_FORMAT, - GST_PAD_NAME (qtpad->collect.pad), - GST_TIME_ARGS (qtpad->first_ts - first_ts)); - lateness = gst_util_uint64_scale_round (qtpad->first_ts - first_ts, - timescale, GST_SECOND); - duration = qtpad->trak->tkhd.duration; - atom_trak_add_elst_entry (qtpad->trak, lateness, (guint32) - 1, - (guint32) (1 * 65536.0)); - atom_trak_add_elst_entry (qtpad->trak, duration, 0, - (guint32) (1 * 65536.0)); - - /* need to add the empty time to the trak duration */ - qtpad->trak->tkhd.duration += lateness; - } - } - - /* tags into file metadata */ - gst_qt_mux_setup_metadata (qtmux); - - large_file = (qtmux->mdat_size > MDAT_LARGE_FILE_LIMIT); - /* if faststart, update the offset of the atoms in the movie with the offset - * that the movie headers before mdat will cause. - * Also, send the ftyp */ - if (qtmux->fast_start_file) { - GstFlowReturn flow_ret; - offset = size = 0; - - flow_ret = gst_qt_mux_prepare_and_send_ftyp (qtmux); - if (flow_ret != GST_FLOW_OK) { - goto ftyp_error; - } - /* copy into NULL to obtain size */ - if (!atom_moov_copy_data (qtmux->moov, NULL, &size, &offset)) - goto serialize_error; - GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT, - offset); - offset += qtmux->header_size + (large_file ? 16 : 8); - - /* sum up with the extra atoms size */ - ret = gst_qt_mux_send_extra_atoms (qtmux, FALSE, &offset, FALSE); - if (ret != GST_FLOW_OK) - return ret; - } else { - offset = qtmux->header_size; - } - atom_moov_chunks_add_offset (qtmux->moov, offset); - - /* moov */ - /* note: as of this point, we no longer care about tracking written data size, - * since there is no more use for it anyway */ - ret = gst_qt_mux_send_moov (qtmux, NULL, FALSE); - if (ret != GST_FLOW_OK) - return ret; - - /* extra atoms */ - ret = gst_qt_mux_send_extra_atoms (qtmux, TRUE, NULL, FALSE); - if (ret != GST_FLOW_OK) - return ret; - - /* if needed, send mdat atom and move buffered data into it */ - if (qtmux->fast_start_file) { - /* mdat_size = accumulated (buffered data) */ - ret = gst_qt_mux_send_mdat_header (qtmux, NULL, qtmux->mdat_size, - large_file); - if (ret != GST_FLOW_OK) - return ret; - ret = gst_qt_mux_send_buffered_data (qtmux, NULL); - if (ret != GST_FLOW_OK) - return ret; - } else { - /* mdat needs update iff not using faststart */ - GST_DEBUG_OBJECT (qtmux, "updating mdat size"); - ret = gst_qt_mux_update_mdat_size (qtmux, qtmux->mdat_pos, - qtmux->mdat_size, NULL); - /* note; no seeking back to the end of file is done, - * since we no longer write anything anyway */ - } - - return ret; - - /* ERRORS */ -serialize_error: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Failed to serialize moov")); - return GST_FLOW_ERROR; - } -ftyp_error: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), ("Failed to send ftyp")); - return GST_FLOW_ERROR; - } -} - -static GstFlowReturn -gst_qt_mux_pad_fragment_add_buffer (GstQTMux * qtmux, GstQTPad * pad, - GstBuffer * buf, gboolean force, guint32 nsamples, gint64 dts, - guint32 delta, guint32 size, gboolean sync, gint64 pts_offset) -{ - GstFlowReturn ret = GST_FLOW_OK; - - /* setup if needed */ - if (G_UNLIKELY (!pad->traf || force)) - goto init; - -flush: - /* flush pad fragment if threshold reached, - * or at new keyframe if we should be minding those in the first place */ - if (G_UNLIKELY (force || (sync && pad->sync) || - pad->fragment_duration < (gint64) delta)) { - AtomMOOF *moof; - guint64 size = 0, offset = 0; - guint8 *data = NULL; - GstBuffer *buffer; - guint i, total_size; - - /* now we know where moof ends up, update offset in tfra */ - if (pad->tfra) - atom_tfra_update_offset (pad->tfra, qtmux->header_size); - - moof = atom_moof_new (qtmux->context, qtmux->fragment_sequence); - /* takes ownership */ - atom_moof_add_traf (moof, pad->traf); - pad->traf = NULL; - atom_moof_copy_data (moof, &data, &size, &offset); - buffer = _gst_buffer_new_take_data (data, offset); - GST_LOG_OBJECT (qtmux, "writing moof size %d", GST_BUFFER_SIZE (buffer)); - ret = gst_qt_mux_send_buffer (qtmux, buffer, &qtmux->header_size, FALSE); - - /* and actual data */ - total_size = 0; - for (i = 0; i < atom_array_get_len (&pad->fragment_buffers); i++) { - total_size += - GST_BUFFER_SIZE (atom_array_index (&pad->fragment_buffers, i)); - } - - GST_LOG_OBJECT (qtmux, "writing %d buffers, total_size %d", - atom_array_get_len (&pad->fragment_buffers), total_size); - if (ret == GST_FLOW_OK) - ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, total_size, - FALSE); - for (i = 0; i < atom_array_get_len (&pad->fragment_buffers); i++) { - if (G_LIKELY (ret == GST_FLOW_OK)) - ret = gst_qt_mux_send_buffer (qtmux, - atom_array_index (&pad->fragment_buffers, i), &qtmux->header_size, - FALSE); - else - gst_buffer_unref (atom_array_index (&pad->fragment_buffers, i)); - } - - atom_array_clear (&pad->fragment_buffers); - atom_moof_free (moof); - qtmux->fragment_sequence++; - force = FALSE; - } - -init: - if (G_UNLIKELY (!pad->traf)) { - GST_LOG_OBJECT (qtmux, "setting up new fragment"); - pad->traf = atom_traf_new (qtmux->context, atom_trak_get_id (pad->trak)); - atom_array_init (&pad->fragment_buffers, 512); - pad->fragment_duration = gst_util_uint64_scale (qtmux->fragment_duration, - atom_trak_get_timescale (pad->trak), 1000); - - if (G_UNLIKELY (qtmux->mfra && !pad->tfra)) { - pad->tfra = atom_tfra_new (qtmux->context, atom_trak_get_id (pad->trak)); - atom_mfra_add_tfra (qtmux->mfra, pad->tfra); - } - } - - /* add buffer and metadata */ - atom_traf_add_samples (pad->traf, delta, size, sync, pts_offset, - pad->sync && sync); - atom_array_append (&pad->fragment_buffers, buf, 256); - pad->fragment_duration -= delta; - - if (pad->tfra) { - guint32 sn = atom_traf_get_sample_num (pad->traf); - - if ((sync && pad->sync) || (sn == 1 && !pad->sync)) - atom_tfra_add_entry (pad->tfra, dts, sn); - } - - if (G_UNLIKELY (force)) - goto flush; - - return ret; -} - -/* sigh, tiny list helpers to re-order stuff */ -static void -gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts) -{ - gint i; - - for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) { - if (ts > pad->ts_entries[i]) - break; - } - memmove (&pad->ts_entries[i + 1], &pad->ts_entries[i], - sizeof (GstClockTime) * (pad->ts_n_entries - i)); - pad->ts_entries[i] = ts; - pad->ts_n_entries++; -} - -/* takes ownership of @buf */ -static GstBuffer * -gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) -{ - const gint wrap = G_N_ELEMENTS (pad->buf_entries); - GstClockTime ts; - - /* store buffer and ts, latter ordered */ - if (buf) { - pad->buf_entries[pad->buf_tail++] = buf; - pad->buf_tail %= wrap; - gst_qt_mux_push_ts (qtmux, pad, GST_BUFFER_TIMESTAMP (buf)); - } - - if (pad->ts_n_entries && (!buf || pad->ts_n_entries >= QTMUX_NO_OF_TS)) { - ts = pad->ts_entries[--pad->ts_n_entries]; - buf = pad->buf_entries[pad->buf_head]; - pad->buf_entries[pad->buf_head++] = NULL; - pad->buf_head %= wrap; - buf = gst_buffer_make_metadata_writable (buf); - /* track original ts (= pts ?) for later */ - GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_TIMESTAMP (buf); - GST_BUFFER_TIMESTAMP (buf) = ts; - GST_DEBUG_OBJECT (qtmux, "next buffer uses reordered ts %" GST_TIME_FORMAT, - GST_TIME_ARGS (ts)); - } else { - buf = NULL; - } - - return buf; -} - -/* - * Here we push the buffer and update the tables in the track atoms - */ -static GstFlowReturn -gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf) -{ - GstBuffer *last_buf = NULL; - GstClockTime duration; - guint nsamples, sample_size; - guint64 chunk_offset; - gint64 last_dts, scaled_duration; - gint64 pts_offset = 0; - gboolean sync = FALSE, do_pts = FALSE; - gboolean drain = (buf == NULL); - GstFlowReturn ret; - - if (!pad->fourcc) - goto not_negotiated; - - /* if this pad has a prepare function, call it */ - if (pad->prepare_buf_func != NULL) { - buf = pad->prepare_buf_func (pad, buf, qtmux); - } - -again: - if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) { - buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf); - if (!buf) { - GST_DEBUG_OBJECT (qtmux, "no reordered buffer yet"); - return GST_FLOW_OK; - } - } - - last_buf = pad->last_buf; - if (last_buf == NULL) { -#ifndef GST_DISABLE_GST_DEBUG - if (buf == NULL) { - GST_DEBUG_OBJECT (qtmux, "Pad %s has no previous buffer stored and " - "received NULL buffer, doing nothing", - GST_PAD_NAME (pad->collect.pad)); - } else { - GST_LOG_OBJECT (qtmux, - "Pad %s has no previous buffer stored, storing now", - GST_PAD_NAME (pad->collect.pad)); - } -#endif - pad->last_buf = buf; - return GST_FLOW_OK; - } else - gst_buffer_ref (last_buf); - - /* nasty heuristic mess to guestimate dealing with DTS/PTS, - * while also trying to stay close to input ts to preserve sync, - * so in DTS_METHOD_DD: - * - prefer using input ts where possible - * - if those detected out-of-order (*), mark as out-of-order - * - if in out-of-order, then - * - if duration available, use that as delta - * Also mind to preserve sync between streams, and adding - * durations might drift, so try to resync when we expect - * input ts == (sum of durations), which is at some keyframe input frame. - * - if no duration available, we are actually in serious trouble and need - * to hack around that, so we fail. - * To remedy failure, alternatively, in DTS_METHOD_REORDER: - * - collect some buffers and re-order timestamp, - * then process the oldest buffer with smallest timestamps. - * This should typically compensate for some codec's handywork with ts. - * ... but in case this makes ts end up where not expected, in DTS_METHOD_ASC: - * - keep each ts with its buffer and still keep a list of most recent X ts, - * use the (ascending) minimum of those as DTS (and the difference as ts delta), - * and use this DTS as a basis to obtain a (positive) CTS offset. - * This should yield exact PTS == buffer ts, but it seems not all players - * out there are aware of ctts pts ... - * - * 0.11 Phew, can we (pretty) please please sort out DTS/PTS on buffers ... - */ - if (G_LIKELY (buf) && !pad->is_out_of_order) { - if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (last_buf) && - GST_BUFFER_TIMESTAMP_IS_VALID (buf))) { - if ((GST_BUFFER_TIMESTAMP (buf) < GST_BUFFER_TIMESTAMP (last_buf))) { - GST_DEBUG_OBJECT (qtmux, "detected out-of-order input"); - pad->is_out_of_order = TRUE; - } - } else { - /* this is pretty bad */ - GST_WARNING_OBJECT (qtmux, "missing input timestamp"); - /* fall back to durations */ - pad->is_out_of_order = TRUE; - } - } - - /* would have to be some unusual input, but not impossible */ - if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER && - pad->is_out_of_order)) { - goto no_order; - } - - /* fall back to duration if last buffer or - * out-of-order (determined previously), otherwise use input ts */ - if (buf == NULL || - (pad->is_out_of_order && qtmux->dts_method == DTS_METHOD_DD)) { - if (!GST_BUFFER_DURATION_IS_VALID (last_buf)) { - /* be forgiving for some possibly last upstream flushed buffer */ - if (buf) - goto no_time; - GST_WARNING_OBJECT (qtmux, "no duration for last buffer"); - /* iso spec recommends some small value, try 0 */ - duration = 0; - } else { - duration = GST_BUFFER_DURATION (last_buf); - /* avoid drift in sum timestamps, - * so use input timestamp for suitable keyframe */ - if (buf && !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) && - GST_BUFFER_TIMESTAMP (buf) >= pad->last_dts) { - GST_DEBUG_OBJECT (qtmux, "resyncing out-of-order input to ts; " - "replacing %" GST_TIME_FORMAT " by %" GST_TIME_FORMAT, - GST_TIME_ARGS (pad->last_dts + duration), - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); - duration = GST_BUFFER_TIMESTAMP (buf) - pad->last_dts; - } - } - } else if (qtmux->dts_method != DTS_METHOD_ASC) { - duration = GST_BUFFER_TIMESTAMP (buf) - GST_BUFFER_TIMESTAMP (last_buf); - } else { - GstClockTime ts; - - g_assert (qtmux->dts_method == DTS_METHOD_ASC); - if (!qtmux->guess_pts) - goto need_pts; - - /* add timestamp to queue; keeps in descending order */ - gst_qt_mux_push_ts (qtmux, pad, GST_BUFFER_TIMESTAMP (last_buf)); - /* chuck out smallest/last one if we have enough */ - if (G_LIKELY (pad->ts_n_entries > QTMUX_NO_OF_TS)) - pad->ts_n_entries--; - /* peek the now smallest timestamp */ - ts = pad->ts_entries[pad->ts_n_entries - 1]; - /* these tails are expected to be (strictly) ascending with - * large enough history */ - GST_DEBUG_OBJECT (qtmux, "ASC method; base timestamp %" GST_TIME_FORMAT, - GST_TIME_ARGS (ts)); - if (ts >= pad->last_dts) { - duration = ts - pad->last_dts; - } else { - /* fallback to previous value, negative ct offset might handle */ - GST_WARNING_OBJECT (qtmux, "unexpected decrease in timestamp"); - duration = 0; - } - /* arrange for small non-zero duration/delta << expected frame time */ - ts = gst_util_uint64_scale (10, GST_SECOND, - atom_trak_get_timescale (pad->trak)); - duration = MAX (duration, ts); - } - - gst_buffer_replace (&pad->last_buf, buf); - - last_dts = gst_util_uint64_scale_round (pad->last_dts, - atom_trak_get_timescale (pad->trak), GST_SECOND); - - /* fragments only deal with 1 buffer == 1 chunk (== 1 sample) */ - if (pad->sample_size && !qtmux->fragment_sequence) { - /* Constant size packets: usually raw audio (with many samples per - buffer (= chunk)), but can also be fixed-packet-size codecs like ADPCM - */ - sample_size = pad->sample_size; - if (GST_BUFFER_SIZE (last_buf) % sample_size != 0) - goto fragmented_sample; - /* note: qt raw audio storage warps it implicitly into a timewise - * perfect stream, discarding buffer times */ - if (GST_BUFFER_DURATION (last_buf) != GST_CLOCK_TIME_NONE) { - nsamples = gst_util_uint64_scale_round (GST_BUFFER_DURATION (last_buf), - atom_trak_get_timescale (pad->trak), GST_SECOND); - } else { - nsamples = GST_BUFFER_SIZE (last_buf) / sample_size; - } - duration = GST_BUFFER_DURATION (last_buf) / nsamples; - - /* timescale = samplerate */ - scaled_duration = 1; - pad->last_dts += duration * nsamples; - } else { - nsamples = 1; - sample_size = GST_BUFFER_SIZE (last_buf); - if (pad->have_dts) { - gint64 scaled_dts; - pad->last_dts = GST_BUFFER_OFFSET_END (last_buf); - if ((gint64) (pad->last_dts) < 0) { - scaled_dts = -gst_util_uint64_scale_round (-pad->last_dts, - atom_trak_get_timescale (pad->trak), GST_SECOND); - } else { - scaled_dts = gst_util_uint64_scale_round (pad->last_dts, - atom_trak_get_timescale (pad->trak), GST_SECOND); - } - scaled_duration = scaled_dts - last_dts; - last_dts = scaled_dts; - } else { - /* first convert intended timestamp (in GstClockTime resolution) to - * trak timescale, then derive delta; - * this ensures sums of (scale)delta add up to converted timestamp, - * which only deviates at most 1/scale from timestamp itself */ - scaled_duration = gst_util_uint64_scale_round (pad->last_dts + duration, - atom_trak_get_timescale (pad->trak), GST_SECOND) - last_dts; - pad->last_dts += duration; - } - } - chunk_offset = qtmux->mdat_size; - - GST_LOG_OBJECT (qtmux, - "Pad (%s) dts updated to %" GST_TIME_FORMAT, - GST_PAD_NAME (pad->collect.pad), GST_TIME_ARGS (pad->last_dts)); - GST_LOG_OBJECT (qtmux, - "Adding %d samples to track, duration: %" G_GUINT64_FORMAT - " size: %" G_GUINT32_FORMAT " chunk offset: %" G_GUINT64_FORMAT, - nsamples, scaled_duration, sample_size, chunk_offset); - - /* might be a sync sample */ - if (pad->sync && - !GST_BUFFER_FLAG_IS_SET (last_buf, GST_BUFFER_FLAG_DELTA_UNIT)) { - GST_LOG_OBJECT (qtmux, "Adding new sync sample entry for track of pad %s", - GST_PAD_NAME (pad->collect.pad)); - sync = TRUE; - } - - /* optionally calculate ctts entry values - * (if composition-time expected different from decoding-time) */ - /* really not recommended: - * - decoder typically takes care of dts/pts issues - * - in case of out-of-order, dts may only be determined as above - * (e.g. sum of duration), which may be totally different from - * buffer timestamps in case of multiple segment, non-perfect streams - * (and just perhaps maybe with some luck segment_to_running_time - * or segment_to_media_time might get near to it) */ - if ((pad->have_dts || qtmux->guess_pts)) { - guint64 pts; - - pts = qtmux->dts_method == DTS_METHOD_REORDER ? - GST_BUFFER_OFFSET_END (last_buf) : GST_BUFFER_TIMESTAMP (last_buf); - pts = gst_util_uint64_scale_round (pts, - atom_trak_get_timescale (pad->trak), GST_SECOND); - pts_offset = (gint64) (pts - last_dts); - do_pts = TRUE; - GST_LOG_OBJECT (qtmux, "Adding ctts entry for pad %s: %" G_GINT64_FORMAT, - GST_PAD_NAME (pad->collect.pad), pts_offset); - } - - /* - * Each buffer starts a new chunk, so we can assume the buffer - * duration is the chunk duration - */ - if (GST_CLOCK_TIME_IS_VALID (duration) && (duration > qtmux->longest_chunk || - !GST_CLOCK_TIME_IS_VALID (qtmux->longest_chunk))) { - GST_DEBUG_OBJECT (qtmux, "New longest chunk found: %" GST_TIME_FORMAT - ", pad %s", GST_TIME_ARGS (duration), GST_PAD_NAME (pad->collect.pad)); - qtmux->longest_chunk = duration; - } - - /* if this is the first buffer, store the timestamp */ - if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) { - if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) { - pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf); - } else { - GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, " - "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad)); - pad->first_ts = 0; - } - GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %" - GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad), - GST_TIME_ARGS (pad->first_ts)); - } - - /* now we go and register this buffer/sample all over */ - /* note that a new chunk is started each time (not fancy but works) */ - if (qtmux->moov_recov_file) { - if (!atoms_recov_write_trak_samples (qtmux->moov_recov_file, pad->trak, - nsamples, (gint32) scaled_duration, sample_size, chunk_offset, sync, - do_pts, pts_offset)) { - GST_WARNING_OBJECT (qtmux, "Failed to write sample information to " - "recovery file, disabling recovery"); - fclose (qtmux->moov_recov_file); - qtmux->moov_recov_file = NULL; - } - } - - if (buf) - gst_buffer_unref (buf); - - if (qtmux->fragment_sequence) { - /* ensure that always sync samples are marked as such */ - ret = gst_qt_mux_pad_fragment_add_buffer (qtmux, pad, last_buf, - buf == NULL, nsamples, last_dts, (gint32) scaled_duration, sample_size, - !pad->sync || sync, pts_offset); - } else { - atom_trak_add_samples (pad->trak, nsamples, (gint32) scaled_duration, - sample_size, chunk_offset, sync, pts_offset); - ret = gst_qt_mux_send_buffer (qtmux, last_buf, &qtmux->mdat_size, TRUE); - } - - if (G_UNLIKELY (drain && qtmux->dts_method == DTS_METHOD_REORDER && - ret == GST_FLOW_OK)) { - buf = NULL; - goto again; - } - - return ret; - - /* ERRORS */ -bail: - { - if (buf) - gst_buffer_unref (buf); - gst_buffer_unref (last_buf); - return GST_FLOW_ERROR; - } -no_time: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Received buffer without timestamp/duration. " - "Using e.g. dts-method=reorder might help.")); - goto bail; - } -no_order: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("DTS method failed to re-order timestamps.")); - goto bail; - } -need_pts: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Selected DTS method also needs PTS enabled.")); - goto bail; - } -fragmented_sample: - { - GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), - ("Audio buffer contains fragmented sample.")); - goto bail; - } -not_negotiated: - { - GST_ELEMENT_ERROR (qtmux, CORE, NEGOTIATION, (NULL), - ("format wasn't negotiated before buffer flow on pad %s", - GST_PAD_NAME (pad->collect.pad))); - if (buf) - gst_buffer_unref (buf); - return GST_FLOW_NOT_NEGOTIATED; - } -} - -static GstFlowReturn -gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data) -{ - GstFlowReturn ret = GST_FLOW_OK; - GstQTMux *qtmux = GST_QT_MUX_CAST (user_data); - GSList *walk; - GstQTPad *best_pad = NULL; - GstClockTime time, best_time = GST_CLOCK_TIME_NONE; - GstBuffer *buf; - - if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_STARTED)) { - if ((ret = gst_qt_mux_start_file (qtmux)) != GST_FLOW_OK) - return ret; - else - qtmux->state = GST_QT_MUX_STATE_DATA; - } - - if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_EOS)) - return GST_FLOW_UNEXPECTED; - - /* select the best buffer */ - walk = qtmux->collect->data; - while (walk) { - GstQTPad *pad; - GstCollectData *data; - - data = (GstCollectData *) walk->data; - pad = (GstQTPad *) data; - - walk = g_slist_next (walk); - - buf = gst_collect_pads_peek (pads, data); - if (buf == NULL) { - GST_LOG_OBJECT (qtmux, "Pad %s has no buffers", - GST_PAD_NAME (pad->collect.pad)); - continue; - } - time = GST_BUFFER_TIMESTAMP (buf); - gst_buffer_unref (buf); - - /* invalid should pass */ - if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) { - time = - gst_segment_to_running_time (&data->segment, GST_FORMAT_TIME, time); - if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) { - GST_DEBUG_OBJECT (qtmux, "clipping buffer on pad %s outside segment", - GST_PAD_NAME (data->pad)); - buf = gst_collect_pads_pop (pads, data); - gst_buffer_unref (buf); - return GST_FLOW_OK; - } - } - - if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) || - (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) { - best_pad = pad; - best_time = time; - } - } - - if (best_pad != NULL) { - GST_LOG_OBJECT (qtmux, "selected pad %s with time %" GST_TIME_FORMAT, - GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time)); - buf = gst_collect_pads_pop (pads, &best_pad->collect); - buf = gst_buffer_make_metadata_writable (buf); - GST_BUFFER_TIMESTAMP (buf) = best_time; - ret = gst_qt_mux_add_buffer (qtmux, best_pad, buf); - } else { - ret = gst_qt_mux_stop_file (qtmux); - if (ret == GST_FLOW_OK) { - GST_DEBUG_OBJECT (qtmux, "Pushing eos"); - gst_pad_push_event (qtmux->srcpad, gst_event_new_eos ()); - ret = GST_FLOW_UNEXPECTED; - } else { - GST_WARNING_OBJECT (qtmux, "Failed to stop file: %s", - gst_flow_get_name (ret)); - } - qtmux->state = GST_QT_MUX_STATE_EOS; - } - - return ret; -} - -static gboolean -check_field (GQuark field_id, const GValue * value, gpointer user_data) -{ - GstStructure *structure = (GstStructure *) user_data; - const GValue *other = gst_structure_id_get_value (structure, field_id); - if (other == NULL) - return FALSE; - return gst_value_compare (value, other) == GST_VALUE_EQUAL; -} - -static gboolean -gst_qtmux_caps_is_subset_full (GstQTMux * qtmux, GstCaps * subset, - GstCaps * superset) -{ - GstStructure *sub_s = gst_caps_get_structure (subset, 0); - GstStructure *sup_s = gst_caps_get_structure (superset, 0); - - return gst_structure_foreach (sub_s, check_field, sup_s); -} - -static gboolean -gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps) -{ - GstQTMux *qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad)); - GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); - GstQTPad *qtpad = NULL; - GstStructure *structure; - const gchar *mimetype; - gint rate, channels; - const GValue *value = NULL; - const GstBuffer *codec_data = NULL; - GstQTMuxFormat format; - AudioSampleEntry entry = { 0, }; - AtomInfo *ext_atom = NULL; - gint constant_size = 0; - const gchar *stream_format; - GstCaps *current_caps = NULL; - - /* find stream data */ - qtpad = (GstQTPad *) gst_pad_get_element_private (pad); - g_assert (qtpad); - - qtpad->prepare_buf_func = NULL; - - /* does not go well to renegotiate stream mid-way, unless - * the old caps are a subset of the new one (this means upstream - * added more info to the caps, as both should be 'fixed' caps) */ - if (qtpad->fourcc) { - g_object_get (pad, "caps", ¤t_caps, NULL); - g_assert (caps != NULL); - - if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) { - goto refuse_renegotiation; - } - GST_DEBUG_OBJECT (qtmux, - "pad %s accepted renegotiation to %" GST_PTR_FORMAT " from %" - GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad)); - } - - GST_DEBUG_OBJECT (qtmux, "%s:%s, caps=%" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (pad), caps); - - format = qtmux_klass->format; - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - /* common info */ - if (!gst_structure_get_int (structure, "channels", &channels) || - !gst_structure_get_int (structure, "rate", &rate)) { - goto refuse_caps; - } - - /* optional */ - value = gst_structure_get_value (structure, "codec_data"); - if (value != NULL) - codec_data = gst_value_get_buffer (value); - - qtpad->is_out_of_order = FALSE; - qtpad->have_dts = FALSE; - - /* set common properties */ - entry.sample_rate = rate; - entry.channels = channels; - /* default */ - entry.sample_size = 16; - /* this is the typical compressed case */ - if (format == GST_QT_MUX_FORMAT_QT) { - entry.version = 1; - entry.compression_id = -2; - } - - /* now map onto a fourcc, and some extra properties */ - if (strcmp (mimetype, "audio/mpeg") == 0) { - gint mpegversion = 0; - gint layer = -1; - - gst_structure_get_int (structure, "mpegversion", &mpegversion); - switch (mpegversion) { - case 1: - gst_structure_get_int (structure, "layer", &layer); - switch (layer) { - case 3: - /* mp3 */ - /* note: QuickTime player does not like mp3 either way in iso/mp4 */ - if (format == GST_QT_MUX_FORMAT_QT) - entry.fourcc = FOURCC__mp3; - else { - entry.fourcc = FOURCC_mp4a; - ext_atom = - build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG1_P3, - ESDS_STREAM_TYPE_AUDIO, codec_data, qtpad->avg_bitrate, - qtpad->max_bitrate); - } - entry.samples_per_packet = 1152; - entry.bytes_per_sample = 2; - break; - } - break; - case 4: - - /* check stream-format */ - stream_format = gst_structure_get_string (structure, "stream-format"); - if (stream_format) { - if (strcmp (stream_format, "raw") != 0) { - GST_WARNING_OBJECT (qtmux, "Unsupported AAC stream-format %s, " - "please use 'raw'", stream_format); - goto refuse_caps; - } - } else { - GST_WARNING_OBJECT (qtmux, "No stream-format present in caps, " - "assuming 'raw'"); - } - - if (!codec_data || GST_BUFFER_SIZE (codec_data) < 2) - GST_WARNING_OBJECT (qtmux, "no (valid) codec_data for AAC audio"); - else { - guint8 profile = GST_READ_UINT8 (GST_BUFFER_DATA (codec_data)); - - /* warn if not Low Complexity profile */ - profile >>= 3; - if (profile != 2) - GST_WARNING_OBJECT (qtmux, - "non-LC AAC may not run well on (Apple) QuickTime/iTunes"); - } - - /* AAC */ - entry.fourcc = FOURCC_mp4a; - - if (format == GST_QT_MUX_FORMAT_QT) - ext_atom = build_mov_aac_extension (qtpad->trak, codec_data, - qtpad->avg_bitrate, qtpad->max_bitrate); - else - ext_atom = - build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG4_P3, - ESDS_STREAM_TYPE_AUDIO, codec_data, qtpad->avg_bitrate, - qtpad->max_bitrate); - break; - default: - break; - } - } else if (strcmp (mimetype, "audio/AMR") == 0) { - entry.fourcc = FOURCC_samr; - entry.sample_size = 16; - entry.samples_per_packet = 160; - entry.bytes_per_sample = 2; - ext_atom = build_amr_extension (); - } else if (strcmp (mimetype, "audio/AMR-WB") == 0) { - entry.fourcc = FOURCC_sawb; - entry.sample_size = 16; - entry.samples_per_packet = 320; - entry.bytes_per_sample = 2; - ext_atom = build_amr_extension (); - } else if (strcmp (mimetype, "audio/x-raw-int") == 0) { - gint width; - gint depth; - gint endianness; - gboolean sign; - - if (!gst_structure_get_int (structure, "width", &width) || - !gst_structure_get_int (structure, "depth", &depth) || - !gst_structure_get_boolean (structure, "signed", &sign)) { - GST_DEBUG_OBJECT (qtmux, "broken caps, width/depth/signed field missing"); - goto refuse_caps; - } - - if (depth <= 8) { - endianness = G_BYTE_ORDER; - } else if (!gst_structure_get_int (structure, "endianness", &endianness)) { - GST_DEBUG_OBJECT (qtmux, "broken caps, endianness field missing"); - goto refuse_caps; - } - - /* spec has no place for a distinction in these */ - if (width != depth) { - GST_DEBUG_OBJECT (qtmux, "width must be same as depth!"); - goto refuse_caps; - } - - if (sign) { - if (endianness == G_LITTLE_ENDIAN) - entry.fourcc = FOURCC_sowt; - else if (endianness == G_BIG_ENDIAN) - entry.fourcc = FOURCC_twos; - /* maximum backward compatibility; only new version for > 16 bit */ - if (depth <= 16) - entry.version = 0; - /* not compressed in any case */ - entry.compression_id = 0; - /* QT spec says: max at 16 bit even if sample size were actually larger, - * however, most players (e.g. QuickTime!) seem to disagree, so ... */ - entry.sample_size = depth; - entry.bytes_per_sample = depth / 8; - entry.samples_per_packet = 1; - entry.bytes_per_packet = depth / 8; - entry.bytes_per_frame = entry.bytes_per_packet * channels; - } else { - if (width == 8 && depth == 8) { - /* fall back to old 8-bit version */ - entry.fourcc = FOURCC_raw_; - entry.version = 0; - entry.compression_id = 0; - entry.sample_size = 8; - } else { - GST_DEBUG_OBJECT (qtmux, "non 8-bit PCM must be signed"); - goto refuse_caps; - } - } - constant_size = (depth / 8) * channels; - } else if (strcmp (mimetype, "audio/x-alaw") == 0) { - entry.fourcc = FOURCC_alaw; - entry.samples_per_packet = 1023; - entry.bytes_per_sample = 2; - } else if (strcmp (mimetype, "audio/x-mulaw") == 0) { - entry.fourcc = FOURCC_ulaw; - entry.samples_per_packet = 1023; - entry.bytes_per_sample = 2; - } else if (strcmp (mimetype, "audio/x-adpcm") == 0) { - gint blocksize; - if (!gst_structure_get_int (structure, "block_align", &blocksize)) { - GST_DEBUG_OBJECT (qtmux, "broken caps, block_align missing"); - goto refuse_caps; - } - /* Currently only supports WAV-style IMA ADPCM, for which the codec id is - 0x11 */ - entry.fourcc = MS_WAVE_FOURCC (0x11); - /* 4 byte header per channel (including one sample). 2 samples per byte - remaining. Simplifying gives the following (samples per block per - channel) */ - entry.samples_per_packet = 2 * blocksize / channels - 7; - entry.bytes_per_sample = 2; - - entry.bytes_per_frame = blocksize; - entry.bytes_per_packet = blocksize / channels; - /* ADPCM has constant size packets */ - constant_size = 1; - /* TODO: I don't really understand why this helps, but it does! Constant - * size and compression_id of -2 seem to be incompatible, and other files - * in the wild use this too. */ - entry.compression_id = -1; - - ext_atom = build_ima_adpcm_extension (channels, rate, blocksize); - } else if (strcmp (mimetype, "audio/x-alac") == 0) { - GstBuffer *codec_config; - gint len; - - entry.fourcc = FOURCC_alac; - /* let's check if codec data already comes with 'alac' atom prefix */ - if (!codec_data || (len = GST_BUFFER_SIZE (codec_data)) < 28) { - GST_DEBUG_OBJECT (qtmux, "broken caps, codec data missing"); - goto refuse_caps; - } - if (GST_READ_UINT32_LE (GST_BUFFER_DATA (codec_data) + 4) == FOURCC_alac) { - len -= 8; - codec_config = gst_buffer_create_sub ((GstBuffer *) codec_data, 8, len); - } else { - codec_config = gst_buffer_ref ((GstBuffer *) codec_data); - } - if (len != 28) { - /* does not look good, but perhaps some trailing unneeded stuff */ - GST_WARNING_OBJECT (qtmux, "unexpected codec-data size, possibly broken"); - } - if (format == GST_QT_MUX_FORMAT_QT) - ext_atom = build_mov_alac_extension (qtpad->trak, codec_config); - else - ext_atom = build_codec_data_extension (FOURCC_alac, codec_config); - /* set some more info */ - entry.bytes_per_sample = 2; - entry.samples_per_packet = - GST_READ_UINT32_BE (GST_BUFFER_DATA (codec_config) + 4); - gst_buffer_unref (codec_config); - } - - if (!entry.fourcc) - goto refuse_caps; - - /* ok, set the pad info accordingly */ - qtpad->fourcc = entry.fourcc; - qtpad->sample_size = constant_size; - atom_trak_set_audio_type (qtpad->trak, qtmux->context, &entry, - qtmux->trak_timescale ? qtmux->trak_timescale : entry.sample_rate, - ext_atom, constant_size); - - gst_object_unref (qtmux); - return TRUE; - - /* ERRORS */ -refuse_caps: - { - GST_WARNING_OBJECT (qtmux, "pad %s refused caps %" GST_PTR_FORMAT, - GST_PAD_NAME (pad), caps); - gst_object_unref (qtmux); - return FALSE; - } -refuse_renegotiation: - { - GST_WARNING_OBJECT (qtmux, - "pad %s refused renegotiation to %" GST_PTR_FORMAT, - GST_PAD_NAME (pad), caps); - gst_object_unref (qtmux); - return FALSE; - } -} - -/* scale rate up or down by factor of 10 to fit into [1000,10000] interval */ -static guint32 -adjust_rate (guint64 rate) -{ - if (rate == 0) - return 10000; - - while (rate >= 10000) - rate /= 10; - - while (rate < 1000) - rate *= 10; - - return (guint32) rate; -} - -static gboolean -gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps) -{ - GstQTMux *qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad)); - GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux)); - GstQTPad *qtpad = NULL; - GstStructure *structure; - const gchar *mimetype; - gint width, height, depth = -1; - gint framerate_num, framerate_den; - guint32 rate; - const GValue *value = NULL; - const GstBuffer *codec_data = NULL; - VisualSampleEntry entry = { 0, }; - GstQTMuxFormat format; - AtomInfo *ext_atom = NULL; - GList *ext_atom_list = NULL; - gboolean sync = FALSE; - int par_num, par_den; - GstCaps *current_caps = NULL; - - /* find stream data */ - qtpad = (GstQTPad *) gst_pad_get_element_private (pad); - g_assert (qtpad); - - qtpad->prepare_buf_func = NULL; - - /* does not go well to renegotiate stream mid-way, unless - * the old caps are a subset of the new one (this means upstream - * added more info to the caps, as both should be 'fixed' caps) */ - if (qtpad->fourcc) { - g_object_get (pad, "caps", ¤t_caps, NULL); - g_assert (caps != NULL); - - if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) { - goto refuse_renegotiation; - } - GST_DEBUG_OBJECT (qtmux, - "pad %s accepted renegotiation to %" GST_PTR_FORMAT " from %" - GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad)); - } - - GST_DEBUG_OBJECT (qtmux, "%s:%s, caps=%" GST_PTR_FORMAT, - GST_DEBUG_PAD_NAME (pad), caps); - - format = qtmux_klass->format; - structure = gst_caps_get_structure (caps, 0); - mimetype = gst_structure_get_name (structure); - - /* required parts */ - if (!gst_structure_get_int (structure, "width", &width) || - !gst_structure_get_int (structure, "height", &height)) - goto refuse_caps; - - /* optional */ - depth = -1; - /* works as a default timebase */ - framerate_num = 10000; - framerate_den = 1; - gst_structure_get_fraction (structure, "framerate", &framerate_num, - &framerate_den); - gst_structure_get_int (structure, "depth", &depth); - value = gst_structure_get_value (structure, "codec_data"); - if (value != NULL) - codec_data = gst_value_get_buffer (value); - - par_num = 1; - par_den = 1; - gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_num, - &par_den); - - qtpad->is_out_of_order = FALSE; - - /* bring frame numerator into a range that ensures both reasonable resolution - * as well as a fair duration */ - rate = qtmux->trak_timescale ? - qtmux->trak_timescale : adjust_rate (framerate_num); - GST_DEBUG_OBJECT (qtmux, "Rate of video track selected: %" G_GUINT32_FORMAT, - rate); - - /* set common properties */ - entry.width = width; - entry.height = height; - entry.par_n = par_num; - entry.par_d = par_den; - /* should be OK according to qt and iso spec, override if really needed */ - entry.color_table_id = -1; - entry.frame_count = 1; - entry.depth = 24; - - /* sync entries by default */ - sync = TRUE; - - /* now map onto a fourcc, and some extra properties */ - if (strcmp (mimetype, "video/x-raw-rgb") == 0) { - gint bpp; - - entry.fourcc = FOURCC_raw_; - gst_structure_get_int (structure, "bpp", &bpp); - entry.depth = bpp; - sync = FALSE; - } else if (strcmp (mimetype, "video/x-raw-yuv") == 0) { - guint32 format = 0; - - sync = FALSE; - gst_structure_get_fourcc (structure, "format", &format); - switch (format) { - case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'): - if (depth == -1) - depth = 24; - entry.fourcc = FOURCC_2vuy; - entry.depth = depth; - break; - } - } else if (strcmp (mimetype, "video/x-h263") == 0) { - ext_atom = NULL; - if (format == GST_QT_MUX_FORMAT_QT) - entry.fourcc = FOURCC_h263; - else - entry.fourcc = FOURCC_s263; - ext_atom = build_h263_extension (); - if (ext_atom != NULL) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - } else if (strcmp (mimetype, "video/x-divx") == 0 || - strcmp (mimetype, "video/mpeg") == 0) { - gint version = 0; - - if (strcmp (mimetype, "video/x-divx") == 0) { - gst_structure_get_int (structure, "divxversion", &version); - version = version == 5 ? 1 : 0; - } else { - gst_structure_get_int (structure, "mpegversion", &version); - version = version == 4 ? 1 : 0; - } - if (version) { - entry.fourcc = FOURCC_mp4v; - ext_atom = - build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG4_P2, - ESDS_STREAM_TYPE_VISUAL, codec_data, qtpad->avg_bitrate, - qtpad->max_bitrate); - if (ext_atom != NULL) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - if (!codec_data) - GST_WARNING_OBJECT (qtmux, "no codec_data for MPEG4 video; " - "output might not play in Apple QuickTime (try global-headers?)"); - } - } else if (strcmp (mimetype, "video/x-h264") == 0) { - entry.fourcc = FOURCC_avc1; - if (qtpad->avg_bitrate == 0) { - gint avg_bitrate = 0; - gst_structure_get_int (structure, "bitrate", &avg_bitrate); - qtpad->avg_bitrate = avg_bitrate; - } - ext_atom = build_btrt_extension (0, qtpad->avg_bitrate, qtpad->max_bitrate); - if (ext_atom != NULL) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - if (!codec_data) - GST_WARNING_OBJECT (qtmux, "no codec_data in h264 caps"); - ext_atom = build_codec_data_extension (FOURCC_avcC, codec_data); - if (ext_atom != NULL) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - } else if (strcmp (mimetype, "video/x-svq") == 0) { - gint version = 0; - const GstBuffer *seqh = NULL; - const GValue *seqh_value; - gdouble gamma = 0; - - gst_structure_get_int (structure, "svqversion", &version); - if (version == 3) { - entry.fourcc = FOURCC_SVQ3; - entry.version = 3; - entry.depth = 32; - - seqh_value = gst_structure_get_value (structure, "seqh"); - if (seqh_value) { - seqh = gst_value_get_buffer (seqh_value); - ext_atom = build_SMI_atom (seqh); - if (ext_atom) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - } - - /* we need to add the gamma anyway because quicktime might crash - * when it doesn't find it */ - if (!gst_structure_get_double (structure, "applied-gamma", &gamma)) { - /* it seems that using 0 here makes it ignored */ - gamma = 0.0; - } - ext_atom = build_gama_atom (gamma); - if (ext_atom) - ext_atom_list = g_list_prepend (ext_atom_list, ext_atom); - } else { - GST_WARNING_OBJECT (qtmux, "SVQ version %d not supported. Please file " - "a bug at http://bugzilla.gnome.org", version); - } - } else if (strcmp (mimetype, "video/x-dv") == 0) { - gint version = 0; - gboolean pal = TRUE; - - sync = FALSE; - if (framerate_num != 25 || framerate_den != 1) - pal = FALSE; - gst_structure_get_int (structure, "dvversion", &version); - /* fall back to typical one */ - if (!version) - version = 25; - switch (version) { - case 25: - if (pal) - entry.fourcc = GST_MAKE_FOURCC ('d', 'v', 'c', 'p'); - else - entry.fourcc = GST_MAKE_FOURCC ('d', 'v', 'c', ' '); - break; - case 50: - if (pal) - entry.fourcc = GST_MAKE_FOURCC ('d', 'v', '5', 'p'); - else - entry.fourcc = GST_MAKE_FOURCC ('d', 'v', '5', 'n'); - break; - default: - GST_WARNING_OBJECT (qtmux, "unrecognized dv version"); - break; - } - } else if (strcmp (mimetype, "image/jpeg") == 0) { - entry.fourcc = FOURCC_jpeg; - sync = FALSE; - } else if (strcmp (mimetype, "image/x-j2c") == 0 || - strcmp (mimetype, "image/x-jpc") == 0) { - guint32 fourcc; - const GValue *cmap_array; - const GValue *cdef_array; - gint ncomp = 0; - gint fields = 1; - - if (strcmp (mimetype, "image/x-jpc") == 0) { - qtpad->prepare_buf_func = gst_qt_mux_prepare_jpc_buffer; - } - - gst_structure_get_int (structure, "num-components", &ncomp); - gst_structure_get_int (structure, "fields", &fields); - cmap_array = gst_structure_get_value (structure, "component-map"); - cdef_array = gst_structure_get_value (structure, "channel-definitions"); - - ext_atom = NULL; - entry.fourcc = FOURCC_mjp2; - sync = FALSE; - if (gst_structure_get_fourcc (structure, "fourcc", &fourcc) && - (ext_atom = - build_jp2h_extension (qtpad->trak, width, height, fourcc, ncomp, - cmap_array, cdef_array)) != NULL) { - ext_atom_list = g_list_append (ext_atom_list, ext_atom); - - ext_atom = build_fiel_extension (fields); - if (ext_atom) - ext_atom_list = g_list_append (ext_atom_list, ext_atom); - - ext_atom = build_jp2x_extension (codec_data); - if (ext_atom) - ext_atom_list = g_list_append (ext_atom_list, ext_atom); - } else { - GST_DEBUG_OBJECT (qtmux, "missing or invalid fourcc in jp2 caps"); - goto refuse_caps; - } - } else if (strcmp (mimetype, "video/x-vp8") == 0) { - entry.fourcc = FOURCC_VP80; - sync = FALSE; - } else if (strcmp (mimetype, "video/x-qt-part") == 0) { - guint32 fourcc; - - gst_structure_get_fourcc (structure, "format", &fourcc); - entry.fourcc = fourcc; - qtpad->have_dts = TRUE; - } else if (strcmp (mimetype, "video/x-mp4-part") == 0) { - guint32 fourcc; - - gst_structure_get_fourcc (structure, "format", &fourcc); - entry.fourcc = fourcc; - qtpad->have_dts = TRUE; - } - - if (!entry.fourcc) - goto refuse_caps; - - /* ok, set the pad info accordingly */ - qtpad->fourcc = entry.fourcc; - qtpad->sync = sync; - atom_trak_set_video_type (qtpad->trak, qtmux->context, &entry, rate, - ext_atom_list); - - gst_object_unref (qtmux); - return TRUE; - - /* ERRORS */ -refuse_caps: - { - GST_WARNING_OBJECT (qtmux, "pad %s refused caps %" GST_PTR_FORMAT, - GST_PAD_NAME (pad), caps); - gst_object_unref (qtmux); - return FALSE; - } -refuse_renegotiation: - { - GST_WARNING_OBJECT (qtmux, - "pad %s refused renegotiation to %" GST_PTR_FORMAT " from %" - GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad)); - gst_object_unref (qtmux); - return FALSE; - } -} - -static gboolean -gst_qt_mux_sink_event (GstPad * pad, GstEvent * event) -{ - gboolean ret; - GstQTMux *qtmux; - guint32 avg_bitrate = 0, max_bitrate = 0; - - qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad)); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_TAG:{ - GstTagList *list; - GstTagSetter *setter = GST_TAG_SETTER (qtmux); - GstTagMergeMode mode; - - GST_OBJECT_LOCK (qtmux); - mode = gst_tag_setter_get_tag_merge_mode (setter); - - GST_DEBUG_OBJECT (qtmux, "received tag event"); - gst_event_parse_tag (event, &list); - - gst_tag_setter_merge_tags (setter, list, mode); - GST_OBJECT_UNLOCK (qtmux); - - if (gst_tag_list_get_uint (list, GST_TAG_BITRATE, &avg_bitrate) | - gst_tag_list_get_uint (list, GST_TAG_MAXIMUM_BITRATE, &max_bitrate)) { - GstQTPad *qtpad = gst_pad_get_element_private (pad); - g_assert (qtpad); - - if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32) - qtpad->avg_bitrate = avg_bitrate; - if (max_bitrate > 0 && max_bitrate < G_MAXUINT32) - qtpad->max_bitrate = max_bitrate; - } - - break; - } - default: - break; - } - - ret = qtmux->collect_event (pad, event); - gst_object_unref (qtmux); - - return ret; -} - -static void -gst_qt_mux_release_pad (GstElement * element, GstPad * pad) -{ - GstQTMux *mux = GST_QT_MUX_CAST (element); - GSList *walk; - - GST_DEBUG_OBJECT (element, "Releasing %s:%s", GST_DEBUG_PAD_NAME (pad)); - - for (walk = mux->sinkpads; walk; walk = g_slist_next (walk)) { - GstQTPad *qtpad = (GstQTPad *) walk->data; - GST_DEBUG ("Checking %s:%s", GST_DEBUG_PAD_NAME (qtpad->collect.pad)); - if (qtpad->collect.pad == pad) { - /* this is it, remove */ - mux->sinkpads = g_slist_delete_link (mux->sinkpads, walk); - gst_element_remove_pad (element, pad); - break; - } - } - - gst_collect_pads_remove_pad (mux->collect, pad); -} - -static GstPad * -gst_qt_mux_request_new_pad (GstElement * element, - GstPadTemplate * templ, const gchar * req_name) -{ - GstElementClass *klass = GST_ELEMENT_GET_CLASS (element); - GstQTMux *qtmux = GST_QT_MUX_CAST (element); - GstQTPad *collect_pad; - GstPad *newpad; - gboolean audio; - gchar *name; - - if (templ->direction != GST_PAD_SINK) - goto wrong_direction; - - if (qtmux->state > GST_QT_MUX_STATE_STARTED) - goto too_late; - - if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) { - audio = TRUE; - name = g_strdup_printf ("audio_%02d", qtmux->audio_pads++); - } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) { - audio = FALSE; - name = g_strdup_printf ("video_%02d", qtmux->video_pads++); - } else - goto wrong_template; - - GST_DEBUG_OBJECT (qtmux, "Requested pad: %s", name); - - /* create pad and add to collections */ - newpad = gst_pad_new_from_template (templ, name); - g_free (name); - collect_pad = (GstQTPad *) - gst_collect_pads_add_pad_full (qtmux->collect, newpad, sizeof (GstQTPad), - (GstCollectDataDestroyNotify) (gst_qt_mux_pad_reset)); - /* set up pad */ - gst_qt_mux_pad_reset (collect_pad); - collect_pad->trak = atom_trak_new (qtmux->context); - atom_moov_add_trak (qtmux->moov, collect_pad->trak); - - qtmux->sinkpads = g_slist_append (qtmux->sinkpads, collect_pad); - - /* set up pad functions */ - if (audio) - gst_pad_set_setcaps_function (newpad, - GST_DEBUG_FUNCPTR (gst_qt_mux_audio_sink_set_caps)); - else - gst_pad_set_setcaps_function (newpad, - GST_DEBUG_FUNCPTR (gst_qt_mux_video_sink_set_caps)); - - /* FIXME: hacked way to override/extend the event function of - * GstCollectPads; because it sets its own event function giving the - * element no access to events. - */ - qtmux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad); - gst_pad_set_event_function (newpad, - GST_DEBUG_FUNCPTR (gst_qt_mux_sink_event)); - - gst_pad_set_active (newpad, TRUE); - gst_element_add_pad (element, newpad); - - return newpad; - - /* ERRORS */ -wrong_direction: - { - GST_WARNING_OBJECT (qtmux, "Request pad that is not a SINK pad."); - return NULL; - } -too_late: - { - GST_WARNING_OBJECT (qtmux, "Not providing request pad after stream start."); - return NULL; - } -wrong_template: - { - GST_WARNING_OBJECT (qtmux, "This is not our template!"); - return NULL; - } -} - -static void -gst_qt_mux_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstQTMux *qtmux = GST_QT_MUX_CAST (object); - - GST_OBJECT_LOCK (qtmux); - switch (prop_id) { - case PROP_MOVIE_TIMESCALE: - g_value_set_uint (value, qtmux->timescale); - break; - case PROP_TRAK_TIMESCALE: - g_value_set_uint (value, qtmux->trak_timescale); - break; - case PROP_DO_CTTS: - g_value_set_boolean (value, qtmux->guess_pts); - break; - case PROP_DTS_METHOD: - g_value_set_enum (value, qtmux->dts_method); - break; - case PROP_FAST_START: - g_value_set_boolean (value, qtmux->fast_start); - break; - case PROP_FAST_START_TEMP_FILE: - g_value_set_string (value, qtmux->fast_start_file_path); - break; - case PROP_MOOV_RECOV_FILE: - g_value_set_string (value, qtmux->moov_recov_file_path); - break; - case PROP_FRAGMENT_DURATION: - g_value_set_uint (value, qtmux->fragment_duration); - break; - case PROP_STREAMABLE: - g_value_set_boolean (value, qtmux->streamable); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (qtmux); -} - -static void -gst_qt_mux_generate_fast_start_file_path (GstQTMux * qtmux) -{ - gchar *tmp; - - g_free (qtmux->fast_start_file_path); - qtmux->fast_start_file_path = NULL; - - tmp = g_strdup_printf ("%s%d", "qtmux", g_random_int ()); - qtmux->fast_start_file_path = g_build_filename (g_get_tmp_dir (), tmp, NULL); - g_free (tmp); -} - -static void -gst_qt_mux_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstQTMux *qtmux = GST_QT_MUX_CAST (object); - - GST_OBJECT_LOCK (qtmux); - switch (prop_id) { - case PROP_MOVIE_TIMESCALE: - qtmux->timescale = g_value_get_uint (value); - break; - case PROP_TRAK_TIMESCALE: - qtmux->trak_timescale = g_value_get_uint (value); - break; - case PROP_DO_CTTS: - qtmux->guess_pts = g_value_get_boolean (value); - break; - case PROP_DTS_METHOD: - qtmux->dts_method = g_value_get_enum (value); - break; - case PROP_FAST_START: - qtmux->fast_start = g_value_get_boolean (value); - break; - case PROP_FAST_START_TEMP_FILE: - g_free (qtmux->fast_start_file_path); - qtmux->fast_start_file_path = g_value_dup_string (value); - /* NULL means to generate a random one */ - if (!qtmux->fast_start_file_path) { - gst_qt_mux_generate_fast_start_file_path (qtmux); - } - break; - case PROP_MOOV_RECOV_FILE: - g_free (qtmux->moov_recov_file_path); - qtmux->moov_recov_file_path = g_value_dup_string (value); - break; - case PROP_FRAGMENT_DURATION: - qtmux->fragment_duration = g_value_get_uint (value); - break; - case PROP_STREAMABLE: - qtmux->streamable = g_value_get_boolean (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } - GST_OBJECT_UNLOCK (qtmux); -} - -static GstStateChangeReturn -gst_qt_mux_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret; - GstQTMux *qtmux = GST_QT_MUX_CAST (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - gst_collect_pads_start (qtmux->collect); - qtmux->state = GST_QT_MUX_STATE_STARTED; - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_collect_pads_stop (qtmux->collect); - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - gst_qt_mux_reset (qtmux, TRUE); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - -gboolean -gst_qt_mux_register (GstPlugin * plugin) -{ - GTypeInfo typeinfo = { - sizeof (GstQTMuxClass), - (GBaseInitFunc) gst_qt_mux_base_init, - NULL, - (GClassInitFunc) gst_qt_mux_class_init, - NULL, - NULL, - sizeof (GstQTMux), - 0, - (GInstanceInitFunc) gst_qt_mux_init, - }; - static const GInterfaceInfo tag_setter_info = { - NULL, NULL, NULL - }; - static const GInterfaceInfo tag_xmp_writer_info = { - NULL, NULL, NULL - }; - GType type; - GstQTMuxFormat format; - GstQTMuxClassParams *params; - guint i = 0; - - GST_DEBUG_CATEGORY_INIT (gst_qt_mux_debug, "qtmux", 0, "QT Muxer"); - - GST_LOG ("Registering muxers"); - - while (TRUE) { - GstQTMuxFormatProp *prop; - - prop = &gst_qt_mux_format_list[i]; - format = prop->format; - if (format == GST_QT_MUX_FORMAT_NONE) - break; - - /* create a cache for these properties */ - params = g_new0 (GstQTMuxClassParams, 1); - params->prop = prop; - params->src_caps = gst_static_caps_get (&prop->src_caps); - params->video_sink_caps = gst_static_caps_get (&prop->video_sink_caps); - params->audio_sink_caps = gst_static_caps_get (&prop->audio_sink_caps); - - /* create the type now */ - type = g_type_register_static (GST_TYPE_ELEMENT, prop->type_name, &typeinfo, - 0); - g_type_set_qdata (type, GST_QT_MUX_PARAMS_QDATA, (gpointer) params); - g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info); - g_type_add_interface_static (type, GST_TYPE_TAG_XMP_WRITER, - &tag_xmp_writer_info); - - if (!gst_element_register (plugin, prop->name, GST_RANK_PRIMARY, type)) - return FALSE; - - i++; - } - - GST_LOG ("Finished registering muxers"); - - /* FIXME: ideally classification tag should be added and - registered in gstreamer core gsttaglist - */ - - GST_LOG ("Registering tags"); - - gst_tag_register (GST_TAG_3GP_CLASSIFICATION, GST_TAG_FLAG_META, - G_TYPE_STRING, GST_TAG_3GP_CLASSIFICATION, "content classification", - gst_tag_merge_use_first); - - GST_LOG ("Finished registering tags"); - - return TRUE; -} diff --git a/gst/qtmux/gstqtmux.h b/gst/qtmux/gstqtmux.h deleted file mode 100644 index 3a2cb492fe..0000000000 --- a/gst/qtmux/gstqtmux.h +++ /dev/null @@ -1,228 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008-2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __GST_QT_MUX_H__ -#define __GST_QT_MUX_H__ - -#include -#include - -#include "fourcc.h" -#include "atoms.h" -#include "atomsrecovery.h" -#include "gstqtmuxmap.h" - -G_BEGIN_DECLS - -#define GST_TYPE_QT_MUX (gst_qt_mux_get_type()) -#define GST_QT_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QT_MUX, GstQTMux)) -#define GST_QT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QT_MUX, GstQTMux)) -#define GST_IS_QT_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QT_MUX)) -#define GST_IS_QT_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QT_MUX)) -#define GST_QT_MUX_CAST(obj) ((GstQTMux*)(obj)) - - -typedef struct _GstQTMux GstQTMux; -typedef struct _GstQTMuxClass GstQTMuxClass; -typedef struct _GstQTPad GstQTPad; - -/* - * GstQTPadPrepareBufferFunc - * - * Receives a buffer (takes ref) and returns a new buffer that should - * replace the passed one. - * - * Useful for when the pad/datatype needs some manipulation before - * being muxed. (Originally added for image/x-jpc support, for which buffers - * need to be wrapped into a isom box) - */ -typedef GstBuffer * (*GstQTPadPrepareBufferFunc) (GstQTPad * pad, - GstBuffer * buf, GstQTMux * qtmux); - -#define QTMUX_NO_OF_TS 10 - -struct _GstQTPad -{ - GstCollectData collect; /* we extend the CollectData */ - - /* fourcc id of stream */ - guint32 fourcc; - /* whether using format that have out of order buffers */ - gboolean is_out_of_order; - /* whether upstream provides valid PTS data */ - gboolean have_dts; - /* if not 0, track with constant sized samples, e.g. raw audio */ - guint sample_size; - /* make sync table entry */ - gboolean sync; - /* bitrates */ - guint32 avg_bitrate, max_bitrate; - - GstBuffer *last_buf; - /* dts of last_buf */ - GstClockTime last_dts; - - /* store the first timestamp for comparing with other streams and - * know if there are late streams */ - GstClockTime first_ts; - GstClockTime ts_entries[QTMUX_NO_OF_TS + 2]; - guint ts_n_entries; - GstBuffer *buf_entries[QTMUX_NO_OF_TS + 2]; - guint buf_head; - guint buf_tail; - - /* all the atom and chunk book-keeping is delegated here - * unowned/uncounted reference, parent MOOV owns */ - AtomTRAK *trak; - /* fragmented support */ - /* meta data book-keeping delegated here */ - AtomTRAF *traf; - /* fragment buffers */ - ATOM_ARRAY (GstBuffer *) fragment_buffers; - /* running fragment duration */ - gint64 fragment_duration; - /* optional fragment index book-keeping */ - AtomTFRA *tfra; - - /* if nothing is set, it won't be called */ - GstQTPadPrepareBufferFunc prepare_buf_func; -}; - -typedef enum _GstQTMuxState -{ - GST_QT_MUX_STATE_NONE, - GST_QT_MUX_STATE_STARTED, - GST_QT_MUX_STATE_DATA, - GST_QT_MUX_STATE_EOS -} GstQTMuxState; - -struct _GstQTMux -{ - GstElement element; - - GstPad *srcpad; - GstCollectPads *collect; - GSList *sinkpads; - - /* state */ - GstQTMuxState state; - - /* size of header (prefix, atoms (ftyp, mdat)) */ - guint64 header_size; - /* accumulated size of raw media data (a priori not including mdat header) */ - guint64 mdat_size; - /* position of mdat atom (for later updating) */ - guint64 mdat_pos; - - /* keep track of the largest chunk to fine-tune brands */ - GstClockTime longest_chunk; - - /* atom helper objects */ - AtomsContext *context; - AtomFTYP *ftyp; - AtomMOOV *moov; - GSList *extra_atoms; /* list of extra top-level atoms (e.g. UUID for xmp) - * Stored as AtomInfo structs */ - - /* fragmented file index */ - AtomMFRA *mfra; - - /* fast start */ - FILE *fast_start_file; - - /* moov recovery */ - FILE *moov_recov_file; - - /* fragment sequence */ - guint32 fragment_sequence; - - /* properties */ - guint32 timescale; - guint32 trak_timescale; - AtomsTreeFlavor flavor; - gboolean fast_start; - gboolean guess_pts; - gint dts_method; - gchar *fast_start_file_path; - gchar *moov_recov_file_path; - guint32 fragment_duration; - gboolean streamable; - - /* for collect pads event handling function */ - GstPadEventFunction collect_event; - - /* for request pad naming */ - guint video_pads, audio_pads; -}; - -struct _GstQTMuxClass -{ - GstElementClass parent_class; - - GstQTMuxFormat format; -}; - -/* type register helper struct */ -typedef struct _GstQTMuxClassParams -{ - GstQTMuxFormatProp *prop; - GstCaps *src_caps; - GstCaps *video_sink_caps; - GstCaps *audio_sink_caps; -} GstQTMuxClassParams; - -#define GST_QT_MUX_PARAMS_QDATA g_quark_from_static_string("qt-mux-params") - -GType gst_qt_mux_get_type (void); -gboolean gst_qt_mux_register (GstPlugin * plugin); - -/* FIXME: ideally classification tag should be added and - * registered in gstreamer core gsttaglist - * - * this tag is a string in the format: entityfourcc://table_num/content - * FIXME Shouldn't we add a field for 'language'? - */ -#define GST_TAG_3GP_CLASSIFICATION "classification" - -G_END_DECLS - -#endif /* __GST_QT_MUX_H__ */ diff --git a/gst/qtmux/gstqtmuxmap.c b/gst/qtmux/gstqtmuxmap.c deleted file mode 100644 index b8859b46ba..0000000000 --- a/gst/qtmux/gstqtmuxmap.c +++ /dev/null @@ -1,368 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * Copyright (C) 2008 Mark Nauwelaerts - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "gstqtmuxmap.h" -#include "fourcc.h" -#include "ftypcc.h" - -/* static info related to various format */ - -#define COMMON_VIDEO_CAPS \ - "width = (int) [ 16, 4096 ], " \ - "height = (int) [ 16, 4096 ], " \ - "framerate = (fraction) [ 0, MAX ]" - -#define COMMON_VIDEO_CAPS_NO_FRAMERATE \ - "width = (int) [ 16, 4096 ], " \ - "height = (int) [ 16, 4096 ] " - -#define H263_CAPS \ - "video/x-h263, " \ - COMMON_VIDEO_CAPS - -#define H264_CAPS \ - "video/x-h264, " \ - "stream-format = (string) avc, " \ - COMMON_VIDEO_CAPS - -#define MPEG4V_CAPS \ - "video/mpeg, " \ - "mpegversion = (int) 4, "\ - "systemstream = (boolean) false, " \ - COMMON_VIDEO_CAPS "; " \ - "video/x-divx, " \ - "divxversion = (int) 5, "\ - COMMON_VIDEO_CAPS - -#define SVQ_CAPS \ - "video/x-svq, " \ - "svqversion = (int) 3, " \ - COMMON_VIDEO_CAPS - -#define COMMON_AUDIO_CAPS(c, r) \ - "channels = (int) [ 1, " G_STRINGIFY (c) " ], " \ - "rate = (int) [ 1, " G_STRINGIFY (r) " ]" - -#define PCM_CAPS \ - "audio/x-raw-int, " \ - "width = (int) 8, " \ - "depth = (int) 8, " \ - COMMON_AUDIO_CAPS (2, MAX) ", " \ - "signed = (boolean) { true, false }; " \ - "audio/x-raw-int, " \ - "width = (int) 16, " \ - "depth = (int) 16, " \ - "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, " \ - COMMON_AUDIO_CAPS (2, MAX) ", " \ - "signed = (boolean) true " \ - -#define PCM_CAPS_FULL \ - PCM_CAPS "; " \ - "audio/x-raw-int, " \ - "width = (int) 24, " \ - "depth = (int) 24, " \ - "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, " \ - COMMON_AUDIO_CAPS (2, MAX) ", " \ - "signed = (boolean) true; " \ - "audio/x-raw-int, " \ - "width = (int) 32, " \ - "depth = (int) 32, " \ - "endianness = (int) { BIG_ENDIAN, LITTLE_ENDIAN }, " \ - COMMON_AUDIO_CAPS (2, MAX) ", " \ - "signed = (boolean) true " - -#define MP3_CAPS \ - "audio/mpeg, " \ - "mpegversion = (int) 1, " \ - "layer = (int) 3, " \ - COMMON_AUDIO_CAPS (2, MAX) - -#define AAC_CAPS \ - "audio/mpeg, " \ - "mpegversion = (int) 4, " \ - "stream-format = (string) raw, " \ - COMMON_AUDIO_CAPS (8, MAX) - -#define AMR_CAPS \ - "audio/AMR, " \ - "rate = (int) 8000, " \ - "channels = [ 1, 2 ]; " \ - "audio/AMR-WB, " \ - "rate = (int) 16000, " \ - "channels = [ 1, 2 ] " - -#define ADPCM_CAPS \ - "audio/x-adpcm, " \ - "layout = (string)dvi, " \ - "block_align = (int)[64, 8096], " \ - COMMON_AUDIO_CAPS(2, MAX) - -#define ALAC_CAPS \ - "audio/x-alac, " \ - COMMON_AUDIO_CAPS(2, MAX) - -/* FIXME 0.11 - take a look at bugs #580005 and #340375 */ -GstQTMuxFormatProp gst_qt_mux_format_list[] = { - /* original QuickTime format; see Apple site (e.g. qtff.pdf) */ - { - GST_QT_MUX_FORMAT_QT, - "qtmux", - "QuickTime", - "GstQTMux", - GST_STATIC_CAPS ("video/quicktime, variant = (string) apple"), - GST_STATIC_CAPS ("video/x-raw-rgb, " - COMMON_VIDEO_CAPS "; " - "video/x-raw-yuv, " - "format = (fourcc) UYVY, " - COMMON_VIDEO_CAPS "; " - MPEG4V_CAPS "; " - H263_CAPS "; " - H264_CAPS "; " - SVQ_CAPS "; " - "video/x-dv, " - "systemstream = (boolean) false, " - COMMON_VIDEO_CAPS "; " - "image/jpeg, " - COMMON_VIDEO_CAPS_NO_FRAMERATE "; " - "video/x-vp8, " - COMMON_VIDEO_CAPS "; " "video/x-qt-part, " COMMON_VIDEO_CAPS), - GST_STATIC_CAPS (PCM_CAPS_FULL "; " - MP3_CAPS " ; " - AAC_CAPS " ; " - ADPCM_CAPS " ; " - "audio/x-alaw, " COMMON_AUDIO_CAPS (2, MAX) "; " - AMR_CAPS " ; " ALAC_CAPS) - } - , - /* ISO 14496-14: mp42 as ISO base media extension - * (supersedes original ISO 144996-1 mp41) */ - { - GST_QT_MUX_FORMAT_MP4, - "mp4mux", - "MP4", - "GstMP4Mux", - GST_STATIC_CAPS ("video/quicktime, variant = (string) iso"), - GST_STATIC_CAPS (MPEG4V_CAPS "; " H264_CAPS ";" - "video/x-mp4-part," COMMON_VIDEO_CAPS), - GST_STATIC_CAPS (MP3_CAPS "; " AAC_CAPS " ; " ALAC_CAPS) - } - , - /* Microsoft Smooth Streaming fmp4/isml */ - /* TODO add WMV/WMA support */ - { - GST_QT_MUX_FORMAT_ISML, - "ismlmux", - "ISML", - "GstISMLMux", - GST_STATIC_CAPS ("video/quicktime, variant = (string) iso"), - GST_STATIC_CAPS (MPEG4V_CAPS "; " H264_CAPS), - GST_STATIC_CAPS (MP3_CAPS "; " AAC_CAPS) - } - , - /* 3GPP Technical Specification 26.244 V7.3.0 - * (extended in 3GPP2 File Formats for Multimedia Services) */ - { - GST_QT_MUX_FORMAT_3GP, - "gppmux", - "3GPP", - "GstGPPMux", - GST_STATIC_CAPS ("video/quicktime, variant = (string) 3gpp"), - GST_STATIC_CAPS (H263_CAPS "; " MPEG4V_CAPS "; " H264_CAPS), - GST_STATIC_CAPS (AMR_CAPS "; " MP3_CAPS "; " AAC_CAPS) - } - , - /* ISO 15444-3: Motion-JPEG-2000 (also ISO base media extension) */ - { - GST_QT_MUX_FORMAT_MJ2, - "mj2mux", - "MJ2", - "GstMJ2Mux", - GST_STATIC_CAPS ("video/mj2"), - GST_STATIC_CAPS ("image/x-j2c, " COMMON_VIDEO_CAPS "; " - "image/x-jpc, " COMMON_VIDEO_CAPS), - GST_STATIC_CAPS (PCM_CAPS) - } - , - { - GST_QT_MUX_FORMAT_NONE, - } - , -}; - -/* pretty static, but may turn out needed a few times */ -AtomsTreeFlavor -gst_qt_mux_map_format_to_flavor (GstQTMuxFormat format) -{ - if (format == GST_QT_MUX_FORMAT_QT) - return ATOMS_TREE_FLAVOR_MOV; - else if (format == GST_QT_MUX_FORMAT_3GP) - return ATOMS_TREE_FLAVOR_3GP; - else if (format == GST_QT_MUX_FORMAT_ISML) - return ATOMS_TREE_FLAVOR_ISML; - else - return ATOMS_TREE_FLAVOR_ISOM; -} - -static void -gst_qt_mux_map_check_tracks (AtomMOOV * moov, gint * _video, gint * _audio, - gboolean * _has_h264) -{ - GList *it; - gint video = 0, audio = 0; - gboolean has_h264 = FALSE; - - for (it = moov->traks; it != NULL; it = g_list_next (it)) { - AtomTRAK *track = it->data; - - if (track->is_video) { - video++; - if (track->is_h264) - has_h264 = TRUE; - } else - audio++; - } - - if (_video) - *_video = video; - if (_audio) - *_audio = audio; - if (_has_h264) - *_has_h264 = has_h264; -} - -/* pretty static, but possibly dynamic format info */ - -/* notes: - * - avc1 brand is not used, since the specific extensions indicated by it - * are not used (e.g. sample groupings, etc) - * - TODO: maybe even more 3GPP brand fine-tuning ?? - * (but that might need ftyp rewriting at the end) */ -void -gst_qt_mux_map_format_to_header (GstQTMuxFormat format, GstBuffer ** _prefix, - guint32 * _major, guint32 * _version, GList ** _compatible, AtomMOOV * moov, - GstClockTime longest_chunk, gboolean faststart) -{ - static guint32 qt_brands[] = { 0 }; - static guint32 mp4_brands[] = { FOURCC_mp41, FOURCC_isom, FOURCC_iso2, 0 }; - static guint32 isml_brands[] = { FOURCC_iso2, 0 }; - static guint32 gpp_brands[] = { FOURCC_isom, FOURCC_iso2, 0 }; - static guint32 mjp2_brands[] = { FOURCC_isom, FOURCC_iso2, 0 }; - static guint8 mjp2_prefix[] = - { 0, 0, 0, 12, 'j', 'P', ' ', ' ', 0x0D, 0x0A, 0x87, 0x0A }; - guint32 *comp = NULL; - guint32 major = 0, version = 0; - GstBuffer *prefix = NULL; - GList *result = NULL; - - g_return_if_fail (_prefix != NULL); - g_return_if_fail (_major != NULL); - g_return_if_fail (_version != NULL); - g_return_if_fail (_compatible != NULL); - - switch (format) { - case GST_QT_MUX_FORMAT_QT: - major = FOURCC_qt__; - comp = qt_brands; - version = 0x20050300; - break; - case GST_QT_MUX_FORMAT_MP4: - major = FOURCC_mp42; - comp = mp4_brands; - break; - case GST_QT_MUX_FORMAT_ISML: - major = FOURCC_isml; - comp = isml_brands; - break; - case GST_QT_MUX_FORMAT_3GP: - { - gint video, audio; - gboolean has_h264; - - gst_qt_mux_map_check_tracks (moov, &video, &audio, &has_h264); - /* only track restriction really matters for Basic Profile */ - if (video <= 1 && audio <= 1) { - /* it seems only newer spec knows about H264 */ - major = has_h264 ? FOURCC_3gp6 : FOURCC_3gp4; - version = has_h264 ? 0x100 : 0x200; - } else { - major = FOURCC_3gg6; - version = 0x100; - } - comp = gpp_brands; - - /* - * We assume that we have chunks in dts order - */ - if (faststart && longest_chunk <= GST_SECOND) { - /* add progressive download profile */ - result = g_list_append (result, GUINT_TO_POINTER (FOURCC_3gr6)); - } - break; - } - case GST_QT_MUX_FORMAT_MJ2: - major = FOURCC_mjp2; - comp = mjp2_brands; - version = 0; - prefix = gst_buffer_new_and_alloc (sizeof (mjp2_prefix)); - memcpy (GST_BUFFER_DATA (prefix), mjp2_prefix, GST_BUFFER_SIZE (prefix)); - break; - default: - g_assert_not_reached (); - break; - } - - /* convert list to list, hm */ - while (comp && *comp != 0) { - /* order matters over efficiency */ - result = g_list_append (result, GUINT_TO_POINTER (*comp)); - comp++; - } - - *_major = major; - *_version = version; - *_prefix = prefix; - *_compatible = result; - - /* TODO 3GPP may include mp42 as compatible if applicable */ - /* TODO 3GPP major brand 3gp7 if at most 1 video and audio track */ -} diff --git a/gst/qtmux/gstqtmuxmap.h b/gst/qtmux/gstqtmuxmap.h deleted file mode 100644 index 767d62a4a7..0000000000 --- a/gst/qtmux/gstqtmuxmap.h +++ /dev/null @@ -1,83 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * Copyright (C) 2008 Mark Nauwelaerts - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __GST_QT_MUX_MAP_H__ -#define __GST_QT_MUX_MAP_H__ - -#include "atoms.h" - -#include -#include - -typedef enum _GstQTMuxFormat -{ - GST_QT_MUX_FORMAT_NONE = 0, - GST_QT_MUX_FORMAT_QT, - GST_QT_MUX_FORMAT_MP4, - GST_QT_MUX_FORMAT_3GP, - GST_QT_MUX_FORMAT_MJ2, - GST_QT_MUX_FORMAT_ISML -} GstQTMuxFormat; - -typedef struct _GstQTMuxFormatProp -{ - GstQTMuxFormat format; - const gchar *name; - const gchar *long_name; - const gchar *type_name; - GstStaticCaps src_caps; - GstStaticCaps video_sink_caps; - GstStaticCaps audio_sink_caps; -} GstQTMuxFormatProp; - -extern GstQTMuxFormatProp gst_qt_mux_format_list[]; - -void gst_qt_mux_map_format_to_header (GstQTMuxFormat format, GstBuffer ** _prefix, - guint32 * _major, guint32 * verson, - GList ** _compatible, AtomMOOV * moov, - GstClockTime longest_chunk, - gboolean faststart); - -AtomsTreeFlavor gst_qt_mux_map_format_to_flavor (GstQTMuxFormat format); - -#endif /* __GST_QT_MUX_MAP_H__ */ diff --git a/gst/qtmux/gstqtmuxplugin.c b/gst/qtmux/gstqtmuxplugin.c deleted file mode 100644 index 440b9808a9..0000000000 --- a/gst/qtmux/gstqtmuxplugin.c +++ /dev/null @@ -1,67 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008-2010 Thiago Santos - * Copyright (C) 2008 Mark Nauwelaerts - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "gstqtmux.h" -#include "gstqtmoovrecover.h" - -static gboolean -gst_qt_mux_plugin_init (GstPlugin * plugin) -{ - if (!gst_qt_mux_register (plugin)) - return FALSE; - if (!gst_qt_moov_recover_register (plugin)) - return FALSE; - - return TRUE; -} - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, - GST_VERSION_MINOR, - "qtmux", - "Quicktime Muxer plugin", - gst_qt_mux_plugin_init, VERSION, "LGPL", GST_PACKAGE_NAME, - GST_PACKAGE_ORIGIN); diff --git a/gst/qtmux/properties.c b/gst/qtmux/properties.c deleted file mode 100644 index 8dafb2e214..0000000000 --- a/gst/qtmux/properties.c +++ /dev/null @@ -1,210 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#include "properties.h" - -/* if needed, re-allocate buffer to ensure size bytes can be written into it - * at offset */ -void -prop_copy_ensure_buffer (guint8 ** buffer, guint64 * bsize, guint64 * offset, - guint64 size) -{ - if (buffer && *bsize - *offset < size) { - *bsize += size + 10 * 1024; - *buffer = g_realloc (*buffer, *bsize); - } -} - -static guint64 -copy_func (void *prop, guint size, guint8 ** buffer, guint64 * bsize, - guint64 * offset) -{ - if (buffer) { - prop_copy_ensure_buffer (buffer, bsize, offset, size); - memcpy (*buffer + *offset, prop, size); - } - *offset += size; - return size; -} - -#define INT_ARRAY_COPY_FUNC_FAST(name, datatype) \ -guint64 prop_copy_ ## name ## _array (datatype *prop, guint size, \ - guint8 ** buffer, guint64 * bsize, guint64 * offset) { \ - return copy_func (prop, sizeof (datatype) * size, buffer, bsize, offset);\ -} - -#define INT_ARRAY_COPY_FUNC(name, datatype) \ -guint64 prop_copy_ ## name ## _array (datatype *prop, guint size, \ - guint8 ** buffer, guint64 * bsize, guint64 * offset) { \ - guint i; \ - \ - for (i = 0; i < size; i++) { \ - prop_copy_ ## name (prop[i], buffer, bsize, offset); \ - } \ - return sizeof (datatype) * size; \ -} - -/* INTEGERS */ -guint64 -prop_copy_uint8 (guint8 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - return copy_func (&prop, sizeof (guint8), buffer, size, offset); -} - -guint64 -prop_copy_uint16 (guint16 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - prop = GUINT16_TO_BE (prop); - return copy_func (&prop, sizeof (guint16), buffer, size, offset); -} - -guint64 -prop_copy_uint32 (guint32 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - prop = GUINT32_TO_BE (prop); - return copy_func (&prop, sizeof (guint32), buffer, size, offset); -} - -guint64 -prop_copy_uint64 (guint64 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - prop = GUINT64_TO_BE (prop); - return copy_func (&prop, sizeof (guint64), buffer, size, offset); -} - -guint64 -prop_copy_int32 (gint32 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - prop = GINT32_TO_BE (prop); - return copy_func (&prop, sizeof (guint32), buffer, size, offset); -} - -/* uint8 can use direct copy in any case, and may be used for large quantity */ -INT_ARRAY_COPY_FUNC_FAST (uint8, guint8); -/* not used in large quantity anyway */ -INT_ARRAY_COPY_FUNC (uint16, guint16); -INT_ARRAY_COPY_FUNC (uint32, guint32); -INT_ARRAY_COPY_FUNC (uint64, guint64); - -/* FOURCC */ -guint64 -prop_copy_fourcc (guint32 prop, guint8 ** buffer, guint64 * size, - guint64 * offset) -{ - prop = GINT32_TO_LE (prop); - return copy_func (&prop, sizeof (guint32), buffer, size, offset); -} - -INT_ARRAY_COPY_FUNC (fourcc, guint32); - -/** - * prop_copy_fixed_size_string: - * @string: the string to be copied - * @str_size: size of the string - * @buffer: the array to copy the string to - * @offset: the position in the buffer array. - * This value is updated to the point right after the copied string. - * - * Copies a string of bytes without placing its size at the beginning. - * - * Returns: the number of bytes copied - */ -guint64 -prop_copy_fixed_size_string (guint8 * string, guint str_size, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - return copy_func (string, str_size * sizeof (guint8), buffer, size, offset); -} - -/** - * prop_copy_size_string: - * - * @string: the string to be copied - * @str_size: size of the string - * @buffer: the array to copy the string to - * @offset: the position in the buffer array. - * This value is updated to the point right after the copied string. - * - * Copies a string and its size to an array. Example: - * string = 'abc\0' - * result in the array: [3][a][b][c] (each [x] represents a position) - * - * Returns: the number of bytes copied - */ -guint64 -prop_copy_size_string (guint8 * string, guint str_size, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - - prop_copy_uint8 (str_size, buffer, size, offset); - prop_copy_fixed_size_string (string, str_size, buffer, size, offset); - return *offset - original_offset; -} - -/** - * prop_copy_null_terminated_string: - * @string: the string to be copied - * @buffer: the array to copy the string to - * @offset: the position in the buffer array. - * This value is updated to the point right after the copied string. - * - * Copies a string including its null terminating char to an array. - * - * Returns: the number of bytes copied - */ -guint64 -prop_copy_null_terminated_string (gchar * string, guint8 ** buffer, - guint64 * size, guint64 * offset) -{ - guint64 original_offset = *offset; - guint len = strlen (string); - - prop_copy_fixed_size_string ((guint8 *) string, len, buffer, size, offset); - prop_copy_uint8 ('\0', buffer, size, offset); - return *offset - original_offset; -} diff --git a/gst/qtmux/properties.h b/gst/qtmux/properties.h deleted file mode 100644 index ad67e0da03..0000000000 --- a/gst/qtmux/properties.h +++ /dev/null @@ -1,87 +0,0 @@ -/* Quicktime muxer plugin for GStreamer - * Copyright (C) 2008 Thiago Sousa Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ -/* - * Unless otherwise indicated, Source Code is licensed under MIT license. - * See further explanation attached in License Statement (distributed in the file - * LICENSE). - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of - * this software and associated documentation files (the "Software"), to deal in - * the Software without restriction, including without limitation the rights to - * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies - * of the Software, and to permit persons to whom the Software is furnished to do - * so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef __PROPERTIES_H__ -#define __PROPERTIES_H__ - -#include -#include - -/** - * Functions for copying atoms properties. - * - * All of them receive, as the input, the property to be copied, the destination - * buffer, and a pointer to an offset in the destination buffer to copy to the right place. - * This offset will be updated to the new value (offset + copied_size) - * The functions return the size of the property that has been copied or 0 - * if it couldn't copy. - */ - -void prop_copy_ensure_buffer (guint8 ** buffer, guint64 * bsize, guint64 * offset, guint64 size); - -guint64 prop_copy_uint8 (guint8 prop, guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_uint16 (guint16 prop, guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_uint32 (guint32 prop, guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_uint64 (guint64 prop, guint8 **buffer, guint64 *size, guint64 *offset); - -guint64 prop_copy_int32 (gint32 prop, guint8 **buffer, guint64 *size, guint64 *offset); - -guint64 prop_copy_uint8_array (guint8 *prop, guint size, - guint8 **buffer, guint64 *bsize, guint64 *offset); -guint64 prop_copy_uint16_array (guint16 *prop, guint size, - guint8 **buffer, guint64 *bsize, guint64 *offset); -guint64 prop_copy_uint32_array (guint32 *prop, guint size, - guint8 **buffer, guint64 *bsize, guint64 *offset); -guint64 prop_copy_uint64_array (guint64 *prop, guint size, - guint8 **buffer, guint64 *bsize, guint64 *offset); - -guint64 prop_copy_fourcc (guint32 prop, guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_fourcc_array (guint32 *prop, guint size, - guint8 **buffer, guint64 *bsize, guint64 *offset); -guint64 prop_copy_fixed_size_string (guint8 *string, guint str_size, - guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_size_string (guint8 *string, guint str_size, - guint8 **buffer, guint64 *size, guint64 *offset); -guint64 prop_copy_null_terminated_string (gchar *string, - guint8 **buffer, guint64 *size, guint64 *offset); - -#endif /* __PROPERTIES_H__ */ diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index 2f8207a8f4..433b94f28f 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -163,7 +163,6 @@ check_PROGRAMS = \ $(check_jifmux) \ elements/jpegparse \ $(check_logoinsert) \ - elements/qtmux \ elements/mxfdemux \ elements/mxfmux \ elements/id3mux \ @@ -174,7 +173,6 @@ check_PROGRAMS = \ $(check_vp8) \ $(check_zbar) \ $(check_orc) \ - pipelines/tagschecking \ $(EXPERIMENTAL_CHECKS) noinst_HEADERS = elements/mxfdemux.h diff --git a/tests/check/elements/.gitignore b/tests/check/elements/.gitignore index df8ab17615..b68288dbe9 100644 --- a/tests/check/elements/.gitignore +++ b/tests/check/elements/.gitignore @@ -25,7 +25,6 @@ mxfdemux mxfmux neonhttpsrc ofa -qtmux rganalysis rglimiter rgvolume diff --git a/tests/check/elements/qtmux.c b/tests/check/elements/qtmux.c deleted file mode 100644 index 1db618cec9..0000000000 --- a/tests/check/elements/qtmux.c +++ /dev/null @@ -1,422 +0,0 @@ -/* GStreamer - * - * unit test for qtmux - * - * Copyright (C) <2008> Mark Nauwelaerts - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include - -/* For ease of programming we use globals to keep refs for our floating - * src and sink pads we create; otherwise we always have to do get_pad, - * get_peer, and then remove references in every test function */ -static GstPad *mysrcpad, *mysinkpad; - -#define AUDIO_CAPS_STRING "audio/mpeg, " \ - "mpegversion = (int) 1, " \ - "layer = (int) 3, " \ - "channels = (int) 2, " \ - "rate = (int) 48000" -#define VIDEO_CAPS_STRING "video/mpeg, " \ - "mpegversion = (int) 4, " \ - "width = (int) 384, " \ - "height = (int) 288, " \ - "framerate = (fraction) 25/1" - -static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("video/quicktime")); -static GstStaticPadTemplate srcvideotemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (VIDEO_CAPS_STRING)); - -static GstStaticPadTemplate srcaudiotemplate = GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS (AUDIO_CAPS_STRING)); - - -/* setup and teardown needs some special handling for muxer */ -static GstPad * -setup_src_pad (GstElement * element, - GstStaticPadTemplate * template, GstCaps * caps, const gchar * sinkname) -{ - GstPad *srcpad, *sinkpad; - - GST_DEBUG_OBJECT (element, "setting up sending pad"); - /* sending pad */ - srcpad = gst_pad_new_from_static_template (template, "src"); - fail_if (srcpad == NULL, "Could not create a srcpad"); - ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1); - - if (!(sinkpad = gst_element_get_static_pad (element, sinkname))) - sinkpad = gst_element_get_request_pad (element, sinkname); - fail_if (sinkpad == NULL, "Could not get sink pad from %s", - GST_ELEMENT_NAME (element)); - /* references are owned by: 1) us, 2) qtmux, 3) collect pads */ - ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); - if (caps) - fail_unless (gst_pad_set_caps (srcpad, caps)); - fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK, - "Could not link source and %s sink pads", GST_ELEMENT_NAME (element)); - gst_object_unref (sinkpad); /* because we got it higher up */ - - /* references are owned by: 1) qtmux, 2) collect pads */ - ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2); - - return srcpad; -} - -static void -teardown_src_pad (GstPad * srcpad) -{ - GstPad *sinkpad; - - /* clean up floating src pad */ - sinkpad = gst_pad_get_peer (srcpad); - fail_if (sinkpad == NULL); - /* pad refs held by 1) qtmux 2) collectpads and 3) us (through _get_peer) */ - ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); - - gst_pad_unlink (srcpad, sinkpad); - - /* after unlinking, pad refs still held by - * 1) qtmux and 2) collectpads and 3) us (through _get_peer) */ - ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 3); - gst_object_unref (sinkpad); - /* one more ref is held by element itself */ - - /* pad refs held by creator */ - ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1); - gst_object_unref (srcpad); -} - -static GstElement * -setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname) -{ - GstElement *qtmux; - - GST_DEBUG ("setup_qtmux"); - qtmux = gst_check_setup_element ("qtmux"); - mysrcpad = setup_src_pad (qtmux, srctemplate, NULL, sinkname); - mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate, NULL); - gst_pad_set_active (mysrcpad, TRUE); - gst_pad_set_active (mysinkpad, TRUE); - - return qtmux; -} - -static void -cleanup_qtmux (GstElement * qtmux, const gchar * sinkname) -{ - GST_DEBUG ("cleanup_qtmux"); - gst_element_set_state (qtmux, GST_STATE_NULL); - - gst_pad_set_active (mysrcpad, FALSE); - gst_pad_set_active (mysinkpad, FALSE); - teardown_src_pad (mysrcpad); - gst_check_teardown_sink_pad (qtmux); - gst_check_teardown_element (qtmux); -} - -static void -check_qtmux_pad (GstStaticPadTemplate * srctemplate, const gchar * sinkname) -{ - GstElement *qtmux; - GstBuffer *inbuffer, *outbuffer; - GstCaps *caps; - int num_buffers; - int i; - guint8 data0[12] = "\000\000\000\024ftypqt "; - guint8 data1[8] = "\000\000\000\001mdat"; - guint8 data2[4] = "moov"; - - qtmux = setup_qtmux (srctemplate, sinkname); - fail_unless (gst_element_set_state (qtmux, - GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, - "could not set to playing"); - - inbuffer = gst_buffer_new_and_alloc (1); - caps = gst_caps_copy (gst_pad_get_pad_template_caps (mysrcpad)); - gst_buffer_set_caps (inbuffer, caps); - gst_caps_unref (caps); - GST_BUFFER_TIMESTAMP (inbuffer) = 0; - GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND; - ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); - - /* send eos to have moov written */ - fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); - - num_buffers = g_list_length (buffers); - /* at least expect ftyp, mdat header, buffer chunk and moov */ - fail_unless (num_buffers >= 4); - - for (i = 0; i < num_buffers; ++i) { - outbuffer = GST_BUFFER (buffers->data); - fail_if (outbuffer == NULL); - buffers = g_list_remove (buffers, outbuffer); - - switch (i) { - case 0: - { - /* ftyp header */ - guint8 *data = GST_BUFFER_DATA (outbuffer); - - fail_unless (GST_BUFFER_SIZE (outbuffer) >= 20); - fail_unless (memcmp (data, data0, sizeof (data0)) == 0); - fail_unless (memcmp (data + 16, data0 + 8, 4) == 0); - break; - } - case 1: /* mdat header */ - fail_unless (GST_BUFFER_SIZE (outbuffer) == 16); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer), data1, sizeof (data1)) - == 0); - break; - case 2: /* buffer we put in */ - fail_unless (GST_BUFFER_SIZE (outbuffer) == 1); - break; - case 3: /* moov */ - fail_unless (GST_BUFFER_SIZE (outbuffer) > 8); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer) + 4, data2, - sizeof (data2)) == 0); - break; - default: - break; - } - - ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1); - gst_buffer_unref (outbuffer); - outbuffer = NULL; - } - - g_list_free (buffers); - buffers = NULL; - - cleanup_qtmux (qtmux, sinkname); -} - -static void -check_qtmux_pad_fragmented (GstStaticPadTemplate * srctemplate, - const gchar * sinkname, gboolean streamable) -{ - GstElement *qtmux; - GstBuffer *inbuffer, *outbuffer; - GstCaps *caps; - int num_buffers; - int i; - guint8 data0[12] = "\000\000\000\024ftypqt "; - guint8 data1[4] = "mdat"; - guint8 data2[4] = "moov"; - guint8 data3[4] = "moof"; - guint8 data4[4] = "mfra"; - - qtmux = setup_qtmux (srctemplate, sinkname); - g_object_set (qtmux, "fragment-duration", 2000, NULL); - g_object_set (qtmux, "streamable", streamable, NULL); - fail_unless (gst_element_set_state (qtmux, - GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS, - "could not set to playing"); - - inbuffer = gst_buffer_new_and_alloc (1); - caps = gst_caps_copy (gst_pad_get_pad_template_caps (mysrcpad)); - gst_buffer_set_caps (inbuffer, caps); - gst_caps_unref (caps); - GST_BUFFER_TIMESTAMP (inbuffer) = 0; - GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND; - ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); - - /* send eos to have all written */ - fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); - - num_buffers = g_list_length (buffers); - /* at least expect ftyp, moov, moof, mdat header, buffer chunk - * and optionally mfra */ - fail_unless (num_buffers >= 5); - - for (i = 0; i < num_buffers; ++i) { - outbuffer = GST_BUFFER (buffers->data); - fail_if (outbuffer == NULL); - buffers = g_list_remove (buffers, outbuffer); - - switch (i) { - case 0: - { - /* ftyp header */ - guint8 *data = GST_BUFFER_DATA (outbuffer); - - fail_unless (GST_BUFFER_SIZE (outbuffer) >= 20); - fail_unless (memcmp (data, data0, sizeof (data0)) == 0); - fail_unless (memcmp (data + 16, data0 + 8, 4) == 0); - break; - } - case 1: /* moov */ - fail_unless (GST_BUFFER_SIZE (outbuffer) > 8); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer) + 4, data2, - sizeof (data2)) == 0); - break; - case 2: /* moof */ - fail_unless (GST_BUFFER_SIZE (outbuffer) > 8); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer) + 4, data3, - sizeof (data3)) == 0); - break; - case 3: /* mdat header */ - fail_unless (GST_BUFFER_SIZE (outbuffer) == 8); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer) + 4, data1, - sizeof (data1)) == 0); - break; - case 4: /* buffer we put in */ - fail_unless (GST_BUFFER_SIZE (outbuffer) == 1); - break; - case 5: /* mfra */ - fail_unless (GST_BUFFER_SIZE (outbuffer) > 8); - fail_unless (memcmp (GST_BUFFER_DATA (outbuffer) + 4, data4, - sizeof (data4)) == 0); - break; - default: - break; - } - - ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1); - gst_buffer_unref (outbuffer); - outbuffer = NULL; - } - - g_list_free (buffers); - buffers = NULL; - - cleanup_qtmux (qtmux, sinkname); -} - - -GST_START_TEST (test_video_pad) -{ - check_qtmux_pad (&srcvideotemplate, "video_%d"); -} - -GST_END_TEST; - -GST_START_TEST (test_audio_pad) -{ - check_qtmux_pad (&srcaudiotemplate, "audio_%d"); -} - -GST_END_TEST; - - -GST_START_TEST (test_video_pad_frag) -{ - check_qtmux_pad_fragmented (&srcvideotemplate, "video_%d", FALSE); -} - -GST_END_TEST; - -GST_START_TEST (test_audio_pad_frag) -{ - check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%d", FALSE); -} - -GST_END_TEST; - - -GST_START_TEST (test_video_pad_frag_streamable) -{ - check_qtmux_pad_fragmented (&srcvideotemplate, "video_%d", TRUE); -} - -GST_END_TEST; - - -GST_START_TEST (test_audio_pad_frag_streamable) -{ - check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%d", TRUE); -} - -GST_END_TEST; - - -GST_START_TEST (test_reuse) -{ - GstElement *qtmux = setup_qtmux (&srcvideotemplate, "video_%d"); - GstBuffer *inbuffer; - GstCaps *caps; - - gst_element_set_state (qtmux, GST_STATE_PLAYING); - gst_element_set_state (qtmux, GST_STATE_NULL); - gst_element_set_state (qtmux, GST_STATE_PLAYING); - gst_pad_set_active (mysrcpad, TRUE); - gst_pad_set_active (mysinkpad, TRUE); - - inbuffer = gst_buffer_new_and_alloc (1); - fail_unless (inbuffer != NULL); - caps = gst_caps_copy (gst_pad_get_pad_template_caps (mysrcpad)); - gst_buffer_set_caps (inbuffer, caps); - gst_caps_unref (caps); - GST_BUFFER_TIMESTAMP (inbuffer) = 0; - GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND; - ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1); - fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK); - - /* send eos to have all written */ - fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE); - - cleanup_qtmux (qtmux, "video_%d"); -} - -GST_END_TEST; - -static Suite * -qtmux_suite (void) -{ - Suite *s = suite_create ("qtmux"); - TCase *tc_chain = tcase_create ("general"); - - suite_add_tcase (s, tc_chain); - tcase_add_test (tc_chain, test_video_pad); - tcase_add_test (tc_chain, test_audio_pad); - tcase_add_test (tc_chain, test_video_pad_frag); - tcase_add_test (tc_chain, test_audio_pad_frag); - tcase_add_test (tc_chain, test_video_pad_frag_streamable); - tcase_add_test (tc_chain, test_audio_pad_frag_streamable); - - tcase_add_test (tc_chain, test_reuse); - - return s; -} - -int -main (int argc, char **argv) -{ - int nf; - - Suite *s = qtmux_suite (); - SRunner *sr = srunner_create (s); - - gst_check_init (&argc, &argv); - - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - srunner_free (sr); - - return nf; -} diff --git a/tests/check/pipelines/tagschecking.c b/tests/check/pipelines/tagschecking.c deleted file mode 100644 index 91a556c52f..0000000000 --- a/tests/check/pipelines/tagschecking.c +++ /dev/null @@ -1,350 +0,0 @@ -/* GStreamer - * Copyright (C) 2008 Nokia Corporation. (contact ) - * Copyright (C) 2010 Thiago Santos - * - * 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., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -static GstTagList *received_tags = NULL; - -static gboolean -bus_handler (GstBus * bus, GstMessage * message, gpointer data) -{ - GMainLoop *loop = (GMainLoop *) data; - - switch (message->type) { - case GST_MESSAGE_EOS: - g_main_loop_quit (loop); - break; - case GST_MESSAGE_WARNING: - case GST_MESSAGE_ERROR:{ - GError *gerror; - - gchar *debug; - - gst_message_parse_error (message, &gerror, &debug); - gst_object_default_error (GST_MESSAGE_SRC (message), gerror, debug); - g_error_free (gerror); - g_free (debug); - g_main_loop_quit (loop); - break; - } - case GST_MESSAGE_TAG:{ - if (received_tags == NULL) { - gst_message_parse_tag (message, &received_tags); - } else { - GstTagList *tl = NULL, *ntl = NULL; - - gst_message_parse_tag (message, &tl); - if (tl) { - ntl = gst_tag_list_merge (received_tags, tl, GST_TAG_MERGE_PREPEND); - if (ntl) { - GST_LOG ("taglists merged: %" GST_PTR_FORMAT, ntl); - gst_tag_list_free (received_tags); - received_tags = ntl; - } - gst_tag_list_free (tl); - } - } - break; - } - default: - break; - } - - return TRUE; -} - -/* - * Creates a pipeline in the form: - * fakesrc num-buffers=1 ! caps ! muxer ! filesink location=file - * - * And sets the tags in tag_str into the muxer via tagsetter. - */ -static void -test_mux_tags (const gchar * tag_str, const gchar * caps, - const gchar * muxer, const gchar * file) -{ - GstElement *pipeline; - GstBus *bus; - GMainLoop *loop; - GstTagList *sent_tags; - GstElement *mux; - GstTagSetter *setter; - gchar *launch_str; - - GST_DEBUG ("testing xmp muxing on : %s", muxer); - - launch_str = g_strdup_printf ("fakesrc num-buffers=1 ! %s ! %s name=mux ! " - "filesink location=%s name=sink", caps, muxer, file); - pipeline = gst_parse_launch (launch_str, NULL); - g_free (launch_str); - fail_unless (pipeline != NULL); - - mux = gst_bin_get_by_name (GST_BIN (pipeline), "mux"); - fail_unless (mux != NULL); - - loop = g_main_loop_new (NULL, TRUE); - fail_unless (loop != NULL); - - bus = gst_element_get_bus (pipeline); - fail_unless (bus != NULL); - gst_bus_add_watch (bus, bus_handler, loop); - gst_object_unref (bus); - - gst_element_set_state (pipeline, GST_STATE_READY); - - setter = GST_TAG_SETTER (mux); - fail_unless (setter != NULL); - sent_tags = gst_structure_from_string (tag_str, NULL); - fail_unless (sent_tags != NULL); - gst_tag_setter_merge_tags (setter, sent_tags, GST_TAG_MERGE_REPLACE); - gst_tag_list_free (sent_tags); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - g_main_loop_run (loop); - - gst_element_set_state (pipeline, GST_STATE_NULL); - - g_main_loop_unref (loop); - g_object_unref (mux); - g_object_unref (pipeline); -} - -/* - * Makes a pipeline in the form: - * filesrc location=file ! demuxer ! fakesink - * - * And gets the tags that are posted on the bus to compare - * with the tags in 'tag_str' - */ -static void -test_demux_tags (const gchar * tag_str, const gchar * demuxer, - const gchar * file) -{ - GstElement *pipeline; - GstBus *bus; - GMainLoop *loop; - GstTagList *sent_tags; - gint i, j, n_recv, n_sent; - const gchar *name_sent, *name_recv; - const GValue *value_sent, *value_recv; - gboolean found; - gint comparison; - GstElement *demux; - gchar *launch_str; - - GST_DEBUG ("testing tags : %s", tag_str); - - if (received_tags) { - gst_tag_list_free (received_tags); - received_tags = NULL; - } - - launch_str = g_strdup_printf ("filesrc location=%s ! %s name=demux ! " - "fakesink", file, demuxer); - pipeline = gst_parse_launch (launch_str, NULL); - g_free (launch_str); - fail_unless (pipeline != NULL); - - demux = gst_bin_get_by_name (GST_BIN (pipeline), "demux"); - fail_unless (demux != NULL); - - loop = g_main_loop_new (NULL, TRUE); - fail_unless (loop != NULL); - - bus = gst_element_get_bus (pipeline); - fail_unless (bus != NULL); - gst_bus_add_watch (bus, bus_handler, loop); - gst_object_unref (bus); - - sent_tags = gst_structure_from_string (tag_str, NULL); - fail_unless (sent_tags != NULL); - - gst_element_set_state (pipeline, GST_STATE_PLAYING); - g_main_loop_run (loop); - - GST_DEBUG ("mainloop done : %p", received_tags); - - /* verify tags */ - fail_unless (received_tags != NULL); - n_recv = gst_structure_n_fields (received_tags); - n_sent = gst_structure_n_fields (sent_tags); - fail_unless (n_recv >= n_sent); - /* FIXME: compare taglits values */ - for (i = 0; i < n_sent; i++) { - name_sent = gst_structure_nth_field_name (sent_tags, i); - value_sent = gst_structure_get_value (sent_tags, name_sent); - found = FALSE; - for (j = 0; j < n_recv; j++) { - name_recv = gst_structure_nth_field_name (received_tags, j); - if (!strcmp (name_sent, name_recv)) { - value_recv = gst_structure_get_value (received_tags, name_recv); - comparison = gst_value_compare (value_sent, value_recv); - if (comparison != GST_VALUE_EQUAL) { - gchar *vs = g_strdup_value_contents (value_sent); - gchar *vr = g_strdup_value_contents (value_recv); - GST_DEBUG ("sent = %s:'%s', recv = %s:'%s'", - G_VALUE_TYPE_NAME (value_sent), vs, - G_VALUE_TYPE_NAME (value_recv), vr); - g_free (vs); - g_free (vr); - } - fail_unless (comparison == GST_VALUE_EQUAL, - "tag item %s has been received with different type or value", - name_sent); - found = TRUE; - break; - } - } - fail_unless (found, "tag item %s is lost", name_sent); - } - - gst_tag_list_free (received_tags); - received_tags = NULL; - gst_tag_list_free (sent_tags); - - gst_element_set_state (pipeline, GST_STATE_NULL); - - g_main_loop_unref (loop); - g_object_unref (demux); - g_object_unref (pipeline); -} - -/* - * Tests if the muxer/demuxer pair can serialize the tags in 'tag_str' - * to a file and recover them correctly. - * - * 'caps' are used to assure the muxer accepts the fake buffer fakesrc - * will send to it. - */ -static void -test_tags (const gchar * tag_str, const gchar * caps, const gchar * muxer, - const gchar * demuxer) -{ - gchar *tmpfile; - gchar *tmp; - - tmp = g_strdup_printf ("%s%d", "gst-check-xmp-test-", g_random_int ()); - tmpfile = g_build_filename (g_get_tmp_dir (), tmp, NULL); - g_free (tmp); - - GST_DEBUG ("testing tags : %s", tag_str); - test_mux_tags (tag_str, caps, muxer, tmpfile); - test_demux_tags (tag_str, demuxer, tmpfile); - g_free (tmpfile); -} - -#define H264_CAPS "video/x-h264, width=(int)320, height=(int)240," \ - " framerate=(fraction)30/1, codec_data=(buffer)" \ - "01401592ffe10017674d401592540a0fd8088000000300" \ - "8000001e478b175001000468ee3c80, stream-format=(string)avc" - -#define COMMON_TAGS \ - "taglist,title=test_title," \ - "artist=test_artist," \ - "keywords=\"key1,key2\"," \ - "description=test_desc" - -GST_START_TEST (test_common_tags) -{ - if (!gst_default_registry_check_feature_version ("qtdemux", 0, 10, 23)) { - GST_INFO ("Skipping test, qtdemux either not available or too old"); - return; - } - test_tags (COMMON_TAGS, H264_CAPS, "qtmux", "qtdemux"); - test_tags (COMMON_TAGS, H264_CAPS, "mp4mux", "qtdemux"); - test_tags (COMMON_TAGS, H264_CAPS, "gppmux", "qtdemux"); -} - -GST_END_TEST; - -#define GEO_LOCATION_TAGS \ - "taglist,geo-location-country=Brazil," \ - "geo-location-city=\"Campina Grande\"," \ - "geo-location-sublocation=Bodocongo," \ - "geo-location-latitude=-12.125," \ - "geo-location-longitude=56.75," \ - "geo-location-elevation=327.5" - -GST_START_TEST (test_geo_location_tags) -{ - if (!gst_default_registry_check_feature_version ("qtdemux", 0, 10, 23)) { - GST_INFO ("Skipping test, qtdemux either not available or too old"); - return; - } - test_tags (GEO_LOCATION_TAGS, H264_CAPS, "qtmux", "qtdemux"); - test_tags (GEO_LOCATION_TAGS, H264_CAPS, "mp4mux", "qtdemux"); - test_tags (GEO_LOCATION_TAGS, H264_CAPS, "gppmux", "qtdemux"); -} - -GST_END_TEST; - - -#define USER_TAGS \ - "taglist,user-rating=(uint)85" - -GST_START_TEST (test_user_tags) -{ - if (!gst_default_registry_check_feature_version ("qtdemux", 0, 10, 23)) { - GST_INFO ("Skipping test, qtdemux either not available or too old"); - return; - } - - test_tags (USER_TAGS, H264_CAPS, "qtmux", "qtdemux"); - test_tags (USER_TAGS, H264_CAPS, "mp4mux", "qtdemux"); - test_tags (USER_TAGS, H264_CAPS, "gppmux", "qtdemux"); -} - -GST_END_TEST; - -static Suite * -metadata_suite (void) -{ - Suite *s = suite_create ("tagschecking"); - - TCase *tc_chain = tcase_create ("general"); - - /* time out after 60s, not the default 3 */ - tcase_set_timeout (tc_chain, 60); - - suite_add_tcase (s, tc_chain); - tcase_add_test (tc_chain, test_common_tags); - tcase_add_test (tc_chain, test_geo_location_tags); - tcase_add_test (tc_chain, test_user_tags); - - return s; -} - -int -main (int argc, char **argv) -{ - int nf; - - Suite *s = metadata_suite (); - - SRunner *sr = srunner_create (s); - - gst_check_init (&argc, &argv); - - srunner_run_all (sr, CK_NORMAL); - nf = srunner_ntests_failed (sr); - srunner_free (sr); - - return nf; -}