1 /*****************************************************************************
2  * ogg.c: ogg muxer module for vlc
3  *****************************************************************************
4  * Copyright (C) 2001, 2002, 2006 VLC authors and VideoLAN
5  * $Id: 3847cc54d681062a577cd28cdff0b83e38bcdcb8 $
6  *
7  * Authors: Laurent Aimar <fenrir@via.ecp.fr>
8  *          Gildas Bazin <gbazin@videolan.org>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24 
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32 
33 #include <vlc_common.h>
34 #include <vlc_plugin.h>
35 #include <vlc_sout.h>
36 #include <vlc_block.h>
37 #include <vlc_codecs.h>
38 #include <limits.h>
39 #include <vlc_rand.h>
40 #include "../demux/xiph.h"
41 
42 #include <ogg/ogg.h>
43 
44 /*****************************************************************************
45  * Module descriptor
46  *****************************************************************************/
47 #define INDEXINTVL_TEXT N_("Index interval")
48 #define INDEXINTVL_LONGTEXT N_("Minimal index interval, in microseconds. " \
49     "Set to 0 to disable index creation.")
50 #define INDEXRATIO_TEXT N_("Index size ratio")
51 #define INDEXRATIO_LONGTEXT N_(\
52     "Set index size ratio. Alters default (60min content) or estimated size." )
53 
54 static int  Open   ( vlc_object_t * );
55 static void Close  ( vlc_object_t * );
56 
57 #define SOUT_CFG_PREFIX "sout-ogg-"
58 
59 vlc_module_begin ()
60     set_description( N_("Ogg/OGM muxer") )
61     set_capability( "sout mux", 10 )
62     set_category( CAT_SOUT )
63     set_subcategory( SUBCAT_SOUT_MUX )
64     add_shortcut( "ogg", "ogm" )
65     add_integer_with_range( SOUT_CFG_PREFIX "indexintvl", 1000, 0, INT_MAX,
66                             INDEXINTVL_TEXT, INDEXINTVL_LONGTEXT, true )
67     add_float_with_range( SOUT_CFG_PREFIX "indexratio", 1.0, 1.0, 1000,
68                           INDEXRATIO_TEXT, INDEXRATIO_LONGTEXT, true )
69     set_callbacks( Open, Close )
70 vlc_module_end ()
71 
72 
73 /*****************************************************************************
74  * Exported prototypes
75  *****************************************************************************/
76 static int Control  ( sout_mux_t *, int, va_list );
77 static int AddStream( sout_mux_t *, sout_input_t * );
78 static void DelStream( sout_mux_t *, sout_input_t * );
79 static int Mux      ( sout_mux_t * );
80 static int MuxBlock ( sout_mux_t *, sout_input_t * );
81 
82 static bool OggCreateHeaders( sout_mux_t * );
83 static void OggFillSkeletonFishead( uint8_t *p_buffer, sout_mux_t *p_mux );
84 
85 /*****************************************************************************
86  * Misc declarations
87  *****************************************************************************/
88 
89 /* Skeleton */
90 #define FISBONE_BASE_SIZE 52
91 #define FISBONE_BASE_OFFSET 44
92 #define INDEX_BASE_SIZE 42
93 
94 /* Structures used for OggDS headers used in ogm files */
95 
96 #define PACKET_TYPE_HEADER   0x01
97 #define PACKET_TYPE_COMMENT  0x03
98 #define PACKET_IS_SYNCPOINT  0x08
99 
100 typedef struct
101 {
102     int32_t i_width;
103     int32_t i_height;
104 } oggds_header_video_t;
105 
106 typedef struct
107 {
108     int16_t i_channels;
109     int16_t i_block_align;
110     int32_t i_avgbytespersec;
111 } oggds_header_audio_t;
112 
113 typedef struct
114 {
115     uint8_t i_packet_type;
116 
117     char stream_type[8];
118     char sub_type[4];
119 
120     int32_t i_size;
121 
122     int64_t i_time_unit;
123     int64_t i_samples_per_unit;
124     int32_t i_default_len;
125 
126     int32_t i_buffer_size;
127     int16_t i_bits_per_sample;
128 
129     int16_t i_padding_0; /* Because the original is using MSVC packing style */
130 
131     union
132     {
133         oggds_header_video_t video;
134         oggds_header_audio_t audio;
135     } header;
136 
137     int32_t i_padding_1; /* Because the original is using MSVC packing style */
138 
139 } oggds_header_t;
140 
141 /*****************************************************************************
142  * Definitions of structures and functions used by this plugins
143  *****************************************************************************/
144 typedef struct
145 {
146     es_format_t fmt;
147 
148     int b_new;
149 
150     mtime_t i_dts;
151     mtime_t i_length;
152     int     i_packet_no;
153     int     i_serial_no;
154     int     i_keyframe_granule_shift; /* Theora and Daala only */
155     int     i_last_keyframe; /* dirac and theora */
156     int     i_num_frames; /* Theora only */
157     uint64_t u_last_granulepos; /* Used for correct EOS page */
158     int64_t i_num_keyframes;
159     ogg_stream_state os;
160 
161     oggds_header_t *p_oggds_header;
162     bool b_started;
163     bool b_finished;
164 
165     struct
166     {
167          bool b_fisbone_done;
168          bool b_index_done;
169          /* Skeleton index storage */
170          unsigned char *p_index;
171          uint64_t i_index_size;
172          uint64_t i_index_payload; /* real index size */
173          uint64_t i_index_count;
174          /* backup values for rewriting index page later */
175          uint64_t i_index_offset;  /* sout offset of the index page */
176          int64_t i_index_packetno;  /* index packet no */
177          int i_index_pageno;
178          /* index creation tracking values */
179          uint64_t i_last_keyframe_pos;
180          uint64_t i_last_keyframe_time;
181     } skeleton;
182 
183     int             i_dirac_last_pt;
184     int             i_dirac_last_dt;
185     mtime_t         i_baseptsdelay;
186 
187 } ogg_stream_t;
188 
189 struct sout_mux_sys_t
190 {
191     int     i_streams;
192 
193     mtime_t i_start_dts;
194     int     i_next_serial_no;
195 
196     /* number of logical streams pending to be added */
197     int i_add_streams;
198     bool b_can_add_streams;
199 
200     /* logical streams pending to be deleted */
201     int i_del_streams;
202     ogg_stream_t **pp_del_streams;
203 
204     /* Skeleton */
205     struct
206     {
207         bool b_create;
208         int i_serial_no;
209         int i_packet_no;
210         ogg_stream_state os;
211         bool b_head_done;
212         /* backup values for rewriting fishead page later */
213         uint64_t i_fishead_offset;  /* sout offset of the fishead page */
214         int i_index_intvl;
215         float i_index_ratio;
216     } skeleton;
217 
218     /* access position */
219     ssize_t i_pos;
220     ssize_t i_data_start;
221     ssize_t i_segment_start;
222 };
223 
224 static void OggSetDate( block_t *, mtime_t , mtime_t  );
225 static block_t *OggStreamFlush( sout_mux_t *, ogg_stream_state *, mtime_t );
226 static void OggCreateStreamFooter( sout_mux_t *p_mux, ogg_stream_t *p_stream );
227 static void OggRewriteFisheadPage( sout_mux_t *p_mux );
228 static bool AllocateIndex( sout_mux_t *p_mux, sout_input_t *p_input );
229 
230 /*****************************************************************************
231  * Open: Open muxer
232  *****************************************************************************/
Open(vlc_object_t * p_this)233 static int Open( vlc_object_t *p_this )
234 {
235     sout_mux_t      *p_mux = (sout_mux_t*)p_this;
236     sout_mux_sys_t  *p_sys;
237 
238     msg_Info( p_mux, "Open" );
239 
240     p_sys                 = malloc( sizeof( sout_mux_sys_t ) );
241     if( !p_sys )
242         return VLC_ENOMEM;
243     p_sys->i_streams      = 0;
244     p_sys->i_add_streams  = 0;
245     p_sys->b_can_add_streams = true;
246     p_sys->i_del_streams  = 0;
247     p_sys->pp_del_streams = 0;
248     p_sys->i_pos = 0;
249     p_sys->skeleton.b_create = false;
250     p_sys->skeleton.b_head_done = false;
251     p_sys->skeleton.i_index_intvl =
252             var_InheritInteger( p_this, SOUT_CFG_PREFIX "indexintvl" );
253     p_sys->skeleton.i_index_ratio =
254             var_InheritFloat( p_this, SOUT_CFG_PREFIX "indexratio" );
255     p_sys->i_data_start = 0;
256     p_sys->i_segment_start = 0;
257     p_mux->p_sys        = p_sys;
258     p_mux->pf_control   = Control;
259     p_mux->pf_addstream = AddStream;
260     p_mux->pf_delstream = DelStream;
261     p_mux->pf_mux       = Mux;
262 
263     /* First serial number is random.
264      * (Done like this because on win32 you need to seed the random number
265      *  generator once per thread). */
266     uint32_t r;
267     vlc_rand_bytes(&r, sizeof(r));
268     p_sys->i_next_serial_no = r & INT_MAX;
269 
270     return VLC_SUCCESS;
271 }
272 
273 /*****************************************************************************
274  * Close: Finalize ogg bitstream and close muxer
275  *****************************************************************************/
Close(vlc_object_t * p_this)276 static void Close( vlc_object_t * p_this )
277 {
278     sout_mux_t     *p_mux = (sout_mux_t*)p_this;
279     sout_mux_sys_t *p_sys = p_mux->p_sys;
280 
281     msg_Info( p_mux, "Close" );
282 
283     if( p_sys->i_del_streams )
284     {
285         /* Close the current ogg stream */
286         msg_Dbg( p_mux, "writing footers" );
287 
288         /* Remove deleted logical streams */
289         for(int i = 0; i < p_sys->i_del_streams; i++ )
290         {
291             ogg_stream_t *p_stream = p_sys->pp_del_streams[i];
292 
293             es_format_Clean( &p_stream->fmt );
294             OggCreateStreamFooter( p_mux, p_stream );
295             free( p_stream->p_oggds_header );
296             free( p_stream->skeleton.p_index );
297             free( p_stream );
298         }
299         free( p_sys->pp_del_streams );
300         p_sys->i_streams -= p_sys->i_del_streams;
301     }
302 
303     /* rewrite fishead with final values */
304     if ( p_sys->skeleton.b_create && p_sys->skeleton.b_head_done )
305     {
306         OggRewriteFisheadPage( p_mux );
307     }
308 
309     free( p_sys );
310 }
311 
312 /*****************************************************************************
313  * Control:
314  *****************************************************************************/
Control(sout_mux_t * p_mux,int i_query,va_list args)315 static int Control( sout_mux_t *p_mux, int i_query, va_list args )
316 {
317     VLC_UNUSED(p_mux);
318     bool *pb_bool;
319     char **ppsz;
320 
321    switch( i_query )
322    {
323        case MUX_CAN_ADD_STREAM_WHILE_MUXING:
324            pb_bool = va_arg( args, bool * );
325            *pb_bool = true;
326            return VLC_SUCCESS;
327 
328        case MUX_GET_ADD_STREAM_WAIT:
329            pb_bool = va_arg( args, bool * );
330            *pb_bool = true;
331            return VLC_SUCCESS;
332 
333        case MUX_GET_MIME:
334            ppsz = va_arg( args, char ** );
335            *ppsz = strdup( "application/ogg" );
336            return VLC_SUCCESS;
337 
338         default:
339             return VLC_EGENERIC;
340    }
341 }
342 /*****************************************************************************
343  * AddStream: Add an elementary stream to the muxed stream
344  *****************************************************************************/
AddStream(sout_mux_t * p_mux,sout_input_t * p_input)345 static int AddStream( sout_mux_t *p_mux, sout_input_t *p_input )
346 {
347     sout_mux_sys_t *p_sys = p_mux->p_sys;
348     ogg_stream_t   *p_stream;
349     uint16_t i_tag;
350 
351     msg_Dbg( p_mux, "adding input" );
352 
353     p_input->p_sys = p_stream = calloc( 1, sizeof( ogg_stream_t ) );
354     if( !p_stream )
355         return VLC_ENOMEM;
356 
357     if( es_format_Copy( &p_stream->fmt, &p_input->fmt ) != VLC_SUCCESS )
358     {
359         free( p_stream );
360         return VLC_ENOMEM;
361     }
362 
363     p_stream->i_serial_no = p_sys->i_next_serial_no++;
364     p_stream->i_packet_no = 0;
365     p_stream->i_last_keyframe = 0;
366     p_stream->i_num_keyframes = 0;
367     p_stream->i_num_frames = 0;
368 
369     p_stream->p_oggds_header = 0;
370 
371     p_stream->i_baseptsdelay = -1;
372     p_stream->i_dirac_last_pt = -1;
373     p_stream->i_dirac_last_dt = -1;
374 
375     switch( p_input->p_fmt->i_cat )
376     {
377     case VIDEO_ES:
378     {
379         if( !p_stream->fmt.video.i_frame_rate ||
380             !p_stream->fmt.video.i_frame_rate_base )
381         {
382             msg_Warn( p_mux, "Missing frame rate, assuming 25fps" );
383             p_stream->fmt.video.i_frame_rate = 25;
384             p_stream->fmt.video.i_frame_rate_base = 1;
385         }
386 
387         switch( p_stream->fmt.i_codec )
388         {
389         case VLC_CODEC_MP4V:
390         case VLC_CODEC_MPGV:
391         case VLC_CODEC_MP1V:
392         case VLC_CODEC_MP2V:
393         case VLC_CODEC_DIV3:
394         case VLC_CODEC_MJPG:
395         case VLC_CODEC_WMV1:
396         case VLC_CODEC_WMV2:
397         case VLC_CODEC_WMV3:
398             p_stream->p_oggds_header = calloc( 1, sizeof(oggds_header_t) );
399             if( !p_stream->p_oggds_header )
400             {
401                 free( p_stream );
402                 return VLC_ENOMEM;
403             }
404             p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
405 
406             memcpy( p_stream->p_oggds_header->stream_type, "video", 5 );
407             if( p_stream->fmt.i_codec == VLC_CODEC_MP4V )
408             {
409                 memcpy( p_stream->p_oggds_header->sub_type, "XVID", 4 );
410             }
411             else if( p_stream->fmt.i_codec == VLC_CODEC_DIV3 )
412             {
413                 memcpy( p_stream->p_oggds_header->sub_type, "DIV3", 4 );
414             }
415             else
416             {
417                 memcpy( p_stream->p_oggds_header->sub_type,
418                         &p_stream->fmt.i_codec, 4 );
419             }
420             p_stream->p_oggds_header->i_size = 0 ;
421             p_stream->p_oggds_header->i_time_unit =
422                      INT64_C(10000000) * p_stream->fmt.video.i_frame_rate_base /
423                      (int64_t)p_stream->fmt.video.i_frame_rate;
424             p_stream->p_oggds_header->i_samples_per_unit = 1;
425             p_stream->p_oggds_header->i_default_len = 1 ; /* ??? */
426             p_stream->p_oggds_header->i_buffer_size = 1024*1024;
427             p_stream->p_oggds_header->i_bits_per_sample = 0;
428             p_stream->p_oggds_header->header.video.i_width = p_input->p_fmt->video.i_width;
429             p_stream->p_oggds_header->header.video.i_height = p_input->p_fmt->video.i_height;
430             msg_Dbg( p_mux, "%4.4s stream", (char *)&p_stream->fmt.i_codec );
431             break;
432 
433         case VLC_CODEC_DIRAC:
434             msg_Dbg( p_mux, "dirac stream" );
435             break;
436 
437         case VLC_CODEC_THEORA:
438             msg_Dbg( p_mux, "theora stream" );
439             break;
440 
441         case VLC_CODEC_DAALA:
442             msg_Dbg( p_mux, "daala stream" );
443             break;
444 
445         case VLC_CODEC_VP8:
446             msg_Dbg( p_mux, "VP8 stream" );
447             break;
448 
449         default:
450             FREENULL( p_input->p_sys );
451             return VLC_EGENERIC;
452         }
453     }
454         break;
455 
456     case AUDIO_ES:
457         switch( p_stream->fmt.i_codec )
458         {
459         case VLC_CODEC_OPUS:
460             msg_Dbg( p_mux, "opus stream" );
461             break;
462 
463         case VLC_CODEC_VORBIS:
464             msg_Dbg( p_mux, "vorbis stream" );
465             break;
466 
467         case VLC_CODEC_SPEEX:
468             msg_Dbg( p_mux, "speex stream" );
469             break;
470 
471         case VLC_CODEC_FLAC:
472             msg_Dbg( p_mux, "flac stream" );
473             break;
474 
475         default:
476             fourcc_to_wf_tag( p_stream->fmt.i_codec, &i_tag );
477             if( i_tag == WAVE_FORMAT_UNKNOWN )
478             {
479                 FREENULL( p_input->p_sys );
480                 return VLC_EGENERIC;
481             }
482 
483             p_stream->p_oggds_header =
484                 malloc( sizeof(oggds_header_t) + p_input->p_fmt->i_extra );
485             if( !p_stream->p_oggds_header )
486             {
487                 free( p_stream );
488                 return VLC_ENOMEM;
489             }
490             memset( p_stream->p_oggds_header, 0, sizeof(oggds_header_t) );
491             p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
492 
493             p_stream->p_oggds_header->i_size = p_input->p_fmt->i_extra;
494 
495             if( p_input->p_fmt->i_extra )
496             {
497                 memcpy( &p_stream->p_oggds_header[1],
498                         p_input->p_fmt->p_extra, p_input->p_fmt->i_extra );
499             }
500 
501             memcpy( p_stream->p_oggds_header->stream_type, "audio", 5 );
502 
503             memset( p_stream->p_oggds_header->sub_type, 0, 4 );
504             char buf[5];
505             snprintf( buf, sizeof(buf), "%"PRIx16, i_tag );
506             strncpy( p_stream->p_oggds_header->sub_type, buf, 4 );
507 
508             p_stream->p_oggds_header->i_time_unit = INT64_C(10000000);
509             p_stream->p_oggds_header->i_default_len = 1;
510             p_stream->p_oggds_header->i_buffer_size = 30*1024 ;
511             p_stream->p_oggds_header->i_samples_per_unit = p_input->p_fmt->audio.i_rate;
512             p_stream->p_oggds_header->i_bits_per_sample = p_input->p_fmt->audio.i_bitspersample;
513             p_stream->p_oggds_header->header.audio.i_channels = p_input->p_fmt->audio.i_channels;
514             p_stream->p_oggds_header->header.audio.i_block_align =  p_input->p_fmt->audio.i_blockalign;
515             p_stream->p_oggds_header->header.audio.i_avgbytespersec =  p_input->p_fmt->i_bitrate / 8;
516             msg_Dbg( p_mux, "%4.4s stream", (char *)&p_stream->fmt.i_codec );
517             break;
518         }
519         break;
520 
521     case SPU_ES:
522         switch( p_stream->fmt.i_codec )
523         {
524         case VLC_CODEC_SUBT:
525             p_stream->p_oggds_header = calloc( 1, sizeof(oggds_header_t) );
526             if( !p_stream->p_oggds_header )
527             {
528                 free( p_stream );
529                 return VLC_ENOMEM;
530             }
531             p_stream->p_oggds_header->i_packet_type = PACKET_TYPE_HEADER;
532 
533             memcpy( p_stream->p_oggds_header->stream_type, "text", 4 );
534             msg_Dbg( p_mux, "subtitles stream" );
535             break;
536 
537         default:
538             FREENULL( p_input->p_sys );
539             return VLC_EGENERIC;
540         }
541         break;
542     default:
543         FREENULL( p_input->p_sys );
544         return VLC_EGENERIC;
545     }
546 
547     p_stream->b_new = true;
548 
549     p_sys->i_add_streams++;
550 
551     return VLC_SUCCESS;
552 }
553 
554 /*****************************************************************************
555  * DelStream: Delete an elementary stream from the muxed stream
556  *****************************************************************************/
DelStream(sout_mux_t * p_mux,sout_input_t * p_input)557 static void DelStream( sout_mux_t *p_mux, sout_input_t *p_input )
558 {
559     sout_mux_sys_t *p_sys  = p_mux->p_sys;
560     ogg_stream_t   *p_stream = (ogg_stream_t*)p_input->p_sys;
561     block_t *p_og;
562 
563     msg_Dbg( p_mux, "removing input" );
564 
565     /* flush all remaining data */
566     if( p_input->p_sys )
567     {
568         if( !p_stream->b_new )
569         {
570             while( block_FifoCount( p_input->p_fifo ) )
571                 MuxBlock( p_mux, p_input );
572         }
573 
574         if( !p_stream->b_new &&
575             ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
576         {
577             OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
578             p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
579         }
580 
581         /* move input in delete queue */
582         if( !p_stream->b_new )
583         {
584             p_sys->pp_del_streams = xrealloc( p_sys->pp_del_streams,
585                         (p_sys->i_del_streams + 1) * sizeof(ogg_stream_t *) );
586             p_sys->pp_del_streams[p_sys->i_del_streams++] = p_stream;
587         }
588         else
589         {
590             /* wasn't already added so get rid of it */
591             FREENULL( p_stream->p_oggds_header );
592             FREENULL( p_stream );
593             p_sys->i_add_streams--;
594         }
595     }
596 
597     p_input->p_sys = NULL;
598 }
599 
600 /*****************************************************************************
601  * Ogg Skeleton helpers
602  *****************************************************************************/
WriteQWVariableLE(uint64_t i_64,uint64_t i_offset,uint8_t * p_buffer,int i_buffer_size)603 static int WriteQWVariableLE( uint64_t i_64, uint64_t i_offset,
604                               uint8_t *p_buffer, int i_buffer_size )
605 {
606     uint8_t *p_dest = p_buffer + i_offset;
607     int i_written = 0;
608 
609     for(;;)
610     {
611         if ( p_dest - p_buffer >= i_buffer_size ) return -1;
612 
613         *p_dest = (uint8_t) ( i_64 & 0x7F );
614         i_64 >>= 7;
615         i_written++;
616 
617         if ( i_64 == 0 )
618         {
619             *p_dest |= 0x80;
620             return i_written;
621         }
622 
623         p_dest++;
624     }
625 }
626 
AddIndexEntry(sout_mux_t * p_mux,uint64_t i_time,sout_input_t * p_input)627 static bool AddIndexEntry( sout_mux_t *p_mux, uint64_t i_time, sout_input_t *p_input )
628 {
629     sout_mux_sys_t *p_sys = p_mux->p_sys;
630     ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
631     uint64_t i_posdelta;
632     uint64_t i_timedelta;
633     if ( !p_sys->skeleton.b_create || p_mux->p_sys->skeleton.i_index_intvl == 0
634          || !p_stream->skeleton.p_index )
635         return false;
636 
637     if ( p_stream->skeleton.i_last_keyframe_pos == 0 )
638         p_stream->skeleton.i_last_keyframe_pos = p_sys->i_segment_start;
639     i_posdelta = p_sys->i_pos - p_stream->skeleton.i_last_keyframe_pos;
640     i_timedelta = i_time - p_stream->skeleton.i_last_keyframe_time;
641 
642     if ( i_timedelta <= ( (uint64_t) p_mux->p_sys->skeleton.i_index_intvl * 1000 )
643          || i_posdelta <= 0xFFFF )
644         return false;
645 
646     /* do inserts */
647     int i_ret;
648     if ( !p_stream->skeleton.p_index ) return false;
649     uint64_t i_offset = p_stream->skeleton.i_index_payload;
650     i_ret = WriteQWVariableLE( i_posdelta, i_offset, p_stream->skeleton.p_index,
651                                p_stream->skeleton.i_index_size );
652     if ( i_ret == -1 ) return false;
653     i_offset += i_ret;
654     i_ret = WriteQWVariableLE( i_timedelta, i_offset, p_stream->skeleton.p_index,
655                                p_stream->skeleton.i_index_size );
656     if ( i_ret == -1 ) return false;
657     p_stream->skeleton.i_index_payload = i_offset + i_ret;
658     p_stream->skeleton.i_index_count++;
659 
660     /* update diff points */
661     p_stream->skeleton.i_last_keyframe_pos = p_sys->i_pos;
662     p_stream->skeleton.i_last_keyframe_time = i_time;
663     msg_Dbg( p_mux, "Added index on stream %d entry %zd %"PRIu64,
664              p_stream->i_serial_no, p_sys->i_pos - p_sys->i_segment_start, i_time );
665 
666     return true;
667 }
668 
669 /*****************************************************************************
670  * Ogg bitstream manipulation routines
671  *****************************************************************************/
OggStreamGetPage(sout_mux_t * p_mux,ogg_stream_state * p_os,mtime_t i_pts,bool flush)672 static block_t *OggStreamGetPage( sout_mux_t *p_mux,
673                                   ogg_stream_state *p_os, mtime_t i_pts,
674                                   bool flush )
675 {
676     (void)p_mux;
677     block_t *p_og, *p_og_first = NULL;
678     ogg_page og;
679     int (*pager)( ogg_stream_state*, ogg_page* ) = flush ? ogg_stream_flush : ogg_stream_pageout;
680 
681     while( pager( p_os, &og ) )
682     {
683         /* Flush all data */
684         p_og = block_Alloc( og.header_len + og.body_len );
685 
686         memcpy( p_og->p_buffer, og.header, og.header_len );
687         memcpy( p_og->p_buffer + og.header_len, og.body, og.body_len );
688         p_og->i_dts     = 0;
689         p_og->i_pts     = i_pts;
690         p_og->i_length  = 0;
691 
692         i_pts = 0; // write it only once
693 
694         block_ChainAppend( &p_og_first, p_og );
695     }
696 
697     return p_og_first;
698 }
699 
OggStreamFlush(sout_mux_t * p_mux,ogg_stream_state * p_os,mtime_t i_pts)700 static block_t *OggStreamFlush( sout_mux_t *p_mux,
701                                 ogg_stream_state *p_os, mtime_t i_pts )
702 {
703     return OggStreamGetPage( p_mux, p_os, i_pts, true );
704 }
705 
OggStreamPageOut(sout_mux_t * p_mux,ogg_stream_state * p_os,mtime_t i_pts)706 static block_t *OggStreamPageOut( sout_mux_t *p_mux,
707                                   ogg_stream_state *p_os, mtime_t i_pts )
708 {
709     return OggStreamGetPage( p_mux, p_os, i_pts, false );
710 }
711 
OggGetSkeletonIndex(uint8_t ** pp_buffer,long * pi_size,ogg_stream_t * p_stream)712 static void OggGetSkeletonIndex( uint8_t **pp_buffer, long *pi_size, ogg_stream_t *p_stream )
713 {
714     uint8_t *p_buffer = calloc( INDEX_BASE_SIZE + p_stream->skeleton.i_index_size, sizeof(uint8_t) );
715     if ( !p_buffer ) return;
716     *pp_buffer = p_buffer;
717 
718     memcpy( p_buffer, "index", 6 );
719     SetDWLE( &p_buffer[6], p_stream->i_serial_no );
720     SetQWLE( &p_buffer[10], p_stream->skeleton.i_index_count ); /* num keypoints */
721     SetQWLE( &p_buffer[18], 1000000 );
722     SetQWLE( &p_buffer[34], p_stream->i_length );
723     memcpy( p_buffer + INDEX_BASE_SIZE, p_stream->skeleton.p_index, p_stream->skeleton.i_index_payload );
724     *pi_size = INDEX_BASE_SIZE + p_stream->skeleton.i_index_size;
725 }
726 
OggGetSkeletonFisbone(uint8_t ** pp_buffer,long * pi_size,sout_input_t * p_input,sout_mux_t * p_mux)727 static void OggGetSkeletonFisbone( uint8_t **pp_buffer, long *pi_size,
728                                    sout_input_t *p_input, sout_mux_t *p_mux )
729 {
730     uint8_t *psz_header;
731     uint8_t *p_buffer;
732     const char *psz_value = NULL;
733     ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
734     struct
735     {
736         char *psz_content_type;
737         char *psz_role;
738         long int i_size;
739         unsigned int i_count;
740     } headers = { NULL, NULL, 0, 0 };
741     *pi_size = 0;
742 
743     switch( p_stream->fmt.i_codec )
744     {
745         case VLC_CODEC_VORBIS:
746             psz_value = "audio/vorbis";
747             break;
748         case VLC_CODEC_THEORA:
749             psz_value = "video/theora";
750             break;
751         case VLC_CODEC_DAALA:
752             psz_value = "video/daala";
753             break;
754         case VLC_CODEC_SPEEX:
755             psz_value = "audio/speex";
756             break;
757         case VLC_CODEC_FLAC:
758             psz_value = "audio/flac";
759             break;
760         case VLC_CODEC_CMML:
761             psz_value = "text/cmml";
762             break;
763         case VLC_CODEC_KATE:
764             psz_value = "application/kate";
765             break;
766         case VLC_CODEC_VP8:
767             psz_value = "video/x-vp8";
768             break;
769         default:
770             psz_value = "application/octet-stream";
771             msg_Warn( p_mux, "Unknown fourcc for stream %s, setting Content-Type to %s",
772                   vlc_fourcc_GetDescription( p_stream->fmt.i_cat, p_stream->fmt.i_codec ),
773                   psz_value );
774     }
775 
776     /* Content Type Header */
777     if ( asprintf( &headers.psz_content_type, "Content-Type: %s\r\n", psz_value ) != -1 )
778     {
779         headers.i_size += strlen( headers.psz_content_type );
780         headers.i_count++;
781     }
782 
783     /* Set Role Header */
784     if ( p_input->p_fmt->i_priority > ES_PRIORITY_NOT_SELECTABLE )
785     {
786         int i_max_prio = ES_PRIORITY_MIN;
787         for ( int i=0; i< p_mux->i_nb_inputs; i++ )
788         {
789             if ( p_mux->pp_inputs[i]->p_fmt->i_cat != p_input->p_fmt->i_cat ) continue;
790             i_max_prio = __MAX( p_mux->pp_inputs[i]->p_fmt->i_priority, i_max_prio );
791         }
792 
793         psz_value = NULL;
794         if ( p_input->p_fmt->i_cat == AUDIO_ES || p_input->p_fmt->i_cat == VIDEO_ES )
795         {
796             if ( p_input->p_fmt->i_priority == i_max_prio && i_max_prio >= ES_PRIORITY_SELECTABLE_MIN )
797                 psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
798                             "video/main" : "audio/main";
799             else
800                 psz_value = ( p_input->p_fmt->i_cat == VIDEO_ES ) ?
801                             "video/alternate" : "audio/alternate";
802         }
803         else if ( p_input->p_fmt->i_cat == SPU_ES )
804         {
805             psz_value = ( p_input->p_fmt->i_codec == VLC_CODEC_KATE ) ?
806                         "text/karaoke" : "text/subtitle";
807         }
808 
809         if ( psz_value && asprintf( &headers.psz_role, "Role: %s\r\n", psz_value ) != -1 )
810         {
811             headers.i_size += strlen( headers.psz_role );
812             headers.i_count++;
813         }
814     }
815 
816     *pp_buffer = calloc( FISBONE_BASE_SIZE + headers.i_size, sizeof(uint8_t) );
817     if ( !*pp_buffer ) return;
818     p_buffer = *pp_buffer;
819 
820     memcpy( p_buffer, "fisbone", 8 );
821     SetDWLE( &p_buffer[8], FISBONE_BASE_OFFSET ); /* offset to message headers */
822     SetDWLE( &p_buffer[12], p_stream->i_serial_no );
823     SetDWLE( &p_buffer[16], headers.i_count );
824 
825     /* granulerate den */
826     switch ( p_input->p_fmt->i_cat )
827     {
828         case VIDEO_ES:
829             SetQWLE( &(*pp_buffer)[20], p_stream->fmt.video.i_frame_rate );
830             SetQWLE( &(*pp_buffer)[28], p_stream->fmt.video.i_frame_rate_base );
831         break;
832         case AUDIO_ES:
833             SetQWLE( &(*pp_buffer)[20], p_input->p_fmt->audio.i_rate );
834             SetQWLE( &(*pp_buffer)[28], 1 );
835         break;
836         default:
837             SetQWLE( &(*pp_buffer)[20], 1000 );
838             SetQWLE( &(*pp_buffer)[28], 1 );
839     }
840 
841     /* preroll */
842     if ( p_input->p_fmt->p_extra )
843         SetDWLE( &(*pp_buffer)[44],
844                 xiph_CountUnknownHeaders( p_input->p_fmt->p_extra,
845                                           p_input->p_fmt->i_extra,
846                                           p_input->p_fmt->i_codec ) );
847 
848     if ( headers.i_size > 0 )
849     {
850         psz_header = *pp_buffer + FISBONE_BASE_SIZE;
851         memcpy( psz_header, headers.psz_content_type, strlen( headers.psz_content_type ) );
852         psz_header += strlen( headers.psz_content_type );
853         if ( headers.psz_role )
854             memcpy( psz_header, headers.psz_role, strlen( headers.psz_role ) );
855     }
856     *pi_size = FISBONE_BASE_SIZE + headers.i_size;
857 
858     free( headers.psz_content_type );
859     free( headers.psz_role );
860 }
861 
OggFillSkeletonFishead(uint8_t * p_buffer,sout_mux_t * p_mux)862 static void OggFillSkeletonFishead( uint8_t *p_buffer, sout_mux_t *p_mux )
863 {
864     memcpy( p_buffer, "fishead", 8 );
865     SetWLE( &p_buffer[8], 4 );
866     SetWLE( &p_buffer[10], 0 );
867     SetQWLE( &p_buffer[20], 1000 );
868     SetQWLE( &p_buffer[36], 1000 );
869     SetQWLE( &p_buffer[64], p_mux->p_sys->i_pos - p_mux->p_sys->i_segment_start ); /* segment length */
870     SetQWLE( &p_buffer[72], p_mux->p_sys->i_data_start - p_mux->p_sys->i_segment_start ); /* data start offset */
871 }
872 
OggFillDsHeader(uint8_t * p_buffer,oggds_header_t * p_oggds_header,int i_cat)873 static int32_t OggFillDsHeader( uint8_t *p_buffer, oggds_header_t *p_oggds_header, int i_cat )
874 {
875     int index = 0;
876     p_buffer[index] = p_oggds_header->i_packet_type;
877     index++;
878     memcpy( &p_buffer[index], p_oggds_header->stream_type, sizeof(p_oggds_header->stream_type) );
879     index += sizeof(p_oggds_header->stream_type);
880     memcpy(&p_buffer[index], p_oggds_header->sub_type, sizeof(p_oggds_header->sub_type) );
881     index += sizeof(p_oggds_header->sub_type);
882 
883     /* The size is filled at the end */
884     uint8_t *p_isize = &p_buffer[index];
885     index += 4;
886 
887     SetQWLE( &p_buffer[index], p_oggds_header->i_time_unit );
888     index += 8;
889     SetQWLE( &p_buffer[index], p_oggds_header->i_samples_per_unit );
890     index += 8;
891     SetDWLE( &p_buffer[index], p_oggds_header->i_default_len );
892     index += 4;
893     SetDWLE( &p_buffer[index], p_oggds_header->i_buffer_size );
894     index += 4;
895     SetWLE( &p_buffer[index], p_oggds_header->i_bits_per_sample );
896     index += 2;
897     SetWLE( &p_buffer[index], p_oggds_header->i_padding_0 );
898     index += 2;
899     /* audio or video */
900     switch( i_cat )
901     {
902     case VIDEO_ES:
903         SetDWLE( &p_buffer[index], p_oggds_header->header.video.i_width );
904         SetDWLE( &p_buffer[index+4], p_oggds_header->header.video.i_height );
905         break;
906     case AUDIO_ES:
907         SetWLE( &p_buffer[index], p_oggds_header->header.audio.i_channels );
908         SetWLE( &p_buffer[index+2], p_oggds_header->header.audio.i_block_align );
909         SetDWLE( &p_buffer[index+4], p_oggds_header->header.audio.i_avgbytespersec );
910         break;
911     }
912     index += 8;
913     SetDWLE( &p_buffer[index], p_oggds_header->i_padding_1 );
914     index += 4;
915 
916     /* extra header */
917     if( p_oggds_header->i_size > 0 )
918     {
919         memcpy( &p_buffer[index], (uint8_t *) p_oggds_header + sizeof(*p_oggds_header), p_oggds_header->i_size );
920         index += p_oggds_header->i_size;
921     }
922 
923     SetDWLE( p_isize, index-1 );
924     return index;
925 }
926 
OggFillVP8Header(uint8_t * p_buffer,sout_input_t * p_input)927 static void OggFillVP8Header( uint8_t *p_buffer, sout_input_t *p_input )
928 {
929     ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
930 
931     memcpy( p_buffer, "OVP80\x01\x01\x00", 8 );
932     SetWBE( &p_buffer[8], p_input->p_fmt->video.i_width );
933     SetDWBE( &p_buffer[14], p_input->p_fmt->video.i_sar_den );/* 24 bits, 15~ */
934     SetDWBE( &p_buffer[11], p_input->p_fmt->video.i_sar_num );/* 24 bits, 12~ */
935     SetWBE( &p_buffer[10], p_input->p_fmt->video.i_height );
936     SetDWBE( &p_buffer[18], p_stream->fmt.video.i_frame_rate );
937     SetDWBE( &p_buffer[22], p_stream->fmt.video.i_frame_rate_base );
938 }
939 
OggCreateHeaders(sout_mux_t * p_mux)940 static bool OggCreateHeaders( sout_mux_t *p_mux )
941 {
942     block_t *p_hdr = NULL;
943     block_t *p_og = NULL;
944     ogg_packet op;
945     ogg_stream_t *p_stream;
946     sout_mux_sys_t *p_sys = p_mux->p_sys;
947 
948     if( sout_AccessOutControl( p_mux->p_access,
949                                ACCESS_OUT_CAN_SEEK,
950                                &p_sys->skeleton.b_create ) )
951     {
952         p_sys->skeleton.b_create = false;
953     }
954 
955     p_sys->skeleton.b_create &= !! p_mux->i_nb_inputs;
956 
957     /* no skeleton for solo vorbis/speex/opus tracks */
958     if ( p_mux->i_nb_inputs == 1 && p_mux->pp_inputs[0]->p_fmt->i_cat == AUDIO_ES )
959     {
960         p_sys->skeleton.b_create = false;
961     }
962     else
963     {
964         for ( int i=0; i< p_mux->i_nb_inputs; i++ )
965         {
966             p_stream = (ogg_stream_t*) p_mux->pp_inputs[i]->p_sys;
967             if ( p_stream->p_oggds_header )
968             {
969                 /* We don't want skeleton for OggDS */
970                 p_sys->skeleton.b_create = false;
971                 break;
972             }
973         }
974     }
975 
976     /* Skeleton's Fishead must be the first page of the stream */
977     if ( p_sys->skeleton.b_create && !p_sys->skeleton.b_head_done )
978     {
979         msg_Dbg( p_mux, "creating header for skeleton" );
980         p_sys->skeleton.i_serial_no = p_sys->i_next_serial_no++;
981         ogg_stream_init( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
982         op.bytes = 80;
983         op.packet = calloc( 1, op.bytes );
984         if ( op.packet == NULL ) return false;
985         op.b_o_s = 1;
986         op.e_o_s = 0;
987         op.granulepos = 0;
988         op.packetno = 0;
989         OggFillSkeletonFishead( op.packet, p_mux );
990         ogg_stream_packetin( &p_sys->skeleton.os, &op );
991         ogg_packet_clear( &op );
992         p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
993         block_ChainAppend( &p_hdr, p_og );
994         p_sys->skeleton.b_head_done = true;
995         p_sys->skeleton.i_fishead_offset = p_sys->i_pos;
996     }
997 
998     /* Write header for each stream. All b_o_s (beginning of stream) packets
999      * must appear first in the ogg stream so we take care of them first. */
1000     for( int pass = 0; pass < 2; pass++ )
1001     {
1002         for( int i = 0; i < p_mux->i_nb_inputs; i++ )
1003         {
1004             sout_input_t *p_input = p_mux->pp_inputs[i];
1005             p_stream = (ogg_stream_t*)p_input->p_sys;
1006 
1007             bool video = ( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
1008                            p_stream->fmt.i_codec == VLC_CODEC_DIRAC ||
1009                            p_stream->fmt.i_codec == VLC_CODEC_DAALA );
1010             if( ( ( pass == 0 && !video ) || ( pass == 1 && video ) ) )
1011                 continue;
1012 
1013             msg_Dbg( p_mux, "creating header for %4.4s",
1014                      (char *)&p_stream->fmt.i_codec );
1015 
1016             ogg_stream_init( &p_stream->os, p_stream->i_serial_no );
1017             p_stream->b_new = false;
1018             p_stream->i_packet_no = 0;
1019             p_stream->b_started = true;
1020 
1021             if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
1022                 p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
1023                 p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
1024                 p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
1025                 p_stream->fmt.i_codec == VLC_CODEC_DAALA )
1026             {
1027                 /* First packet in order: vorbis/speex/opus/theora/daala info */
1028                 unsigned pi_size[XIPH_MAX_HEADER_COUNT];
1029                 const void *pp_data[XIPH_MAX_HEADER_COUNT];
1030                 unsigned i_count;
1031 
1032                 if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
1033                                        p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
1034                 {
1035                     i_count = 0;
1036                     pi_size[0] = 0;
1037                     pp_data[0] = NULL;
1038                 }
1039 
1040                 op.bytes  = pi_size[0];
1041                 op.packet = (void *)pp_data[0];
1042                 if( pi_size[0] <= 0 )
1043                     msg_Err( p_mux, "header data corrupted");
1044 
1045                 op.b_o_s  = 1;
1046                 op.e_o_s  = 0;
1047                 op.granulepos = 0;
1048                 op.packetno = p_stream->i_packet_no++;
1049                 ogg_stream_packetin( &p_stream->os, &op );
1050                 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1051 
1052                 /* Get keyframe_granule_shift for theora or daala granulepos calculation */
1053                 if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
1054                     p_stream->fmt.i_codec == VLC_CODEC_DAALA )
1055                 {
1056                     p_stream->i_keyframe_granule_shift =
1057                         ( (op.packet[40] & 0x03) << 3 ) | ( (op.packet[41] & 0xe0) >> 5 );
1058                 }
1059             }
1060             else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
1061             {
1062                 op.packet = p_input->p_fmt->p_extra;
1063                 op.bytes  = p_input->p_fmt->i_extra;
1064                 op.b_o_s  = 1;
1065                 op.e_o_s  = 0;
1066                 op.granulepos = ~0;
1067                 op.packetno = p_stream->i_packet_no++;
1068                 ogg_stream_packetin( &p_stream->os, &op );
1069                 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1070             }
1071             else if( p_stream->fmt.i_codec == VLC_CODEC_FLAC )
1072             {
1073                 /* flac stream marker (yeah, only that in the 1st packet) */
1074                 op.packet = (unsigned char *)"fLaC";
1075                 op.bytes  = 4;
1076                 op.b_o_s  = 1;
1077                 op.e_o_s  = 0;
1078                 op.granulepos = 0;
1079                 op.packetno = p_stream->i_packet_no++;
1080                 ogg_stream_packetin( &p_stream->os, &op );
1081                 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1082             }
1083             else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
1084             {
1085                 /* VP8 Header */
1086                 op.packet = malloc( 26 );
1087                 if( !op.packet )
1088                     return false;
1089                 op.bytes = 26;
1090                 OggFillVP8Header( op.packet, p_input );
1091                 op.b_o_s = 1;
1092                 op.e_o_s = 0;
1093                 op.granulepos = 0;
1094                 op.packetno = p_stream->i_packet_no++;
1095                 ogg_stream_packetin( &p_stream->os, &op );
1096                 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1097                 free( op.packet );
1098             }
1099             else if( p_stream->p_oggds_header )
1100             {
1101                 /* ds header */
1102                 op.packet = malloc( sizeof(*p_stream->p_oggds_header) + p_stream->p_oggds_header->i_size );
1103                 if( !op.packet )
1104                     return false;
1105                 op.bytes  = OggFillDsHeader( op.packet, p_stream->p_oggds_header, p_stream->fmt.i_cat );
1106                 op.b_o_s  = 1;
1107                 op.e_o_s  = 0;
1108                 op.granulepos = 0;
1109                 op.packetno = p_stream->i_packet_no++;
1110                 ogg_stream_packetin( &p_stream->os, &op );
1111                 p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1112                 free( op.packet );
1113             }
1114 
1115             block_ChainAppend( &p_hdr, p_og );
1116         }
1117     }
1118 
1119     /* Create fisbones if any */
1120     if ( p_sys->skeleton.b_create )
1121     {
1122         for( int i = 0; i < p_mux->i_nb_inputs; i++ )
1123         {
1124             sout_input_t *p_input = p_mux->pp_inputs[i];
1125             ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1126             if ( p_stream->skeleton.b_fisbone_done ) continue;
1127             OggGetSkeletonFisbone( &op.packet, &op.bytes, p_input, p_mux );
1128             if ( op.packet == NULL ) return false;
1129             op.b_o_s = 0;
1130             op.e_o_s = 0;
1131             op.granulepos = 0;
1132             op.packetno = p_sys->skeleton.i_packet_no++;
1133             ogg_stream_packetin( &p_sys->skeleton.os, &op );
1134             ogg_packet_clear( &op );
1135             p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1136             block_ChainAppend( &p_hdr, p_og );
1137             p_stream->skeleton.b_fisbone_done = true;
1138         }
1139     }
1140 
1141     /* Write previous headers */
1142     for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
1143     {
1144         /* flag headers to be resent for streaming clients */
1145         p_og->i_flags |= BLOCK_FLAG_HEADER;
1146     }
1147     p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_hdr );
1148     p_hdr = NULL;
1149 
1150     /* Create indexes if any */
1151     for( int i = 0; i < p_mux->i_nb_inputs; i++ )
1152     {
1153         sout_input_t *p_input = p_mux->pp_inputs[i];
1154         ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1155         /* flush stream && save offset */
1156         if ( p_sys->skeleton.b_create && !p_stream->skeleton.b_index_done )
1157         {
1158             if ( !p_stream->skeleton.p_index ) AllocateIndex( p_mux, p_input );
1159             if ( p_stream->skeleton.p_index )
1160             {
1161                 msg_Dbg( p_mux, "Creating index for stream %d", p_stream->i_serial_no );
1162                 OggGetSkeletonIndex( &op.packet, &op.bytes, p_stream );
1163                 if ( op.packet == NULL ) return false;
1164                 op.b_o_s = 0;
1165                 op.e_o_s = 0;
1166                 op.granulepos = 0;
1167                 op.packetno = p_sys->skeleton.i_packet_no++;
1168 
1169                 /* backup some values */
1170                 p_stream->skeleton.i_index_offset = p_mux->p_sys->i_pos;
1171                 p_stream->skeleton.i_index_packetno = p_sys->skeleton.os.packetno;
1172                 p_stream->skeleton.i_index_pageno = p_sys->skeleton.os.pageno;
1173 
1174                 ogg_stream_packetin( &p_sys->skeleton.os, &op );
1175                 ogg_packet_clear( &op );
1176                 p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1177                 p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1178             }
1179             p_stream->skeleton.b_index_done = true;
1180         }
1181     }
1182 
1183     /* Take care of the non b_o_s headers */
1184     for( int i = 0; i < p_mux->i_nb_inputs; i++ )
1185     {
1186         sout_input_t *p_input = p_mux->pp_inputs[i];
1187         ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1188 
1189         if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
1190             p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
1191             p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
1192             p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
1193             p_stream->fmt.i_codec == VLC_CODEC_DAALA )
1194         {
1195             unsigned pi_size[XIPH_MAX_HEADER_COUNT];
1196             const void *pp_data[XIPH_MAX_HEADER_COUNT];
1197             unsigned i_count;
1198 
1199             if( xiph_SplitHeaders( pi_size, pp_data, &i_count,
1200                                    p_input->p_fmt->i_extra, p_input->p_fmt->p_extra ) )
1201                 i_count = 0;
1202 
1203             /* Special case, headers are already there in the incoming stream.
1204              * We need to gather them an mark them as headers. */
1205             for( unsigned j = 1; j < i_count; j++ )
1206             {
1207                 op.bytes  = pi_size[j];
1208                 op.packet = (void *)pp_data[j];
1209                 if( pi_size[j] <= 0 )
1210                     msg_Err( p_mux, "header data corrupted");
1211 
1212                 op.b_o_s  = 0;
1213                 op.e_o_s  = 0;
1214                 op.granulepos = 0;
1215                 op.packetno = p_stream->i_packet_no++;
1216                 ogg_stream_packetin( &p_stream->os, &op );
1217                 msg_Dbg( p_mux, "adding non bos, secondary header" );
1218                 if( j == i_count - 1 )
1219                     p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1220                 else
1221                     p_og = OggStreamPageOut( p_mux, &p_stream->os, 0 );
1222                 if( p_og )
1223                     block_ChainAppend( &p_hdr, p_og );
1224             }
1225         }
1226         else if( p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
1227                  p_stream->fmt.i_codec != VLC_CODEC_DIRAC )
1228         {
1229             uint8_t com[128];
1230             int     i_com;
1231 
1232             /* comment */
1233             com[0] = PACKET_TYPE_COMMENT;
1234             i_com = snprintf( (char *)(com+1), 127,
1235                               PACKAGE_VERSION" stream output" )
1236                      + 1;
1237             op.packet = com;
1238             op.bytes  = i_com;
1239             op.b_o_s  = 0;
1240             op.e_o_s  = 0;
1241             op.granulepos = 0;
1242             op.packetno = p_stream->i_packet_no++;
1243             ogg_stream_packetin( &p_stream->os, &op );
1244             p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1245             block_ChainAppend( &p_hdr, p_og );
1246         }
1247 
1248         /* Special case for mp4v and flac */
1249         if( ( p_stream->fmt.i_codec == VLC_CODEC_MP4V ||
1250               p_stream->fmt.i_codec == VLC_CODEC_FLAC ) &&
1251             p_input->p_fmt->i_extra )
1252         {
1253             /* Send a packet with the VOL data for mp4v
1254              * or STREAMINFO for flac */
1255             msg_Dbg( p_mux, "writing extra data" );
1256             op.bytes  = p_input->p_fmt->i_extra;
1257             op.packet = p_input->p_fmt->p_extra;
1258             uint8_t flac_streaminfo[34 + 4];
1259             if( p_stream->fmt.i_codec == VLC_CODEC_FLAC )
1260             {
1261                 if (op.bytes == 42 && !memcmp(op.packet, "fLaC", 4)) {
1262                     op.bytes -= 4;
1263                     memcpy(flac_streaminfo, op.packet + 4, 38);
1264                     op.packet = flac_streaminfo;
1265                 } else if (op.bytes == 34) {
1266                     op.bytes += 4;
1267                     memcpy(flac_streaminfo + 4, op.packet, 34);
1268                     flac_streaminfo[0] = 0x80; /* last block, streaminfo */
1269                     flac_streaminfo[1] = 0;
1270                     flac_streaminfo[2] = 0;
1271                     flac_streaminfo[3] = 34; /* block size */
1272                     op.packet = flac_streaminfo;
1273                 } else {
1274                     msg_Err(p_mux, "Invalid FLAC streaminfo (%ld bytes)",
1275                             op.bytes);
1276                 }
1277             }
1278             op.b_o_s  = 0;
1279             op.e_o_s  = 0;
1280             op.granulepos = 0;
1281             op.packetno = p_stream->i_packet_no++;
1282             ogg_stream_packetin( &p_stream->os, &op );
1283             p_og = OggStreamFlush( p_mux, &p_stream->os, 0 );
1284             block_ChainAppend( &p_hdr, p_og );
1285         }
1286     }
1287 
1288     if ( p_sys->skeleton.b_create )
1289     {
1290         msg_Dbg( p_mux, "ending skeleton" );
1291         op.packet = NULL;
1292         op.bytes = 0;
1293         op.b_o_s = 0;
1294         op.e_o_s = 1;
1295         op.granulepos = 0;
1296         op.packetno = p_sys->skeleton.i_packet_no++;
1297         ogg_stream_packetin( &p_sys->skeleton.os, &op );
1298         p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1299         block_ChainAppend( &p_hdr, p_og );
1300     }
1301 
1302     /* set HEADER flag */
1303     /* flag headers to be resent for streaming clients */
1304     for( p_og = p_hdr; p_og != NULL; p_og = p_og->p_next )
1305     {
1306         p_og->i_flags |= BLOCK_FLAG_HEADER;
1307     }
1308 
1309     /* Write previous headers */
1310     p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_hdr );
1311 
1312     return true;
1313 }
1314 
OggCreateStreamFooter(sout_mux_t * p_mux,ogg_stream_t * p_stream)1315 static void OggCreateStreamFooter( sout_mux_t *p_mux, ogg_stream_t *p_stream )
1316 {
1317     block_t *p_og;
1318     ogg_packet op;
1319     sout_mux_sys_t *p_sys = p_mux->p_sys;
1320 
1321     /* as stream is finished, overwrite the index, if there was any */
1322     if ( p_sys->skeleton.b_create && p_stream->skeleton.p_index
1323          && p_stream->skeleton.i_index_payload )
1324     {
1325         sout_AccessOutSeek( p_mux->p_access, p_stream->skeleton.i_index_offset );
1326         OggGetSkeletonIndex( &op.packet, &op.bytes, p_stream );
1327         if ( op.packet != NULL )
1328         {
1329             msg_Dbg(p_mux, "Rewriting index at %"PRId64, p_stream->skeleton.i_index_offset );
1330             ogg_stream_reset_serialno( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
1331             op.b_o_s = 0;
1332             op.e_o_s = 0;
1333             op.granulepos = 0;
1334             op.packetno = p_stream->skeleton.i_index_packetno + 1;
1335             /* fake our stream state */
1336             p_sys->skeleton.os.pageno = p_stream->skeleton.i_index_pageno;
1337             p_sys->skeleton.os.packetno = p_stream->skeleton.i_index_packetno;
1338             p_sys->skeleton.os.granulepos = 0;
1339             p_sys->skeleton.os.b_o_s = 1;
1340             p_sys->skeleton.os.e_o_s = 0;
1341             ogg_stream_packetin( &p_sys->skeleton.os, &op );
1342             ogg_packet_clear( &op );
1343             p_og = OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 );
1344             sout_AccessOutWrite( p_mux->p_access, p_og );
1345         }
1346         sout_AccessOutSeek( p_mux->p_access, p_sys->i_pos );
1347     }
1348 
1349     /* clear skeleton */
1350     p_stream->skeleton.b_fisbone_done = false;
1351     p_stream->skeleton.b_index_done = false;
1352     p_stream->skeleton.i_index_offset = 0;
1353     p_stream->skeleton.i_index_payload = 0;
1354     p_stream->skeleton.i_last_keyframe_pos = 0;
1355     p_stream->skeleton.i_last_keyframe_time = 0;
1356     /* clear accounting */
1357     p_stream->i_num_frames = 0;
1358     p_stream->i_num_keyframes = 0;
1359 
1360     /* Write eos packet for stream. */
1361     op.packet = NULL;
1362     op.bytes  = 0;
1363     op.b_o_s  = 0;
1364     op.e_o_s  = 1;
1365     op.granulepos = p_stream->u_last_granulepos;
1366     op.packetno = p_stream->i_packet_no++;
1367     ogg_stream_packetin( &p_stream->os, &op );
1368 
1369     /* flush it with all remaining data */
1370     if( ( p_og = OggStreamFlush( p_mux, &p_stream->os, 0 ) ) )
1371     {
1372         /* Write footer */
1373         OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
1374         p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1375     }
1376 
1377     ogg_stream_clear( &p_stream->os );
1378 }
1379 
OggSetDate(block_t * p_og,mtime_t i_dts,mtime_t i_length)1380 static void OggSetDate( block_t *p_og, mtime_t i_dts, mtime_t i_length )
1381 {
1382     int i_count;
1383     block_t *p_tmp;
1384     mtime_t i_delta;
1385 
1386     for( p_tmp = p_og, i_count = 0; p_tmp != NULL; p_tmp = p_tmp->p_next )
1387     {
1388         i_count++;
1389     }
1390 
1391     if( i_count == 0 ) return; /* ignore. */
1392 
1393     i_delta = i_length / i_count;
1394 
1395     for( p_tmp = p_og; p_tmp != NULL; p_tmp = p_tmp->p_next )
1396     {
1397         p_tmp->i_dts    = i_dts;
1398         p_tmp->i_length = i_delta;
1399 
1400         i_dts += i_delta;
1401     }
1402 }
1403 
OggRewriteFisheadPage(sout_mux_t * p_mux)1404 static void OggRewriteFisheadPage( sout_mux_t *p_mux )
1405 {
1406     sout_mux_sys_t *p_sys = p_mux->p_sys;
1407     ogg_packet op;
1408     op.bytes = 80;
1409     op.packet = calloc( 1, op.bytes );
1410     if ( op.packet != NULL )
1411     {
1412         op.b_o_s = 1;
1413         op.e_o_s = 0;
1414         op.granulepos = 0;
1415         op.packetno = 0;
1416         ogg_stream_reset_serialno( &p_sys->skeleton.os, p_sys->skeleton.i_serial_no );
1417         OggFillSkeletonFishead( op.packet, p_mux );
1418         ogg_stream_packetin( &p_sys->skeleton.os, &op );
1419         ogg_packet_clear( &op );
1420         msg_Dbg( p_mux, "rewriting fishead at %"PRId64, p_mux->p_sys->skeleton.i_fishead_offset );
1421         sout_AccessOutSeek( p_mux->p_access, p_mux->p_sys->skeleton.i_fishead_offset );
1422         sout_AccessOutWrite( p_mux->p_access,
1423                              OggStreamFlush( p_mux, &p_sys->skeleton.os, 0 ) );
1424         sout_AccessOutSeek( p_mux->p_access, p_mux->p_sys->i_pos );
1425     }
1426 }
1427 
AllocateIndex(sout_mux_t * p_mux,sout_input_t * p_input)1428 static bool AllocateIndex( sout_mux_t *p_mux, sout_input_t *p_input )
1429 {
1430     ogg_stream_t *p_stream = (ogg_stream_t *) p_input->p_sys;
1431     size_t i_size;
1432 
1433     if ( p_stream->i_length )
1434     {
1435         uint64_t i_interval = (uint64_t)p_mux->p_sys->skeleton.i_index_intvl * 1000;
1436         uint64_t i;
1437 
1438         if( p_input->p_fmt->i_cat == VIDEO_ES &&
1439                 p_stream->fmt.video.i_frame_rate )
1440         {
1441             /* optimize for fps < 1 */
1442             i_interval= __MAX( p_mux->p_sys->skeleton.i_index_intvl * 1000,
1443                        INT64_C(10000000) *
1444                        p_stream->fmt.video.i_frame_rate_base /
1445                        p_stream->fmt.video.i_frame_rate );
1446         }
1447 
1448         size_t i_tuple_size = 0;
1449         /* estimate length of pos value */
1450         if ( p_input->p_fmt->i_bitrate )
1451         {
1452             i = i_interval * p_input->p_fmt->i_bitrate / 1000000;
1453             while ( i <<= 1 ) i_tuple_size++;
1454         }
1455         else
1456         {
1457             /* Likely 64KB<<keyframe interval<<16MB */
1458             /* We can't really guess due to muxing */
1459             i_tuple_size = 24 / 8;
1460         }
1461 
1462         /* add length of interval value */
1463         i = i_interval;
1464         while ( i <<= 1 ) i_tuple_size++;
1465 
1466         i_size = i_tuple_size * ( p_stream->i_length / i_interval + 2 );
1467     }
1468     else
1469     {
1470         i_size = ( INT64_C(3600) * 11.2 * 1000 / p_mux->p_sys->skeleton.i_index_intvl )
1471                 * p_mux->p_sys->skeleton.i_index_ratio;
1472         msg_Dbg( p_mux, "No stream length, using default allocation for index" );
1473     }
1474     i_size *= ( 8.0 / 7 ); /* 7bits encoding overhead */
1475     msg_Dbg( p_mux, "allocating %zu bytes for index", i_size );
1476     p_stream->skeleton.p_index = calloc( i_size, sizeof(uint8_t) );
1477     if ( !p_stream->skeleton.p_index ) return false;
1478     p_stream->skeleton.i_index_size = i_size;
1479     p_stream->skeleton.i_index_payload = 0;
1480     return true;
1481 }
1482 
1483 /*****************************************************************************
1484  * Mux: multiplex available data in input fifos into the Ogg bitstream
1485  *****************************************************************************/
Mux(sout_mux_t * p_mux)1486 static int Mux( sout_mux_t *p_mux )
1487 {
1488     sout_mux_sys_t *p_sys = p_mux->p_sys;
1489     mtime_t        i_dts;
1490 
1491     /* End any stream that ends in that group */
1492     if ( p_sys->i_del_streams )
1493     {
1494         /* Remove deleted logical streams */
1495         for( int i = 0; i < p_sys->i_del_streams; i++ )
1496         {
1497             OggCreateStreamFooter( p_mux, p_sys->pp_del_streams[i] );
1498             FREENULL( p_sys->pp_del_streams[i]->p_oggds_header );
1499             FREENULL( p_sys->pp_del_streams[i] );
1500         }
1501         FREENULL( p_sys->pp_del_streams );
1502         p_sys->i_del_streams = 0;
1503     }
1504 
1505     if ( p_sys->i_streams == 0 )
1506     {
1507         /* All streams have been deleted, or none have ever been created
1508            From this point, we are allowed to start a new group of logical streams */
1509         p_sys->skeleton.b_head_done = false;
1510         p_sys->b_can_add_streams = true;
1511         p_sys->i_segment_start = p_sys->i_pos;
1512     }
1513 
1514     if ( p_sys->i_add_streams )
1515     {
1516         if ( !p_sys->b_can_add_streams )
1517         {
1518             msg_Warn( p_mux, "Can't add new stream %d/%d: Considerer increasing sout-mux-caching variable", p_sys->i_del_streams, p_mux->p_sys->i_streams);
1519             msg_Warn( p_mux, "Resetting and setting new identity to current streams");
1520 
1521             /* resetting all active streams */
1522             for ( int i=0; i < p_mux->p_sys->i_streams; i++ )
1523             {
1524                 ogg_stream_t * p_stream = (ogg_stream_t *) p_mux->pp_inputs[i]->p_sys;
1525                 if ( p_stream->b_finished || !p_stream->b_started ) continue;
1526                 OggCreateStreamFooter( p_mux, p_stream );
1527                 p_stream->i_serial_no = p_sys->i_next_serial_no++;
1528                 p_stream->i_packet_no = 0;
1529                 p_stream->b_finished = true;
1530             }
1531 
1532             /* rewrite fishead with final values */
1533             if ( p_sys->skeleton.b_head_done )
1534             {
1535                 OggRewriteFisheadPage( p_mux );
1536             }
1537 
1538             p_sys->b_can_add_streams = true;
1539             p_sys->skeleton.b_head_done = false;
1540             p_sys->i_segment_start = p_sys->i_pos;
1541         }
1542 
1543         /* Open new ogg stream */
1544         if( sout_MuxGetStream( p_mux, 1, &i_dts) < 0 )
1545         {
1546             msg_Dbg( p_mux, "waiting for data..." );
1547             return VLC_SUCCESS;
1548         }
1549         msg_Dbg( p_mux, "writing streams headers" );
1550         p_sys->i_start_dts = i_dts;
1551         p_sys->i_streams = p_mux->i_nb_inputs;
1552         p_sys->i_del_streams = 0;
1553         p_sys->i_add_streams = 0;
1554         p_sys->skeleton.b_create = true;
1555 
1556         if ( ! OggCreateHeaders( p_mux ) )
1557             return VLC_ENOMEM;
1558 
1559         /* If we're switching to end of headers, then that's data start */
1560         if ( p_sys->b_can_add_streams )
1561         {
1562             msg_Dbg( p_mux, "data starts from %zu", p_sys->i_pos );
1563             p_sys->i_data_start = p_sys->i_pos;
1564         }
1565 
1566         /* Since we started sending secondaryheader or data pages,
1567              * we're no longer allowed to create new streams, until all streams end */
1568         p_sys->b_can_add_streams = false;
1569     }
1570 
1571     /* Do the regular data mux thing */
1572     for( ;; )
1573     {
1574         int i_stream = sout_MuxGetStream( p_mux, 1, NULL );
1575         if( i_stream < 0 )
1576             return VLC_SUCCESS;
1577         MuxBlock( p_mux, p_mux->pp_inputs[i_stream] );
1578     }
1579 
1580     return VLC_SUCCESS;
1581 }
1582 
MuxBlock(sout_mux_t * p_mux,sout_input_t * p_input)1583 static int MuxBlock( sout_mux_t *p_mux, sout_input_t *p_input )
1584 {
1585     sout_mux_sys_t *p_sys = p_mux->p_sys;
1586     ogg_stream_t *p_stream = (ogg_stream_t*)p_input->p_sys;
1587     block_t *p_data = block_FifoGet( p_input->p_fifo );
1588     block_t *p_og = NULL;
1589     ogg_packet op;
1590     uint64_t i_time;
1591 
1592     if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS &&
1593         p_stream->fmt.i_codec != VLC_CODEC_FLAC &&
1594         p_stream->fmt.i_codec != VLC_CODEC_SPEEX &&
1595         p_stream->fmt.i_codec != VLC_CODEC_OPUS &&
1596         p_stream->fmt.i_codec != VLC_CODEC_THEORA &&
1597         p_stream->fmt.i_codec != VLC_CODEC_DAALA &&
1598         p_stream->fmt.i_codec != VLC_CODEC_VP8 &&
1599         p_stream->fmt.i_codec != VLC_CODEC_DIRAC )
1600     {
1601         p_data = block_Realloc( p_data, 1, p_data->i_buffer );
1602         p_data->p_buffer[0] = PACKET_IS_SYNCPOINT;      // FIXME
1603     }
1604 
1605     if ( p_stream->fmt.i_codec == VLC_CODEC_DIRAC && p_stream->i_baseptsdelay < 0 )
1606         p_stream->i_baseptsdelay = p_data->i_pts - p_data->i_dts;
1607 
1608     op.packet   = p_data->p_buffer;
1609     op.bytes    = p_data->i_buffer;
1610     op.b_o_s    = 0;
1611     op.e_o_s    = 0;
1612     op.packetno = p_stream->i_packet_no++;
1613     op.granulepos = -1;
1614 
1615     if( p_stream->fmt.i_cat == AUDIO_ES )
1616     {
1617         if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ||
1618             p_stream->fmt.i_codec == VLC_CODEC_FLAC ||
1619             p_stream->fmt.i_codec == VLC_CODEC_OPUS ||
1620             p_stream->fmt.i_codec == VLC_CODEC_SPEEX )
1621         {
1622             /* number of sample from begining + current packet */
1623             op.granulepos =
1624                 ( p_data->i_dts - p_sys->i_start_dts + p_data->i_length ) *
1625                 (mtime_t)p_input->p_fmt->audio.i_rate / CLOCK_FREQ;
1626 
1627             i_time = p_data->i_dts - p_sys->i_start_dts;
1628             AddIndexEntry( p_mux, i_time, p_input );
1629         }
1630         else if( p_stream->p_oggds_header )
1631         {
1632             /* number of sample from begining */
1633             op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) *
1634                 p_stream->p_oggds_header->i_samples_per_unit / CLOCK_FREQ;
1635         }
1636     }
1637     else if( p_stream->fmt.i_cat == VIDEO_ES )
1638     {
1639         if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ||
1640             p_stream->fmt.i_codec == VLC_CODEC_DAALA )
1641         {
1642             p_stream->i_num_frames++;
1643             if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
1644             {
1645                 p_stream->i_num_keyframes++;
1646                 p_stream->i_last_keyframe = p_stream->i_num_frames;
1647 
1648                 /* presentation time */
1649                 i_time = CLOCK_FREQ * ( p_stream->i_num_frames - 1 ) *
1650                         p_stream->fmt.video.i_frame_rate_base /
1651                         p_stream->fmt.video.i_frame_rate;
1652                 AddIndexEntry( p_mux, i_time, p_input );
1653             }
1654 
1655             op.granulepos = (p_stream->i_last_keyframe << p_stream->i_keyframe_granule_shift )
1656                           | (p_stream->i_num_frames-p_stream->i_last_keyframe);
1657         }
1658         else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
1659         {
1660 
1661 #define FRAME_ROUND(a) \
1662     if ( ( a + 5000 / CLOCK_FREQ ) > ( a / CLOCK_FREQ ) )\
1663         a += 5000;\
1664     a /= CLOCK_FREQ;
1665 
1666             mtime_t dt = (p_data->i_dts - p_sys->i_start_dts) * p_stream->fmt.video.i_frame_rate /
1667                     p_stream->fmt.video.i_frame_rate_base;
1668             FRAME_ROUND( dt );
1669 
1670             mtime_t pt = (p_data->i_pts - p_sys->i_start_dts - p_stream->i_baseptsdelay ) *
1671                     p_stream->fmt.video.i_frame_rate / p_stream->fmt.video.i_frame_rate_base;
1672             FRAME_ROUND( pt );
1673 
1674             /* (shro) some PTS could be repeated within 1st frames */
1675             if ( pt == p_stream->i_dirac_last_pt )
1676                 pt++;
1677             else
1678                 p_stream->i_dirac_last_pt = pt;
1679 
1680             /* (shro) some DTS could be repeated within 1st frames */
1681             if ( dt == p_stream->i_dirac_last_dt )
1682                 dt++;
1683             else
1684                 p_stream->i_dirac_last_dt = dt;
1685 
1686             if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
1687                 p_stream->i_last_keyframe = dt;
1688             mtime_t dist = dt - p_stream->i_last_keyframe;
1689 
1690             /* Everything increments by two for progressive */
1691             if ( true )
1692             {
1693                 pt *=2;
1694                 dt *=2;
1695             }
1696 
1697             mtime_t delay = pt - dt;
1698             if ( delay < 0 ) delay *= -1;
1699 
1700             op.granulepos = (pt - delay) << 31 | (dist&0xff00) << 14
1701                           | (delay&0x1fff) << 9 | (dist&0xff);
1702 #ifndef NDEBUG
1703             msg_Dbg( p_mux, "dts %"PRId64" pts %"PRId64" dt %"PRId64" pt %"PRId64" delay %"PRId64" granule %"PRId64,
1704                      (p_data->i_dts - p_sys->i_start_dts),
1705                      (p_data->i_pts - p_sys->i_start_dts ),
1706                      dt, pt, delay, op.granulepos );
1707 #endif
1708 
1709             AddIndexEntry( p_mux, dt, p_input );
1710         }
1711         else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 )
1712         {
1713             p_stream->i_num_frames++;
1714             if( p_data->i_flags & BLOCK_FLAG_TYPE_I )
1715             {
1716                 p_stream->i_num_keyframes++;
1717                 p_stream->i_last_keyframe = p_stream->i_num_frames;
1718 
1719                 /* presentation time */
1720                 i_time = CLOCK_FREQ * ( p_stream->i_num_frames - 1 ) *
1721                          p_stream->fmt.video.i_frame_rate_base / p_stream->fmt.video.i_frame_rate;
1722                 AddIndexEntry( p_mux, i_time, p_input );
1723             }
1724             op.granulepos = ( ((int64_t)p_stream->i_num_frames) << 32 ) |
1725             ( ( ( p_stream->i_num_frames - p_stream->i_last_keyframe ) & 0x07FFFFFF ) << 3 );
1726         }
1727         else if( p_stream->p_oggds_header )
1728             op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) * INT64_C(10) /
1729                 p_stream->p_oggds_header->i_time_unit;
1730     }
1731     else if( p_stream->fmt.i_cat == SPU_ES )
1732     {
1733         /* granulepos is in millisec */
1734         op.granulepos = ( p_data->i_dts - p_sys->i_start_dts ) / 1000;
1735     }
1736     else
1737         return VLC_EGENERIC;
1738 
1739     p_stream->u_last_granulepos = op.granulepos;
1740     ogg_stream_packetin( &p_stream->os, &op );
1741 
1742     if( p_stream->fmt.i_cat == SPU_ES ||
1743         p_stream->fmt.i_codec == VLC_CODEC_SPEEX ||
1744         p_stream->fmt.i_codec == VLC_CODEC_DIRAC )
1745     {
1746         /* Subtitles or Speex packets are quite small so they
1747          * need to be flushed to be sent on time */
1748         /* The OggDirac mapping suggests ever so strongly that a
1749          * page flush occurs after each OggDirac packet, so to make
1750          * the timestamps unambiguous */
1751         p_og = OggStreamFlush( p_mux, &p_stream->os, p_data->i_dts );
1752     }
1753     else
1754     {
1755         p_og = OggStreamPageOut( p_mux, &p_stream->os, p_data->i_dts );
1756     }
1757 
1758     if( p_og )
1759     {
1760         OggSetDate( p_og, p_stream->i_dts, p_stream->i_length );
1761         p_stream->i_dts = -1;
1762         p_stream->i_length = 0;
1763         p_mux->p_sys->i_pos += sout_AccessOutWrite( p_mux->p_access, p_og );
1764     }
1765     else
1766     {
1767         if( p_stream->i_dts < 0 )
1768         {
1769             p_stream->i_dts = p_data->i_dts;
1770         }
1771         p_stream->i_length += p_data->i_length;
1772     }
1773 
1774     block_Release( p_data );
1775     return VLC_SUCCESS;
1776 }
1777