LCOV - code coverage report
Current view: top level - gst-plugins-bad/gst/qtmux - gstqtmux.c (source / functions) Hit Total Coverage
Test: GStreamer Lines: 951 1632 58.3 %
Date: 2011-03-25 Functions: 44 53 83.0 %
Branches: 361 1300 27.8 %

           Branch data     Line data    Source code
       1                 :            : /* Quicktime muxer plugin for GStreamer
       2                 :            :  * Copyright (C) 2008-2010 Thiago Santos <thiagoss@embedded.ufcg.edu.br>
       3                 :            :  * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
       4                 :            :  * Copyright (C) 2010 Nokia Corporation. All rights reserved.
       5                 :            :  * Contact: Stefan Kost <stefan.kost@nokia.com>
       6                 :            : 
       7                 :            :  * This library is free software; you can redistribute it and/or
       8                 :            :  * modify it under the terms of the GNU Library General Public
       9                 :            :  * License as published by the Free Software Foundation; either
      10                 :            :  * version 2 of the License, or (at your option) any later version.
      11                 :            :  *
      12                 :            :  * This library is distributed in the hope that it will be useful,
      13                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      14                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      15                 :            :  * Library General Public License for more details.
      16                 :            :  *
      17                 :            :  * You should have received a copy of the GNU Library General Public
      18                 :            :  * License along with this library; if not, write to the
      19                 :            :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      20                 :            :  * Boston, MA 02111-1307, USA.
      21                 :            :  */
      22                 :            : /*
      23                 :            :  * Unless otherwise indicated, Source Code is licensed under MIT license.
      24                 :            :  * See further explanation attached in License Statement (distributed in the file
      25                 :            :  * LICENSE).
      26                 :            :  *
      27                 :            :  * Permission is hereby granted, free of charge, to any person obtaining a copy of
      28                 :            :  * this software and associated documentation files (the "Software"), to deal in
      29                 :            :  * the Software without restriction, including without limitation the rights to
      30                 :            :  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
      31                 :            :  * of the Software, and to permit persons to whom the Software is furnished to do
      32                 :            :  * so, subject to the following conditions:
      33                 :            :  *
      34                 :            :  * The above copyright notice and this permission notice shall be included in all
      35                 :            :  * copies or substantial portions of the Software.
      36                 :            :  *
      37                 :            :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      38                 :            :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      39                 :            :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      40                 :            :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      41                 :            :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      42                 :            :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      43                 :            :  * SOFTWARE.
      44                 :            :  */
      45                 :            : 
      46                 :            : 
      47                 :            : /**
      48                 :            :  * SECTION:element-qtmux
      49                 :            :  * @short_description: Muxer for quicktime(.mov) files
      50                 :            :  *
      51                 :            :  * This element merges streams (audio and video) into QuickTime(.mov) files.
      52                 :            :  *
      53                 :            :  * The following background intends to explain why various similar muxers
      54                 :            :  * are present in this plugin.
      55                 :            :  *
      56                 :            :  * The <ulink url="http://www.apple.com/quicktime/resources/qtfileformat.pdf">
      57                 :            :  * QuickTime file format specification</ulink> served as basis for the MP4 file
      58                 :            :  * format specification (mp4mux), and as such the QuickTime file structure is
      59                 :            :  * nearly identical to the so-called ISO Base Media file format defined in
      60                 :            :  * ISO 14496-12 (except for some media specific parts).
      61                 :            :  * In turn, the latter ISO Base Media format was further specialized as a
      62                 :            :  * Motion JPEG-2000 file format in ISO 15444-3 (mj2mux)
      63                 :            :  * and in various 3GPP(2) specs (gppmux).
      64                 :            :  * The fragmented file features defined (only) in ISO Base Media are used by
      65                 :            :  * ISMV files making up (a.o.) Smooth Streaming (ismlmux).
      66                 :            :  *
      67                 :            :  * A few properties (<link linkend="GstQTMux--movie-timescale">movie-timescale</link>,
      68                 :            :  * <link linkend="GstQTMux--trak-timescale">trak-timescale</link>) allow adjusting
      69                 :            :  * some technical parameters, which might be useful in (rare) cases to resolve
      70                 :            :  * compatibility issues in some situations.
      71                 :            :  *
      72                 :            :  * Some other properties influence the result more fundamentally.
      73                 :            :  * A typical mov/mp4 file's metadata (aka moov) is located at the end of the file,
      74                 :            :  * somewhat contrary to this usually being called "the header".
      75                 :            :  * However, a <link linkend="GstQTMux--faststart">faststart</link> file will
      76                 :            :  * (with some effort) arrange this to be located near start of the file,
      77                 :            :  * which then allows it e.g. to be played while downloading.
      78                 :            :  * Alternatively, rather than having one chunk of metadata at start (or end),
      79                 :            :  * there can be some metadata at start and most of the other data can be spread
      80                 :            :  * out into fragments of <link linkend="GstQTMux--fragment-duration">fragment-duration</link>.
      81                 :            :  * If such fragmented layout is intended for streaming purposes, then
      82                 :            :  * <link linkend="GstQTMux--streamable">streamable</link> allows foregoing to add
      83                 :            :  * index metadata (at the end of file).
      84                 :            :  *
      85                 :            :  * <link linkend="GstQTMux--dts-method">dts-method</link> allows selecting a
      86                 :            :  * method for managing input timestamps (stay tuned for 0.11 to have this
      87                 :            :  * automagically settled).  The default delta/duration method should handle nice
      88                 :            :  * (aka perfect streams) just fine, but may experience problems otherwise
      89                 :            :  * (e.g. input stream with re-ordered B-frames and/or with frame dropping).
      90                 :            :  * The re-ordering approach re-assigns incoming timestamps in ascending order
      91                 :            :  * to incoming buffers and offers an alternative in such cases.  In cases where
      92                 :            :  * that might fail, the remaining method can be tried, which is exact and
      93                 :            :  * according to specs, but might experience playback on not so spec-wise players.
      94                 :            :  * Note that this latter approach also requires one to enable
      95                 :            :  * <link linkend="GstQTMux--presentation-timestamp">presentation-timestamp</link>.
      96                 :            :  *
      97                 :            :  * <refsect2>
      98                 :            :  * <title>Example pipelines</title>
      99                 :            :  * |[
     100                 :            :  * gst-launch v4l2src num-buffers=500 ! video/x-raw-yuv,width=320,height=240 ! ffmpegcolorspace ! qtmux ! filesink location=video.mov
     101                 :            :  * ]|
     102                 :            :  * Records a video stream captured from a v4l2 device and muxes it into a qt file.
     103                 :            :  * </refsect2>
     104                 :            :  *
     105                 :            :  * Last reviewed on 2010-12-03
     106                 :            :  */
     107                 :            : 
     108                 :            : /*
     109                 :            :  * Based on avimux
     110                 :            :  */
     111                 :            : 
     112                 :            : #ifdef HAVE_CONFIG_H
     113                 :            : #include "config.h"
     114                 :            : #endif
     115                 :            : 
     116                 :            : #include <glib/gstdio.h>
     117                 :            : 
     118                 :            : #include <gst/gst.h>
     119                 :            : #include <gst/base/gstcollectpads.h>
     120                 :            : 
     121                 :            : #include <sys/types.h>
     122                 :            : #ifdef G_OS_WIN32
     123                 :            : #include <io.h>                 /* lseek, open, close, read */
     124                 :            : #undef lseek
     125                 :            : #define lseek _lseeki64
     126                 :            : #undef off_t
     127                 :            : #define off_t guint64
     128                 :            : #endif
     129                 :            : 
     130                 :            : #ifdef _MSC_VER
     131                 :            : #define ftruncate g_win32_ftruncate
     132                 :            : #endif
     133                 :            : 
     134                 :            : #ifdef HAVE_UNISTD_H
     135                 :            : #  include <unistd.h>
     136                 :            : #endif
     137                 :            : 
     138                 :            : #include "gstqtmux.h"
     139                 :            : 
     140                 :            : GST_DEBUG_CATEGORY_STATIC (gst_qt_mux_debug);
     141                 :            : #define GST_CAT_DEFAULT gst_qt_mux_debug
     142                 :            : 
     143                 :            : enum
     144                 :            : {
     145                 :            :   DTS_METHOD_DD,
     146                 :            :   DTS_METHOD_REORDER,
     147                 :            :   DTS_METHOD_ASC
     148                 :            : };
     149                 :            : 
     150                 :            : static GType
     151                 :         46 : gst_qt_mux_dts_method_get_type (void)
     152                 :            : {
     153                 :            :   static GType gst_qt_mux_dts_method = 0;
     154                 :            : 
     155         [ +  + ]:         46 :   if (!gst_qt_mux_dts_method) {
     156                 :            :     static const GEnumValue dts_methods[] = {
     157                 :            :       {DTS_METHOD_DD, "delta/duration", "dd"},
     158                 :            :       {DTS_METHOD_REORDER, "reorder", "reorder"},
     159                 :            :       {DTS_METHOD_ASC, "ascending", "asc"},
     160                 :            :       {0, NULL, NULL},
     161                 :            :     };
     162                 :            : 
     163                 :         20 :     gst_qt_mux_dts_method =
     164                 :         20 :         g_enum_register_static ("GstQTMuxDtsMethods", dts_methods);
     165                 :            :   }
     166                 :            : 
     167                 :         46 :   return gst_qt_mux_dts_method;
     168                 :            : }
     169                 :            : 
     170                 :            : #define GST_TYPE_QT_MUX_DTS_METHOD \
     171                 :            :   (gst_qt_mux_dts_method_get_type ())
     172                 :            : 
     173                 :            : /* QTMux signals and args */
     174                 :            : enum
     175                 :            : {
     176                 :            :   /* FILL ME */
     177                 :            :   LAST_SIGNAL
     178                 :            : };
     179                 :            : 
     180                 :            : enum
     181                 :            : {
     182                 :            :   PROP_0,
     183                 :            :   PROP_MOVIE_TIMESCALE,
     184                 :            :   PROP_TRAK_TIMESCALE,
     185                 :            :   PROP_FAST_START,
     186                 :            :   PROP_FAST_START_TEMP_FILE,
     187                 :            :   PROP_MOOV_RECOV_FILE,
     188                 :            :   PROP_FRAGMENT_DURATION,
     189                 :            :   PROP_STREAMABLE,
     190                 :            :   PROP_DTS_METHOD,
     191                 :            :   PROP_DO_CTTS,
     192                 :            : };
     193                 :            : 
     194                 :            : /* some spare for header size as well */
     195                 :            : #define MDAT_LARGE_FILE_LIMIT           ((guint64) 1024 * 1024 * 1024 * 2)
     196                 :            : #define MAX_TOLERATED_LATENESS          (GST_SECOND / 10)
     197                 :            : 
     198                 :            : #define DEFAULT_MOVIE_TIMESCALE         1000
     199                 :            : #define DEFAULT_TRAK_TIMESCALE          0
     200                 :            : #define DEFAULT_DO_CTTS                 FALSE
     201                 :            : #define DEFAULT_FAST_START              FALSE
     202                 :            : #define DEFAULT_FAST_START_TEMP_FILE    NULL
     203                 :            : #define DEFAULT_MOOV_RECOV_FILE         NULL
     204                 :            : #define DEFAULT_FRAGMENT_DURATION       0
     205                 :            : #define DEFAULT_STREAMABLE              FALSE
     206                 :            : #define DEFAULT_DTS_METHOD              DTS_METHOD_DD
     207                 :            : 
     208                 :            : 
     209                 :            : static void gst_qt_mux_finalize (GObject * object);
     210                 :            : 
     211                 :            : static GstStateChangeReturn gst_qt_mux_change_state (GstElement * element,
     212                 :            :     GstStateChange transition);
     213                 :            : 
     214                 :            : /* property functions */
     215                 :            : static void gst_qt_mux_set_property (GObject * object,
     216                 :            :     guint prop_id, const GValue * value, GParamSpec * pspec);
     217                 :            : static void gst_qt_mux_get_property (GObject * object,
     218                 :            :     guint prop_id, GValue * value, GParamSpec * pspec);
     219                 :            : 
     220                 :            : /* pad functions */
     221                 :            : static GstPad *gst_qt_mux_request_new_pad (GstElement * element,
     222                 :            :     GstPadTemplate * templ, const gchar * name);
     223                 :            : static void gst_qt_mux_release_pad (GstElement * element, GstPad * pad);
     224                 :            : 
     225                 :            : /* event */
     226                 :            : static gboolean gst_qt_mux_sink_event (GstPad * pad, GstEvent * event);
     227                 :            : 
     228                 :            : static GstFlowReturn gst_qt_mux_collected (GstCollectPads * pads,
     229                 :            :     gpointer user_data);
     230                 :            : static GstFlowReturn gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad,
     231                 :            :     GstBuffer * buf);
     232                 :            : 
     233                 :            : static GstElementClass *parent_class = NULL;
     234                 :            : 
     235                 :            : static void
     236                 :         46 : gst_qt_mux_base_init (gpointer g_class)
     237                 :            : {
     238                 :         46 :   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
     239                 :         46 :   GstQTMuxClass *klass = (GstQTMuxClass *) g_class;
     240                 :            :   GstQTMuxClassParams *params;
     241                 :            :   GstPadTemplate *videosinktempl, *audiosinktempl, *srctempl;
     242                 :            :   gchar *longname, *description;
     243                 :            : 
     244                 :         46 :   params =
     245                 :         46 :       (GstQTMuxClassParams *) g_type_get_qdata (G_OBJECT_CLASS_TYPE (g_class),
     246                 :            :       GST_QT_MUX_PARAMS_QDATA);
     247         [ -  + ]:         46 :   g_assert (params != NULL);
     248                 :            : 
     249                 :            :   /* construct the element details struct */
     250                 :         46 :   longname = g_strdup_printf ("%s Muxer", params->prop->long_name);
     251                 :         46 :   description = g_strdup_printf ("Multiplex audio and video into a %s file",
     252                 :         46 :       params->prop->long_name);
     253                 :         46 :   gst_element_class_set_details_simple (element_class, longname,
     254                 :            :       "Codec/Muxer", description,
     255                 :            :       "Thiago Sousa Santos <thiagoss@embedded.ufcg.edu.br>");
     256                 :         46 :   g_free (longname);
     257                 :         46 :   g_free (description);
     258                 :            : 
     259                 :            :   /* pad templates */
     260                 :         46 :   srctempl = gst_pad_template_new ("src", GST_PAD_SRC,
     261                 :            :       GST_PAD_ALWAYS, params->src_caps);
     262                 :         46 :   gst_element_class_add_pad_template (element_class, srctempl);
     263                 :            : 
     264         [ +  - ]:         46 :   if (params->audio_sink_caps) {
     265                 :         46 :     audiosinktempl = gst_pad_template_new ("audio_%d",
     266                 :            :         GST_PAD_SINK, GST_PAD_REQUEST, params->audio_sink_caps);
     267                 :         46 :     gst_element_class_add_pad_template (element_class, audiosinktempl);
     268                 :            :   }
     269                 :            : 
     270         [ +  - ]:         46 :   if (params->video_sink_caps) {
     271                 :         46 :     videosinktempl = gst_pad_template_new ("video_%d",
     272                 :            :         GST_PAD_SINK, GST_PAD_REQUEST, params->video_sink_caps);
     273                 :         46 :     gst_element_class_add_pad_template (element_class, videosinktempl);
     274                 :            :   }
     275                 :            : 
     276                 :         46 :   klass->format = params->prop->format;
     277                 :         46 : }
     278                 :            : 
     279                 :            : static void
     280                 :         46 : gst_qt_mux_class_init (GstQTMuxClass * klass)
     281                 :            : {
     282                 :            :   GObjectClass *gobject_class;
     283                 :            :   GstElementClass *gstelement_class;
     284                 :            : 
     285                 :         46 :   gobject_class = (GObjectClass *) klass;
     286                 :         46 :   gstelement_class = (GstElementClass *) klass;
     287                 :            : 
     288                 :         46 :   parent_class = g_type_class_peek_parent (klass);
     289                 :            : 
     290                 :         46 :   gobject_class->finalize = gst_qt_mux_finalize;
     291                 :         46 :   gobject_class->get_property = gst_qt_mux_get_property;
     292                 :         46 :   gobject_class->set_property = gst_qt_mux_set_property;
     293                 :            : 
     294                 :         46 :   g_object_class_install_property (gobject_class, PROP_MOVIE_TIMESCALE,
     295                 :            :       g_param_spec_uint ("movie-timescale", "Movie timescale",
     296                 :            :           "Timescale to use in the movie (units per second)",
     297                 :            :           1, G_MAXUINT32, DEFAULT_MOVIE_TIMESCALE,
     298                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     299                 :         46 :   g_object_class_install_property (gobject_class, PROP_TRAK_TIMESCALE,
     300                 :            :       g_param_spec_uint ("trak-timescale", "Track timescale",
     301                 :            :           "Timescale to use for the tracks (units per second, 0 is automatic)",
     302                 :            :           0, G_MAXUINT32, DEFAULT_TRAK_TIMESCALE,
     303                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     304                 :         46 :   g_object_class_install_property (gobject_class, PROP_DO_CTTS,
     305                 :            :       g_param_spec_boolean ("presentation-time",
     306                 :            :           "Include presentation-time info",
     307                 :            :           "Calculate and include presentation/composition time "
     308                 :            :           "(in addition to decoding time) (use with caution)",
     309                 :            :           DEFAULT_DO_CTTS,
     310                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     311                 :         46 :   g_object_class_install_property (gobject_class, PROP_DTS_METHOD,
     312                 :            :       g_param_spec_enum ("dts-method", "dts-method",
     313                 :            :           "Method to determine DTS time",
     314                 :            :           GST_TYPE_QT_MUX_DTS_METHOD, DEFAULT_DTS_METHOD,
     315                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     316                 :         46 :   g_object_class_install_property (gobject_class, PROP_FAST_START,
     317                 :            :       g_param_spec_boolean ("faststart", "Format file to faststart",
     318                 :            :           "If the file should be formated for faststart (headers first). ",
     319                 :            :           DEFAULT_FAST_START, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
     320                 :         46 :   g_object_class_install_property (gobject_class, PROP_FAST_START_TEMP_FILE,
     321                 :            :       g_param_spec_string ("faststart-file", "File to use for storing buffers",
     322                 :            :           "File that will be used temporarily to store data from the stream "
     323                 :            :           "when creating a faststart file. If null a filepath will be "
     324                 :            :           "created automatically", DEFAULT_FAST_START_TEMP_FILE,
     325                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     326                 :         46 :   g_object_class_install_property (gobject_class, PROP_MOOV_RECOV_FILE,
     327                 :            :       g_param_spec_string ("moov-recovery-file",
     328                 :            :           "File to store data for posterior moov atom recovery",
     329                 :            :           "File to be used to store "
     330                 :            :           "data for moov atom making movie file recovery possible in case "
     331                 :            :           "of a crash during muxing. Null for disabled. (Experimental)",
     332                 :            :           DEFAULT_MOOV_RECOV_FILE,
     333                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     334         [ +  + ]:         46 :   g_object_class_install_property (gobject_class, PROP_FRAGMENT_DURATION,
     335                 :            :       g_param_spec_uint ("fragment-duration", "Fragment duration",
     336                 :            :           "Fragment durations in ms (produce a fragmented file if > 0)",
     337                 :         46 :           0, G_MAXUINT32, klass->format == GST_QT_MUX_FORMAT_ISML ?
     338                 :            :           2000 : DEFAULT_FRAGMENT_DURATION,
     339                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     340                 :         46 :   g_object_class_install_property (gobject_class, PROP_STREAMABLE,
     341                 :            :       g_param_spec_boolean ("streamable", "Streamable",
     342                 :            :           "If set to true, the output should be as if it is to be streamed "
     343                 :            :           "and hence no indexes written or duration written.",
     344                 :            :           DEFAULT_STREAMABLE,
     345                 :            :           G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));
     346                 :            : 
     347                 :         46 :   gstelement_class->request_new_pad =
     348                 :         46 :       GST_DEBUG_FUNCPTR (gst_qt_mux_request_new_pad);
     349                 :         46 :   gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_qt_mux_change_state);
     350                 :         46 :   gstelement_class->release_pad = GST_DEBUG_FUNCPTR (gst_qt_mux_release_pad);
     351                 :         46 : }
     352                 :            : 
     353                 :            : static void
     354                 :         65 : gst_qt_mux_pad_reset (GstQTPad * qtpad)
     355                 :            : {
     356                 :            :   gint i;
     357                 :            : 
     358                 :         65 :   qtpad->fourcc = 0;
     359                 :         65 :   qtpad->is_out_of_order = FALSE;
     360                 :         65 :   qtpad->have_dts = FALSE;
     361                 :         65 :   qtpad->sample_size = 0;
     362                 :         65 :   qtpad->sync = FALSE;
     363                 :         65 :   qtpad->last_dts = 0;
     364                 :         65 :   qtpad->first_ts = GST_CLOCK_TIME_NONE;
     365                 :         65 :   qtpad->prepare_buf_func = NULL;
     366                 :         65 :   qtpad->avg_bitrate = 0;
     367                 :         65 :   qtpad->max_bitrate = 0;
     368                 :         65 :   qtpad->ts_n_entries = 0;
     369                 :            : 
     370                 :         65 :   qtpad->buf_head = 0;
     371                 :         65 :   qtpad->buf_tail = 0;
     372         [ +  + ]:        845 :   for (i = 0; i < G_N_ELEMENTS (qtpad->buf_entries); i++) {
     373         [ -  + ]:        780 :     if (qtpad->buf_entries[i]) {
     374                 :          0 :       gst_buffer_unref (qtpad->buf_entries[i]);
     375                 :          0 :       qtpad->buf_entries[i] = NULL;
     376                 :            :     }
     377                 :            :   }
     378                 :            : 
     379         [ -  + ]:         65 :   if (qtpad->last_buf)
     380                 :          0 :     gst_buffer_replace (&qtpad->last_buf, NULL);
     381                 :            : 
     382                 :            :   /* reference owned elsewhere */
     383                 :         65 :   qtpad->trak = NULL;
     384                 :            : 
     385         [ +  + ]:         65 :   if (qtpad->traf) {
     386                 :          4 :     atom_traf_free (qtpad->traf);
     387                 :          4 :     qtpad->traf = NULL;
     388                 :            :   }
     389                 :         65 :   atom_array_clear (&qtpad->fragment_buffers);
     390                 :            : 
     391                 :            :   /* reference owned elsewhere */
     392                 :         65 :   qtpad->tfra = NULL;
     393                 :         65 : }
     394                 :            : 
     395                 :            : /*
     396                 :            :  * Takes GstQTMux back to its initial state
     397                 :            :  */
     398                 :            : static void
     399                 :        124 : gst_qt_mux_reset (GstQTMux * qtmux, gboolean alloc)
     400                 :            : {
     401                 :            :   GSList *walk;
     402                 :            : 
     403                 :        124 :   qtmux->state = GST_QT_MUX_STATE_NONE;
     404                 :        124 :   qtmux->header_size = 0;
     405                 :        124 :   qtmux->mdat_size = 0;
     406                 :        124 :   qtmux->mdat_pos = 0;
     407                 :        124 :   qtmux->longest_chunk = GST_CLOCK_TIME_NONE;
     408                 :        124 :   qtmux->video_pads = 0;
     409                 :        124 :   qtmux->audio_pads = 0;
     410                 :        124 :   qtmux->fragment_sequence = 0;
     411                 :            : 
     412         [ +  + ]:        124 :   if (qtmux->ftyp) {
     413                 :         16 :     atom_ftyp_free (qtmux->ftyp);
     414                 :         16 :     qtmux->ftyp = NULL;
     415                 :            :   }
     416         [ +  + ]:        124 :   if (qtmux->moov) {
     417                 :         88 :     atom_moov_free (qtmux->moov);
     418                 :         88 :     qtmux->moov = NULL;
     419                 :            :   }
     420         [ +  + ]:        124 :   if (qtmux->mfra) {
     421                 :          2 :     atom_mfra_free (qtmux->mfra);
     422                 :          2 :     qtmux->mfra = NULL;
     423                 :            :   }
     424         [ -  + ]:        124 :   if (qtmux->fast_start_file) {
     425                 :          0 :     fclose (qtmux->fast_start_file);
     426                 :          0 :     g_remove (qtmux->fast_start_file_path);
     427                 :          0 :     qtmux->fast_start_file = NULL;
     428                 :            :   }
     429         [ -  + ]:        124 :   if (qtmux->moov_recov_file) {
     430                 :          0 :     fclose (qtmux->moov_recov_file);
     431                 :          0 :     qtmux->moov_recov_file = NULL;
     432                 :            :   }
     433 [ +  - ][ +  + ]:        130 :   for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
     434                 :          6 :     AtomInfo *ainfo = (AtomInfo *) walk->data;
     435                 :          6 :     ainfo->free_func (ainfo->atom);
     436                 :          6 :     g_free (ainfo);
     437                 :            :   }
     438                 :        124 :   g_slist_free (qtmux->extra_atoms);
     439                 :        124 :   qtmux->extra_atoms = NULL;
     440                 :            : 
     441                 :        124 :   GST_OBJECT_LOCK (qtmux);
     442                 :        124 :   gst_tag_setter_reset_tags (GST_TAG_SETTER (qtmux));
     443                 :        124 :   GST_OBJECT_UNLOCK (qtmux);
     444                 :            : 
     445                 :            :   /* reset pad data */
     446 [ +  - ][ +  + ]:        157 :   for (walk = qtmux->sinkpads; walk; walk = g_slist_next (walk)) {
     447                 :         33 :     GstQTPad *qtpad = (GstQTPad *) walk->data;
     448                 :         33 :     gst_qt_mux_pad_reset (qtpad);
     449                 :            : 
     450                 :            :     /* hm, moov_free above yanked the traks away from us,
     451                 :            :      * so do not free, but do clear */
     452                 :         33 :     qtpad->trak = NULL;
     453                 :            :   }
     454                 :            : 
     455         [ +  + ]:        124 :   if (alloc) {
     456                 :         88 :     qtmux->moov = atom_moov_new (qtmux->context);
     457                 :            :     /* ensure all is as nice and fresh as request_new_pad would provide it */
     458 [ +  - ][ +  + ]:        105 :     for (walk = qtmux->sinkpads; walk; walk = g_slist_next (walk)) {
     459                 :         17 :       GstQTPad *qtpad = (GstQTPad *) walk->data;
     460                 :            : 
     461                 :         17 :       qtpad->trak = atom_trak_new (qtmux->context);
     462                 :         17 :       atom_moov_add_trak (qtmux->moov, qtpad->trak);
     463                 :            :     }
     464                 :            :   }
     465                 :        124 : }
     466                 :            : 
     467                 :            : static void
     468                 :         36 : gst_qt_mux_init (GstQTMux * qtmux, GstQTMuxClass * qtmux_klass)
     469                 :            : {
     470                 :         36 :   GstElementClass *klass = GST_ELEMENT_CLASS (qtmux_klass);
     471                 :            :   GstPadTemplate *templ;
     472                 :            : 
     473                 :         36 :   templ = gst_element_class_get_pad_template (klass, "src");
     474                 :         36 :   qtmux->srcpad = gst_pad_new_from_template (templ, "src");
     475                 :         36 :   gst_pad_use_fixed_caps (qtmux->srcpad);
     476                 :         36 :   gst_element_add_pad (GST_ELEMENT (qtmux), qtmux->srcpad);
     477                 :            : 
     478                 :         36 :   qtmux->sinkpads = NULL;
     479                 :         36 :   qtmux->collect = gst_collect_pads_new ();
     480                 :         36 :   gst_collect_pads_set_function (qtmux->collect,
     481                 :         36 :       (GstCollectPadsFunction) GST_DEBUG_FUNCPTR (gst_qt_mux_collected), qtmux);
     482                 :            : 
     483                 :            :   /* properties set to default upon construction */
     484                 :            : 
     485                 :            :   /* always need this */
     486                 :         36 :   qtmux->context =
     487                 :         36 :       atoms_context_new (gst_qt_mux_map_format_to_flavor (qtmux_klass->format));
     488                 :            : 
     489                 :            :   /* internals to initial state */
     490                 :         36 :   gst_qt_mux_reset (qtmux, TRUE);
     491                 :         36 : }
     492                 :            : 
     493                 :            : 
     494                 :            : static void
     495                 :         36 : gst_qt_mux_finalize (GObject * object)
     496                 :            : {
     497                 :         36 :   GstQTMux *qtmux = GST_QT_MUX_CAST (object);
     498                 :            : 
     499                 :         36 :   gst_qt_mux_reset (qtmux, FALSE);
     500                 :            : 
     501                 :         36 :   g_free (qtmux->fast_start_file_path);
     502                 :         36 :   g_free (qtmux->moov_recov_file_path);
     503                 :            : 
     504                 :         36 :   atoms_context_free (qtmux->context);
     505                 :         36 :   gst_object_unref (qtmux->collect);
     506                 :            : 
     507                 :         36 :   g_slist_free (qtmux->sinkpads);
     508                 :            : 
     509                 :         36 :   G_OBJECT_CLASS (parent_class)->finalize (object);
     510                 :         36 : }
     511                 :            : 
     512                 :            : static GstBuffer *
     513                 :          0 : gst_qt_mux_prepare_jpc_buffer (GstQTPad * qtpad, GstBuffer * buf,
     514                 :            :     GstQTMux * qtmux)
     515                 :            : {
     516                 :            :   GstBuffer *newbuf;
     517                 :            : 
     518         [ #  # ]:          0 :   GST_LOG_OBJECT (qtmux, "Preparing jpc buffer");
     519                 :            : 
     520         [ #  # ]:          0 :   if (buf == NULL)
     521                 :          0 :     return NULL;
     522                 :            : 
     523                 :          0 :   newbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 8);
     524                 :          0 :   gst_buffer_copy_metadata (newbuf, buf, GST_BUFFER_COPY_ALL);
     525                 :            : 
     526                 :          0 :   GST_WRITE_UINT32_BE (GST_BUFFER_DATA (newbuf), GST_BUFFER_SIZE (newbuf));
     527                 :          0 :   GST_WRITE_UINT32_LE (GST_BUFFER_DATA (newbuf) + 4, FOURCC_jp2c);
     528                 :            : 
     529                 :          0 :   memcpy (GST_BUFFER_DATA (newbuf) + 8, GST_BUFFER_DATA (buf),
     530                 :          0 :       GST_BUFFER_SIZE (buf));
     531                 :          0 :   gst_buffer_unref (buf);
     532                 :            : 
     533                 :          0 :   return newbuf;
     534                 :            : }
     535                 :            : 
     536                 :            : static void
     537                 :        156 : gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list,
     538                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     539                 :            : {
     540   [ +  +  +  - ]:        156 :   switch (gst_tag_get_type (tag)) {
     541                 :            :       /* strings */
     542                 :            :     case G_TYPE_STRING:
     543                 :            :     {
     544                 :        126 :       gchar *str = NULL;
     545                 :            : 
     546 [ +  + ][ +  - ]:        126 :       if (!gst_tag_list_get_string (list, tag, &str) || !str)
     547                 :            :         break;
     548         [ -  + ]:          8 :       GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
     549                 :            :           GST_FOURCC_ARGS (fourcc), str);
     550                 :          8 :       atom_moov_add_str_tag (qtmux->moov, fourcc, str);
     551                 :          8 :       g_free (str);
     552                 :          8 :       break;
     553                 :            :     }
     554                 :            :       /* double */
     555                 :            :     case G_TYPE_DOUBLE:
     556                 :            :     {
     557                 :            :       gdouble value;
     558                 :            : 
     559         [ +  - ]:          6 :       if (!gst_tag_list_get_double (list, tag, &value))
     560                 :          6 :         break;
     561         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
     562                 :            :           GST_FOURCC_ARGS (fourcc), (gint) value);
     563                 :          0 :       atom_moov_add_uint_tag (qtmux->moov, fourcc, 21, (gint) value);
     564                 :          0 :       break;
     565                 :            :     }
     566                 :            :     case G_TYPE_UINT:
     567                 :            :     {
     568                 :         24 :       guint value = 0;
     569         [ +  + ]:         24 :       if (tag2) {
     570                 :            :         /* paired unsigned integers */
     571                 :         12 :         guint count = 0;
     572                 :            : 
     573   [ +  -  +  - ]:         24 :         if (!(gst_tag_list_get_uint (list, tag, &value) ||
     574                 :         12 :                 gst_tag_list_get_uint (list, tag2, &count)))
     575                 :         12 :           break;
     576         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u/%u",
     577                 :            :             GST_FOURCC_ARGS (fourcc), value, count);
     578                 :          0 :         atom_moov_add_uint_tag (qtmux->moov, fourcc, 0,
     579                 :          0 :             value << 16 | (count & 0xFFFF));
     580                 :            :       } else {
     581                 :            :         /* unpaired unsigned integers */
     582         [ +  - ]:         12 :         if (!gst_tag_list_get_uint (list, tag, &value))
     583                 :         12 :           break;
     584         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %u",
     585                 :            :             GST_FOURCC_ARGS (fourcc), value);
     586                 :          0 :         atom_moov_add_uint_tag (qtmux->moov, fourcc, 1, value);
     587                 :            :       }
     588                 :          0 :       break;
     589                 :            :     }
     590                 :            :     default:
     591                 :          0 :       g_assert_not_reached ();
     592                 :            :       break;
     593                 :            :   }
     594                 :        156 : }
     595                 :            : 
     596                 :            : static void
     597                 :          6 : gst_qt_mux_add_mp4_date (GstQTMux * qtmux, const GstTagList * list,
     598                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     599                 :            : {
     600                 :          6 :   GDate *date = NULL;
     601                 :            :   GDateYear year;
     602                 :            :   GDateMonth month;
     603                 :            :   GDateDay day;
     604                 :            :   gchar *str;
     605                 :            : 
     606         [ -  + ]:          6 :   g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
     607                 :            : 
     608 [ -  + ][ #  # ]:          6 :   if (!gst_tag_list_get_date (list, tag, &date) || !date)
     609                 :          6 :     return;
     610                 :            : 
     611                 :          0 :   year = g_date_get_year (date);
     612                 :          0 :   month = g_date_get_month (date);
     613                 :          0 :   day = g_date_get_day (date);
     614                 :            : 
     615 [ #  # ][ #  # ]:          0 :   if (year == G_DATE_BAD_YEAR && month == G_DATE_BAD_MONTH &&
                 [ #  # ]
     616                 :            :       day == G_DATE_BAD_DAY) {
     617         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "invalid date in tag");
     618                 :          0 :     return;
     619                 :            :   }
     620                 :            : 
     621                 :          0 :   str = g_strdup_printf ("%u-%u-%u", year, month, day);
     622         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
     623                 :            :       GST_FOURCC_ARGS (fourcc), str);
     624                 :          0 :   atom_moov_add_str_tag (qtmux->moov, fourcc, str);
     625                 :          6 :   g_free (str);
     626                 :            : }
     627                 :            : 
     628                 :            : static void
     629                 :          6 : gst_qt_mux_add_mp4_cover (GstQTMux * qtmux, const GstTagList * list,
     630                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     631                 :            : {
     632                 :          6 :   GValue value = { 0, };
     633                 :            :   GstBuffer *buf;
     634                 :            :   GstCaps *caps;
     635                 :            :   GstStructure *structure;
     636                 :          6 :   gint flags = 0;
     637                 :            : 
     638         [ -  + ]:          6 :   g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_BUFFER);
     639                 :            : 
     640         [ +  - ]:          6 :   if (!gst_tag_list_copy_value (&value, list, tag))
     641                 :          6 :     return;
     642                 :            : 
     643                 :          0 :   buf = gst_value_get_buffer (&value);
     644         [ #  # ]:          0 :   if (!buf)
     645                 :          0 :     goto done;
     646                 :            : 
     647                 :          0 :   caps = gst_buffer_get_caps (buf);
     648         [ #  # ]:          0 :   if (!caps) {
     649         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "preview image without caps");
     650                 :          0 :     goto done;
     651                 :            :   }
     652                 :            : 
     653         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "preview image caps %" GST_PTR_FORMAT, caps);
     654                 :            : 
     655                 :          0 :   structure = gst_caps_get_structure (caps, 0);
     656         [ #  # ]:          0 :   if (gst_structure_has_name (structure, "image/jpeg"))
     657                 :          0 :     flags = 13;
     658         [ #  # ]:          0 :   else if (gst_structure_has_name (structure, "image/png"))
     659                 :          0 :     flags = 14;
     660                 :          0 :   gst_caps_unref (caps);
     661                 :            : 
     662         [ #  # ]:          0 :   if (!flags) {
     663         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "preview image format not supported");
     664                 :          0 :     goto done;
     665                 :            :   }
     666                 :            : 
     667         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT
     668                 :            :       " -> image size %d", GST_FOURCC_ARGS (fourcc), GST_BUFFER_SIZE (buf));
     669                 :          0 :   atom_moov_add_tag (qtmux->moov, fourcc, flags, GST_BUFFER_DATA (buf),
     670                 :            :       GST_BUFFER_SIZE (buf));
     671                 :            : done:
     672                 :          6 :   g_value_unset (&value);
     673                 :            : }
     674                 :            : 
     675                 :            : static void
     676                 :         21 : gst_qt_mux_add_3gp_str (GstQTMux * qtmux, const GstTagList * list,
     677                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     678                 :            : {
     679                 :         21 :   gchar *str = NULL;
     680                 :            :   guint number;
     681                 :            : 
     682         [ -  + ]:         21 :   g_return_if_fail (gst_tag_get_type (tag) == G_TYPE_STRING);
     683 [ +  + ][ -  + ]:         21 :   g_return_if_fail (!tag2 || gst_tag_get_type (tag2) == G_TYPE_UINT);
     684                 :            : 
     685 [ +  + ][ -  + ]:         21 :   if (!gst_tag_list_get_string (list, tag, &str) || !str)
     686                 :         18 :     return;
     687                 :            : 
     688         [ -  + ]:          3 :   if (tag2)
     689         [ #  # ]:          0 :     if (!gst_tag_list_get_uint (list, tag2, &number))
     690                 :          0 :       tag2 = NULL;
     691                 :            : 
     692         [ +  - ]:          3 :   if (!tag2) {
     693         [ -  + ]:          3 :     GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
     694                 :            :         GST_FOURCC_ARGS (fourcc), str);
     695                 :          3 :     atom_moov_add_3gp_str_tag (qtmux->moov, fourcc, str);
     696                 :            :   } else {
     697         [ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s/%d",
     698                 :            :         GST_FOURCC_ARGS (fourcc), str, number);
     699                 :          0 :     atom_moov_add_3gp_str_int_tag (qtmux->moov, fourcc, str, number);
     700                 :            :   }
     701                 :            : 
     702                 :         21 :   g_free (str);
     703                 :            : }
     704                 :            : 
     705                 :            : static void
     706                 :          3 : gst_qt_mux_add_3gp_date (GstQTMux * qtmux, const GstTagList * list,
     707                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     708                 :            : {
     709                 :          3 :   GDate *date = NULL;
     710                 :            :   GDateYear year;
     711                 :            : 
     712         [ -  + ]:          3 :   g_return_if_fail (gst_tag_get_type (tag) == GST_TYPE_DATE);
     713                 :            : 
     714 [ -  + ][ #  # ]:          3 :   if (!gst_tag_list_get_date (list, tag, &date) || !date)
     715                 :          3 :     return;
     716                 :            : 
     717                 :          0 :   year = g_date_get_year (date);
     718                 :            : 
     719         [ #  # ]:          0 :   if (year == G_DATE_BAD_YEAR) {
     720         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "invalid date in tag");
     721                 :          0 :     return;
     722                 :            :   }
     723                 :            : 
     724         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %d",
     725                 :            :       GST_FOURCC_ARGS (fourcc), year);
     726                 :          3 :   atom_moov_add_3gp_uint_tag (qtmux->moov, fourcc, year);
     727                 :            : }
     728                 :            : 
     729                 :            : static void
     730                 :          3 : gst_qt_mux_add_3gp_location (GstQTMux * qtmux, const GstTagList * list,
     731                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     732                 :            : {
     733                 :          3 :   gdouble latitude = -360, longitude = -360, altitude = 0;
     734                 :          3 :   gchar *location = NULL;
     735                 :            :   guint8 *data, *ddata;
     736                 :          3 :   gint size = 0, len = 0;
     737                 :          3 :   gboolean ret = FALSE;
     738                 :            : 
     739         [ -  + ]:          3 :   g_return_if_fail (strcmp (tag, GST_TAG_GEO_LOCATION_NAME) == 0);
     740                 :            : 
     741                 :          3 :   ret = gst_tag_list_get_string (list, tag, &location);
     742                 :          3 :   ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LONGITUDE,
     743                 :            :       &longitude);
     744                 :          3 :   ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_LATITUDE,
     745                 :            :       &latitude);
     746                 :          3 :   ret |= gst_tag_list_get_double (list, GST_TAG_GEO_LOCATION_ELEVATION,
     747                 :            :       &altitude);
     748                 :            : 
     749         [ +  + ]:          3 :   if (!ret)
     750                 :          2 :     return;
     751                 :            : 
     752         [ -  + ]:          1 :   if (location)
     753                 :          0 :     len = strlen (location);
     754                 :          1 :   size += len + 1 + 2;
     755                 :            : 
     756                 :            :   /* role + (long, lat, alt) + body + notes */
     757                 :          1 :   size += 1 + 3 * 4 + 1 + 1;
     758                 :            : 
     759                 :          1 :   data = ddata = g_malloc (size);
     760                 :            : 
     761                 :            :   /* language tag */
     762                 :          1 :   GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
     763                 :            :   /* location */
     764         [ -  + ]:          1 :   if (location)
     765                 :          0 :     memcpy (data + 2, location, len);
     766                 :          1 :   GST_WRITE_UINT8 (data + 2 + len, 0);
     767                 :          1 :   data += len + 1 + 2;
     768                 :            :   /* role */
     769                 :          1 :   GST_WRITE_UINT8 (data, 0);
     770                 :            :   /* long, lat, alt */
     771                 :          1 :   GST_WRITE_UINT32_BE (data + 1, (guint32) (longitude * 65536.0));
     772                 :          1 :   GST_WRITE_UINT32_BE (data + 5, (guint32) (latitude * 65536.0));
     773                 :          1 :   GST_WRITE_UINT32_BE (data + 9, (guint32) (altitude * 65536.0));
     774                 :            :   /* neither astronomical body nor notes */
     775                 :          1 :   GST_WRITE_UINT16_BE (data + 13, 0);
     776                 :            : 
     777         [ -  + ]:          1 :   GST_DEBUG_OBJECT (qtmux, "Adding tag 'loci'");
     778                 :          1 :   atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
     779                 :          3 :   g_free (ddata);
     780                 :            : }
     781                 :            : 
     782                 :            : static void
     783                 :          3 : gst_qt_mux_add_3gp_keywords (GstQTMux * qtmux, const GstTagList * list,
     784                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     785                 :            : {
     786                 :          3 :   gchar *keywords = NULL;
     787                 :            :   guint8 *data, *ddata;
     788                 :          3 :   gint size = 0, i;
     789                 :            :   gchar **kwds;
     790                 :            : 
     791         [ -  + ]:          3 :   g_return_if_fail (strcmp (tag, GST_TAG_KEYWORDS) == 0);
     792                 :            : 
     793 [ +  + ][ -  + ]:          3 :   if (!gst_tag_list_get_string (list, tag, &keywords) || !keywords)
     794                 :          2 :     return;
     795                 :            : 
     796                 :          1 :   kwds = g_strsplit (keywords, ",", 0);
     797                 :          1 :   g_free (keywords);
     798                 :            : 
     799                 :          1 :   size = 0;
     800         [ +  + ]:          3 :   for (i = 0; kwds[i]; i++) {
     801                 :            :     /* size byte + null-terminator */
     802                 :          2 :     size += strlen (kwds[i]) + 1 + 1;
     803                 :            :   }
     804                 :            : 
     805                 :            :   /* language tag + count + keywords */
     806                 :          1 :   size += 2 + 1;
     807                 :            : 
     808                 :          1 :   data = ddata = g_malloc (size);
     809                 :            : 
     810                 :            :   /* language tag */
     811                 :          1 :   GST_WRITE_UINT16_BE (data, language_code (GST_QT_MUX_DEFAULT_TAG_LANGUAGE));
     812                 :            :   /* count */
     813                 :          1 :   GST_WRITE_UINT8 (data + 2, i);
     814                 :          1 :   data += 3;
     815                 :            :   /* keywords */
     816         [ +  + ]:          3 :   for (i = 0; kwds[i]; ++i) {
     817                 :          2 :     gint len = strlen (kwds[i]);
     818                 :            : 
     819         [ -  + ]:          2 :     GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
     820                 :            :         GST_FOURCC_ARGS (fourcc), kwds[i]);
     821                 :            :     /* size */
     822                 :          2 :     GST_WRITE_UINT8 (data, len + 1);
     823                 :          2 :     memcpy (data + 1, kwds[i], len + 1);
     824                 :          2 :     data += len + 2;
     825                 :            :   }
     826                 :            : 
     827                 :          1 :   g_strfreev (kwds);
     828                 :            : 
     829                 :          1 :   atom_moov_add_3gp_tag (qtmux->moov, fourcc, ddata, size);
     830                 :          3 :   g_free (ddata);
     831                 :            : }
     832                 :            : 
     833                 :            : static gboolean
     834                 :          0 : gst_qt_mux_parse_classification_string (GstQTMux * qtmux, const gchar * input,
     835                 :            :     guint32 * p_fourcc, guint16 * p_table, gchar ** p_content)
     836                 :            : {
     837                 :            :   guint32 fourcc;
     838                 :            :   gint table;
     839                 :            :   gint size;
     840                 :            :   const gchar *data;
     841                 :            : 
     842                 :          0 :   data = input;
     843                 :          0 :   size = strlen (input);
     844                 :            : 
     845         [ #  # ]:          0 :   if (size < 4 + 3 + 1 + 1 + 1) {
     846                 :            :     /* at least the minimum xxxx://y/z */
     847         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "Classification tag input (%s) too short, "
     848                 :            :         "ignoring", input);
     849                 :          0 :     return FALSE;
     850                 :            :   }
     851                 :            : 
     852                 :            :   /* read the fourcc */
     853                 :          0 :   memcpy (&fourcc, data, 4);
     854                 :          0 :   size -= 4;
     855                 :          0 :   data += 4;
     856                 :            : 
     857         [ #  # ]:          0 :   if (strncmp (data, "://", 3) != 0) {
     858                 :          0 :     goto mismatch;
     859                 :            :   }
     860                 :          0 :   data += 3;
     861                 :          0 :   size -= 3;
     862                 :            : 
     863                 :            :   /* read the table number */
     864         [ #  # ]:          0 :   if (sscanf (data, "%d", &table) != 1) {
     865                 :          0 :     goto mismatch;
     866                 :            :   }
     867         [ #  # ]:          0 :   if (table < 0) {
     868         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "Invalid table number in classification tag (%d)"
     869                 :            :         ", table numbers should be positive, ignoring tag", table);
     870                 :          0 :     return FALSE;
     871                 :            :   }
     872                 :            : 
     873                 :            :   /* find the next / */
     874 [ #  # ][ #  # ]:          0 :   while (size > 0 && data[0] != '/') {
     875                 :          0 :     data += 1;
     876                 :          0 :     size -= 1;
     877                 :            :   }
     878         [ #  # ]:          0 :   if (size == 0) {
     879                 :          0 :     goto mismatch;
     880                 :            :   }
     881         [ #  # ]:          0 :   g_assert (data[0] == '/');
     882                 :            : 
     883                 :            :   /* skip the '/' */
     884                 :          0 :   data += 1;
     885                 :          0 :   size -= 1;
     886         [ #  # ]:          0 :   if (size == 0) {
     887                 :          0 :     goto mismatch;
     888                 :            :   }
     889                 :            : 
     890                 :            :   /* read up the rest of the string */
     891                 :          0 :   *p_content = g_strdup (data);
     892                 :          0 :   *p_table = (guint16) table;
     893                 :          0 :   *p_fourcc = fourcc;
     894                 :          0 :   return TRUE;
     895                 :            : 
     896                 :            : mismatch:
     897                 :            :   {
     898         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "Ignoring classification tag as "
     899                 :            :         "input (%s) didn't match the expected entitycode://table/content",
     900                 :            :         input);
     901                 :          0 :     return FALSE;
     902                 :            :   }
     903                 :            : }
     904                 :            : 
     905                 :            : static void
     906                 :          3 : gst_qt_mux_add_3gp_classification (GstQTMux * qtmux, const GstTagList * list,
     907                 :            :     const char *tag, const char *tag2, guint32 fourcc)
     908                 :            : {
     909                 :          3 :   gchar *clsf_data = NULL;
     910                 :          3 :   gint size = 0;
     911                 :          3 :   guint32 entity = 0;
     912                 :          3 :   guint16 table = 0;
     913                 :          3 :   gchar *content = NULL;
     914                 :            :   guint8 *data;
     915                 :            : 
     916         [ -  + ]:          3 :   g_return_if_fail (strcmp (tag, GST_TAG_3GP_CLASSIFICATION) == 0);
     917                 :            : 
     918 [ -  + ][ #  # ]:          3 :   if (!gst_tag_list_get_string (list, tag, &clsf_data) || !clsf_data)
     919                 :          3 :     return;
     920                 :            : 
     921         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "Adding tag %" GST_FOURCC_FORMAT " -> %s",
     922                 :            :       GST_FOURCC_ARGS (fourcc), clsf_data);
     923                 :            : 
     924                 :            :   /* parse the string, format is:
     925                 :            :    * entityfourcc://table/content
     926                 :            :    */
     927                 :          0 :   gst_qt_mux_parse_classification_string (qtmux, clsf_data, &entity, &table,
     928                 :            :       &content);
     929                 :          0 :   g_free (clsf_data);
     930                 :            :   /* +1 for the \0 */
     931                 :          0 :   size = strlen (content) + 1;
     932                 :            : 
     933                 :            :   /* now we have everything, build the atom
     934                 :            :    * atom description is at 3GPP TS 26.244 V8.2.0 (2009-09) */
     935                 :          0 :   data = g_malloc (4 + 2 + 2 + size);
     936                 :          0 :   GST_WRITE_UINT32_LE (data, entity);
     937                 :          0 :   GST_WRITE_UINT16_BE (data + 4, (guint16) table);
     938                 :          0 :   GST_WRITE_UINT16_BE (data + 6, 0);
     939                 :          0 :   memcpy (data + 8, content, size);
     940                 :          0 :   g_free (content);
     941                 :            : 
     942                 :          0 :   atom_moov_add_3gp_tag (qtmux->moov, fourcc, data, 4 + 2 + 2 + size);
     943                 :          3 :   g_free (data);
     944                 :            : }
     945                 :            : 
     946                 :            : typedef void (*GstQTMuxAddTagFunc) (GstQTMux * mux, const GstTagList * list,
     947                 :            :     const char *tag, const char *tag2, guint32 fourcc);
     948                 :            : 
     949                 :            : /*
     950                 :            :  * Struct to record mappings from gstreamer tags to fourcc codes
     951                 :            :  */
     952                 :            : typedef struct _GstTagToFourcc
     953                 :            : {
     954                 :            :   guint32 fourcc;
     955                 :            :   const gchar *gsttag;
     956                 :            :   const gchar *gsttag2;
     957                 :            :   const GstQTMuxAddTagFunc func;
     958                 :            : } GstTagToFourcc;
     959                 :            : 
     960                 :            : /* tag list tags to fourcc matching */
     961                 :            : static const GstTagToFourcc tag_matches_mp4[] = {
     962                 :            :   {FOURCC__alb, GST_TAG_ALBUM, NULL, gst_qt_mux_add_mp4_tag},
     963                 :            :   {FOURCC_soal, GST_TAG_ALBUM_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     964                 :            :   {FOURCC__ART, GST_TAG_ARTIST, NULL, gst_qt_mux_add_mp4_tag},
     965                 :            :   {FOURCC_soar, GST_TAG_ARTIST_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     966                 :            :   {FOURCC_aART, GST_TAG_ALBUM_ARTIST, NULL, gst_qt_mux_add_mp4_tag},
     967                 :            :   {FOURCC_soaa, GST_TAG_ALBUM_ARTIST_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     968                 :            :   {FOURCC__cmt, GST_TAG_COMMENT, NULL, gst_qt_mux_add_mp4_tag},
     969                 :            :   {FOURCC__wrt, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_mp4_tag},
     970                 :            :   {FOURCC_soco, GST_TAG_COMPOSER_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     971                 :            :   {FOURCC_tvsh, GST_TAG_SHOW_NAME, NULL, gst_qt_mux_add_mp4_tag},
     972                 :            :   {FOURCC_sosn, GST_TAG_SHOW_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     973                 :            :   {FOURCC_tvsn, GST_TAG_SHOW_SEASON_NUMBER, NULL, gst_qt_mux_add_mp4_tag},
     974                 :            :   {FOURCC_tves, GST_TAG_SHOW_EPISODE_NUMBER, NULL, gst_qt_mux_add_mp4_tag},
     975                 :            :   {FOURCC__gen, GST_TAG_GENRE, NULL, gst_qt_mux_add_mp4_tag},
     976                 :            :   {FOURCC__nam, GST_TAG_TITLE, NULL, gst_qt_mux_add_mp4_tag},
     977                 :            :   {FOURCC_sonm, GST_TAG_TITLE_SORTNAME, NULL, gst_qt_mux_add_mp4_tag},
     978                 :            :   {FOURCC_perf, GST_TAG_PERFORMER, NULL, gst_qt_mux_add_mp4_tag},
     979                 :            :   {FOURCC__grp, GST_TAG_GROUPING, NULL, gst_qt_mux_add_mp4_tag},
     980                 :            :   {FOURCC__des, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_mp4_tag},
     981                 :            :   {FOURCC__lyr, GST_TAG_LYRICS, NULL, gst_qt_mux_add_mp4_tag},
     982                 :            :   {FOURCC__too, GST_TAG_ENCODER, NULL, gst_qt_mux_add_mp4_tag},
     983                 :            :   {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_mp4_tag},
     984                 :            :   {FOURCC_keyw, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_mp4_tag},
     985                 :            :   {FOURCC__day, GST_TAG_DATE, NULL, gst_qt_mux_add_mp4_date},
     986                 :            :   {FOURCC_tmpo, GST_TAG_BEATS_PER_MINUTE, NULL, gst_qt_mux_add_mp4_tag},
     987                 :            :   {FOURCC_trkn, GST_TAG_TRACK_NUMBER, GST_TAG_TRACK_COUNT,
     988                 :            :       gst_qt_mux_add_mp4_tag},
     989                 :            :   {FOURCC_disk, GST_TAG_ALBUM_VOLUME_NUMBER, GST_TAG_ALBUM_VOLUME_COUNT,
     990                 :            :       gst_qt_mux_add_mp4_tag},
     991                 :            :   {FOURCC_covr, GST_TAG_PREVIEW_IMAGE, NULL, gst_qt_mux_add_mp4_cover},
     992                 :            :   {0, NULL,}
     993                 :            : };
     994                 :            : 
     995                 :            : static const GstTagToFourcc tag_matches_3gp[] = {
     996                 :            :   {FOURCC_titl, GST_TAG_TITLE, NULL, gst_qt_mux_add_3gp_str},
     997                 :            :   {FOURCC_dscp, GST_TAG_DESCRIPTION, NULL, gst_qt_mux_add_3gp_str},
     998                 :            :   {FOURCC_cprt, GST_TAG_COPYRIGHT, NULL, gst_qt_mux_add_3gp_str},
     999                 :            :   {FOURCC_perf, GST_TAG_ARTIST, NULL, gst_qt_mux_add_3gp_str},
    1000                 :            :   {FOURCC_auth, GST_TAG_COMPOSER, NULL, gst_qt_mux_add_3gp_str},
    1001                 :            :   {FOURCC_gnre, GST_TAG_GENRE, NULL, gst_qt_mux_add_3gp_str},
    1002                 :            :   {FOURCC_kywd, GST_TAG_KEYWORDS, NULL, gst_qt_mux_add_3gp_keywords},
    1003                 :            :   {FOURCC_yrrc, GST_TAG_DATE, NULL, gst_qt_mux_add_3gp_date},
    1004                 :            :   {FOURCC_albm, GST_TAG_ALBUM, GST_TAG_TRACK_NUMBER, gst_qt_mux_add_3gp_str},
    1005                 :            :   {FOURCC_loci, GST_TAG_GEO_LOCATION_NAME, NULL, gst_qt_mux_add_3gp_location},
    1006                 :            :   {FOURCC_clsf, GST_TAG_3GP_CLASSIFICATION, NULL,
    1007                 :            :       gst_qt_mux_add_3gp_classification},
    1008                 :            :   {0, NULL,}
    1009                 :            : };
    1010                 :            : 
    1011                 :            : /* qtdemux produces these for atoms it cannot parse */
    1012                 :            : #define GST_QT_DEMUX_PRIVATE_TAG "private-qt-tag"
    1013                 :            : 
    1014                 :            : static void
    1015                 :          9 : gst_qt_mux_add_xmp_tags (GstQTMux * qtmux, const GstTagList * list)
    1016                 :            : {
    1017                 :          9 :   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
    1018                 :            : 
    1019                 :            :   /* adobe specs only have 'quicktime' and 'mp4',
    1020                 :            :    * but I guess we can extrapolate to gpp.
    1021                 :            :    * Keep mj2 out for now as we don't add any tags for it yet.
    1022                 :            :    * If you have further info about xmp on these formats, please share */
    1023         [ -  + ]:          9 :   if (qtmux_klass->format == GST_QT_MUX_FORMAT_MJ2)
    1024                 :          9 :     return;
    1025                 :            : 
    1026         [ -  + ]:          9 :   GST_DEBUG_OBJECT (qtmux, "Adding xmp tags");
    1027                 :            : 
    1028         [ +  + ]:          9 :   if (qtmux_klass->format == GST_QT_MUX_FORMAT_QT) {
    1029                 :          3 :     atom_moov_add_xmp_tags (qtmux->moov, list);
    1030                 :            :   } else {
    1031                 :            :     /* for isom/mp4, it is a top level uuid atom */
    1032                 :          6 :     AtomInfo *ainfo = build_uuid_xmp_atom (list);
    1033         [ +  - ]:          6 :     if (ainfo) {
    1034                 :          6 :       qtmux->extra_atoms = g_slist_prepend (qtmux->extra_atoms, ainfo);
    1035                 :            :     }
    1036                 :            :   }
    1037                 :            : }
    1038                 :            : 
    1039                 :            : static void
    1040                 :          9 : gst_qt_mux_add_metadata_tags (GstQTMux * qtmux, const GstTagList * list)
    1041                 :            : {
    1042                 :          9 :   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
    1043                 :            :   guint32 fourcc;
    1044                 :            :   gint i;
    1045                 :            :   const gchar *tag, *tag2;
    1046                 :            :   const GstTagToFourcc *tag_matches;
    1047                 :            : 
    1048      [ +  -  + ]:          9 :   switch (qtmux_klass->format) {
    1049                 :            :     case GST_QT_MUX_FORMAT_3GP:
    1050                 :          3 :       tag_matches = tag_matches_3gp;
    1051                 :          3 :       break;
    1052                 :            :     case GST_QT_MUX_FORMAT_MJ2:
    1053                 :          0 :       tag_matches = NULL;
    1054                 :          0 :       break;
    1055                 :            :     default:
    1056                 :            :       /* sort of iTunes style for mp4 and QT (?) */
    1057                 :          6 :       tag_matches = tag_matches_mp4;
    1058                 :          6 :       break;
    1059                 :            :   }
    1060                 :            : 
    1061         [ -  + ]:          9 :   if (!tag_matches)
    1062                 :          0 :     return;
    1063                 :            : 
    1064         [ +  + ]:        210 :   for (i = 0; tag_matches[i].fourcc; i++) {
    1065                 :        201 :     fourcc = tag_matches[i].fourcc;
    1066                 :        201 :     tag = tag_matches[i].gsttag;
    1067                 :        201 :     tag2 = tag_matches[i].gsttag2;
    1068                 :            : 
    1069         [ -  + ]:        201 :     g_assert (tag_matches[i].func);
    1070                 :        201 :     tag_matches[i].func (qtmux, list, tag, tag2, fourcc);
    1071                 :            :   }
    1072                 :            : 
    1073                 :            :   /* add unparsed blobs if present */
    1074         [ +  + ]:          9 :   if (gst_tag_exists (GST_QT_DEMUX_PRIVATE_TAG)) {
    1075                 :            :     guint num_tags;
    1076                 :            : 
    1077                 :          6 :     num_tags = gst_tag_list_get_tag_size (list, GST_QT_DEMUX_PRIVATE_TAG);
    1078         [ -  + ]:          6 :     for (i = 0; i < num_tags; ++i) {
    1079                 :            :       const GValue *val;
    1080                 :            :       GstBuffer *buf;
    1081                 :          0 :       GstCaps *caps = NULL;
    1082                 :            : 
    1083                 :          0 :       val = gst_tag_list_get_value_index (list, GST_QT_DEMUX_PRIVATE_TAG, i);
    1084                 :          0 :       buf = (GstBuffer *) gst_value_get_mini_object (val);
    1085                 :            : 
    1086 [ #  # ][ #  # ]:          0 :       if (buf && (caps = gst_buffer_get_caps (buf))) {
    1087                 :            :         GstStructure *s;
    1088                 :          0 :         const gchar *style = NULL;
    1089                 :            : 
    1090         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "Found private tag %d/%d; size %d, caps %"
    1091                 :            :             GST_PTR_FORMAT, i, num_tags, GST_BUFFER_SIZE (buf), caps);
    1092                 :          0 :         s = gst_caps_get_structure (caps, 0);
    1093 [ #  # ][ #  # ]:          0 :         if (s && (style = gst_structure_get_string (s, "style"))) {
    1094                 :            :           /* try to prevent some style tag ending up into another variant
    1095                 :            :            * (todo: make into a list if more cases) */
    1096 [ #  # ][ #  # ]:          0 :           if ((strcmp (style, "itunes") == 0 &&
    1097         [ #  # ]:          0 :                   qtmux_klass->format == GST_QT_MUX_FORMAT_MP4) ||
    1098         [ #  # ]:          0 :               (strcmp (style, "iso") == 0 &&
    1099                 :          0 :                   qtmux_klass->format == GST_QT_MUX_FORMAT_3GP)) {
    1100         [ #  # ]:          0 :             GST_DEBUG_OBJECT (qtmux, "Adding private tag");
    1101                 :          0 :             atom_moov_add_blob_tag (qtmux->moov, GST_BUFFER_DATA (buf),
    1102                 :            :                 GST_BUFFER_SIZE (buf));
    1103                 :            :           }
    1104                 :            :         }
    1105                 :          0 :         gst_caps_unref (caps);
    1106                 :            :       }
    1107                 :            :     }
    1108                 :            :   }
    1109                 :            : 
    1110                 :          9 :   return;
    1111                 :            : }
    1112                 :            : 
    1113                 :            : /*
    1114                 :            :  * Gets the tagsetter iface taglist and puts the known tags
    1115                 :            :  * into the output stream
    1116                 :            :  */
    1117                 :            : static void
    1118                 :         16 : gst_qt_mux_setup_metadata (GstQTMux * qtmux)
    1119                 :            : {
    1120                 :            :   const GstTagList *tags;
    1121                 :            : 
    1122                 :         16 :   GST_OBJECT_LOCK (qtmux);
    1123                 :         16 :   tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (qtmux));
    1124                 :         16 :   GST_OBJECT_UNLOCK (qtmux);
    1125                 :            : 
    1126         [ -  + ]:         16 :   GST_LOG_OBJECT (qtmux, "tags: %" GST_PTR_FORMAT, tags);
    1127                 :            : 
    1128 [ +  + ][ +  - ]:         25 :   if (tags && !gst_tag_list_is_empty (tags)) {
    1129                 :          9 :     GstTagList *copy = gst_tag_list_copy (tags);
    1130                 :            : 
    1131         [ -  + ]:          9 :     GST_DEBUG_OBJECT (qtmux, "Removing bogus tags");
    1132                 :          9 :     gst_tag_list_remove_tag (copy, GST_TAG_VIDEO_CODEC);
    1133                 :          9 :     gst_tag_list_remove_tag (copy, GST_TAG_AUDIO_CODEC);
    1134                 :          9 :     gst_tag_list_remove_tag (copy, GST_TAG_CONTAINER_FORMAT);
    1135                 :            : 
    1136         [ -  + ]:          9 :     GST_DEBUG_OBJECT (qtmux, "Formatting tags");
    1137                 :          9 :     gst_qt_mux_add_metadata_tags (qtmux, copy);
    1138                 :          9 :     gst_qt_mux_add_xmp_tags (qtmux, copy);
    1139                 :          9 :     gst_tag_list_free (copy);
    1140                 :            :   } else {
    1141         [ -  + ]:          7 :     GST_DEBUG_OBJECT (qtmux, "No tags received");
    1142                 :            :   }
    1143                 :         16 : }
    1144                 :            : 
    1145                 :            : static inline GstBuffer *
    1146                 :         62 : _gst_buffer_new_take_data (guint8 * data, guint size)
    1147                 :            : {
    1148                 :            :   GstBuffer *buf;
    1149                 :            : 
    1150                 :         62 :   buf = gst_buffer_new ();
    1151                 :         62 :   GST_BUFFER_DATA (buf) = GST_BUFFER_MALLOCDATA (buf) = data;
    1152                 :         62 :   GST_BUFFER_SIZE (buf) = size;
    1153                 :            : 
    1154                 :         62 :   return buf;
    1155                 :            : }
    1156                 :            : 
    1157                 :            : static GstFlowReturn
    1158                 :         90 : gst_qt_mux_send_buffer (GstQTMux * qtmux, GstBuffer * buf, guint64 * offset,
    1159                 :            :     gboolean mind_fast)
    1160                 :            : {
    1161                 :            :   GstFlowReturn res;
    1162                 :            :   guint8 *data;
    1163                 :            :   guint size;
    1164                 :            : 
    1165         [ -  + ]:         90 :   g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
    1166                 :            : 
    1167                 :         90 :   data = GST_BUFFER_DATA (buf);
    1168                 :         90 :   size = GST_BUFFER_SIZE (buf);
    1169                 :            : 
    1170         [ -  + ]:         90 :   GST_LOG_OBJECT (qtmux, "sending buffer size %d", size);
    1171                 :            : 
    1172 [ +  + ][ -  + ]:         90 :   if (mind_fast && qtmux->fast_start_file) {
    1173                 :            :     gint ret;
    1174                 :            : 
    1175         [ #  # ]:          0 :     GST_LOG_OBJECT (qtmux, "to temporary file");
    1176                 :          0 :     ret = fwrite (data, sizeof (guint8), size, qtmux->fast_start_file);
    1177                 :          0 :     gst_buffer_unref (buf);
    1178         [ #  # ]:          0 :     if (ret != size)
    1179                 :          0 :       goto write_error;
    1180                 :            :     else
    1181                 :          0 :       res = GST_FLOW_OK;
    1182                 :            :   } else {
    1183         [ -  + ]:         90 :     GST_LOG_OBJECT (qtmux, "downstream");
    1184                 :            : 
    1185                 :         90 :     buf = gst_buffer_make_metadata_writable (buf);
    1186                 :         90 :     gst_buffer_set_caps (buf, GST_PAD_CAPS (qtmux->srcpad));
    1187                 :         90 :     res = gst_pad_push (qtmux->srcpad, buf);
    1188                 :            :   }
    1189                 :            : 
    1190         [ +  + ]:         90 :   if (G_LIKELY (offset))
    1191                 :         56 :     *offset += size;
    1192                 :            : 
    1193                 :         90 :   return res;
    1194                 :            : 
    1195                 :            :   /* ERRORS */
    1196                 :            : write_error:
    1197                 :            :   {
    1198 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, RESOURCE, WRITE,
         [ #  # ][ #  # ]
    1199                 :            :         ("Failed to write to temporary file"), GST_ERROR_SYSTEM);
    1200                 :         90 :     return GST_FLOW_ERROR;
    1201                 :            :   }
    1202                 :            : }
    1203                 :            : 
    1204                 :            : static gboolean
    1205                 :          0 : gst_qt_mux_seek_to_beginning (FILE * f)
    1206                 :            : {
    1207                 :            : #ifdef HAVE_FSEEKO
    1208                 :            :   if (fseeko (f, (off_t) 0, SEEK_SET) != 0)
    1209                 :            :     return FALSE;
    1210                 :            : #elif defined (G_OS_UNIX) || defined (G_OS_WIN32)
    1211         [ #  # ]:          0 :   if (lseek (fileno (f), (off_t) 0, SEEK_SET) == (off_t) - 1)
    1212                 :          0 :     return FALSE;
    1213                 :            : #else
    1214                 :            :   if (fseek (f, (long) 0, SEEK_SET) != 0)
    1215                 :            :     return FALSE;
    1216                 :            : #endif
    1217                 :          0 :   return TRUE;
    1218                 :            : }
    1219                 :            : 
    1220                 :            : static GstFlowReturn
    1221                 :          0 : gst_qt_mux_send_buffered_data (GstQTMux * qtmux, guint64 * offset)
    1222                 :            : {
    1223                 :          0 :   GstFlowReturn ret = GST_FLOW_OK;
    1224                 :          0 :   GstBuffer *buf = NULL;
    1225                 :            : 
    1226         [ #  # ]:          0 :   if (fflush (qtmux->fast_start_file))
    1227                 :          0 :     goto flush_failed;
    1228                 :            : 
    1229         [ #  # ]:          0 :   if (!gst_qt_mux_seek_to_beginning (qtmux->fast_start_file))
    1230                 :          0 :     goto seek_failed;
    1231                 :            : 
    1232                 :            :   /* hm, this could all take a really really long time,
    1233                 :            :    * but there may not be another way to get moov atom first
    1234                 :            :    * (somehow optimize copy?) */
    1235         [ #  # ]:          0 :   GST_DEBUG_OBJECT (qtmux, "Sending buffered data");
    1236         [ #  # ]:          0 :   while (ret == GST_FLOW_OK) {
    1237                 :            :     gint r;
    1238                 :          0 :     const int bufsize = 4096;
    1239                 :            : 
    1240                 :          0 :     buf = gst_buffer_new_and_alloc (bufsize);
    1241                 :          0 :     r = fread (GST_BUFFER_DATA (buf), sizeof (guint8), bufsize,
    1242                 :            :         qtmux->fast_start_file);
    1243         [ #  # ]:          0 :     if (r == 0)
    1244                 :          0 :       break;
    1245                 :          0 :     GST_BUFFER_SIZE (buf) = r;
    1246         [ #  # ]:          0 :     GST_LOG_OBJECT (qtmux, "Pushing buffered buffer of size %d", r);
    1247                 :          0 :     ret = gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE);
    1248                 :          0 :     buf = NULL;
    1249                 :            :   }
    1250         [ #  # ]:          0 :   if (buf)
    1251                 :          0 :     gst_buffer_unref (buf);
    1252                 :            : 
    1253         [ #  # ]:          0 :   if (ftruncate (fileno (qtmux->fast_start_file), 0))
    1254                 :          0 :     goto seek_failed;
    1255         [ #  # ]:          0 :   if (!gst_qt_mux_seek_to_beginning (qtmux->fast_start_file))
    1256                 :          0 :     goto seek_failed;
    1257                 :            : 
    1258                 :          0 :   return ret;
    1259                 :            : 
    1260                 :            :   /* ERRORS */
    1261                 :            : flush_failed:
    1262                 :            :   {
    1263 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, RESOURCE, WRITE,
         [ #  # ][ #  # ]
    1264                 :            :         ("Failed to flush temporary file"), GST_ERROR_SYSTEM);
    1265                 :          0 :     ret = GST_FLOW_ERROR;
    1266                 :          0 :     goto fail;
    1267                 :            :   }
    1268                 :            : seek_failed:
    1269                 :            :   {
    1270 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, RESOURCE, SEEK,
         [ #  # ][ #  # ]
    1271                 :            :         ("Failed to seek temporary file"), GST_ERROR_SYSTEM);
    1272                 :          0 :     ret = GST_FLOW_ERROR;
    1273                 :          0 :     goto fail;
    1274                 :            :   }
    1275                 :            : fail:
    1276                 :            :   {
    1277                 :            :     /* clear descriptor so we don't remove temp file later on,
    1278                 :            :      * might be possible to recover */
    1279                 :          0 :     fclose (qtmux->fast_start_file);
    1280                 :          0 :     qtmux->fast_start_file = NULL;
    1281                 :          0 :     return ret;
    1282                 :            :   }
    1283                 :            : }
    1284                 :            : 
    1285                 :            : /*
    1286                 :            :  * Sends the initial mdat atom fields (size fields and fourcc type),
    1287                 :            :  * the subsequent buffers are considered part of it's data.
    1288                 :            :  * As we can't predict the amount of data that we are going to place in mdat
    1289                 :            :  * we need to record the position of the size field in the stream so we can
    1290                 :            :  * seek back to it later and update when the streams have finished.
    1291                 :            :  */
    1292                 :            : static GstFlowReturn
    1293                 :         16 : gst_qt_mux_send_mdat_header (GstQTMux * qtmux, guint64 * off, guint64 size,
    1294                 :            :     gboolean extended)
    1295                 :            : {
    1296                 :            :   Atom *node_header;
    1297                 :            :   GstBuffer *buf;
    1298                 :         16 :   guint8 *data = NULL;
    1299                 :         16 :   guint64 offset = 0;
    1300                 :            : 
    1301         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Sending mdat's atom header, "
    1302                 :            :       "size %" G_GUINT64_FORMAT, size);
    1303                 :            : 
    1304                 :         16 :   node_header = g_malloc0 (sizeof (Atom));
    1305                 :         16 :   node_header->type = FOURCC_mdat;
    1306         [ +  + ]:         16 :   if (extended) {
    1307                 :            :     /* use extended size */
    1308                 :         12 :     node_header->size = 1;
    1309                 :         12 :     node_header->extended_size = 0;
    1310         [ -  + ]:         12 :     if (size)
    1311                 :          0 :       node_header->extended_size = size + 16;
    1312                 :            :   } else {
    1313                 :          4 :     node_header->size = size + 8;
    1314                 :            :   }
    1315                 :            : 
    1316                 :         16 :   size = offset = 0;
    1317         [ -  + ]:         16 :   if (atom_copy_data (node_header, &data, &size, &offset) == 0)
    1318                 :          0 :     goto serialize_error;
    1319                 :            : 
    1320                 :         16 :   buf = _gst_buffer_new_take_data (data, offset);
    1321                 :         16 :   g_free (node_header);
    1322                 :            : 
    1323         [ -  + ]:         16 :   GST_LOG_OBJECT (qtmux, "Pushing mdat start");
    1324                 :         16 :   return gst_qt_mux_send_buffer (qtmux, buf, off, FALSE);
    1325                 :            : 
    1326                 :            :   /* ERRORS */
    1327                 :            : serialize_error:
    1328                 :            :   {
    1329 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    1330                 :            :         ("Failed to serialize mdat"));
    1331                 :         16 :     return GST_FLOW_ERROR;
    1332                 :            :   }
    1333                 :            : }
    1334                 :            : 
    1335                 :            : /*
    1336                 :            :  * We get the position of the mdat size field, seek back to it
    1337                 :            :  * and overwrite with the real value
    1338                 :            :  */
    1339                 :            : static GstFlowReturn
    1340                 :         12 : gst_qt_mux_update_mdat_size (GstQTMux * qtmux, guint64 mdat_pos,
    1341                 :            :     guint64 mdat_size, guint64 * offset)
    1342                 :            : {
    1343                 :            :   GstEvent *event;
    1344                 :            :   GstBuffer *buf;
    1345                 :            :   gboolean large_file;
    1346                 :            : 
    1347                 :         12 :   large_file = (mdat_size > MDAT_LARGE_FILE_LIMIT);
    1348                 :            : 
    1349         [ -  + ]:         12 :   if (large_file)
    1350                 :          0 :     mdat_pos += 8;
    1351                 :            : 
    1352                 :            :   /* seek and rewrite the header */
    1353                 :         12 :   event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
    1354                 :            :       mdat_pos, GST_CLOCK_TIME_NONE, 0);
    1355                 :         12 :   gst_pad_push_event (qtmux->srcpad, event);
    1356                 :            : 
    1357         [ -  + ]:         12 :   if (large_file) {
    1358                 :          0 :     buf = gst_buffer_new_and_alloc (sizeof (guint64));
    1359                 :          0 :     GST_WRITE_UINT64_BE (GST_BUFFER_DATA (buf), mdat_size + 16);
    1360                 :            :   } else {
    1361                 :            :     guint8 *data;
    1362                 :            : 
    1363                 :         12 :     buf = gst_buffer_new_and_alloc (16);
    1364                 :         12 :     data = GST_BUFFER_DATA (buf);
    1365                 :         12 :     GST_WRITE_UINT32_BE (data, 8);
    1366                 :         12 :     GST_WRITE_UINT32_LE (data + 4, FOURCC_free);
    1367                 :         12 :     GST_WRITE_UINT32_BE (data + 8, mdat_size + 8);
    1368                 :         12 :     GST_WRITE_UINT32_LE (data + 12, FOURCC_mdat);
    1369                 :            :   }
    1370                 :            : 
    1371                 :         12 :   return gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE);
    1372                 :            : }
    1373                 :            : 
    1374                 :            : static GstFlowReturn
    1375                 :         16 : gst_qt_mux_send_ftyp (GstQTMux * qtmux, guint64 * off)
    1376                 :            : {
    1377                 :            :   GstBuffer *buf;
    1378                 :         16 :   guint64 size = 0, offset = 0;
    1379                 :         16 :   guint8 *data = NULL;
    1380                 :            : 
    1381         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Sending ftyp atom");
    1382                 :            : 
    1383         [ -  + ]:         16 :   if (!atom_ftyp_copy_data (qtmux->ftyp, &data, &size, &offset))
    1384                 :          0 :     goto serialize_error;
    1385                 :            : 
    1386                 :         16 :   buf = _gst_buffer_new_take_data (data, offset);
    1387                 :            : 
    1388         [ -  + ]:         16 :   GST_LOG_OBJECT (qtmux, "Pushing ftyp");
    1389                 :         16 :   return gst_qt_mux_send_buffer (qtmux, buf, off, FALSE);
    1390                 :            : 
    1391                 :            :   /* ERRORS */
    1392                 :            : serialize_error:
    1393                 :            :   {
    1394 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    1395                 :            :         ("Failed to serialize ftyp"));
    1396                 :         16 :     return GST_FLOW_ERROR;
    1397                 :            :   }
    1398                 :            : }
    1399                 :            : 
    1400                 :            : static void
    1401                 :         16 : gst_qt_mux_prepare_ftyp (GstQTMux * qtmux, AtomFTYP ** p_ftyp,
    1402                 :            :     GstBuffer ** p_prefix)
    1403                 :            : {
    1404                 :         16 :   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
    1405                 :            :   guint32 major, version;
    1406                 :            :   GList *comp;
    1407                 :         16 :   GstBuffer *prefix = NULL;
    1408                 :         16 :   AtomFTYP *ftyp = NULL;
    1409                 :            : 
    1410         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Preparing ftyp and possible prefix atom");
    1411                 :            : 
    1412                 :            :   /* init and send context and ftyp based on current property state */
    1413                 :         16 :   gst_qt_mux_map_format_to_header (qtmux_klass->format, &prefix, &major,
    1414                 :            :       &version, &comp, qtmux->moov, qtmux->longest_chunk,
    1415                 :         16 :       qtmux->fast_start_file != NULL);
    1416                 :         16 :   ftyp = atom_ftyp_new (qtmux->context, major, version, comp);
    1417         [ +  + ]:         16 :   if (comp)
    1418                 :          6 :     g_list_free (comp);
    1419         [ -  + ]:         16 :   if (prefix) {
    1420         [ #  # ]:          0 :     if (p_prefix)
    1421                 :          0 :       *p_prefix = prefix;
    1422                 :            :     else
    1423                 :          0 :       gst_buffer_unref (prefix);
    1424                 :            :   }
    1425                 :         16 :   *p_ftyp = ftyp;
    1426                 :         16 : }
    1427                 :            : 
    1428                 :            : static GstFlowReturn
    1429                 :         16 : gst_qt_mux_prepare_and_send_ftyp (GstQTMux * qtmux)
    1430                 :            : {
    1431                 :         16 :   GstFlowReturn ret = GST_FLOW_OK;
    1432                 :         16 :   GstBuffer *prefix = NULL;
    1433                 :            : 
    1434         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Preparing to send ftyp atom");
    1435                 :            : 
    1436                 :            :   /* init and send context and ftyp based on current property state */
    1437         [ -  + ]:         16 :   if (qtmux->ftyp) {
    1438                 :          0 :     atom_ftyp_free (qtmux->ftyp);
    1439                 :          0 :     qtmux->ftyp = NULL;
    1440                 :            :   }
    1441                 :         16 :   gst_qt_mux_prepare_ftyp (qtmux, &qtmux->ftyp, &prefix);
    1442         [ -  + ]:         16 :   if (prefix) {
    1443                 :          0 :     ret = gst_qt_mux_send_buffer (qtmux, prefix, &qtmux->header_size, FALSE);
    1444         [ #  # ]:          0 :     if (ret != GST_FLOW_OK)
    1445                 :          0 :       return ret;
    1446                 :            :   }
    1447                 :         16 :   return gst_qt_mux_send_ftyp (qtmux, &qtmux->header_size);
    1448                 :            : }
    1449                 :            : 
    1450                 :            : static void
    1451                 :         18 : gst_qt_mux_set_header_on_caps (GstQTMux * mux, GstBuffer * buf)
    1452                 :            : {
    1453                 :            :   GstStructure *structure;
    1454                 :         18 :   GValue array = { 0 };
    1455                 :         18 :   GValue value = { 0 };
    1456                 :         18 :   GstCaps *caps = GST_PAD_CAPS (mux->srcpad);
    1457                 :            : 
    1458                 :         18 :   caps = gst_caps_copy (GST_PAD_CAPS (mux->srcpad));
    1459                 :         18 :   structure = gst_caps_get_structure (caps, 0);
    1460                 :            : 
    1461                 :         18 :   g_value_init (&array, GST_TYPE_ARRAY);
    1462                 :            : 
    1463                 :         18 :   GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_IN_CAPS);
    1464                 :         18 :   g_value_init (&value, GST_TYPE_BUFFER);
    1465                 :         18 :   gst_value_take_buffer (&value, gst_buffer_ref (buf));
    1466                 :         18 :   gst_value_array_append_value (&array, &value);
    1467                 :         18 :   g_value_unset (&value);
    1468                 :            : 
    1469                 :         18 :   gst_structure_set_value (structure, "streamheader", &array);
    1470                 :         18 :   g_value_unset (&array);
    1471                 :         18 :   gst_pad_set_caps (mux->srcpad, caps);
    1472                 :         18 :   gst_caps_unref (caps);
    1473                 :         18 : }
    1474                 :            : 
    1475                 :            : static void
    1476                 :         16 : gst_qt_mux_configure_moov (GstQTMux * qtmux, guint32 * _timescale)
    1477                 :            : {
    1478                 :            :   gboolean fragmented;
    1479                 :            :   guint32 timescale;
    1480                 :            : 
    1481                 :         16 :   GST_OBJECT_LOCK (qtmux);
    1482                 :         16 :   timescale = qtmux->timescale;
    1483                 :         16 :   fragmented = qtmux->fragment_sequence > 0;
    1484                 :         16 :   GST_OBJECT_UNLOCK (qtmux);
    1485                 :            : 
    1486                 :            :   /* inform lower layers of our property wishes, and determine duration.
    1487                 :            :    * Let moov take care of this using its list of traks;
    1488                 :            :    * so that released pads are also included */
    1489         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Updating timescale to %" G_GUINT32_FORMAT,
    1490                 :            :       timescale);
    1491                 :         16 :   atom_moov_update_timescale (qtmux->moov, timescale);
    1492                 :         16 :   atom_moov_set_fragmented (qtmux->moov, fragmented);
    1493                 :            : 
    1494                 :         16 :   atom_moov_update_duration (qtmux->moov);
    1495                 :            : 
    1496         [ +  + ]:         16 :   if (_timescale)
    1497                 :         12 :     *_timescale = timescale;
    1498                 :         16 : }
    1499                 :            : 
    1500                 :            : static GstFlowReturn
    1501                 :         18 : gst_qt_mux_send_moov (GstQTMux * qtmux, guint64 * _offset, gboolean mind_fast)
    1502                 :            : {
    1503                 :         18 :   guint64 offset = 0, size = 0;
    1504                 :            :   guint8 *data;
    1505                 :            :   GstBuffer *buf;
    1506                 :         18 :   GstFlowReturn ret = GST_FLOW_OK;
    1507                 :            : 
    1508                 :            :   /* serialize moov */
    1509                 :         18 :   offset = size = 0;
    1510                 :         18 :   data = NULL;
    1511         [ -  + ]:         18 :   GST_LOG_OBJECT (qtmux, "Copying movie header into buffer");
    1512         [ -  + ]:         18 :   if (!atom_moov_copy_data (qtmux->moov, &data, &size, &offset))
    1513                 :          0 :     goto serialize_error;
    1514                 :            : 
    1515                 :         18 :   buf = _gst_buffer_new_take_data (data, offset);
    1516         [ -  + ]:         18 :   GST_DEBUG_OBJECT (qtmux, "Pushing moov atoms");
    1517                 :         18 :   gst_qt_mux_set_header_on_caps (qtmux, buf);
    1518                 :         18 :   ret = gst_qt_mux_send_buffer (qtmux, buf, _offset, mind_fast);
    1519                 :            : 
    1520                 :         18 :   return ret;
    1521                 :            : 
    1522                 :            : serialize_error:
    1523                 :            :   {
    1524                 :          0 :     g_free (data);
    1525                 :         18 :     return GST_FLOW_ERROR;
    1526                 :            :   }
    1527                 :            : }
    1528                 :            : 
    1529                 :            : /* either calculates size of extra atoms or pushes them */
    1530                 :            : static GstFlowReturn
    1531                 :         16 : gst_qt_mux_send_extra_atoms (GstQTMux * qtmux, gboolean send, guint64 * offset,
    1532                 :            :     gboolean mind_fast)
    1533                 :            : {
    1534                 :            :   GSList *walk;
    1535                 :         16 :   guint64 loffset = 0, size = 0;
    1536                 :            :   guint8 *data;
    1537                 :         16 :   GstFlowReturn ret = GST_FLOW_OK;
    1538                 :            : 
    1539 [ +  - ][ +  + ]:         22 :   for (walk = qtmux->extra_atoms; walk; walk = g_slist_next (walk)) {
    1540                 :          6 :     AtomInfo *ainfo = (AtomInfo *) walk->data;
    1541                 :            : 
    1542                 :          6 :     loffset = size = 0;
    1543                 :          6 :     data = NULL;
    1544 [ +  - ][ -  + ]:          6 :     if (!ainfo->copy_data_func (ainfo->atom,
    1545                 :            :             send ? &data : NULL, &size, &loffset))
    1546                 :          0 :       goto serialize_error;
    1547                 :            : 
    1548         [ +  - ]:          6 :     if (send) {
    1549                 :            :       GstBuffer *buf;
    1550                 :            : 
    1551         [ -  + ]:          6 :       GST_DEBUG_OBJECT (qtmux,
    1552                 :            :           "Pushing extra top-level atom %" GST_FOURCC_FORMAT,
    1553                 :            :           GST_FOURCC_ARGS (ainfo->atom->type));
    1554                 :          6 :       buf = _gst_buffer_new_take_data (data, loffset);
    1555                 :          6 :       ret = gst_qt_mux_send_buffer (qtmux, buf, offset, FALSE);
    1556         [ -  + ]:          6 :       if (ret != GST_FLOW_OK)
    1557                 :          0 :         break;
    1558                 :            :     } else {
    1559         [ #  # ]:          0 :       if (offset)
    1560                 :          0 :         *offset += loffset;
    1561                 :            :     }
    1562                 :            :   }
    1563                 :            : 
    1564                 :         16 :   return ret;
    1565                 :            : 
    1566                 :            : serialize_error:
    1567                 :            :   {
    1568                 :          0 :     g_free (data);
    1569                 :         16 :     return GST_FLOW_ERROR;
    1570                 :            :   }
    1571                 :            : }
    1572                 :            : 
    1573                 :            : static GstFlowReturn
    1574                 :         16 : gst_qt_mux_start_file (GstQTMux * qtmux)
    1575                 :            : {
    1576                 :         16 :   GstFlowReturn ret = GST_FLOW_OK;
    1577                 :            :   GstCaps *caps;
    1578                 :            : 
    1579         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "starting file");
    1580                 :            : 
    1581                 :         16 :   caps = gst_caps_copy (gst_pad_get_pad_template_caps (qtmux->srcpad));
    1582                 :         16 :   gst_pad_set_caps (qtmux->srcpad, caps);
    1583                 :         16 :   gst_caps_unref (caps);
    1584                 :            : 
    1585                 :            :   /* let downstream know we think in BYTES and expect to do seeking later on */
    1586                 :         16 :   gst_pad_push_event (qtmux->srcpad,
    1587                 :            :       gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0));
    1588                 :            : 
    1589                 :            :   /* initialize our moov recovery file */
    1590                 :         16 :   GST_OBJECT_LOCK (qtmux);
    1591         [ -  + ]:         16 :   if (qtmux->moov_recov_file_path) {
    1592         [ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux, "Openning moov recovery file: %s",
    1593                 :            :         qtmux->moov_recov_file_path);
    1594                 :          0 :     qtmux->moov_recov_file = g_fopen (qtmux->moov_recov_file_path, "wb+");
    1595         [ #  # ]:          0 :     if (qtmux->moov_recov_file == NULL) {
    1596         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "Failed to open moov recovery file in %s",
    1597                 :            :           qtmux->moov_recov_file_path);
    1598                 :            :     } else {
    1599                 :            :       GSList *walk;
    1600                 :          0 :       gboolean fail = FALSE;
    1601                 :          0 :       AtomFTYP *ftyp = NULL;
    1602                 :          0 :       GstBuffer *prefix = NULL;
    1603                 :            : 
    1604                 :          0 :       gst_qt_mux_prepare_ftyp (qtmux, &ftyp, &prefix);
    1605                 :            : 
    1606         [ #  # ]:          0 :       if (!atoms_recov_write_headers (qtmux->moov_recov_file, ftyp, prefix,
    1607                 :            :               qtmux->moov, qtmux->timescale,
    1608                 :            :               g_slist_length (qtmux->sinkpads))) {
    1609         [ #  # ]:          0 :         GST_WARNING_OBJECT (qtmux, "Failed to write moov recovery file "
    1610                 :            :             "headers");
    1611                 :          0 :         fail = TRUE;
    1612                 :            :       }
    1613                 :            : 
    1614                 :          0 :       atom_ftyp_free (ftyp);
    1615         [ #  # ]:          0 :       if (prefix)
    1616                 :          0 :         gst_buffer_unref (prefix);
    1617                 :            : 
    1618 [ #  # ][ #  # ]:          0 :       for (walk = qtmux->sinkpads; walk && !fail; walk = g_slist_next (walk)) {
                 [ #  # ]
    1619                 :          0 :         GstCollectData *cdata = (GstCollectData *) walk->data;
    1620                 :          0 :         GstQTPad *qpad = (GstQTPad *) cdata;
    1621                 :            :         /* write info for each stream */
    1622                 :          0 :         fail = atoms_recov_write_trak_info (qtmux->moov_recov_file, qpad->trak);
    1623         [ #  # ]:          0 :         if (fail) {
    1624         [ #  # ]:          0 :           GST_WARNING_OBJECT (qtmux, "Failed to write trak info to recovery "
    1625                 :            :               "file");
    1626                 :            :         }
    1627                 :            :       }
    1628         [ #  # ]:          0 :       if (fail) {
    1629                 :            :         /* cleanup */
    1630                 :          0 :         fclose (qtmux->moov_recov_file);
    1631                 :          0 :         qtmux->moov_recov_file = NULL;
    1632         [ #  # ]:          0 :         GST_WARNING_OBJECT (qtmux, "An error was detected while writing to "
    1633                 :            :             "recover file, moov recovery won't work");
    1634                 :            :       }
    1635                 :            :     }
    1636                 :            :   }
    1637                 :         16 :   GST_OBJECT_UNLOCK (qtmux);
    1638                 :            : 
    1639                 :            :   /* 
    1640                 :            :    * send mdat header if already needed, and mark position for later update.
    1641                 :            :    * We don't send ftyp now if we are on fast start mode, because we can
    1642                 :            :    * better fine tune using the information we gather to create the whole moov
    1643                 :            :    * atom.
    1644                 :            :    */
    1645         [ -  + ]:         16 :   if (qtmux->fast_start) {
    1646                 :          0 :     GST_OBJECT_LOCK (qtmux);
    1647                 :          0 :     qtmux->fast_start_file = g_fopen (qtmux->fast_start_file_path, "wb+");
    1648         [ #  # ]:          0 :     if (!qtmux->fast_start_file)
    1649                 :          0 :       goto open_failed;
    1650                 :          0 :     GST_OBJECT_UNLOCK (qtmux);
    1651                 :            : 
    1652                 :            :     /* send a dummy buffer for preroll */
    1653                 :          0 :     ret = gst_qt_mux_send_buffer (qtmux, gst_buffer_new (), NULL, FALSE);
    1654         [ #  # ]:          0 :     if (ret != GST_FLOW_OK)
    1655                 :          0 :       goto exit;
    1656                 :            : 
    1657                 :            :   } else {
    1658                 :         16 :     ret = gst_qt_mux_prepare_and_send_ftyp (qtmux);
    1659         [ -  + ]:         16 :     if (ret != GST_FLOW_OK) {
    1660                 :          0 :       goto exit;
    1661                 :            :     }
    1662                 :            : 
    1663                 :            :     /* well, it's moov pos if fragmented ... */
    1664                 :         16 :     qtmux->mdat_pos = qtmux->header_size;
    1665                 :            : 
    1666         [ +  + ]:         16 :     if (qtmux->fragment_duration) {
    1667         [ -  + ]:          4 :       GST_DEBUG_OBJECT (qtmux, "fragment duration %d ms, writing headers",
    1668                 :            :           qtmux->fragment_duration);
    1669                 :            :       /* also used as snapshot marker to indicate fragmented file */
    1670                 :          4 :       qtmux->fragment_sequence = 1;
    1671                 :            :       /* prepare moov and/or tags */
    1672                 :          4 :       gst_qt_mux_configure_moov (qtmux, NULL);
    1673                 :          4 :       gst_qt_mux_setup_metadata (qtmux);
    1674                 :          4 :       ret = gst_qt_mux_send_moov (qtmux, &qtmux->header_size, FALSE);
    1675         [ -  + ]:          4 :       if (ret != GST_FLOW_OK)
    1676                 :          0 :         return ret;
    1677                 :            :       /* extra atoms */
    1678                 :          4 :       ret =
    1679                 :          4 :           gst_qt_mux_send_extra_atoms (qtmux, TRUE, &qtmux->header_size, FALSE);
    1680         [ -  + ]:          4 :       if (ret != GST_FLOW_OK)
    1681                 :          0 :         return ret;
    1682                 :            :       /* prepare index */
    1683         [ +  + ]:          4 :       if (!qtmux->streamable)
    1684                 :          2 :         qtmux->mfra = atom_mfra_new (qtmux->context);
    1685                 :            :     } else {
    1686                 :            :       /* extended to ensure some spare space */
    1687                 :         12 :       ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, 0, TRUE);
    1688                 :            :     }
    1689                 :            :   }
    1690                 :            : 
    1691                 :            : exit:
    1692                 :         16 :   return ret;
    1693                 :            : 
    1694                 :            :   /* ERRORS */
    1695                 :            : open_failed:
    1696                 :            :   {
    1697 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, RESOURCE, OPEN_READ_WRITE,
         [ #  # ][ #  # ]
    1698                 :            :         (("Could not open temporary file \"%s\""), qtmux->fast_start_file_path),
    1699                 :            :         GST_ERROR_SYSTEM);
    1700                 :          0 :     GST_OBJECT_UNLOCK (qtmux);
    1701                 :         16 :     return GST_FLOW_ERROR;
    1702                 :            :   }
    1703                 :            : }
    1704                 :            : 
    1705                 :            : static GstFlowReturn
    1706                 :         16 : gst_qt_mux_stop_file (GstQTMux * qtmux)
    1707                 :            : {
    1708                 :         16 :   gboolean ret = GST_FLOW_OK;
    1709                 :         16 :   guint64 offset = 0, size = 0;
    1710                 :            :   GSList *walk;
    1711                 :            :   gboolean large_file;
    1712                 :            :   guint32 timescale;
    1713                 :         16 :   GstClockTime first_ts = GST_CLOCK_TIME_NONE;
    1714                 :            : 
    1715         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Updating remaining values and sending last data");
    1716                 :            : 
    1717                 :            :   /* pushing last buffers for each pad */
    1718 [ +  - ][ +  + ]:         32 :   for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) {
    1719                 :         16 :     GstCollectData *cdata = (GstCollectData *) walk->data;
    1720                 :         16 :     GstQTPad *qtpad = (GstQTPad *) cdata;
    1721                 :            : 
    1722         [ -  + ]:         16 :     if (!qtpad->last_buf) {
    1723         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "Pad %s has no buffers",
    1724                 :            :           GST_PAD_NAME (qtpad->collect.pad));
    1725                 :          0 :       continue;
    1726                 :            :     }
    1727                 :            :     /* send last buffer */
    1728         [ -  + ]:         16 :     GST_DEBUG_OBJECT (qtmux, "Sending the last buffer for pad %s",
    1729                 :            :         GST_PAD_NAME (qtpad->collect.pad));
    1730                 :         16 :     ret = gst_qt_mux_add_buffer (qtmux, qtpad, NULL);
    1731         [ -  + ]:         16 :     if (ret != GST_FLOW_OK)
    1732         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "Failed to send last buffer for %s, "
    1733                 :            :           "flow return: %s", GST_PAD_NAME (qtpad->collect.pad),
    1734                 :            :           gst_flow_get_name (ret));
    1735                 :            :     /* determine max stream duration */
    1736 [ -  + ][ #  # ]:         16 :     if (!GST_CLOCK_TIME_IS_VALID (first_ts) ||
    1737         [ #  # ]:          0 :         (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) &&
    1738                 :          0 :             qtpad->last_dts > first_ts)) {
    1739                 :         16 :       first_ts = qtpad->last_dts;
    1740                 :            :     }
    1741                 :            :   }
    1742                 :            : 
    1743         [ +  + ]:         16 :   if (qtmux->fragment_sequence) {
    1744                 :            :     GstEvent *event;
    1745                 :            : 
    1746         [ +  + ]:          4 :     if (qtmux->mfra) {
    1747                 :          2 :       guint8 *data = NULL;
    1748                 :            :       GstBuffer *buf;
    1749                 :            : 
    1750                 :          2 :       size = offset = 0;
    1751         [ -  + ]:          2 :       GST_DEBUG_OBJECT (qtmux, "adding mfra");
    1752         [ -  + ]:          2 :       if (!atom_mfra_copy_data (qtmux->mfra, &data, &size, &offset))
    1753                 :          0 :         goto serialize_error;
    1754                 :          2 :       buf = _gst_buffer_new_take_data (data, offset);
    1755                 :          2 :       ret = gst_qt_mux_send_buffer (qtmux, buf, NULL, FALSE);
    1756         [ -  + ]:          2 :       if (ret != GST_FLOW_OK)
    1757                 :          0 :         return ret;
    1758                 :            :     } else {
    1759                 :            :       /* must have been streamable; no need to write duration */
    1760         [ -  + ]:          2 :       GST_DEBUG_OBJECT (qtmux, "streamable file; nothing to stop");
    1761                 :          2 :       return GST_FLOW_OK;
    1762                 :            :     }
    1763                 :            : 
    1764                 :            : 
    1765                 :          2 :     timescale = qtmux->timescale;
    1766                 :            :     /* only mvex duration is updated,
    1767                 :            :      * mvhd should be consistent with empty moov
    1768                 :            :      * (but TODO maybe some clients do not handle that well ?) */
    1769                 :          4 :     qtmux->moov->mvex.mehd.fragment_duration =
    1770                 :          2 :         gst_util_uint64_scale (first_ts, timescale, GST_SECOND);
    1771 [ -  + ][ #  # ]:          2 :     GST_DEBUG_OBJECT (qtmux, "rewriting moov with mvex duration %"
         [ #  # ][ #  # ]
                 [ #  # ]
    1772                 :            :         GST_TIME_FORMAT, GST_TIME_ARGS (first_ts));
    1773                 :            :     /* seek and rewrite the header */
    1774                 :          2 :     event = gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
    1775                 :          2 :         qtmux->mdat_pos, GST_CLOCK_TIME_NONE, 0);
    1776                 :          2 :     gst_pad_push_event (qtmux->srcpad, event);
    1777                 :            :     /* no need to seek back */
    1778                 :          2 :     return gst_qt_mux_send_moov (qtmux, NULL, FALSE);
    1779                 :            :   }
    1780                 :            : 
    1781                 :         12 :   gst_qt_mux_configure_moov (qtmux, &timescale);
    1782                 :            : 
    1783                 :            :   /* check for late streams */
    1784                 :         12 :   first_ts = GST_CLOCK_TIME_NONE;
    1785 [ +  - ][ +  + ]:         24 :   for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) {
    1786                 :         12 :     GstCollectData *cdata = (GstCollectData *) walk->data;
    1787                 :         12 :     GstQTPad *qtpad = (GstQTPad *) cdata;
    1788                 :            : 
    1789 [ -  + ][ #  # ]:         12 :     if (!GST_CLOCK_TIME_IS_VALID (first_ts) ||
    1790         [ #  # ]:          0 :         (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) &&
    1791                 :          0 :             qtpad->first_ts < first_ts)) {
    1792                 :         12 :       first_ts = qtpad->first_ts;
    1793                 :            :     }
    1794                 :            :   }
    1795 [ -  + ][ #  # ]:         12 :   GST_DEBUG_OBJECT (qtmux, "Media first ts selected: %" GST_TIME_FORMAT,
         [ #  # ][ #  # ]
                 [ #  # ]
    1796                 :            :       GST_TIME_ARGS (first_ts));
    1797                 :            :   /* add EDTSs for late streams */
    1798 [ +  - ][ +  + ]:         24 :   for (walk = qtmux->collect->data; walk; walk = g_slist_next (walk)) {
    1799                 :         12 :     GstCollectData *cdata = (GstCollectData *) walk->data;
    1800                 :         12 :     GstQTPad *qtpad = (GstQTPad *) cdata;
    1801                 :            :     guint32 lateness;
    1802                 :            :     guint32 duration;
    1803                 :            : 
    1804 [ +  - ][ -  + ]:         12 :     if (GST_CLOCK_TIME_IS_VALID (qtpad->first_ts) &&
    1805                 :         12 :         qtpad->first_ts > first_ts + MAX_TOLERATED_LATENESS) {
    1806 [ #  # ][ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "Pad %s is a late stream by %" GST_TIME_FORMAT,
         [ #  # ][ #  # ]
                 [ #  # ]
    1807                 :            :           GST_PAD_NAME (qtpad->collect.pad),
    1808                 :            :           GST_TIME_ARGS (qtpad->first_ts - first_ts));
    1809                 :          0 :       lateness = gst_util_uint64_scale_round (qtpad->first_ts - first_ts,
    1810                 :            :           timescale, GST_SECOND);
    1811                 :          0 :       duration = qtpad->trak->tkhd.duration;
    1812                 :          0 :       atom_trak_add_elst_entry (qtpad->trak, lateness, (guint32) - 1,
    1813                 :            :           (guint32) (1 * 65536.0));
    1814                 :          0 :       atom_trak_add_elst_entry (qtpad->trak, duration, 0,
    1815                 :            :           (guint32) (1 * 65536.0));
    1816                 :            : 
    1817                 :            :       /* need to add the empty time to the trak duration */
    1818                 :          0 :       qtpad->trak->tkhd.duration += lateness;
    1819                 :            :     }
    1820                 :            :   }
    1821                 :            : 
    1822                 :            :   /* tags into file metadata */
    1823                 :         12 :   gst_qt_mux_setup_metadata (qtmux);
    1824                 :            : 
    1825                 :         12 :   large_file = (qtmux->mdat_size > MDAT_LARGE_FILE_LIMIT);
    1826                 :            :   /* if faststart, update the offset of the atoms in the movie with the offset
    1827                 :            :    * that the movie headers before mdat will cause.
    1828                 :            :    * Also, send the ftyp */
    1829         [ -  + ]:         12 :   if (qtmux->fast_start_file) {
    1830                 :            :     GstFlowReturn flow_ret;
    1831                 :          0 :     offset = size = 0;
    1832                 :            : 
    1833                 :          0 :     flow_ret = gst_qt_mux_prepare_and_send_ftyp (qtmux);
    1834         [ #  # ]:          0 :     if (flow_ret != GST_FLOW_OK) {
    1835                 :          0 :       goto ftyp_error;
    1836                 :            :     }
    1837                 :            :     /* copy into NULL to obtain size */
    1838         [ #  # ]:          0 :     if (!atom_moov_copy_data (qtmux->moov, NULL, &size, &offset))
    1839                 :          0 :       goto serialize_error;
    1840         [ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux, "calculated moov atom size %" G_GUINT64_FORMAT,
    1841                 :            :         offset);
    1842         [ #  # ]:          0 :     offset += qtmux->header_size + (large_file ? 16 : 8);
    1843                 :            : 
    1844                 :            :     /* sum up with the extra atoms size */
    1845                 :          0 :     ret = gst_qt_mux_send_extra_atoms (qtmux, FALSE, &offset, FALSE);
    1846         [ #  # ]:          0 :     if (ret != GST_FLOW_OK)
    1847                 :          0 :       return ret;
    1848                 :            :   } else {
    1849                 :         12 :     offset = qtmux->header_size;
    1850                 :            :   }
    1851                 :         12 :   atom_moov_chunks_add_offset (qtmux->moov, offset);
    1852                 :            : 
    1853                 :            :   /* moov */
    1854                 :            :   /* note: as of this point, we no longer care about tracking written data size,
    1855                 :            :    * since there is no more use for it anyway */
    1856                 :         12 :   ret = gst_qt_mux_send_moov (qtmux, NULL, FALSE);
    1857         [ -  + ]:         12 :   if (ret != GST_FLOW_OK)
    1858                 :          0 :     return ret;
    1859                 :            : 
    1860                 :            :   /* extra atoms */
    1861                 :         12 :   ret = gst_qt_mux_send_extra_atoms (qtmux, TRUE, NULL, FALSE);
    1862         [ -  + ]:         12 :   if (ret != GST_FLOW_OK)
    1863                 :          0 :     return ret;
    1864                 :            : 
    1865                 :            :   /* if needed, send mdat atom and move buffered data into it */
    1866         [ -  + ]:         12 :   if (qtmux->fast_start_file) {
    1867                 :            :     /* mdat_size = accumulated (buffered data) */
    1868                 :          0 :     ret = gst_qt_mux_send_mdat_header (qtmux, NULL, qtmux->mdat_size,
    1869                 :            :         large_file);
    1870         [ #  # ]:          0 :     if (ret != GST_FLOW_OK)
    1871                 :          0 :       return ret;
    1872                 :          0 :     ret = gst_qt_mux_send_buffered_data (qtmux, NULL);
    1873         [ #  # ]:          0 :     if (ret != GST_FLOW_OK)
    1874                 :          0 :       return ret;
    1875                 :            :   } else {
    1876                 :            :     /* mdat needs update iff not using faststart */
    1877         [ -  + ]:         12 :     GST_DEBUG_OBJECT (qtmux, "updating mdat size");
    1878                 :         12 :     ret = gst_qt_mux_update_mdat_size (qtmux, qtmux->mdat_pos,
    1879                 :            :         qtmux->mdat_size, NULL);
    1880                 :            :     /* note; no seeking back to the end of file is done,
    1881                 :            :      * since we no longer write anything anyway */
    1882                 :            :   }
    1883                 :            : 
    1884                 :         12 :   return ret;
    1885                 :            : 
    1886                 :            :   /* ERRORS */
    1887                 :            : serialize_error:
    1888                 :            :   {
    1889 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    1890                 :            :         ("Failed to serialize moov"));
    1891                 :          0 :     return GST_FLOW_ERROR;
    1892                 :            :   }
    1893                 :            : ftyp_error:
    1894                 :            :   {
    1895 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL), ("Failed to send ftyp"));
         [ #  # ][ #  # ]
    1896                 :         16 :     return GST_FLOW_ERROR;
    1897                 :            :   }
    1898                 :            : }
    1899                 :            : 
    1900                 :            : static GstFlowReturn
    1901                 :          4 : gst_qt_mux_pad_fragment_add_buffer (GstQTMux * qtmux, GstQTPad * pad,
    1902                 :            :     GstBuffer * buf, gboolean force, guint32 nsamples, gint64 dts,
    1903                 :            :     guint32 delta, guint32 size, gboolean sync, gint64 pts_offset)
    1904                 :            : {
    1905                 :          4 :   GstFlowReturn ret = GST_FLOW_OK;
    1906                 :            : 
    1907                 :            :   /* setup if needed */
    1908 [ -  + ][ #  # ]:          4 :   if (G_UNLIKELY (!pad->traf || force))
    1909                 :            :     goto init;
    1910                 :            : 
    1911                 :            : flush:
    1912                 :            :   /* flush pad fragment if threshold reached,
    1913                 :            :    * or at new keyframe if we should be minding those in the first place */
    1914 [ -  + ][ #  # ]:          4 :   if (G_UNLIKELY (force || (sync && pad->sync) ||
         [ #  # ][ #  # ]
    1915                 :            :           pad->fragment_duration < (gint64) delta)) {
    1916                 :            :     AtomMOOF *moof;
    1917                 :          4 :     guint64 size = 0, offset = 0;
    1918                 :          4 :     guint8 *data = NULL;
    1919                 :            :     GstBuffer *buffer;
    1920                 :            :     guint i, total_size;
    1921                 :            : 
    1922                 :            :     /* now we know where moof ends up, update offset in tfra */
    1923         [ +  + ]:          4 :     if (pad->tfra)
    1924                 :          2 :       atom_tfra_update_offset (pad->tfra, qtmux->header_size);
    1925                 :            : 
    1926                 :          4 :     moof = atom_moof_new (qtmux->context, qtmux->fragment_sequence);
    1927                 :            :     /* takes ownership */
    1928                 :          4 :     atom_moof_add_traf (moof, pad->traf);
    1929                 :          4 :     pad->traf = NULL;
    1930                 :          4 :     atom_moof_copy_data (moof, &data, &size, &offset);
    1931                 :          4 :     buffer = _gst_buffer_new_take_data (data, offset);
    1932         [ -  + ]:          4 :     GST_LOG_OBJECT (qtmux, "writing moof size %d", GST_BUFFER_SIZE (buffer));
    1933                 :          4 :     ret = gst_qt_mux_send_buffer (qtmux, buffer, &qtmux->header_size, FALSE);
    1934                 :            : 
    1935                 :            :     /* and actual data */
    1936                 :          4 :     total_size = 0;
    1937         [ +  + ]:          8 :     for (i = 0; i < atom_array_get_len (&pad->fragment_buffers); i++) {
    1938                 :          4 :       total_size +=
    1939                 :          4 :           GST_BUFFER_SIZE (atom_array_index (&pad->fragment_buffers, i));
    1940                 :            :     }
    1941                 :            : 
    1942         [ -  + ]:          4 :     GST_LOG_OBJECT (qtmux, "writing %d buffers, total_size %d",
    1943                 :            :         atom_array_get_len (&pad->fragment_buffers), total_size);
    1944         [ +  - ]:          4 :     if (ret == GST_FLOW_OK)
    1945                 :          4 :       ret = gst_qt_mux_send_mdat_header (qtmux, &qtmux->header_size, total_size,
    1946                 :            :           FALSE);
    1947         [ +  + ]:          8 :     for (i = 0; i < atom_array_get_len (&pad->fragment_buffers); i++) {
    1948         [ +  - ]:          4 :       if (G_LIKELY (ret == GST_FLOW_OK))
    1949                 :          4 :         ret = gst_qt_mux_send_buffer (qtmux,
    1950                 :          4 :             atom_array_index (&pad->fragment_buffers, i), &qtmux->header_size,
    1951                 :            :             FALSE);
    1952                 :            :       else
    1953                 :          0 :         gst_buffer_unref (atom_array_index (&pad->fragment_buffers, i));
    1954                 :            :     }
    1955                 :            : 
    1956                 :          4 :     atom_array_clear (&pad->fragment_buffers);
    1957                 :          4 :     atom_moof_free (moof);
    1958                 :          4 :     qtmux->fragment_sequence++;
    1959                 :          4 :     force = FALSE;
    1960                 :            :   }
    1961                 :            : 
    1962                 :            : init:
    1963         [ +  - ]:          8 :   if (G_UNLIKELY (!pad->traf)) {
    1964         [ -  + ]:          8 :     GST_LOG_OBJECT (qtmux, "setting up new fragment");
    1965                 :          8 :     pad->traf = atom_traf_new (qtmux->context, atom_trak_get_id (pad->trak));
    1966                 :          8 :     atom_array_init (&pad->fragment_buffers, 512);
    1967                 :          8 :     pad->fragment_duration = gst_util_uint64_scale (qtmux->fragment_duration,
    1968                 :          8 :         atom_trak_get_timescale (pad->trak), 1000);
    1969                 :            : 
    1970 [ +  + ][ +  + ]:          8 :     if (G_UNLIKELY (qtmux->mfra && !pad->tfra)) {
    1971                 :          2 :       pad->tfra = atom_tfra_new (qtmux->context, atom_trak_get_id (pad->trak));
    1972                 :          2 :       atom_mfra_add_tfra (qtmux->mfra, pad->tfra);
    1973                 :            :     }
    1974                 :            :   }
    1975                 :            : 
    1976                 :            :   /* add buffer and metadata */
    1977 [ +  + ][ +  - ]:          8 :   atom_traf_add_samples (pad->traf, delta, size, sync, pts_offset,
    1978                 :          8 :       pad->sync && sync);
    1979 [ -  + ][ -  + ]:          8 :   atom_array_append (&pad->fragment_buffers, buf, 256);
    1980                 :          8 :   pad->fragment_duration -= delta;
    1981                 :            : 
    1982         [ +  + ]:          8 :   if (pad->tfra) {
    1983                 :          4 :     guint32 sn = atom_traf_get_sample_num (pad->traf);
    1984                 :            : 
    1985 [ +  - ][ +  + ]:          4 :     if ((sync && pad->sync) || (sn == 1 && !pad->sync))
         [ +  - ][ +  - ]
    1986                 :          4 :       atom_tfra_add_entry (pad->tfra, dts, sn);
    1987                 :            :   }
    1988                 :            : 
    1989         [ +  + ]:          8 :   if (G_UNLIKELY (force))
    1990                 :          4 :     goto flush;
    1991                 :            : 
    1992                 :          4 :   return ret;
    1993                 :            : }
    1994                 :            : 
    1995                 :            : /* sigh, tiny list helpers to re-order stuff */
    1996                 :            : static void
    1997                 :          0 : gst_qt_mux_push_ts (GstQTMux * qtmux, GstQTPad * pad, GstClockTime ts)
    1998                 :            : {
    1999                 :            :   gint i;
    2000                 :            : 
    2001 [ #  # ][ #  # ]:          0 :   for (i = 0; (i < QTMUX_NO_OF_TS) && (i < pad->ts_n_entries); i++) {
    2002         [ #  # ]:          0 :     if (ts > pad->ts_entries[i])
    2003                 :          0 :       break;
    2004                 :            :   }
    2005                 :          0 :   memmove (&pad->ts_entries[i + 1], &pad->ts_entries[i],
    2006                 :          0 :       sizeof (GstClockTime) * (pad->ts_n_entries - i));
    2007                 :          0 :   pad->ts_entries[i] = ts;
    2008                 :          0 :   pad->ts_n_entries++;
    2009                 :          0 : }
    2010                 :            : 
    2011                 :            : /* takes ownership of @buf */
    2012                 :            : static GstBuffer *
    2013                 :          0 : gst_qt_mux_get_asc_buffer_ts (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
    2014                 :            : {
    2015                 :          0 :   const gint wrap = G_N_ELEMENTS (pad->buf_entries);
    2016                 :            :   GstClockTime ts;
    2017                 :            : 
    2018                 :            :   /* store buffer and ts, latter ordered */
    2019         [ #  # ]:          0 :   if (buf) {
    2020                 :          0 :     pad->buf_entries[pad->buf_tail++] = buf;
    2021                 :          0 :     pad->buf_tail %= wrap;
    2022                 :          0 :     gst_qt_mux_push_ts (qtmux, pad, GST_BUFFER_TIMESTAMP (buf));
    2023                 :            :   }
    2024                 :            : 
    2025 [ #  # ][ #  # ]:          0 :   if (pad->ts_n_entries && (!buf || pad->ts_n_entries >= QTMUX_NO_OF_TS)) {
                 [ #  # ]
    2026                 :          0 :     ts = pad->ts_entries[--pad->ts_n_entries];
    2027                 :          0 :     buf = pad->buf_entries[pad->buf_head];
    2028                 :          0 :     pad->buf_entries[pad->buf_head++] = NULL;
    2029                 :          0 :     pad->buf_head %= wrap;
    2030                 :          0 :     buf = gst_buffer_make_metadata_writable (buf);
    2031                 :            :     /* track original ts (= pts ?) for later */
    2032                 :          0 :     GST_BUFFER_OFFSET_END (buf) = GST_BUFFER_TIMESTAMP (buf);
    2033                 :          0 :     GST_BUFFER_TIMESTAMP (buf) = ts;
    2034 [ #  # ][ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux, "next buffer uses reordered ts %" GST_TIME_FORMAT,
         [ #  # ][ #  # ]
                 [ #  # ]
    2035                 :            :         GST_TIME_ARGS (ts));
    2036                 :            :   } else {
    2037                 :          0 :     buf = NULL;
    2038                 :            :   }
    2039                 :            : 
    2040                 :          0 :   return buf;
    2041                 :            : }
    2042                 :            : 
    2043                 :            : /*
    2044                 :            :  * Here we push the buffer and update the tables in the track atoms
    2045                 :            :  */
    2046                 :            : static GstFlowReturn
    2047                 :         32 : gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
    2048                 :            : {
    2049                 :         32 :   GstBuffer *last_buf = NULL;
    2050                 :            :   GstClockTime duration;
    2051                 :            :   guint nsamples, sample_size;
    2052                 :            :   guint64 chunk_offset;
    2053                 :            :   gint64 last_dts, scaled_duration;
    2054                 :         32 :   gint64 pts_offset = 0;
    2055                 :         32 :   gboolean sync = FALSE, do_pts = FALSE;
    2056                 :         32 :   gboolean drain = (buf == NULL);
    2057                 :            :   GstFlowReturn ret;
    2058                 :            : 
    2059         [ -  + ]:         32 :   if (!pad->fourcc)
    2060                 :          0 :     goto not_negotiated;
    2061                 :            : 
    2062                 :            :   /* if this pad has a prepare function, call it */
    2063         [ -  + ]:         32 :   if (pad->prepare_buf_func != NULL) {
    2064                 :          0 :     buf = pad->prepare_buf_func (pad, buf, qtmux);
    2065                 :            :   }
    2066                 :            : 
    2067                 :            : again:
    2068         [ -  + ]:         32 :   if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER)) {
    2069                 :          0 :     buf = gst_qt_mux_get_asc_buffer_ts (qtmux, pad, buf);
    2070         [ #  # ]:          0 :     if (!buf) {
    2071         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "no reordered buffer yet");
    2072                 :          0 :       return GST_FLOW_OK;
    2073                 :            :     }
    2074                 :            :   }
    2075                 :            : 
    2076                 :         32 :   last_buf = pad->last_buf;
    2077         [ +  + ]:         32 :   if (last_buf == NULL) {
    2078                 :            : #ifndef GST_DISABLE_GST_DEBUG
    2079         [ -  + ]:         16 :     if (buf == NULL) {
    2080         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "Pad %s has no previous buffer stored and "
    2081                 :            :           "received NULL buffer, doing nothing",
    2082                 :            :           GST_PAD_NAME (pad->collect.pad));
    2083                 :            :     } else {
    2084         [ -  + ]:         16 :       GST_LOG_OBJECT (qtmux,
    2085                 :            :           "Pad %s has no previous buffer stored, storing now",
    2086                 :            :           GST_PAD_NAME (pad->collect.pad));
    2087                 :            :     }
    2088                 :            : #endif
    2089                 :         16 :     pad->last_buf = buf;
    2090                 :         16 :     return GST_FLOW_OK;
    2091                 :            :   } else
    2092                 :         16 :     gst_buffer_ref (last_buf);
    2093                 :            : 
    2094                 :            :   /* nasty heuristic mess to guestimate dealing with DTS/PTS,
    2095                 :            :    * while also trying to stay close to input ts to preserve sync,
    2096                 :            :    * so in DTS_METHOD_DD:
    2097                 :            :    * - prefer using input ts where possible
    2098                 :            :    * - if those detected out-of-order (*), mark as out-of-order
    2099                 :            :    * - if in out-of-order, then
    2100                 :            :    *   - if duration available, use that as delta
    2101                 :            :    *     Also mind to preserve sync between streams, and adding
    2102                 :            :    *     durations might drift, so try to resync when we expect
    2103                 :            :    *     input ts == (sum of durations), which is at some keyframe input frame.
    2104                 :            :    *   - if no duration available, we are actually in serious trouble and need
    2105                 :            :    *     to hack around that, so we fail.
    2106                 :            :    * To remedy failure, alternatively, in DTS_METHOD_REORDER:
    2107                 :            :    * - collect some buffers and re-order timestamp,
    2108                 :            :    *   then process the oldest buffer with smallest timestamps.
    2109                 :            :    *   This should typically compensate for some codec's handywork with ts.
    2110                 :            :    * ... but in case this makes ts end up where not expected, in DTS_METHOD_ASC:
    2111                 :            :    * - keep each ts with its buffer and still keep a list of most recent X ts,
    2112                 :            :    *   use the (ascending) minimum of those as DTS (and the difference as ts delta),
    2113                 :            :    *   and use this DTS as a basis to obtain a (positive) CTS offset.
    2114                 :            :    *   This should yield exact PTS == buffer ts, but it seems not all players
    2115                 :            :    *   out there are aware of ctts pts ...
    2116                 :            :    *
    2117                 :            :    * 0.11 Phew, can we (pretty) please please sort out DTS/PTS on buffers ...
    2118                 :            :    */
    2119 [ -  + ][ #  # ]:         16 :   if (G_LIKELY (buf) && !pad->is_out_of_order) {
    2120 [ #  # ][ #  # ]:          0 :     if (G_LIKELY (GST_BUFFER_TIMESTAMP_IS_VALID (last_buf) &&
    2121                 :            :             GST_BUFFER_TIMESTAMP_IS_VALID (buf))) {
    2122         [ #  # ]:          0 :       if ((GST_BUFFER_TIMESTAMP (buf) < GST_BUFFER_TIMESTAMP (last_buf))) {
    2123         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "detected out-of-order input");
    2124                 :          0 :         pad->is_out_of_order = TRUE;
    2125                 :            :       }
    2126                 :            :     } else {
    2127                 :            :       /* this is pretty bad */
    2128         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "missing input timestamp");
    2129                 :            :       /* fall back to durations */
    2130                 :          0 :       pad->is_out_of_order = TRUE;
    2131                 :            :     }
    2132                 :            :   }
    2133                 :            : 
    2134                 :            :   /* would have to be some unusual input, but not impossible */
    2135 [ -  + ][ #  # ]:         16 :   if (G_UNLIKELY (qtmux->dts_method == DTS_METHOD_REORDER &&
    2136                 :            :           pad->is_out_of_order)) {
    2137                 :          0 :     goto no_order;
    2138                 :            :   }
    2139                 :            : 
    2140                 :            :   /* fall back to duration if last buffer or
    2141                 :            :    * out-of-order (determined previously), otherwise use input ts */
    2142 [ -  + ][ #  # ]:         16 :   if (buf == NULL ||
    2143         [ #  # ]:          0 :       (pad->is_out_of_order && qtmux->dts_method == DTS_METHOD_DD)) {
    2144         [ +  + ]:         32 :     if (!GST_BUFFER_DURATION_IS_VALID (last_buf)) {
    2145                 :            :       /* be forgiving for some possibly last upstream flushed buffer */
    2146         [ -  + ]:          9 :       if (buf)
    2147                 :          0 :         goto no_time;
    2148         [ -  + ]:          9 :       GST_WARNING_OBJECT (qtmux, "no duration for last buffer");
    2149                 :            :       /* iso spec recommends some small value, try 0 */
    2150                 :          9 :       duration = 0;
    2151                 :            :     } else {
    2152                 :          7 :       duration = GST_BUFFER_DURATION (last_buf);
    2153                 :            :       /* avoid drift in sum timestamps,
    2154                 :            :        * so use input timestamp for suitable keyframe */
    2155 [ -  + ][ #  # ]:          7 :       if (buf && !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT) &&
                 [ #  # ]
    2156                 :          0 :           GST_BUFFER_TIMESTAMP (buf) >= pad->last_dts) {
    2157 [ #  # ][ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "resyncing out-of-order input to ts; "
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
                 [ #  # ]
    2158                 :            :             "replacing %" GST_TIME_FORMAT " by %" GST_TIME_FORMAT,
    2159                 :            :             GST_TIME_ARGS (pad->last_dts + duration),
    2160                 :            :             GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
    2161                 :          0 :         duration = GST_BUFFER_TIMESTAMP (buf) - pad->last_dts;
    2162                 :            :       }
    2163                 :            :     }
    2164         [ #  # ]:          0 :   } else if (qtmux->dts_method != DTS_METHOD_ASC) {
    2165                 :          0 :     duration = GST_BUFFER_TIMESTAMP (buf) - GST_BUFFER_TIMESTAMP (last_buf);
    2166                 :            :   } else {
    2167                 :            :     GstClockTime ts;
    2168                 :            : 
    2169         [ #  # ]:          0 :     g_assert (qtmux->dts_method == DTS_METHOD_ASC);
    2170         [ #  # ]:          0 :     if (!qtmux->guess_pts)
    2171                 :          0 :       goto need_pts;
    2172                 :            : 
    2173                 :            :     /* add timestamp to queue; keeps in descending order */
    2174                 :          0 :     gst_qt_mux_push_ts (qtmux, pad, GST_BUFFER_TIMESTAMP (last_buf));
    2175                 :            :     /* chuck out smallest/last one if we have enough */
    2176         [ #  # ]:          0 :     if (G_LIKELY (pad->ts_n_entries > QTMUX_NO_OF_TS))
    2177                 :          0 :       pad->ts_n_entries--;
    2178                 :            :     /* peek the now smallest timestamp */
    2179                 :          0 :     ts = pad->ts_entries[pad->ts_n_entries - 1];
    2180                 :            :     /* these tails are expected to be (strictly) ascending with
    2181                 :            :      * large enough history */
    2182 [ #  # ][ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux, "ASC method; base timestamp %" GST_TIME_FORMAT,
         [ #  # ][ #  # ]
                 [ #  # ]
    2183                 :            :         GST_TIME_ARGS (ts));
    2184         [ #  # ]:          0 :     if (ts >= pad->last_dts) {
    2185                 :          0 :       duration = ts - pad->last_dts;
    2186                 :            :     } else {
    2187                 :            :       /* fallback to previous value, negative ct offset might handle */
    2188         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "unexpected decrease in timestamp");
    2189                 :          0 :       duration = 0;
    2190                 :            :     }
    2191                 :            :     /* arrange for small non-zero duration/delta << expected frame time */
    2192                 :          0 :     ts = gst_util_uint64_scale (10, GST_SECOND,
    2193                 :          0 :         atom_trak_get_timescale (pad->trak));
    2194                 :          0 :     duration = MAX (duration, ts);
    2195                 :            :   }
    2196                 :            : 
    2197                 :         16 :   gst_buffer_replace (&pad->last_buf, buf);
    2198                 :            : 
    2199                 :         16 :   last_dts = gst_util_uint64_scale_round (pad->last_dts,
    2200                 :         16 :       atom_trak_get_timescale (pad->trak), GST_SECOND);
    2201                 :            : 
    2202                 :            :   /* fragments only deal with 1 buffer == 1 chunk (== 1 sample) */
    2203 [ -  + ][ #  # ]:         16 :   if (pad->sample_size && !qtmux->fragment_sequence) {
    2204                 :            :     /* Constant size packets: usually raw audio (with many samples per
    2205                 :            :        buffer (= chunk)), but can also be fixed-packet-size codecs like ADPCM
    2206                 :            :      */
    2207                 :          0 :     sample_size = pad->sample_size;
    2208         [ #  # ]:          0 :     if (GST_BUFFER_SIZE (last_buf) % sample_size != 0)
    2209                 :          0 :       goto fragmented_sample;
    2210                 :            :     /* note: qt raw audio storage warps it implicitly into a timewise
    2211                 :            :      * perfect stream, discarding buffer times */
    2212         [ #  # ]:          0 :     if (GST_BUFFER_DURATION (last_buf) != GST_CLOCK_TIME_NONE) {
    2213                 :          0 :       nsamples = gst_util_uint64_scale_round (GST_BUFFER_DURATION (last_buf),
    2214                 :          0 :           atom_trak_get_timescale (pad->trak), GST_SECOND);
    2215                 :            :     } else {
    2216                 :          0 :       nsamples = GST_BUFFER_SIZE (last_buf) / sample_size;
    2217                 :            :     }
    2218                 :          0 :     duration = GST_BUFFER_DURATION (last_buf) / nsamples;
    2219                 :            : 
    2220                 :            :     /* timescale = samplerate */
    2221                 :          0 :     scaled_duration = 1;
    2222                 :          0 :     pad->last_dts += duration * nsamples;
    2223                 :            :   } else {
    2224                 :         16 :     nsamples = 1;
    2225                 :         16 :     sample_size = GST_BUFFER_SIZE (last_buf);
    2226         [ -  + ]:         16 :     if (pad->have_dts) {
    2227                 :            :       gint64 scaled_dts;
    2228                 :          0 :       pad->last_dts = GST_BUFFER_OFFSET_END (last_buf);
    2229         [ #  # ]:          0 :       if ((gint64) (pad->last_dts) < 0) {
    2230                 :          0 :         scaled_dts = -gst_util_uint64_scale_round (-pad->last_dts,
    2231                 :          0 :             atom_trak_get_timescale (pad->trak), GST_SECOND);
    2232                 :            :       } else {
    2233                 :          0 :         scaled_dts = gst_util_uint64_scale_round (pad->last_dts,
    2234                 :          0 :             atom_trak_get_timescale (pad->trak), GST_SECOND);
    2235                 :            :       }
    2236                 :          0 :       scaled_duration = scaled_dts - last_dts;
    2237                 :          0 :       last_dts = scaled_dts;
    2238                 :            :     } else {
    2239                 :            :       /* first convert intended timestamp (in GstClockTime resolution) to
    2240                 :            :        * trak timescale, then derive delta;
    2241                 :            :        * this ensures sums of (scale)delta add up to converted timestamp,
    2242                 :            :        * which only deviates at most 1/scale from timestamp itself */
    2243                 :         16 :       scaled_duration = gst_util_uint64_scale_round (pad->last_dts + duration,
    2244                 :         16 :           atom_trak_get_timescale (pad->trak), GST_SECOND) - last_dts;
    2245                 :         16 :       pad->last_dts += duration;
    2246                 :            :     }
    2247                 :            :   }
    2248                 :         16 :   chunk_offset = qtmux->mdat_size;
    2249                 :            : 
    2250 [ -  + ][ #  # ]:         16 :   GST_LOG_OBJECT (qtmux,
         [ #  # ][ #  # ]
                 [ #  # ]
    2251                 :            :       "Pad (%s) dts updated to %" GST_TIME_FORMAT,
    2252                 :            :       GST_PAD_NAME (pad->collect.pad), GST_TIME_ARGS (pad->last_dts));
    2253         [ -  + ]:         16 :   GST_LOG_OBJECT (qtmux,
    2254                 :            :       "Adding %d samples to track, duration: %" G_GUINT64_FORMAT
    2255                 :            :       " size: %" G_GUINT32_FORMAT " chunk offset: %" G_GUINT64_FORMAT,
    2256                 :            :       nsamples, scaled_duration, sample_size, chunk_offset);
    2257                 :            : 
    2258                 :            :   /* might be a sync sample */
    2259 [ +  + ][ +  - ]:         16 :   if (pad->sync &&
    2260                 :         13 :       !GST_BUFFER_FLAG_IS_SET (last_buf, GST_BUFFER_FLAG_DELTA_UNIT)) {
    2261         [ -  + ]:         13 :     GST_LOG_OBJECT (qtmux, "Adding new sync sample entry for track of pad %s",
    2262                 :            :         GST_PAD_NAME (pad->collect.pad));
    2263                 :         13 :     sync = TRUE;
    2264                 :            :   }
    2265                 :            : 
    2266                 :            :   /* optionally calculate ctts entry values
    2267                 :            :    * (if composition-time expected different from decoding-time) */
    2268                 :            :   /* really not recommended:
    2269                 :            :    * - decoder typically takes care of dts/pts issues
    2270                 :            :    * - in case of out-of-order, dts may only be determined as above
    2271                 :            :    *   (e.g. sum of duration), which may be totally different from
    2272                 :            :    *   buffer timestamps in case of multiple segment, non-perfect streams
    2273                 :            :    *  (and just perhaps maybe with some luck segment_to_running_time
    2274                 :            :    *   or segment_to_media_time might get near to it) */
    2275 [ +  - ][ -  + ]:         16 :   if ((pad->have_dts || qtmux->guess_pts)) {
    2276                 :            :     guint64 pts;
    2277                 :            : 
    2278                 :          0 :     pts = qtmux->dts_method == DTS_METHOD_REORDER ?
    2279         [ #  # ]:          0 :         GST_BUFFER_OFFSET_END (last_buf) : GST_BUFFER_TIMESTAMP (last_buf);
    2280                 :          0 :     pts = gst_util_uint64_scale_round (pts,
    2281                 :          0 :         atom_trak_get_timescale (pad->trak), GST_SECOND);
    2282                 :          0 :     pts_offset = (gint64) (pts - last_dts);
    2283                 :          0 :     do_pts = TRUE;
    2284         [ #  # ]:          0 :     GST_LOG_OBJECT (qtmux, "Adding ctts entry for pad %s: %" G_GINT64_FORMAT,
    2285                 :            :         GST_PAD_NAME (pad->collect.pad), pts_offset);
    2286                 :            :   }
    2287                 :            : 
    2288                 :            :   /*
    2289                 :            :    * Each buffer starts a new chunk, so we can assume the buffer
    2290                 :            :    * duration is the chunk duration
    2291                 :            :    */
    2292 [ +  - ][ +  - ]:         16 :   if (GST_CLOCK_TIME_IS_VALID (duration) && (duration > qtmux->longest_chunk ||
                 [ +  - ]
    2293                 :         16 :           !GST_CLOCK_TIME_IS_VALID (qtmux->longest_chunk))) {
    2294 [ -  + ][ #  # ]:         16 :     GST_DEBUG_OBJECT (qtmux, "New longest chunk found: %" GST_TIME_FORMAT
         [ #  # ][ #  # ]
                 [ #  # ]
    2295                 :            :         ", pad %s", GST_TIME_ARGS (duration), GST_PAD_NAME (pad->collect.pad));
    2296                 :         16 :     qtmux->longest_chunk = duration;
    2297                 :            :   }
    2298                 :            : 
    2299                 :            :   /* if this is the first buffer, store the timestamp */
    2300 [ +  - ][ +  - ]:         16 :   if (G_UNLIKELY (pad->first_ts == GST_CLOCK_TIME_NONE) && last_buf) {
    2301         [ +  - ]:         16 :     if (GST_CLOCK_TIME_IS_VALID (GST_BUFFER_TIMESTAMP (last_buf))) {
    2302                 :         16 :       pad->first_ts = GST_BUFFER_TIMESTAMP (last_buf);
    2303                 :            :     } else {
    2304         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "First buffer for pad %s has no timestamp, "
    2305                 :            :           "using 0 as first timestamp", GST_PAD_NAME (pad->collect.pad));
    2306                 :          0 :       pad->first_ts = 0;
    2307                 :            :     }
    2308 [ -  + ][ #  # ]:         16 :     GST_DEBUG_OBJECT (qtmux, "Stored first timestamp for pad %s %"
         [ #  # ][ #  # ]
                 [ #  # ]
    2309                 :            :         GST_TIME_FORMAT, GST_PAD_NAME (pad->collect.pad),
    2310                 :            :         GST_TIME_ARGS (pad->first_ts));
    2311                 :            :   }
    2312                 :            : 
    2313                 :            :   /* now we go and register this buffer/sample all over */
    2314                 :            :   /* note that a new chunk is started each time (not fancy but works) */
    2315         [ -  + ]:         16 :   if (qtmux->moov_recov_file) {
    2316         [ #  # ]:          0 :     if (!atoms_recov_write_trak_samples (qtmux->moov_recov_file, pad->trak,
    2317                 :            :             nsamples, (gint32) scaled_duration, sample_size, chunk_offset, sync,
    2318                 :            :             do_pts, pts_offset)) {
    2319         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "Failed to write sample information to "
    2320                 :            :           "recovery file, disabling recovery");
    2321                 :          0 :       fclose (qtmux->moov_recov_file);
    2322                 :          0 :       qtmux->moov_recov_file = NULL;
    2323                 :            :     }
    2324                 :            :   }
    2325                 :            : 
    2326         [ -  + ]:         16 :   if (buf)
    2327                 :          0 :     gst_buffer_unref (buf);
    2328                 :            : 
    2329         [ +  + ]:         16 :   if (qtmux->fragment_sequence) {
    2330                 :            :     /* ensure that always sync samples are marked as such */
    2331 [ +  + ][ +  - ]:          4 :     ret = gst_qt_mux_pad_fragment_add_buffer (qtmux, pad, last_buf,
    2332                 :            :         buf == NULL, nsamples, last_dts, (gint32) scaled_duration, sample_size,
    2333                 :          4 :         !pad->sync || sync, pts_offset);
    2334                 :            :   } else {
    2335                 :         12 :     atom_trak_add_samples (pad->trak, nsamples, (gint32) scaled_duration,
    2336                 :            :         sample_size, chunk_offset, sync, pts_offset);
    2337                 :         12 :     ret = gst_qt_mux_send_buffer (qtmux, last_buf, &qtmux->mdat_size, TRUE);
    2338                 :            :   }
    2339                 :            : 
    2340 [ +  - ][ -  + ]:         16 :   if (G_UNLIKELY (drain && qtmux->dts_method == DTS_METHOD_REORDER &&
                 [ #  # ]
    2341                 :            :           ret == GST_FLOW_OK)) {
    2342                 :          0 :     buf = NULL;
    2343                 :          0 :     goto again;
    2344                 :            :   }
    2345                 :            : 
    2346                 :         16 :   return ret;
    2347                 :            : 
    2348                 :            :   /* ERRORS */
    2349                 :            : bail:
    2350                 :            :   {
    2351         [ #  # ]:          0 :     if (buf)
    2352                 :          0 :       gst_buffer_unref (buf);
    2353                 :          0 :     gst_buffer_unref (last_buf);
    2354                 :          0 :     return GST_FLOW_ERROR;
    2355                 :            :   }
    2356                 :            : no_time:
    2357                 :            :   {
    2358 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    2359                 :            :         ("Received buffer without timestamp/duration."));
    2360                 :          0 :     goto bail;
    2361                 :            :   }
    2362                 :            : no_order:
    2363                 :            :   {
    2364 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    2365                 :            :         ("DTS method failed to re-order timestamps."));
    2366                 :          0 :     goto bail;
    2367                 :            :   }
    2368                 :            : need_pts:
    2369                 :            :   {
    2370 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    2371                 :            :         ("Selected DTS method also needs PTS enabled."));
    2372                 :          0 :     goto bail;
    2373                 :            :   }
    2374                 :            : fragmented_sample:
    2375                 :            :   {
    2376 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, STREAM, MUX, (NULL),
         [ #  # ][ #  # ]
    2377                 :            :         ("Audio buffer contains fragmented sample."));
    2378                 :          0 :     goto bail;
    2379                 :            :   }
    2380                 :            : not_negotiated:
    2381                 :            :   {
    2382 [ #  # ][ #  # ]:          0 :     GST_ELEMENT_ERROR (qtmux, CORE, NEGOTIATION, (NULL),
         [ #  # ][ #  # ]
    2383                 :            :         ("format wasn't negotiated before buffer flow on pad %s",
    2384                 :            :             GST_PAD_NAME (pad->collect.pad)));
    2385         [ #  # ]:          0 :     if (buf)
    2386                 :          0 :       gst_buffer_unref (buf);
    2387                 :         32 :     return GST_FLOW_NOT_NEGOTIATED;
    2388                 :            :   }
    2389                 :            : }
    2390                 :            : 
    2391                 :            : static GstFlowReturn
    2392                 :         32 : gst_qt_mux_collected (GstCollectPads * pads, gpointer user_data)
    2393                 :            : {
    2394                 :         32 :   GstFlowReturn ret = GST_FLOW_OK;
    2395                 :         32 :   GstQTMux *qtmux = GST_QT_MUX_CAST (user_data);
    2396                 :            :   GSList *walk;
    2397                 :         32 :   GstQTPad *best_pad = NULL;
    2398                 :         32 :   GstClockTime time, best_time = GST_CLOCK_TIME_NONE;
    2399                 :            :   GstBuffer *buf;
    2400                 :            : 
    2401         [ +  + ]:         32 :   if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_STARTED)) {
    2402         [ -  + ]:         16 :     if ((ret = gst_qt_mux_start_file (qtmux)) != GST_FLOW_OK)
    2403                 :          0 :       return ret;
    2404                 :            :     else
    2405                 :         16 :       qtmux->state = GST_QT_MUX_STATE_DATA;
    2406                 :            :   }
    2407                 :            : 
    2408         [ -  + ]:         32 :   if (G_UNLIKELY (qtmux->state == GST_QT_MUX_STATE_EOS))
    2409                 :          0 :     return GST_FLOW_UNEXPECTED;
    2410                 :            : 
    2411                 :            :   /* select the best buffer */
    2412                 :         32 :   walk = qtmux->collect->data;
    2413         [ +  + ]:         64 :   while (walk) {
    2414                 :            :     GstQTPad *pad;
    2415                 :            :     GstCollectData *data;
    2416                 :            : 
    2417                 :         32 :     data = (GstCollectData *) walk->data;
    2418                 :         32 :     pad = (GstQTPad *) data;
    2419                 :            : 
    2420         [ +  - ]:         32 :     walk = g_slist_next (walk);
    2421                 :            : 
    2422                 :         32 :     buf = gst_collect_pads_peek (pads, data);
    2423         [ +  + ]:         32 :     if (buf == NULL) {
    2424         [ -  + ]:         16 :       GST_LOG_OBJECT (qtmux, "Pad %s has no buffers",
    2425                 :            :           GST_PAD_NAME (pad->collect.pad));
    2426                 :         16 :       continue;
    2427                 :            :     }
    2428                 :         16 :     time = GST_BUFFER_TIMESTAMP (buf);
    2429                 :         16 :     gst_buffer_unref (buf);
    2430                 :            : 
    2431                 :            :     /* invalid should pass */
    2432         [ +  - ]:         16 :     if (G_LIKELY (GST_CLOCK_TIME_IS_VALID (time))) {
    2433                 :         16 :       time =
    2434                 :         16 :           gst_segment_to_running_time (&data->segment, GST_FORMAT_TIME, time);
    2435         [ -  + ]:         16 :       if (G_UNLIKELY (!GST_CLOCK_TIME_IS_VALID (time))) {
    2436         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "clipping buffer on pad %s outside segment",
    2437                 :            :             GST_PAD_NAME (data->pad));
    2438                 :          0 :         buf = gst_collect_pads_pop (pads, data);
    2439                 :          0 :         gst_buffer_unref (buf);
    2440                 :          0 :         return GST_FLOW_OK;
    2441                 :            :       }
    2442                 :            :     }
    2443                 :            : 
    2444 [ -  + ][ #  # ]:         16 :     if (best_pad == NULL || !GST_CLOCK_TIME_IS_VALID (time) ||
                 [ #  # ]
    2445         [ #  # ]:          0 :         (GST_CLOCK_TIME_IS_VALID (best_time) && time < best_time)) {
    2446                 :         16 :       best_pad = pad;
    2447                 :         16 :       best_time = time;
    2448                 :            :     }
    2449                 :            :   }
    2450                 :            : 
    2451         [ +  + ]:         32 :   if (best_pad != NULL) {
    2452 [ -  + ][ #  # ]:         16 :     GST_LOG_OBJECT (qtmux, "selected pad %s with time %" GST_TIME_FORMAT,
         [ #  # ][ #  # ]
                 [ #  # ]
    2453                 :            :         GST_PAD_NAME (best_pad->collect.pad), GST_TIME_ARGS (best_time));
    2454                 :         16 :     buf = gst_collect_pads_pop (pads, &best_pad->collect);
    2455                 :         16 :     buf = gst_buffer_make_metadata_writable (buf);
    2456                 :         16 :     GST_BUFFER_TIMESTAMP (buf) = best_time;
    2457                 :         16 :     ret = gst_qt_mux_add_buffer (qtmux, best_pad, buf);
    2458                 :            :   } else {
    2459                 :         16 :     ret = gst_qt_mux_stop_file (qtmux);
    2460         [ +  - ]:         16 :     if (ret == GST_FLOW_OK) {
    2461         [ -  + ]:         16 :       GST_DEBUG_OBJECT (qtmux, "Pushing eos");
    2462                 :         16 :       gst_pad_push_event (qtmux->srcpad, gst_event_new_eos ());
    2463                 :         16 :       ret = GST_FLOW_UNEXPECTED;
    2464                 :            :     } else {
    2465         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "Failed to stop file: %s",
    2466                 :            :           gst_flow_get_name (ret));
    2467                 :            :     }
    2468                 :         16 :     qtmux->state = GST_QT_MUX_STATE_EOS;
    2469                 :            :   }
    2470                 :            : 
    2471                 :         32 :   return ret;
    2472                 :            : }
    2473                 :            : 
    2474                 :            : static gboolean
    2475                 :          0 : check_field (GQuark field_id, const GValue * value, gpointer user_data)
    2476                 :            : {
    2477                 :          0 :   GstStructure *structure = (GstStructure *) user_data;
    2478                 :          0 :   const GValue *other = gst_structure_id_get_value (structure, field_id);
    2479         [ #  # ]:          0 :   if (other == NULL)
    2480                 :          0 :     return FALSE;
    2481                 :          0 :   return gst_value_compare (value, other) == GST_VALUE_EQUAL;
    2482                 :            : }
    2483                 :            : 
    2484                 :            : static gboolean
    2485                 :          0 : gst_qtmux_caps_is_subset_full (GstQTMux * qtmux, GstCaps * subset,
    2486                 :            :     GstCaps * superset)
    2487                 :            : {
    2488                 :          0 :   GstStructure *sub_s = gst_caps_get_structure (subset, 0);
    2489                 :          0 :   GstStructure *sup_s = gst_caps_get_structure (superset, 0);
    2490                 :            : 
    2491                 :          0 :   return gst_structure_foreach (sub_s, check_field, sup_s);
    2492                 :            : }
    2493                 :            : 
    2494                 :            : static gboolean
    2495                 :          3 : gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps)
    2496                 :            : {
    2497                 :          3 :   GstQTMux *qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad));
    2498                 :          3 :   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
    2499                 :          3 :   GstQTPad *qtpad = NULL;
    2500                 :            :   GstStructure *structure;
    2501                 :            :   const gchar *mimetype;
    2502                 :            :   gint rate, channels;
    2503                 :          3 :   const GValue *value = NULL;
    2504                 :          3 :   const GstBuffer *codec_data = NULL;
    2505                 :            :   GstQTMuxFormat format;
    2506                 :          3 :   AudioSampleEntry entry = { 0, };
    2507                 :          3 :   AtomInfo *ext_atom = NULL;
    2508                 :          3 :   gint constant_size = 0;
    2509                 :            :   const gchar *stream_format;
    2510                 :          3 :   GstCaps *current_caps = NULL;
    2511                 :            : 
    2512                 :            :   /* find stream data */
    2513                 :          3 :   qtpad = (GstQTPad *) gst_pad_get_element_private (pad);
    2514         [ -  + ]:          3 :   g_assert (qtpad);
    2515                 :            : 
    2516                 :          3 :   qtpad->prepare_buf_func = NULL;
    2517                 :            : 
    2518                 :            :   /* does not go well to renegotiate stream mid-way, unless
    2519                 :            :    * the old caps are a subset of the new one (this means upstream
    2520                 :            :    * added more info to the caps, as both should be 'fixed' caps) */
    2521         [ -  + ]:          3 :   if (qtpad->fourcc) {
    2522                 :          0 :     g_object_get (pad, "caps", &current_caps, NULL);
    2523         [ #  # ]:          0 :     g_assert (caps != NULL);
    2524                 :            : 
    2525         [ #  # ]:          0 :     if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) {
    2526                 :          0 :       goto refuse_renegotiation;
    2527                 :            :     }
    2528         [ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux,
    2529                 :            :         "pad %s accepted renegotiation to %" GST_PTR_FORMAT " from %"
    2530                 :            :         GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad));
    2531                 :            :   }
    2532                 :            : 
    2533 [ -  + ][ #  # ]:          3 :   GST_DEBUG_OBJECT (qtmux, "%s:%s, caps=%" GST_PTR_FORMAT,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    2534                 :            :       GST_DEBUG_PAD_NAME (pad), caps);
    2535                 :            : 
    2536                 :          3 :   format = qtmux_klass->format;
    2537                 :          3 :   structure = gst_caps_get_structure (caps, 0);
    2538                 :          3 :   mimetype = gst_structure_get_name (structure);
    2539                 :            : 
    2540                 :            :   /* common info */
    2541   [ +  -  +  - ]:          6 :   if (!gst_structure_get_int (structure, "channels", &channels) ||
    2542                 :          3 :       !gst_structure_get_int (structure, "rate", &rate)) {
    2543                 :            :     goto refuse_caps;
    2544                 :            :   }
    2545                 :            : 
    2546                 :            :   /* optional */
    2547                 :          3 :   value = gst_structure_get_value (structure, "codec_data");
    2548         [ -  + ]:          3 :   if (value != NULL)
    2549                 :          0 :     codec_data = gst_value_get_buffer (value);
    2550                 :            : 
    2551                 :          3 :   qtpad->is_out_of_order = FALSE;
    2552                 :          3 :   qtpad->have_dts = FALSE;
    2553                 :            : 
    2554                 :            :   /* set common properties */
    2555                 :          3 :   entry.sample_rate = rate;
    2556                 :          3 :   entry.channels = channels;
    2557                 :            :   /* default */
    2558                 :          3 :   entry.sample_size = 16;
    2559                 :            :   /* this is the typical compressed case */
    2560         [ +  - ]:          3 :   if (format == GST_QT_MUX_FORMAT_QT) {
    2561                 :          3 :     entry.version = 1;
    2562                 :          3 :     entry.compression_id = -2;
    2563                 :            :   }
    2564                 :            : 
    2565                 :            :   /* now map onto a fourcc, and some extra properties */
    2566         [ +  - ]:          3 :   if (strcmp (mimetype, "audio/mpeg") == 0) {
    2567                 :          3 :     gint mpegversion = 0;
    2568                 :          3 :     gint layer = -1;
    2569                 :            : 
    2570                 :          3 :     gst_structure_get_int (structure, "mpegversion", &mpegversion);
    2571      [ +  -  - ]:          3 :     switch (mpegversion) {
    2572                 :            :       case 1:
    2573                 :          3 :         gst_structure_get_int (structure, "layer", &layer);
    2574         [ +  - ]:          3 :         switch (layer) {
    2575                 :            :           case 3:
    2576                 :            :             /* mp3 */
    2577                 :            :             /* note: QuickTime player does not like mp3 either way in iso/mp4 */
    2578         [ +  - ]:          3 :             if (format == GST_QT_MUX_FORMAT_QT)
    2579                 :          3 :               entry.fourcc = FOURCC__mp3;
    2580                 :            :             else {
    2581                 :          0 :               entry.fourcc = FOURCC_mp4a;
    2582                 :          0 :               ext_atom =
    2583                 :          0 :                   build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG1_P3,
    2584                 :            :                   ESDS_STREAM_TYPE_AUDIO, codec_data, qtpad->avg_bitrate,
    2585                 :            :                   qtpad->max_bitrate);
    2586                 :            :             }
    2587                 :          3 :             entry.samples_per_packet = 1152;
    2588                 :          3 :             entry.bytes_per_sample = 2;
    2589                 :          3 :             break;
    2590                 :            :         }
    2591                 :          3 :         break;
    2592                 :            :       case 4:
    2593                 :            : 
    2594                 :            :         /* check stream-format */
    2595                 :          0 :         stream_format = gst_structure_get_string (structure, "stream-format");
    2596         [ #  # ]:          0 :         if (stream_format) {
    2597         [ #  # ]:          0 :           if (strcmp (stream_format, "raw") != 0) {
    2598         [ #  # ]:          0 :             GST_WARNING_OBJECT (qtmux, "Unsupported AAC stream-format %s, "
    2599                 :            :                 "please use 'raw'", stream_format);
    2600                 :          0 :             goto refuse_caps;
    2601                 :            :           }
    2602                 :            :         } else {
    2603         [ #  # ]:          0 :           GST_WARNING_OBJECT (qtmux, "No stream-format present in caps, "
    2604                 :            :               "assuming 'raw'");
    2605                 :            :         }
    2606                 :            : 
    2607 [ #  # ][ #  # ]:          0 :         if (!codec_data || GST_BUFFER_SIZE (codec_data) < 2)
    2608         [ #  # ]:          0 :           GST_WARNING_OBJECT (qtmux, "no (valid) codec_data for AAC audio");
    2609                 :            :         else {
    2610                 :          0 :           guint8 profile = GST_READ_UINT8 (GST_BUFFER_DATA (codec_data));
    2611                 :            : 
    2612                 :            :           /* warn if not Low Complexity profile */
    2613                 :          0 :           profile >>= 3;
    2614         [ #  # ]:          0 :           if (profile != 2)
    2615         [ #  # ]:          0 :             GST_WARNING_OBJECT (qtmux,
    2616                 :            :                 "non-LC AAC may not run well on (Apple) QuickTime/iTunes");
    2617                 :            :         }
    2618                 :            : 
    2619                 :            :         /* AAC */
    2620                 :          0 :         entry.fourcc = FOURCC_mp4a;
    2621                 :            : 
    2622         [ #  # ]:          0 :         if (format == GST_QT_MUX_FORMAT_QT)
    2623                 :          0 :           ext_atom = build_mov_aac_extension (qtpad->trak, codec_data,
    2624                 :            :               qtpad->avg_bitrate, qtpad->max_bitrate);
    2625                 :            :         else
    2626                 :          0 :           ext_atom =
    2627                 :          0 :               build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG4_P3,
    2628                 :            :               ESDS_STREAM_TYPE_AUDIO, codec_data, qtpad->avg_bitrate,
    2629                 :            :               qtpad->max_bitrate);
    2630                 :          0 :         break;
    2631                 :            :       default:
    2632                 :          3 :         break;
    2633                 :            :     }
    2634         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/AMR") == 0) {
    2635                 :          0 :     entry.fourcc = FOURCC_samr;
    2636                 :          0 :     entry.sample_size = 16;
    2637                 :          0 :     entry.samples_per_packet = 160;
    2638                 :          0 :     entry.bytes_per_sample = 2;
    2639                 :          0 :     ext_atom = build_amr_extension ();
    2640         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/AMR-WB") == 0) {
    2641                 :          0 :     entry.fourcc = FOURCC_sawb;
    2642                 :          0 :     entry.sample_size = 16;
    2643                 :          0 :     entry.samples_per_packet = 320;
    2644                 :          0 :     entry.bytes_per_sample = 2;
    2645                 :          0 :     ext_atom = build_amr_extension ();
    2646         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/x-raw-int") == 0) {
    2647                 :            :     gint width;
    2648                 :            :     gint depth;
    2649                 :            :     gint endianness;
    2650                 :            :     gboolean sign;
    2651                 :            : 
    2652   [ #  #  #  # ]:          0 :     if (!gst_structure_get_int (structure, "width", &width) ||
    2653         [ #  # ]:          0 :         !gst_structure_get_int (structure, "depth", &depth) ||
    2654                 :          0 :         !gst_structure_get_boolean (structure, "signed", &sign)) {
    2655         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "broken caps, width/depth/signed field missing");
    2656                 :          0 :       goto refuse_caps;
    2657                 :            :     }
    2658                 :            : 
    2659         [ #  # ]:          0 :     if (depth <= 8) {
    2660                 :          0 :       endianness = G_BYTE_ORDER;
    2661         [ #  # ]:          0 :     } else if (!gst_structure_get_int (structure, "endianness", &endianness)) {
    2662         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "broken caps, endianness field missing");
    2663                 :          0 :       goto refuse_caps;
    2664                 :            :     }
    2665                 :            : 
    2666                 :            :     /* spec has no place for a distinction in these */
    2667         [ #  # ]:          0 :     if (width != depth) {
    2668         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "width must be same as depth!");
    2669                 :          0 :       goto refuse_caps;
    2670                 :            :     }
    2671                 :            : 
    2672         [ #  # ]:          0 :     if (sign) {
    2673         [ #  # ]:          0 :       if (endianness == G_LITTLE_ENDIAN)
    2674                 :          0 :         entry.fourcc = FOURCC_sowt;
    2675         [ #  # ]:          0 :       else if (endianness == G_BIG_ENDIAN)
    2676                 :          0 :         entry.fourcc = FOURCC_twos;
    2677                 :            :       /* maximum backward compatibility; only new version for > 16 bit */
    2678         [ #  # ]:          0 :       if (depth <= 16)
    2679                 :          0 :         entry.version = 0;
    2680                 :            :       /* not compressed in any case */
    2681                 :          0 :       entry.compression_id = 0;
    2682                 :            :       /* QT spec says: max at 16 bit even if sample size were actually larger,
    2683                 :            :        * however, most players (e.g. QuickTime!) seem to disagree, so ... */
    2684                 :          0 :       entry.sample_size = depth;
    2685                 :          0 :       entry.bytes_per_sample = depth / 8;
    2686                 :          0 :       entry.samples_per_packet = 1;
    2687                 :          0 :       entry.bytes_per_packet = depth / 8;
    2688                 :          0 :       entry.bytes_per_frame = entry.bytes_per_packet * channels;
    2689                 :            :     } else {
    2690 [ #  # ][ #  # ]:          0 :       if (width == 8 && depth == 8) {
    2691                 :            :         /* fall back to old 8-bit version */
    2692                 :          0 :         entry.fourcc = FOURCC_raw_;
    2693                 :          0 :         entry.version = 0;
    2694                 :          0 :         entry.compression_id = 0;
    2695                 :          0 :         entry.sample_size = 8;
    2696                 :            :       } else {
    2697         [ #  # ]:          0 :         GST_DEBUG_OBJECT (qtmux, "non 8-bit PCM must be signed");
    2698                 :          0 :         goto refuse_caps;
    2699                 :            :       }
    2700                 :            :     }
    2701                 :          0 :     constant_size = (depth / 8) * channels;
    2702         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/x-alaw") == 0) {
    2703                 :          0 :     entry.fourcc = FOURCC_alaw;
    2704                 :          0 :     entry.samples_per_packet = 1023;
    2705                 :          0 :     entry.bytes_per_sample = 2;
    2706         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/x-mulaw") == 0) {
    2707                 :          0 :     entry.fourcc = FOURCC_ulaw;
    2708                 :          0 :     entry.samples_per_packet = 1023;
    2709                 :          0 :     entry.bytes_per_sample = 2;
    2710         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/x-adpcm") == 0) {
    2711                 :            :     gint blocksize;
    2712         [ #  # ]:          0 :     if (!gst_structure_get_int (structure, "block_align", &blocksize)) {
    2713         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "broken caps, block_align missing");
    2714                 :          0 :       goto refuse_caps;
    2715                 :            :     }
    2716                 :            :     /* Currently only supports WAV-style IMA ADPCM, for which the codec id is
    2717                 :            :        0x11 */
    2718                 :          0 :     entry.fourcc = MS_WAVE_FOURCC (0x11);
    2719                 :            :     /* 4 byte header per channel (including one sample). 2 samples per byte
    2720                 :            :        remaining. Simplifying gives the following (samples per block per
    2721                 :            :        channel) */
    2722                 :          0 :     entry.samples_per_packet = 2 * blocksize / channels - 7;
    2723                 :          0 :     entry.bytes_per_sample = 2;
    2724                 :            : 
    2725                 :          0 :     entry.bytes_per_frame = blocksize;
    2726                 :          0 :     entry.bytes_per_packet = blocksize / channels;
    2727                 :            :     /* ADPCM has constant size packets */
    2728                 :          0 :     constant_size = 1;
    2729                 :            :     /* TODO: I don't really understand why this helps, but it does! Constant
    2730                 :            :      * size and compression_id of -2 seem to be incompatible, and other files
    2731                 :            :      * in the wild use this too. */
    2732                 :          0 :     entry.compression_id = -1;
    2733                 :            : 
    2734                 :          0 :     ext_atom = build_ima_adpcm_extension (channels, rate, blocksize);
    2735         [ #  # ]:          0 :   } else if (strcmp (mimetype, "audio/x-alac") == 0) {
    2736                 :            :     GstBuffer *codec_config;
    2737                 :            :     gint len;
    2738                 :            : 
    2739                 :          0 :     entry.fourcc = FOURCC_alac;
    2740                 :            :     /* let's check if codec data already comes with 'alac' atom prefix */
    2741 [ #  # ][ #  # ]:          0 :     if (!codec_data || (len = GST_BUFFER_SIZE (codec_data)) < 28) {
    2742         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "broken caps, codec data missing");
    2743                 :          0 :       goto refuse_caps;
    2744                 :            :     }
    2745         [ #  # ]:          0 :     if (GST_READ_UINT32_LE (GST_BUFFER_DATA (codec_data) + 4) == FOURCC_alac) {
    2746                 :          0 :       len -= 8;
    2747                 :          0 :       codec_config = gst_buffer_create_sub ((GstBuffer *) codec_data, 8, len);
    2748                 :            :     } else {
    2749                 :          0 :       codec_config = gst_buffer_ref ((GstBuffer *) codec_data);
    2750                 :            :     }
    2751         [ #  # ]:          0 :     if (len != 28) {
    2752                 :            :       /* does not look good, but perhaps some trailing unneeded stuff */
    2753         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "unexpected codec-data size, possibly broken");
    2754                 :            :     }
    2755         [ #  # ]:          0 :     if (format == GST_QT_MUX_FORMAT_QT)
    2756                 :          0 :       ext_atom = build_mov_alac_extension (qtpad->trak, codec_config);
    2757                 :            :     else
    2758                 :          0 :       ext_atom = build_codec_data_extension (FOURCC_alac, codec_config);
    2759                 :            :     /* set some more info */
    2760                 :          0 :     entry.bytes_per_sample = 2;
    2761                 :          0 :     entry.samples_per_packet =
    2762                 :          0 :         GST_READ_UINT32_BE (GST_BUFFER_DATA (codec_config) + 4);
    2763                 :          0 :     gst_buffer_unref (codec_config);
    2764                 :            :   }
    2765                 :            : 
    2766         [ -  + ]:          3 :   if (!entry.fourcc)
    2767                 :          0 :     goto refuse_caps;
    2768                 :            : 
    2769                 :            :   /* ok, set the pad info accordingly */
    2770                 :          3 :   qtpad->fourcc = entry.fourcc;
    2771                 :          3 :   qtpad->sample_size = constant_size;
    2772         [ -  + ]:          3 :   atom_trak_set_audio_type (qtpad->trak, qtmux->context, &entry,
    2773                 :          3 :       qtmux->trak_timescale ? qtmux->trak_timescale : entry.sample_rate,
    2774                 :            :       ext_atom, constant_size);
    2775                 :            : 
    2776                 :          3 :   gst_object_unref (qtmux);
    2777                 :          3 :   return TRUE;
    2778                 :            : 
    2779                 :            :   /* ERRORS */
    2780                 :            : refuse_caps:
    2781                 :            :   {
    2782         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "pad %s refused caps %" GST_PTR_FORMAT,
    2783                 :            :         GST_PAD_NAME (pad), caps);
    2784                 :          0 :     gst_object_unref (qtmux);
    2785                 :          0 :     return FALSE;
    2786                 :            :   }
    2787                 :            : refuse_renegotiation:
    2788                 :            :   {
    2789         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux,
    2790                 :            :         "pad %s refused renegotiation to %" GST_PTR_FORMAT,
    2791                 :            :         GST_PAD_NAME (pad), caps);
    2792                 :          0 :     gst_object_unref (qtmux);
    2793                 :          3 :     return FALSE;
    2794                 :            :   }
    2795                 :            : }
    2796                 :            : 
    2797                 :            : /* scale rate up or down by factor of 10 to fit into [1000,10000] interval */
    2798                 :            : static guint32
    2799                 :         13 : adjust_rate (guint64 rate)
    2800                 :            : {
    2801         [ -  + ]:         13 :   if (rate == 0)
    2802                 :          0 :     return 10000;
    2803                 :            : 
    2804         [ -  + ]:         13 :   while (rate >= 10000)
    2805                 :          0 :     rate /= 10;
    2806                 :            : 
    2807         [ +  + ]:         39 :   while (rate < 1000)
    2808                 :         26 :     rate *= 10;
    2809                 :            : 
    2810                 :         13 :   return (guint32) rate;
    2811                 :            : }
    2812                 :            : 
    2813                 :            : static gboolean
    2814                 :         13 : gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps)
    2815                 :            : {
    2816                 :         13 :   GstQTMux *qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad));
    2817                 :         13 :   GstQTMuxClass *qtmux_klass = (GstQTMuxClass *) (G_OBJECT_GET_CLASS (qtmux));
    2818                 :         13 :   GstQTPad *qtpad = NULL;
    2819                 :            :   GstStructure *structure;
    2820                 :            :   const gchar *mimetype;
    2821                 :         13 :   gint width, height, depth = -1;
    2822                 :            :   gint framerate_num, framerate_den;
    2823                 :            :   guint32 rate;
    2824                 :         13 :   const GValue *value = NULL;
    2825                 :         13 :   const GstBuffer *codec_data = NULL;
    2826                 :         13 :   VisualSampleEntry entry = { 0, };
    2827                 :            :   GstQTMuxFormat format;
    2828                 :         13 :   AtomInfo *ext_atom = NULL;
    2829                 :         13 :   GList *ext_atom_list = NULL;
    2830                 :         13 :   gboolean sync = FALSE;
    2831                 :            :   int par_num, par_den;
    2832                 :         13 :   GstCaps *current_caps = NULL;
    2833                 :            : 
    2834                 :            :   /* find stream data */
    2835                 :         13 :   qtpad = (GstQTPad *) gst_pad_get_element_private (pad);
    2836         [ -  + ]:         13 :   g_assert (qtpad);
    2837                 :            : 
    2838                 :         13 :   qtpad->prepare_buf_func = NULL;
    2839                 :            : 
    2840                 :            :   /* does not go well to renegotiate stream mid-way, unless
    2841                 :            :    * the old caps are a subset of the new one (this means upstream
    2842                 :            :    * added more info to the caps, as both should be 'fixed' caps) */
    2843         [ -  + ]:         13 :   if (qtpad->fourcc) {
    2844                 :          0 :     g_object_get (pad, "caps", &current_caps, NULL);
    2845         [ #  # ]:          0 :     g_assert (caps != NULL);
    2846                 :            : 
    2847         [ #  # ]:          0 :     if (!gst_qtmux_caps_is_subset_full (qtmux, current_caps, caps)) {
    2848                 :          0 :       goto refuse_renegotiation;
    2849                 :            :     }
    2850         [ #  # ]:          0 :     GST_DEBUG_OBJECT (qtmux,
    2851                 :            :         "pad %s accepted renegotiation to %" GST_PTR_FORMAT " from %"
    2852                 :            :         GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad));
    2853                 :            :   }
    2854                 :            : 
    2855 [ -  + ][ #  # ]:         13 :   GST_DEBUG_OBJECT (qtmux, "%s:%s, caps=%" GST_PTR_FORMAT,
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    2856                 :            :       GST_DEBUG_PAD_NAME (pad), caps);
    2857                 :            : 
    2858                 :         13 :   format = qtmux_klass->format;
    2859                 :         13 :   structure = gst_caps_get_structure (caps, 0);
    2860                 :         13 :   mimetype = gst_structure_get_name (structure);
    2861                 :            : 
    2862                 :            :   /* required parts */
    2863   [ +  -  +  - ]:         26 :   if (!gst_structure_get_int (structure, "width", &width) ||
    2864                 :         13 :       !gst_structure_get_int (structure, "height", &height))
    2865                 :            :     goto refuse_caps;
    2866                 :            : 
    2867                 :            :   /* optional */
    2868                 :         13 :   depth = -1;
    2869                 :            :   /* works as a default timebase */
    2870                 :         13 :   framerate_num = 10000;
    2871                 :         13 :   framerate_den = 1;
    2872                 :         13 :   gst_structure_get_fraction (structure, "framerate", &framerate_num,
    2873                 :            :       &framerate_den);
    2874                 :         13 :   gst_structure_get_int (structure, "depth", &depth);
    2875                 :         13 :   value = gst_structure_get_value (structure, "codec_data");
    2876         [ +  + ]:         13 :   if (value != NULL)
    2877                 :          9 :     codec_data = gst_value_get_buffer (value);
    2878                 :            : 
    2879                 :         13 :   par_num = 1;
    2880                 :         13 :   par_den = 1;
    2881                 :         13 :   gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_num,
    2882                 :            :       &par_den);
    2883                 :            : 
    2884                 :         13 :   qtpad->is_out_of_order = FALSE;
    2885                 :            : 
    2886                 :            :   /* bring frame numerator into a range that ensures both reasonable resolution
    2887                 :            :    * as well as a fair duration */
    2888                 :         26 :   rate = qtmux->trak_timescale ?
    2889         [ -  + ]:         13 :       qtmux->trak_timescale : adjust_rate (framerate_num);
    2890         [ -  + ]:         13 :   GST_DEBUG_OBJECT (qtmux, "Rate of video track selected: %" G_GUINT32_FORMAT,
    2891                 :            :       rate);
    2892                 :            : 
    2893                 :            :   /* set common properties */
    2894                 :         13 :   entry.width = width;
    2895                 :         13 :   entry.height = height;
    2896                 :         13 :   entry.par_n = par_num;
    2897                 :         13 :   entry.par_d = par_den;
    2898                 :            :   /* should be OK according to qt and iso spec, override if really needed */
    2899                 :         13 :   entry.color_table_id = -1;
    2900                 :         13 :   entry.frame_count = 1;
    2901                 :         13 :   entry.depth = 24;
    2902                 :            : 
    2903                 :            :   /* sync entries by default */
    2904                 :         13 :   sync = TRUE;
    2905                 :            : 
    2906                 :            :   /* now map onto a fourcc, and some extra properties */
    2907         [ -  + ]:         13 :   if (strcmp (mimetype, "video/x-raw-rgb") == 0) {
    2908                 :            :     gint bpp;
    2909                 :            : 
    2910                 :          0 :     entry.fourcc = FOURCC_raw_;
    2911                 :          0 :     gst_structure_get_int (structure, "bpp", &bpp);
    2912                 :          0 :     entry.depth = bpp;
    2913                 :          0 :     sync = FALSE;
    2914         [ -  + ]:         13 :   } else if (strcmp (mimetype, "video/x-raw-yuv") == 0) {
    2915                 :          0 :     guint32 format = 0;
    2916                 :            : 
    2917                 :          0 :     sync = FALSE;
    2918                 :          0 :     gst_structure_get_fourcc (structure, "format", &format);
    2919         [ #  # ]:          0 :     switch (format) {
    2920                 :            :       case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
    2921         [ #  # ]:          0 :         if (depth == -1)
    2922                 :          0 :           depth = 24;
    2923                 :          0 :         entry.fourcc = FOURCC_2vuy;
    2924                 :          0 :         entry.depth = depth;
    2925                 :          0 :         break;
    2926                 :            :     }
    2927         [ -  + ]:         13 :   } else if (strcmp (mimetype, "video/x-h263") == 0) {
    2928                 :          0 :     ext_atom = NULL;
    2929         [ #  # ]:          0 :     if (format == GST_QT_MUX_FORMAT_QT)
    2930                 :          0 :       entry.fourcc = FOURCC_h263;
    2931                 :            :     else
    2932                 :          0 :       entry.fourcc = FOURCC_s263;
    2933                 :          0 :     ext_atom = build_h263_extension ();
    2934         [ #  # ]:          0 :     if (ext_atom != NULL)
    2935                 :          0 :       ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    2936 [ +  - ][ +  + ]:         13 :   } else if (strcmp (mimetype, "video/x-divx") == 0 ||
    2937                 :          4 :       strcmp (mimetype, "video/mpeg") == 0) {
    2938                 :          4 :     gint version = 0;
    2939                 :            : 
    2940         [ -  + ]:          4 :     if (strcmp (mimetype, "video/x-divx") == 0) {
    2941                 :          0 :       gst_structure_get_int (structure, "divxversion", &version);
    2942                 :          0 :       version = version == 5 ? 1 : 0;
    2943                 :            :     } else {
    2944                 :          4 :       gst_structure_get_int (structure, "mpegversion", &version);
    2945                 :          4 :       version = version == 4 ? 1 : 0;
    2946                 :            :     }
    2947         [ +  - ]:          4 :     if (version) {
    2948                 :          4 :       entry.fourcc = FOURCC_mp4v;
    2949                 :          4 :       ext_atom =
    2950                 :          4 :           build_esds_extension (qtpad->trak, ESDS_OBJECT_TYPE_MPEG4_P2,
    2951                 :            :           ESDS_STREAM_TYPE_VISUAL, codec_data, qtpad->avg_bitrate,
    2952                 :            :           qtpad->max_bitrate);
    2953         [ +  - ]:          4 :       if (ext_atom != NULL)
    2954                 :          4 :         ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    2955         [ +  - ]:          4 :       if (!codec_data)
    2956         [ -  + ]:          4 :         GST_WARNING_OBJECT (qtmux, "no codec_data for MPEG4 video; "
    2957                 :            :             "output might not play in Apple QuickTime (try global-headers?)");
    2958                 :            :     }
    2959         [ +  - ]:          9 :   } else if (strcmp (mimetype, "video/x-h264") == 0) {
    2960                 :          9 :     entry.fourcc = FOURCC_avc1;
    2961         [ +  - ]:          9 :     if (qtpad->avg_bitrate == 0) {
    2962                 :          9 :       gint avg_bitrate = 0;
    2963                 :          9 :       gst_structure_get_int (structure, "bitrate", &avg_bitrate);
    2964                 :          9 :       qtpad->avg_bitrate = avg_bitrate;
    2965                 :            :     }
    2966                 :          9 :     ext_atom = build_btrt_extension (0, qtpad->avg_bitrate, qtpad->max_bitrate);
    2967         [ -  + ]:          9 :     if (ext_atom != NULL)
    2968                 :          0 :       ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    2969         [ -  + ]:          9 :     if (!codec_data)
    2970         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "no codec_data in h264 caps");
    2971                 :          9 :     ext_atom = build_codec_data_extension (FOURCC_avcC, codec_data);
    2972         [ +  - ]:          9 :     if (ext_atom != NULL)
    2973                 :          9 :       ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    2974         [ #  # ]:          0 :   } else if (strcmp (mimetype, "video/x-svq") == 0) {
    2975                 :          0 :     gint version = 0;
    2976                 :          0 :     const GstBuffer *seqh = NULL;
    2977                 :            :     const GValue *seqh_value;
    2978                 :          0 :     gdouble gamma = 0;
    2979                 :            : 
    2980                 :          0 :     gst_structure_get_int (structure, "svqversion", &version);
    2981         [ #  # ]:          0 :     if (version == 3) {
    2982                 :          0 :       entry.fourcc = FOURCC_SVQ3;
    2983                 :          0 :       entry.version = 3;
    2984                 :          0 :       entry.depth = 32;
    2985                 :            : 
    2986                 :          0 :       seqh_value = gst_structure_get_value (structure, "seqh");
    2987         [ #  # ]:          0 :       if (seqh_value) {
    2988                 :          0 :         seqh = gst_value_get_buffer (seqh_value);
    2989                 :          0 :         ext_atom = build_SMI_atom (seqh);
    2990         [ #  # ]:          0 :         if (ext_atom)
    2991                 :          0 :           ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    2992                 :            :       }
    2993                 :            : 
    2994                 :            :       /* we need to add the gamma anyway because quicktime might crash
    2995                 :            :        * when it doesn't find it */
    2996         [ #  # ]:          0 :       if (!gst_structure_get_double (structure, "applied-gamma", &gamma)) {
    2997                 :            :         /* it seems that using 0 here makes it ignored */
    2998                 :          0 :         gamma = 0.0;
    2999                 :            :       }
    3000                 :          0 :       ext_atom = build_gama_atom (gamma);
    3001         [ #  # ]:          0 :       if (ext_atom)
    3002                 :          0 :         ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
    3003                 :            :     } else {
    3004         [ #  # ]:          0 :       GST_WARNING_OBJECT (qtmux, "SVQ version %d not supported. Please file "
    3005                 :            :           "a bug at http://bugzilla.gnome.org", version);
    3006                 :            :     }
    3007         [ #  # ]:          0 :   } else if (strcmp (mimetype, "video/x-dv") == 0) {
    3008                 :          0 :     gint version = 0;
    3009                 :          0 :     gboolean pal = TRUE;
    3010                 :            : 
    3011                 :          0 :     sync = FALSE;
    3012 [ #  # ][ #  # ]:          0 :     if (framerate_num != 25 || framerate_den != 1)
    3013                 :          0 :       pal = FALSE;
    3014                 :          0 :     gst_structure_get_int (structure, "dvversion", &version);
    3015                 :            :     /* fall back to typical one */
    3016         [ #  # ]:          0 :     if (!version)
    3017                 :          0 :       version = 25;
    3018      [ #  #  # ]:          0 :     switch (version) {
    3019                 :            :       case 25:
    3020         [ #  # ]:          0 :         if (pal)
    3021                 :          0 :           entry.fourcc = GST_MAKE_FOURCC ('d', 'v', 'c', 'p');
    3022                 :            :         else
    3023                 :          0 :           entry.fourcc = GST_MAKE_FOURCC ('d', 'v', 'c', ' ');
    3024                 :          0 :         break;
    3025                 :            :       case 50:
    3026         [ #  # ]:          0 :         if (pal)
    3027                 :          0 :           entry.fourcc = GST_MAKE_FOURCC ('d', 'v', '5', 'p');
    3028                 :            :         else
    3029                 :          0 :           entry.fourcc = GST_MAKE_FOURCC ('d', 'v', '5', 'n');
    3030                 :          0 :         break;
    3031                 :            :       default:
    3032         [ #  # ]:          0 :         GST_WARNING_OBJECT (qtmux, "unrecognized dv version");
    3033                 :          0 :         break;
    3034                 :            :     }
    3035         [ #  # ]:          0 :   } else if (strcmp (mimetype, "image/jpeg") == 0) {
    3036                 :          0 :     entry.fourcc = FOURCC_jpeg;
    3037                 :          0 :     sync = FALSE;
    3038 [ #  # ][ #  # ]:          0 :   } else if (strcmp (mimetype, "image/x-j2c") == 0 ||
    3039                 :          0 :       strcmp (mimetype, "image/x-jpc") == 0) {
    3040                 :            :     guint32 fourcc;
    3041                 :            :     const GValue *cmap_array;
    3042                 :            :     const GValue *cdef_array;
    3043                 :          0 :     gint ncomp = 0;
    3044                 :          0 :     gint fields = 1;
    3045                 :            : 
    3046         [ #  # ]:          0 :     if (strcmp (mimetype, "image/x-jpc") == 0) {
    3047                 :          0 :       qtpad->prepare_buf_func = gst_qt_mux_prepare_jpc_buffer;
    3048                 :            :     }
    3049                 :            : 
    3050                 :          0 :     gst_structure_get_int (structure, "num-components", &ncomp);
    3051                 :          0 :     gst_structure_get_int (structure, "fields", &fields);
    3052                 :          0 :     cmap_array = gst_structure_get_value (structure, "component-map");
    3053                 :          0 :     cdef_array = gst_structure_get_value (structure, "channel-definitions");
    3054                 :            : 
    3055                 :          0 :     ext_atom = NULL;
    3056                 :          0 :     entry.fourcc = FOURCC_mjp2;
    3057                 :          0 :     sync = FALSE;
    3058 [ #  # ][ #  # ]:          0 :     if (gst_structure_get_fourcc (structure, "fourcc", &fourcc) &&
    3059                 :            :         (ext_atom =
    3060                 :          0 :             build_jp2h_extension (qtpad->trak, width, height, fourcc, ncomp,
    3061                 :            :                 cmap_array, cdef_array)) != NULL) {
    3062                 :          0 :       ext_atom_list = g_list_append (ext_atom_list, ext_atom);
    3063                 :            : 
    3064                 :          0 :       ext_atom = build_fiel_extension (fields);
    3065         [ #  # ]:          0 :       if (ext_atom)
    3066                 :          0 :         ext_atom_list = g_list_append (ext_atom_list, ext_atom);
    3067                 :            : 
    3068                 :          0 :       ext_atom = build_jp2x_extension (codec_data);
    3069         [ #  # ]:          0 :       if (ext_atom)
    3070                 :          0 :         ext_atom_list = g_list_append (ext_atom_list, ext_atom);
    3071                 :            :     } else {
    3072         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "missing or invalid fourcc in jp2 caps");
    3073                 :          0 :       goto refuse_caps;
    3074                 :            :     }
    3075         [ #  # ]:          0 :   } else if (strcmp (mimetype, "video/x-vp8") == 0) {
    3076                 :          0 :     entry.fourcc = FOURCC_VP80;
    3077                 :          0 :     sync = FALSE;
    3078         [ #  # ]:          0 :   } else if (strcmp (mimetype, "video/x-qt-part") == 0) {
    3079                 :            :     guint32 fourcc;
    3080                 :            : 
    3081                 :          0 :     gst_structure_get_fourcc (structure, "format", &fourcc);
    3082                 :          0 :     entry.fourcc = fourcc;
    3083                 :          0 :     qtpad->have_dts = TRUE;
    3084         [ #  # ]:          0 :   } else if (strcmp (mimetype, "video/x-mp4-part") == 0) {
    3085                 :            :     guint32 fourcc;
    3086                 :            : 
    3087                 :          0 :     gst_structure_get_fourcc (structure, "format", &fourcc);
    3088                 :          0 :     entry.fourcc = fourcc;
    3089                 :          0 :     qtpad->have_dts = TRUE;
    3090                 :            :   }
    3091                 :            : 
    3092         [ -  + ]:         13 :   if (!entry.fourcc)
    3093                 :          0 :     goto refuse_caps;
    3094                 :            : 
    3095                 :            :   /* ok, set the pad info accordingly */
    3096                 :         13 :   qtpad->fourcc = entry.fourcc;
    3097                 :         13 :   qtpad->sync = sync;
    3098                 :         13 :   atom_trak_set_video_type (qtpad->trak, qtmux->context, &entry, rate,
    3099                 :            :       ext_atom_list);
    3100                 :            : 
    3101                 :         13 :   gst_object_unref (qtmux);
    3102                 :         13 :   return TRUE;
    3103                 :            : 
    3104                 :            :   /* ERRORS */
    3105                 :            : refuse_caps:
    3106                 :            :   {
    3107         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "pad %s refused caps %" GST_PTR_FORMAT,
    3108                 :            :         GST_PAD_NAME (pad), caps);
    3109                 :          0 :     gst_object_unref (qtmux);
    3110                 :          0 :     return FALSE;
    3111                 :            :   }
    3112                 :            : refuse_renegotiation:
    3113                 :            :   {
    3114         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux,
    3115                 :            :         "pad %s refused renegotiation to %" GST_PTR_FORMAT " from %"
    3116                 :            :         GST_PTR_FORMAT, GST_PAD_NAME (pad), caps, GST_PAD_CAPS (pad));
    3117                 :          0 :     gst_object_unref (qtmux);
    3118                 :         13 :     return FALSE;
    3119                 :            :   }
    3120                 :            : }
    3121                 :            : 
    3122                 :            : static gboolean
    3123                 :         25 : gst_qt_mux_sink_event (GstPad * pad, GstEvent * event)
    3124                 :            : {
    3125                 :            :   gboolean ret;
    3126                 :            :   GstQTMux *qtmux;
    3127                 :         25 :   guint32 avg_bitrate = 0, max_bitrate = 0;
    3128                 :            : 
    3129                 :         25 :   qtmux = GST_QT_MUX_CAST (gst_pad_get_parent (pad));
    3130         [ -  + ]:         25 :   switch (GST_EVENT_TYPE (event)) {
    3131                 :            :     case GST_EVENT_TAG:{
    3132                 :            :       GstTagList *list;
    3133                 :          0 :       GstTagSetter *setter = GST_TAG_SETTER (qtmux);
    3134                 :            :       GstTagMergeMode mode;
    3135                 :            : 
    3136                 :          0 :       GST_OBJECT_LOCK (qtmux);
    3137                 :          0 :       mode = gst_tag_setter_get_tag_merge_mode (setter);
    3138                 :            : 
    3139         [ #  # ]:          0 :       GST_DEBUG_OBJECT (qtmux, "received tag event");
    3140                 :          0 :       gst_event_parse_tag (event, &list);
    3141                 :            : 
    3142                 :          0 :       gst_tag_setter_merge_tags (setter, list, mode);
    3143                 :          0 :       GST_OBJECT_UNLOCK (qtmux);
    3144                 :            : 
    3145         [ #  # ]:          0 :       if (gst_tag_list_get_uint (list, GST_TAG_BITRATE, &avg_bitrate) |
    3146                 :          0 :           gst_tag_list_get_uint (list, GST_TAG_MAXIMUM_BITRATE, &max_bitrate)) {
    3147                 :          0 :         GstQTPad *qtpad = gst_pad_get_element_private (pad);
    3148         [ #  # ]:          0 :         g_assert (qtpad);
    3149                 :            : 
    3150 [ #  # ][ #  # ]:          0 :         if (avg_bitrate > 0 && avg_bitrate < G_MAXUINT32)
    3151                 :          0 :           qtpad->avg_bitrate = avg_bitrate;
    3152 [ #  # ][ #  # ]:          0 :         if (max_bitrate > 0 && max_bitrate < G_MAXUINT32)
    3153                 :          0 :           qtpad->max_bitrate = max_bitrate;
    3154                 :            :       }
    3155                 :            : 
    3156                 :          0 :       break;
    3157                 :            :     }
    3158                 :            :     default:
    3159                 :         25 :       break;
    3160                 :            :   }
    3161                 :            : 
    3162                 :         25 :   ret = qtmux->collect_event (pad, event);
    3163                 :         25 :   gst_object_unref (qtmux);
    3164                 :            : 
    3165                 :         25 :   return ret;
    3166                 :            : }
    3167                 :            : 
    3168                 :            : static void
    3169                 :          0 : gst_qt_mux_release_pad (GstElement * element, GstPad * pad)
    3170                 :            : {
    3171                 :          0 :   GstQTMux *mux = GST_QT_MUX_CAST (element);
    3172                 :            :   GSList *walk;
    3173                 :            : 
    3174 [ #  # ][ #  # ]:          0 :   GST_DEBUG_OBJECT (element, "Releasing %s:%s", GST_DEBUG_PAD_NAME (pad));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    3175                 :            : 
    3176 [ #  # ][ #  # ]:          0 :   for (walk = mux->sinkpads; walk; walk = g_slist_next (walk)) {
    3177                 :          0 :     GstQTPad *qtpad = (GstQTPad *) walk->data;
    3178 [ #  # ][ #  # ]:          0 :     GST_DEBUG ("Checking %s:%s", GST_DEBUG_PAD_NAME (qtpad->collect.pad));
         [ #  # ][ #  # ]
         [ #  # ][ #  # ]
    3179         [ #  # ]:          0 :     if (qtpad->collect.pad == pad) {
    3180                 :            :       /* this is it, remove */
    3181                 :          0 :       mux->sinkpads = g_slist_delete_link (mux->sinkpads, walk);
    3182                 :          0 :       gst_element_remove_pad (element, pad);
    3183                 :          0 :       break;
    3184                 :            :     }
    3185                 :            :   }
    3186                 :            : 
    3187                 :          0 :   gst_collect_pads_remove_pad (mux->collect, pad);
    3188                 :          0 : }
    3189                 :            : 
    3190                 :            : static GstPad *
    3191                 :         16 : gst_qt_mux_request_new_pad (GstElement * element,
    3192                 :            :     GstPadTemplate * templ, const gchar * req_name)
    3193                 :            : {
    3194                 :         16 :   GstElementClass *klass = GST_ELEMENT_GET_CLASS (element);
    3195                 :         16 :   GstQTMux *qtmux = GST_QT_MUX_CAST (element);
    3196                 :            :   GstQTPad *collect_pad;
    3197                 :            :   GstPad *newpad;
    3198                 :            :   gboolean audio;
    3199                 :            :   gchar *name;
    3200                 :            : 
    3201         [ -  + ]:         16 :   if (templ->direction != GST_PAD_SINK)
    3202                 :          0 :     goto wrong_direction;
    3203                 :            : 
    3204         [ -  + ]:         16 :   if (qtmux->state > GST_QT_MUX_STATE_STARTED)
    3205                 :          0 :     goto too_late;
    3206                 :            : 
    3207         [ +  + ]:         16 :   if (templ == gst_element_class_get_pad_template (klass, "audio_%d")) {
    3208                 :          3 :     audio = TRUE;
    3209                 :          3 :     name = g_strdup_printf ("audio_%02d", qtmux->audio_pads++);
    3210         [ +  - ]:         13 :   } else if (templ == gst_element_class_get_pad_template (klass, "video_%d")) {
    3211                 :         13 :     audio = FALSE;
    3212                 :         13 :     name = g_strdup_printf ("video_%02d", qtmux->video_pads++);
    3213                 :            :   } else
    3214                 :          0 :     goto wrong_template;
    3215                 :            : 
    3216         [ -  + ]:         16 :   GST_DEBUG_OBJECT (qtmux, "Requested pad: %s", name);
    3217                 :            : 
    3218                 :            :   /* create pad and add to collections */
    3219                 :         16 :   newpad = gst_pad_new_from_template (templ, name);
    3220                 :         16 :   g_free (name);
    3221                 :         16 :   collect_pad = (GstQTPad *)
    3222                 :         16 :       gst_collect_pads_add_pad_full (qtmux->collect, newpad, sizeof (GstQTPad),
    3223                 :            :       (GstCollectDataDestroyNotify) (gst_qt_mux_pad_reset));
    3224                 :            :   /* set up pad */
    3225                 :         16 :   gst_qt_mux_pad_reset (collect_pad);
    3226                 :         16 :   collect_pad->trak = atom_trak_new (qtmux->context);
    3227                 :         16 :   atom_moov_add_trak (qtmux->moov, collect_pad->trak);
    3228                 :            : 
    3229                 :         16 :   qtmux->sinkpads = g_slist_append (qtmux->sinkpads, collect_pad);
    3230                 :            : 
    3231                 :            :   /* set up pad functions */
    3232         [ +  + ]:         16 :   if (audio)
    3233                 :          3 :     gst_pad_set_setcaps_function (newpad,
    3234                 :          3 :         GST_DEBUG_FUNCPTR (gst_qt_mux_audio_sink_set_caps));
    3235                 :            :   else
    3236                 :         13 :     gst_pad_set_setcaps_function (newpad,
    3237                 :         13 :         GST_DEBUG_FUNCPTR (gst_qt_mux_video_sink_set_caps));
    3238                 :            : 
    3239                 :            :   /* FIXME: hacked way to override/extend the event function of
    3240                 :            :    * GstCollectPads; because it sets its own event function giving the
    3241                 :            :    * element no access to events.
    3242                 :            :    */
    3243                 :         16 :   qtmux->collect_event = (GstPadEventFunction) GST_PAD_EVENTFUNC (newpad);
    3244                 :         16 :   gst_pad_set_event_function (newpad,
    3245                 :         16 :       GST_DEBUG_FUNCPTR (gst_qt_mux_sink_event));
    3246                 :            : 
    3247                 :         16 :   gst_pad_set_active (newpad, TRUE);
    3248                 :         16 :   gst_element_add_pad (element, newpad);
    3249                 :            : 
    3250                 :         16 :   return newpad;
    3251                 :            : 
    3252                 :            :   /* ERRORS */
    3253                 :            : wrong_direction:
    3254                 :            :   {
    3255         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "Request pad that is not a SINK pad.");
    3256                 :          0 :     return NULL;
    3257                 :            :   }
    3258                 :            : too_late:
    3259                 :            :   {
    3260         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "Not providing request pad after stream start.");
    3261                 :          0 :     return NULL;
    3262                 :            :   }
    3263                 :            : wrong_template:
    3264                 :            :   {
    3265         [ #  # ]:          0 :     GST_WARNING_OBJECT (qtmux, "This is not our template!");
    3266                 :         16 :     return NULL;
    3267                 :            :   }
    3268                 :            : }
    3269                 :            : 
    3270                 :            : static void
    3271                 :         45 : gst_qt_mux_get_property (GObject * object,
    3272                 :            :     guint prop_id, GValue * value, GParamSpec * pspec)
    3273                 :            : {
    3274                 :         45 :   GstQTMux *qtmux = GST_QT_MUX_CAST (object);
    3275                 :            : 
    3276                 :         45 :   GST_OBJECT_LOCK (qtmux);
    3277   [ +  +  +  +  :         45 :   switch (prop_id) {
          +  +  +  +  +  
                      - ]
    3278                 :            :     case PROP_MOVIE_TIMESCALE:
    3279                 :          5 :       g_value_set_uint (value, qtmux->timescale);
    3280                 :          5 :       break;
    3281                 :            :     case PROP_TRAK_TIMESCALE:
    3282                 :          5 :       g_value_set_uint (value, qtmux->trak_timescale);
    3283                 :          5 :       break;
    3284                 :            :     case PROP_DO_CTTS:
    3285                 :          5 :       g_value_set_boolean (value, qtmux->guess_pts);
    3286                 :          5 :       break;
    3287                 :            :     case PROP_DTS_METHOD:
    3288                 :          5 :       g_value_set_enum (value, qtmux->dts_method);
    3289                 :          5 :       break;
    3290                 :            :     case PROP_FAST_START:
    3291                 :          5 :       g_value_set_boolean (value, qtmux->fast_start);
    3292                 :          5 :       break;
    3293                 :            :     case PROP_FAST_START_TEMP_FILE:
    3294                 :          5 :       g_value_set_string (value, qtmux->fast_start_file_path);
    3295                 :          5 :       break;
    3296                 :            :     case PROP_MOOV_RECOV_FILE:
    3297                 :          5 :       g_value_set_string (value, qtmux->moov_recov_file_path);
    3298                 :          5 :       break;
    3299                 :            :     case PROP_FRAGMENT_DURATION:
    3300                 :          5 :       g_value_set_uint (value, qtmux->fragment_duration);
    3301                 :          5 :       break;
    3302                 :            :     case PROP_STREAMABLE:
    3303                 :          5 :       g_value_set_boolean (value, qtmux->streamable);
    3304                 :          5 :       break;
    3305                 :            :     default:
    3306                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    3307                 :          0 :       break;
    3308                 :            :   }
    3309                 :         45 :   GST_OBJECT_UNLOCK (qtmux);
    3310                 :         45 : }
    3311                 :            : 
    3312                 :            : static void
    3313                 :         36 : gst_qt_mux_generate_fast_start_file_path (GstQTMux * qtmux)
    3314                 :            : {
    3315                 :            :   gchar *tmp;
    3316                 :            : 
    3317                 :         36 :   g_free (qtmux->fast_start_file_path);
    3318                 :         36 :   qtmux->fast_start_file_path = NULL;
    3319                 :            : 
    3320                 :         36 :   tmp = g_strdup_printf ("%s%d", "qtmux", g_random_int ());
    3321                 :         36 :   qtmux->fast_start_file_path = g_build_filename (g_get_tmp_dir (), tmp, NULL);
    3322                 :         36 :   g_free (tmp);
    3323                 :         36 : }
    3324                 :            : 
    3325                 :            : static void
    3326                 :        296 : gst_qt_mux_set_property (GObject * object,
    3327                 :            :     guint prop_id, const GValue * value, GParamSpec * pspec)
    3328                 :            : {
    3329                 :        296 :   GstQTMux *qtmux = GST_QT_MUX_CAST (object);
    3330                 :            : 
    3331                 :        296 :   GST_OBJECT_LOCK (qtmux);
    3332   [ +  +  +  +  :        296 :   switch (prop_id) {
          -  +  +  +  +  
                      - ]
    3333                 :            :     case PROP_MOVIE_TIMESCALE:
    3334                 :         36 :       qtmux->timescale = g_value_get_uint (value);
    3335                 :         36 :       break;
    3336                 :            :     case PROP_TRAK_TIMESCALE:
    3337                 :         36 :       qtmux->trak_timescale = g_value_get_uint (value);
    3338                 :         36 :       break;
    3339                 :            :     case PROP_DO_CTTS:
    3340                 :         36 :       qtmux->guess_pts = g_value_get_boolean (value);
    3341                 :         36 :       break;
    3342                 :            :     case PROP_DTS_METHOD:
    3343                 :         36 :       qtmux->dts_method = g_value_get_enum (value);
    3344                 :         36 :       break;
    3345                 :            :     case PROP_FAST_START:
    3346                 :          0 :       qtmux->fast_start = g_value_get_boolean (value);
    3347                 :          0 :       break;
    3348                 :            :     case PROP_FAST_START_TEMP_FILE:
    3349                 :         36 :       g_free (qtmux->fast_start_file_path);
    3350                 :         36 :       qtmux->fast_start_file_path = g_value_dup_string (value);
    3351                 :            :       /* NULL means to generate a random one */
    3352         [ +  - ]:         36 :       if (!qtmux->fast_start_file_path) {
    3353                 :         36 :         gst_qt_mux_generate_fast_start_file_path (qtmux);
    3354                 :            :       }
    3355                 :         36 :       break;
    3356                 :            :     case PROP_MOOV_RECOV_FILE:
    3357                 :         36 :       g_free (qtmux->moov_recov_file_path);
    3358                 :         36 :       qtmux->moov_recov_file_path = g_value_dup_string (value);
    3359                 :         36 :       break;
    3360                 :            :     case PROP_FRAGMENT_DURATION:
    3361                 :         40 :       qtmux->fragment_duration = g_value_get_uint (value);
    3362                 :         40 :       break;
    3363                 :            :     case PROP_STREAMABLE:
    3364                 :         40 :       qtmux->streamable = g_value_get_boolean (value);
    3365                 :         40 :       break;
    3366                 :            :     default:
    3367                 :          0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
    3368                 :          0 :       break;
    3369                 :            :   }
    3370                 :        296 :   GST_OBJECT_UNLOCK (qtmux);
    3371                 :        296 : }
    3372                 :            : 
    3373                 :            : static GstStateChangeReturn
    3374                 :        279 : gst_qt_mux_change_state (GstElement * element, GstStateChange transition)
    3375                 :            : {
    3376                 :            :   GstStateChangeReturn ret;
    3377                 :        279 :   GstQTMux *qtmux = GST_QT_MUX_CAST (element);
    3378                 :            : 
    3379   [ +  +  +  +  :        279 :   switch (transition) {
                      + ]
    3380                 :            :     case GST_STATE_CHANGE_NULL_TO_READY:
    3381                 :         37 :       break;
    3382                 :            :     case GST_STATE_CHANGE_READY_TO_PAUSED:
    3383                 :         52 :       gst_collect_pads_start (qtmux->collect);
    3384                 :         52 :       qtmux->state = GST_QT_MUX_STATE_STARTED;
    3385                 :         52 :       break;
    3386                 :            :     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
    3387                 :         47 :       break;
    3388                 :            :     case GST_STATE_CHANGE_PAUSED_TO_READY:
    3389                 :         52 :       gst_collect_pads_stop (qtmux->collect);
    3390                 :         52 :       break;
    3391                 :            :     default:
    3392                 :         91 :       break;
    3393                 :            :   }
    3394                 :            : 
    3395                 :        279 :   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
    3396                 :            : 
    3397   [ +  +  +  + ]:        279 :   switch (transition) {
    3398                 :            :     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
    3399                 :         47 :       break;
    3400                 :            :     case GST_STATE_CHANGE_PAUSED_TO_READY:
    3401                 :         52 :       gst_qt_mux_reset (qtmux, TRUE);
    3402                 :         52 :       break;
    3403                 :            :     case GST_STATE_CHANGE_READY_TO_NULL:
    3404                 :         37 :       break;
    3405                 :            :     default:
    3406                 :        143 :       break;
    3407                 :            :   }
    3408                 :            : 
    3409                 :        279 :   return ret;
    3410                 :            : }
    3411                 :            : 
    3412                 :            : gboolean
    3413                 :         21 : gst_qt_mux_register (GstPlugin * plugin)
    3414                 :            : {
    3415                 :         42 :   GTypeInfo typeinfo = {
    3416                 :            :     sizeof (GstQTMuxClass),
    3417                 :            :     (GBaseInitFunc) gst_qt_mux_base_init,
    3418                 :            :     NULL,
    3419                 :         21 :     (GClassInitFunc) gst_qt_mux_class_init,
    3420                 :            :     NULL,
    3421                 :            :     NULL,
    3422                 :            :     sizeof (GstQTMux),
    3423                 :            :     0,
    3424                 :            :     (GInstanceInitFunc) gst_qt_mux_init,
    3425                 :            :   };
    3426                 :            :   static const GInterfaceInfo tag_setter_info = {
    3427                 :            :     NULL, NULL, NULL
    3428                 :            :   };
    3429                 :            :   GType type;
    3430                 :            :   GstQTMuxFormat format;
    3431                 :            :   GstQTMuxClassParams *params;
    3432                 :         21 :   guint i = 0;
    3433                 :            : 
    3434         [ +  - ]:         21 :   GST_DEBUG_CATEGORY_INIT (gst_qt_mux_debug, "qtmux", 0, "QT Muxer");
    3435                 :            : 
    3436         [ -  + ]:         21 :   GST_LOG ("Registering muxers");
    3437                 :            : 
    3438                 :            :   while (TRUE) {
    3439                 :            :     GstQTMuxFormatProp *prop;
    3440                 :            : 
    3441                 :        126 :     prop = &gst_qt_mux_format_list[i];
    3442                 :        126 :     format = prop->format;
    3443         [ +  + ]:        126 :     if (format == GST_QT_MUX_FORMAT_NONE)
    3444                 :         21 :       break;
    3445                 :            : 
    3446                 :            :     /* create a cache for these properties */
    3447                 :        105 :     params = g_new0 (GstQTMuxClassParams, 1);
    3448                 :        105 :     params->prop = prop;
    3449                 :        105 :     params->src_caps = gst_static_caps_get (&prop->src_caps);
    3450                 :        105 :     params->video_sink_caps = gst_static_caps_get (&prop->video_sink_caps);
    3451                 :        105 :     params->audio_sink_caps = gst_static_caps_get (&prop->audio_sink_caps);
    3452                 :            : 
    3453                 :            :     /* create the type now */
    3454                 :        105 :     type = g_type_register_static (GST_TYPE_ELEMENT, prop->type_name, &typeinfo,
    3455                 :            :         0);
    3456                 :        105 :     g_type_set_qdata (type, GST_QT_MUX_PARAMS_QDATA, (gpointer) params);
    3457                 :        105 :     g_type_add_interface_static (type, GST_TYPE_TAG_SETTER, &tag_setter_info);
    3458                 :            : 
    3459         [ -  + ]:        105 :     if (!gst_element_register (plugin, prop->name, GST_RANK_PRIMARY, type))
    3460                 :          0 :       return FALSE;
    3461                 :            : 
    3462                 :        105 :     i++;
    3463                 :        105 :   }
    3464                 :            : 
    3465         [ -  + ]:         21 :   GST_LOG ("Finished registering muxers");
    3466                 :            : 
    3467                 :            :   /* FIXME: ideally classification tag should be added and
    3468                 :            :      registered in gstreamer core gsttaglist
    3469                 :            :    */
    3470                 :            : 
    3471         [ -  + ]:         21 :   GST_LOG ("Registering tags");
    3472                 :            : 
    3473                 :         21 :   gst_tag_register (GST_TAG_3GP_CLASSIFICATION, GST_TAG_FLAG_META,
    3474                 :            :       G_TYPE_STRING, GST_TAG_3GP_CLASSIFICATION, "content classification",
    3475                 :            :       gst_tag_merge_use_first);
    3476                 :            : 
    3477         [ -  + ]:         21 :   GST_LOG ("Finished registering tags");
    3478                 :            : 
    3479                 :         21 :   return TRUE;
    3480                 :            : }

Generated by: LCOV version 1.9