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