[vlc-devel] [PATCH 2/3] packetizer/dirac: New fully featured dirac packetizer

davidf+nntp at woaf.net davidf+nntp at woaf.net
Tue Apr 21 20:26:47 CEST 2009


From: David Flynn <davidf at rd.bbc.co.uk>

Packetizer is capable of:
 - repacketizing and reconstructing timestamps for a Dirac ES
   suitable for remultiplexing and decoding.
 - sanitizing an input stream that has non-signalled discontinuities
 - handling the basket case AVI fake-pts-is-dts method too:
   For a non well formed input (only has pts or dts), this is
   implemented by simulating the ReOrder Buffer model to resolve
   timestamps.

Signed-off-by: David Flynn <davidf at rd.bbc.co.uk>
---
 modules/packetizer/Modules.am |    5 +-
 modules/packetizer/dirac.c    | 1259 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1263 insertions(+), 1 deletions(-)
 create mode 100644 modules/packetizer/dirac.c

diff --git a/modules/packetizer/Modules.am b/modules/packetizer/Modules.am
index 15acce9..3c56828 100644
--- a/modules/packetizer/Modules.am
+++ b/modules/packetizer/Modules.am
@@ -5,6 +5,7 @@ SOURCES_packetizer_mpeg4audio = mpeg4audio.c
 SOURCES_packetizer_h264 = h264.c
 SOURCES_packetizer_vc1 = vc1.c
 SOURCES_packetizer_mlp = mlp.c
+SOURCES_packetizer_dirac = dirac.c
 
 libvlc_LTLIBRARIES += \
 	libpacketizer_mpegvideo_plugin.la \
@@ -12,7 +13,9 @@ libvlc_LTLIBRARIES += \
 	libpacketizer_mpeg4audio_plugin.la \
 	libpacketizer_h264_plugin.la \
 	libpacketizer_vc1_plugin.la \
-	libpacketizer_mlp_plugin.la
+	libpacketizer_mlp_plugin.la \
+	libpacketizer_dirac_plugin.la \
+	$(NULL)
 if ENABLE_SOUT
 libvlc_LTLIBRARIES += libpacketizer_copy_plugin.la
 endif
diff --git a/modules/packetizer/dirac.c b/modules/packetizer/dirac.c
new file mode 100644
index 0000000..9318243
--- /dev/null
+++ b/modules/packetizer/dirac.c
@@ -0,0 +1,1259 @@
+/*****************************************************************************
+ * dirac.c
+ *****************************************************************************
+ * Copyright (C) 2008 the VideoLAN team
+ * $Id$
+ *
+ * Authors: David Flynn <davidf at rd.bbc.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/* Dirac packetizer, formed of three parts:
+ *  1) Bitstream synchroniser (dirac_DoSync)
+ *      - Given an arbitary sequence of bytes, extract whole Dirac Data Units
+ *      - Maps timestamps in supplied block_t's to the extracted Data Unit
+ *        A time stamp applies to the next Data Unit to commence at, or after
+ *        the first byte of the block_t with the timestamp.
+ *  2) Encapsulation Unit generation (dirac_BuildEncapsulationUnit)
+ *      - Takes multiple well formed Dirac Data Units and forms them into a
+ *        single encapsulation unit, suitable for muxing.
+ *      - Sorts out any time stamps so that they only apply to pictures.
+ *  3) Timestamp generator (dirac_TimeGenPush)
+ *      - Many streams will not be correctly timestamped, ie, DTS&PTS for
+ *        every encapsulation unit.  Timestamp generator syncs to avaliable
+ *        timestamps and produces DTS&PTS for each encapsulation unit.
+ *      - For 'Occasional' missing PTS|DTS:
+ *          Missing timestamp is generated using interpolation from last
+ *          known good values.
+ *      - for All PTS missing:
+ *          It is assumed that DTS values are fake, and are actually
+ *          in the sequence of the PTS values at the output of a decoder.
+ *          Fill in PTS by copying from DTS (accounting for reordering,
+ *          by simulating reorder buffer); adjust DTS to provide correct
+ *          value.  This is how demuxers like AVI work.
+ *      - for All DTS missing:
+ *          (Ie, PTS is present), reorder buffer is simulated to determine
+ *          PTS for each encapsulation unit.
+ *      - NB, doesn't handle all pts missing with real dts. (no way to
+ *        distinguish from the fake dts case.)
+ *
+ *  The block flags VALID_DTS and VALID_PTS are used to signify that
+ *  a timestamp is valid for a block_t, ie, allows 0 and -ve values as
+ *  valid times.  It would be nice if these semantics made their way
+ *  outside of this module one day.
+ *
+ *  NON_DATED is used to show a block should not have a time stamp
+ *  associated (ie, don't interpolate a counter).  At the ouput, these
+ *  blocks get dated with the last used timestamp (or are merged with
+ *  another encapsulation unit).
+ *
+ *  DIRAC_BLK is used by the raw dirac demuxer to enable use of the
+ *  VALID_DTS|PTS flags.
+ */
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+#include <vlc_block.h>
+
+#include "vlc_bits.h"
+#include "vlc_block_helper.h"
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int  Open ( vlc_object_t * );
+static void Close( vlc_object_t * );
+
+vlc_module_begin()
+    set_category( CAT_SOUT )
+    set_subcategory( SUBCAT_SOUT_PACKETIZER )
+    set_description( N_("Dirac packetizer") )
+    set_capability( "packetizer", 50 )
+    set_callbacks( Open, Close )
+vlc_module_end()
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+struct decoder_sys_t
+{
+    /* sync state */
+    block_bytestream_t bytestream;
+    int i_state;
+    size_t i_offset;
+    uint32_t u_last_npo;
+    /* recovered timestamp from bytesteram for use
+     * by synchroniser: should only get reset by the
+     * synchronizer upon a discontinuity sentinel */
+    mtime_t i_sync_pts;
+    mtime_t i_sync_dts;
+    int i_sync_flags;
+
+    /* build encapsulation unit state */
+    block_t *p_eu; /*< Current encapsulation unit being built */
+    block_t **pp_eu_last;
+
+    /* timestamp generator state */
+    date_t dts; /*< timegen decode clock, increments at picture rate */
+    bool b_dts; /*< timegen decode clock valid */
+
+    bool b_pts; /*< timegen presentation time valid */
+    mtime_t i_pts; /*< timegen presentation time of picture u_pts_picnum */
+    uint32_t u_pts_picnum; /*< picture number of timegen presentation time */
+
+    mtime_t i_pts_offset; /*< maximum time between pts and dts */
+
+    /* p_outqueue is the list of encapsulation units that have been
+     * fed to the timegenerator.  the timegenerator stamps them in
+     * the order it solves the time.  the main packetizer loop removes
+     * completed encapsulation units from the front */
+    block_t *p_outqueue;
+    block_t **pp_outqueue_last;
+    /* p_out_dts points to an element in p_outqueue.  It is used for VLC's
+     * fake pts hidden in DTS hack, as used by AVI */
+    block_t *p_out_dts;
+
+    uint32_t u_tg_last_picnum; /*< most recent picturenumber output from RoB */
+    bool b_tg_last_picnum; /*< u_tg_last_picnum valid */
+
+    struct dirac_reorder_buffer {
+        int u_size_max;
+        int u_size;
+        struct dirac_reorder_entry {
+            struct dirac_reorder_entry *p_next;
+            block_t *p_eu;
+            uint32_t u_picnum;
+        } p_entries[32], *p_head, *p_empty;
+    } reorder_buf; /*< reorder buffer, used by timegenerator */
+
+    /* packetizer state */
+    mtime_t i_pts_last_out; /*< last output [from packetizer] pts */
+    mtime_t i_dts_last_out; /*< last output [from packetizer] dts */
+
+    struct seq_hdr_t {
+        uint32_t u_width;
+        uint32_t u_height;
+        uint32_t u_fps_num;
+        uint32_t u_fps_den;
+        enum picture_coding_mode_t {
+            DIRAC_FRAME_CODING=0,
+            DIRAC_FIELD_CODING=1,
+        } u_picture_coding_mode;
+    } seq_hdr; /*< sequence header */
+    bool b_seen_seq_hdr; /* sequence header valid */
+};
+
+typedef struct {
+    uint32_t u_next_offset;
+    uint32_t u_prev_offset;
+    int i_parse_code;
+} parse_info_t;
+
+enum {
+    NOT_SYNCED=0,
+    TRY_SYNC,
+    SYNCED,
+    SYNCED_INCOMPLETEDU,
+};
+
+enum {
+    VALID_PTS = 0x01000000,
+    VALID_DTS = 0x02000000,
+    NON_DATED = 0x04000000,
+    DIRAC_BLK = 0x80000000,
+};
+
+static void dirac_ReorderInit( struct dirac_reorder_buffer *p_rb );
+static block_t *dirac_DoSync( decoder_t *p_dec );
+static block_t *dirac_BuildEncapsulationUnit( decoder_t *p_dec, block_t *p_block );
+static int dirac_TimeGenPush( decoder_t *p_dec, block_t *p_block_in );
+static int dirac_FindEOS( block_t *p_block );
+static bool dirac_UnpackSeqHdr( struct seq_hdr_t *p_sh, block_t *p_block );
+static block_t *Packetize( decoder_t *p_dec, block_t **pp_block );
+static block_t *dirac_EmitEOS( decoder_t *p_dec, uint32_t i_prev_parse_offset );
+static int dirac_FindPictureNumber( uint32_t *pu_picnum, block_t *p_block );
+
+/*****************************************************************************
+ * Open: probe the packetizer and return score
+ *****************************************************************************
+ * Tries to launch a decoder and return score so that the interface is able
+ * to choose.
+ *****************************************************************************/
+static int Open( vlc_object_t *p_this )
+{
+    decoder_t     *p_dec = (decoder_t*)p_this;
+    decoder_sys_t *p_sys;
+
+    if( p_dec->fmt_in.i_codec !=  VLC_FOURCC( 'd','r','a','c' ) )
+        return VLC_EGENERIC;
+
+    p_dec->pf_packetize = Packetize;
+
+    /* Create the output format */
+    es_format_Copy( &p_dec->fmt_out, &p_dec->fmt_in );
+    p_dec->p_sys = p_sys = calloc( 1, sizeof( decoder_sys_t ) );
+
+    if( !p_sys )
+        return VLC_ENOMEM;
+
+    p_sys->i_state = NOT_SYNCED;
+    p_sys->bytestream = block_BytestreamInit();
+
+    p_sys->pp_outqueue_last = &p_sys->p_outqueue;
+    p_sys->pp_eu_last = &p_sys->p_eu;
+
+    date_Init( &p_sys->dts, 1, 1 );
+    dirac_ReorderInit( &p_sys->reorder_buf );
+
+    if( p_dec->fmt_in.i_extra > 0 )
+    {
+        /* handle hacky systems like ogg that dump some headers
+         * in p_extra. and packetizers that expect it to be filled
+         * in before real startup */
+        block_t *p_init = block_New( p_dec, p_dec->fmt_in.i_extra );
+        if( !p_init )
+        {
+            /* memory might be avaliable soon.  it isn't the end of
+             * the world that fmt_in.i_extra isn't handled */
+            return VLC_SUCCESS;
+        }
+        memcpy( p_init->p_buffer, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra );
+        p_init->p_next = dirac_EmitEOS( p_dec, 0 );
+
+        block_t *p_block;
+        while( ( p_block = Packetize( p_dec, &p_init ) ) )
+            block_Release( p_block );
+    }
+
+    return VLC_SUCCESS;
+}
+
+/*****************************************************************************
+ * Close:
+ *****************************************************************************/
+static void Close( vlc_object_t *p_this )
+{
+    decoder_t     *p_dec = (decoder_t*)p_this;
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( !p_sys )
+        return;
+
+    block_BytestreamRelease( &p_sys->bytestream );
+    if( p_sys->p_outqueue )
+        block_ChainRelease( p_sys->p_outqueue );
+    if( p_sys->p_eu )
+        block_ChainRelease( p_sys->p_eu );
+    free( p_sys );
+}
+
+/*****************************************************************************
+ * Packetize: packetize an access unit
+ *****************************************************************************/
+static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    block_t *p_block = NULL;
+    int i_flushing = 0;
+
+    if( pp_block && *pp_block )
+    {
+        /* fixup flags for our use, unless the dirac block
+         * flag is set */
+        if( !((*pp_block)->i_flags & DIRAC_BLK) )
+        {
+            if( (*pp_block)->i_dts )
+                (*pp_block)->i_flags |= VALID_DTS;
+            if( (*pp_block)->i_pts )
+                (*pp_block)->i_flags |= VALID_PTS;
+        }
+        /* NB, *pp_block may be a discontinuity sentinal, this is handled
+         * in the sync code */
+        block_BytestreamPush( &p_sys->bytestream, *pp_block );
+        *pp_block = NULL;
+    }
+
+    /* form as many encapsulation units as possible, give up
+     * when the syncronizer runs out of input data */
+    while( ( p_block = dirac_DoSync( p_dec ) ) )
+    {
+        p_block = dirac_BuildEncapsulationUnit( p_dec, p_block );
+        if( !p_block )
+            continue;
+        /* insert encapsulation unit into timestamp generator
+         * which then calculates some timestamps if required */
+        i_flushing = dirac_TimeGenPush( p_dec, p_block );
+        if( i_flushing )
+            break;
+    }
+
+    block_t *p_output = NULL;
+    block_t **pp_output = &p_output;
+
+    /* extract all the dated packets from the head of the ouput queue */
+    /* explicitly nondated packets repeat the previous timestamps to
+     * stop vlc discarding them */
+    while( (p_block = p_sys->p_outqueue) )
+    {
+        if( i_flushing || p_block->i_flags & NON_DATED )
+        {
+            p_block->i_dts = p_sys->i_dts_last_out;
+            p_block->i_pts = p_sys->i_pts_last_out;
+        }
+        else if( (p_block->i_flags & (VALID_DTS | VALID_PTS)) != (VALID_DTS | VALID_PTS) )
+            break;
+
+        p_sys->i_dts_last_out = p_block->i_dts;
+        p_sys->i_pts_last_out = p_block->i_pts;
+
+        p_sys->p_outqueue = p_block->p_next;
+        if( ! p_sys->p_outqueue )
+            p_sys->pp_outqueue_last = &p_sys->p_outqueue;
+
+        p_block->p_next = NULL;
+        /* clear any flags we set */
+        p_block->i_flags &= ~BLOCK_FLAG_PRIVATE_MASK;
+        block_ChainLastAppend( &pp_output, p_block );
+
+        mtime_t i_delay = p_block->i_pts - p_block->i_dts;
+        if( i_delay < 0 )
+            msg_Err( p_dec, "pts - dts is negative(%"PRId64"): incorrect RoB size", i_delay );
+    }
+
+    if( i_flushing )
+    {
+        /* reset timegen state (except synchronizer) */
+        p_sys->b_seen_seq_hdr = false;
+        if( i_flushing < 2 )
+        {
+            /* this state isn't safe to loose if there was
+             * an unsignalled discontinuity */
+            p_sys->b_pts = p_sys->b_dts = false;
+        }
+        p_sys->b_tg_last_picnum = false;
+        dirac_ReorderInit( &p_sys->reorder_buf );
+
+        assert( p_sys->p_outqueue == NULL );
+        p_sys->pp_outqueue_last = &p_sys->p_outqueue;
+        p_sys->p_out_dts = NULL;
+    }
+
+    /* perform sanity check:
+     *  if there were a block at the front of outqueue that never
+     *  satisfied the extraction criteria, but all blocks after did,
+     *  the output queue would grow bounded by the stream length.
+     * If there are 10 data units in the output queue, assume this
+     * has happened and purge all blocks that fail extraction criteria */
+    unsigned count = 0;
+    for( p_block = p_sys->p_outqueue; p_block; p_block = p_block->p_next )
+    {
+        count++;
+    }
+    if( count > 9 )
+    {
+        p_block = p_sys->p_outqueue;
+        while( p_block )
+        {
+            block_t *p_block_next = p_block->p_next;
+            if( (p_block->i_flags & (VALID_DTS | VALID_PTS)) == (VALID_DTS | VALID_PTS) )
+                break;
+            block_Release( p_block );
+            p_sys->p_outqueue = p_block = p_block_next;
+        }
+        if( !p_sys->p_outqueue )
+        {
+            p_sys->pp_outqueue_last = &p_sys->p_outqueue;
+        }
+    }
+
+    return p_output;
+}
+
+/* assumes u_pos is parse_info aligned */
+static int dirac_FindByMask( uint32_t *pu_pos, block_t *p_block, int parse_code_mask, int parse_code_value )
+{
+    uint32_t u_pos = pu_pos ? *pu_pos : 0;
+    /* protect against falling off the edge: parseinfo = 13bytes */
+    while( u_pos + 13 <= p_block->i_buffer )
+    {
+        /* find the picture startcode */
+        if( (p_block->p_buffer[u_pos+4] & parse_code_mask) == parse_code_value )
+        {
+            if( pu_pos )
+                *pu_pos = u_pos;
+            return 1;
+        }
+
+        /* skip to the next dirac data unit */
+        uint32_t u_npo = GetDWBE( p_block->p_buffer + u_pos + 5 );
+        if( u_npo > UINT32_MAX - u_pos )
+            return 0;
+        if( u_npo == 0 )
+            u_npo = 13;
+        u_pos += u_npo;
+    }
+    return 0;
+}
+
+/* Attempt to find dirac EOS data unit in an encapsulation unit.
+ * this assumes the block is aligned to a parse_info_prefix.
+ */
+static int dirac_FindEOS( block_t *p_block )
+{
+    return dirac_FindByMask( NULL, p_block, 0xff, 0x10 );
+}
+
+/* Attempt to find dirac picture number in an encapsulation unit.
+ * this assumes the block is aligned to a parse_info_prefix.
+ */
+static int dirac_FindPictureNumber( uint32_t *pu_picnum, block_t *p_block )
+{
+    uint32_t u_pos = 0;
+    if( !dirac_FindByMask( &u_pos, p_block, 0x08, 0x08 ) )
+        return 0;
+    /* u_pos points to start of parse_info structure (13 bytes), next four
+     * bytes comprise the picture number */
+    if( u_pos + 17 >= p_block->i_buffer )
+        return 0;
+    *pu_picnum = GetDWBE( p_block->p_buffer + u_pos + 13 );
+    return 1;
+}
+
+static void dirac_ReorderInit( struct dirac_reorder_buffer *p_rb )
+{
+    memset( p_rb, 0, sizeof(*p_rb) );
+    p_rb->u_size_max = 2;
+    p_rb->p_empty = p_rb->p_entries;
+    p_rb->p_entries[31].p_next = NULL;
+
+    for( int i = 0; i < 31; i++ ) {
+        p_rb->p_entries[i].p_next = &p_rb->p_entries[i+1];
+    }
+}
+
+static inline bool dirac_PictureNbeforeM( uint32_t u_n, uint32_t u_m )
+{
+    /* specified as: u_n occurs before u_m if:
+     *   (u_m - u_n) mod (1<<32) < D */
+    return (uint32_t)(u_m - u_n) < (1u<<31);
+}
+
+/* simulate the dirac picture reorder buffer */
+static block_t *dirac_Reorder( decoder_t *p_dec, block_t *p_block_in, uint32_t u_picnum )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    if( !p_sys->reorder_buf.u_size_max )
+        /* reorder buffer disabled */
+        return p_block_in;
+
+    /* Modeling the reorder buffer:
+     * 1. If the reorder buffer is not full, insert picture for reordering.
+     *    No picture is output by the system this picture period
+     * 2. If the reorder buffer is full:
+     *    a. The picture decoded this period (u_picnum) bypasses the reorder
+     *       buffer if it has a lower picture number than any entry in the
+     *       reorder buffer. This picture is output by the system.
+     *    b. Otherwise, the lowest picture number in the reorder buffer is
+     *       removed from the buffer and output by the system.  The current
+     *       decoded picture (u_picnum) is inserted into the reorder buffer
+     */
+
+    block_t *p_block = NULL;
+    /* Determine if the picture needs to be inserted */
+    if( p_sys->reorder_buf.u_size == p_sys->reorder_buf.u_size_max )
+    {
+        /* (2) reorder buffer is full */
+        if( !p_sys->reorder_buf.u_size_max ||
+            dirac_PictureNbeforeM( u_picnum, p_sys->reorder_buf.p_head->u_picnum ) )
+        {
+            /* (2a) current picture is first in order */
+            return p_block_in;
+        }
+
+        /* (2b) extract the youngest picture in the buffer */
+        p_block = p_sys->reorder_buf.p_head->p_eu;
+
+        struct dirac_reorder_entry *p_tmp = p_sys->reorder_buf.p_head;
+        p_sys->reorder_buf.p_head = p_tmp->p_next;
+        p_tmp->p_next = p_sys->reorder_buf.p_empty;
+        p_sys->reorder_buf.p_empty = p_tmp;
+
+        p_sys->reorder_buf.u_size--;
+    }
+
+    /* (1) and (2b) both require u_picnum to be inserted */
+    struct dirac_reorder_entry *p_current = p_sys->reorder_buf.p_empty;
+    p_sys->reorder_buf.p_empty = p_current->p_next;
+    p_sys->reorder_buf.u_size++;
+
+    /* insertion sort to keep p_head always sorted, earliest first */
+    struct dirac_reorder_entry **pp_at = &p_sys->reorder_buf.p_head;
+    for( ; *pp_at; pp_at = &(*pp_at)->p_next )
+        if( dirac_PictureNbeforeM( u_picnum, (*pp_at)->u_picnum ) )
+            break;
+
+    p_current->u_picnum = u_picnum;
+    p_current->p_eu = p_block_in;
+    p_current->p_next = *pp_at;
+    *pp_at = p_current;
+
+    return p_block;
+}
+
+static mtime_t date_Decrement( date_t *p_date, uint32_t i_nb_samples )
+{
+    mtime_t i_dividend = (mtime_t)i_nb_samples * 1000000 * p_date->i_divider_den;
+    p_date->date -= i_dividend / p_date->i_divider_num;
+    unsigned u_rem_adjust = i_dividend % p_date->i_divider_num;
+
+    if( p_date->i_remainder < u_rem_adjust )
+    {
+        /* This is Bresenham algorithm. */
+        assert( p_date->i_remainder > -p_date->i_divider_num);
+        p_date->date -= 1;
+        p_date->i_remainder += p_date->i_divider_num;
+    }
+
+    p_date->i_remainder -= u_rem_adjust;
+
+    return p_date->date;
+}
+
+/**
+ * given a chain of block_t, allocate and return an array containing
+ * pointers to all the blocks. (Acts as a replacement for the old p_prev
+ * member of block_t) */
+static int block_ChainToArray( block_t *p_block, block_t ***ppp_array)
+{
+    if( !ppp_array )
+        return 0;
+
+    int num_blocks;
+    block_ChainProperties( p_block, &num_blocks, NULL, NULL );
+
+    *ppp_array = malloc( sizeof( block_t* ) * num_blocks );
+    if( !ppp_array ) return 0;
+
+    for( int i = 0; i < num_blocks; i++ )
+    {
+        (*ppp_array)[i] = p_block;
+        p_block = p_block->p_next;
+    }
+
+    return num_blocks;
+}
+
+/**
+ * dirac_TimeGenPush:
+ * @p_dec: vlc object
+ * @p_block_in: whole encapsulation unit to generate timestamps for
+ *
+ * Returns:
+ *  0: everything ok
+ *  1: EOS occured, please flush and reset
+ *  2: picture number discontinuity, please flush and reset
+ */
+static int dirac_TimeGenPush( decoder_t *p_dec, block_t *p_block_in )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    /* add to tail of output queue (ie, not reordered) */
+    block_ChainLastAppend( &p_sys->pp_outqueue_last, p_block_in );
+
+    if( dirac_FindEOS( p_block_in ) )
+    {
+        /* NB, this test occurs after the timegen push, so as to
+         * push the block into the output queue */
+        return 1;
+    }
+
+    uint32_t u_picnum;
+    if( !dirac_FindPictureNumber( &u_picnum, p_block_in ) )
+    {
+        /* no picture found, which means p_block_in is a non-dated EU,
+         * do not try and put a date on it */
+        p_block_in->i_flags |= NON_DATED;
+        return 0;
+    }
+
+    /*
+     * Simple DTS regeneration:
+     *  - DTS values linearly increase in stream order.
+     *  - Every time a DTS occurs at the input, sync to it
+     *    - If this is the first DTS seen, backdate all the previous ones that are undated
+     *  - If a DTS is missing, guess that it increases by one picture period
+     *  - If never seen DTS, don't do anything
+     */
+    if( p_block_in->i_flags & VALID_DTS )
+    {
+        /* if timestamps exist, sync to them */
+        date_Set( &p_sys->dts, p_block_in->i_dts );
+        if( !p_sys->b_dts )
+        {
+            p_sys->b_dts = true;
+            date_t dts = p_sys->dts;
+            /* Transverse p_sys->p_outqueue backwards.  (no p_prev anymore) */
+            block_t **pp_array = NULL;
+            int n = block_ChainToArray( p_sys->p_outqueue, &pp_array );
+            while( n ) if( pp_array[--n] == p_block_in ) break;
+            /* want to start at p_block_in->p_prev */
+            while( n-- )
+            {
+                if( pp_array[n]->i_flags & NON_DATED )
+                    continue;
+                pp_array[n]->i_dts = date_Decrement( &dts, 1 );
+                pp_array[n]->i_flags |= VALID_DTS;
+            }
+            free( pp_array );
+        }
+    }
+    else if( p_sys->b_dts )
+    {
+        /* dts has previously been seen, but not this time, interpolate */
+        p_block_in->i_dts = date_Increment( &p_sys->dts, 1 );
+        p_block_in->i_flags |= VALID_DTS;
+    }
+
+    /*
+     * Simple PTS regeneration
+     *  - PTS values do not linearly increase in stream order.
+     *  - Every time a PTS occurs at the input, sync to it and record picture number
+     *  - If a PTS is missing, guess that it differs by the product of picture
+     *    period and difference between picture number of sync point and current picture
+     *
+     *  - If this is the first PTS seen, backdate all previous ones that are undated
+     *  - If never seen PTS, don't do anything
+     */
+    if( p_block_in->i_flags & VALID_PTS )
+    {
+        /* if timestamps exist, sync to them */
+        p_sys->u_pts_picnum = u_picnum;
+        p_sys->i_pts = p_block_in->i_pts;
+        if( !p_sys->b_pts )
+        {
+            p_sys->b_pts = true;
+            /* Transverse p_sys->p_outqueue backwards.  (no p_prev anymore) */
+            block_t **pp_array = NULL;
+            int n = block_ChainToArray( p_sys->p_outqueue, &pp_array );
+            while( n ) if( pp_array[--n] == p_block_in ) break;
+            /* want to start at p_block_in->p_prev */
+            while( n-- )
+            {
+                uint32_t u_picnum;
+                if( !dirac_FindPictureNumber( &u_picnum, pp_array[n] ) )
+                    continue;
+                int32_t i_dist = u_picnum - p_sys->u_pts_picnum;
+                pp_array[n]->i_pts = p_sys->i_pts + i_dist * 1000000ll
+                                   * p_dec->fmt_out.video.i_frame_rate_base
+                                   / p_dec->fmt_out.video.i_frame_rate;
+                pp_array[n]->i_flags |= VALID_PTS;
+            }
+            free( pp_array );
+        }
+    }
+    else if( p_sys->b_pts )
+    {
+        /* pts has previously been seen, but not this time, interpolate */
+        int32_t i_dist = u_picnum - p_sys->u_pts_picnum;
+        p_block_in->i_pts = p_sys->i_pts + i_dist * 1000000ll
+                          * p_dec->fmt_out.video.i_frame_rate_base
+                          / p_dec->fmt_out.video.i_frame_rate;
+        p_block_in->i_flags |= VALID_PTS;
+    }
+
+    /* If pts and dts have been seen, there is no need to simulate operation
+     * of the decoder reorder buffer */
+    /* If neither have been seen, there is little point in simulating */
+    if( p_sys->b_dts == p_sys->b_pts )
+        return 0;
+
+    if( !p_sys->p_out_dts )
+        p_sys->p_out_dts = p_sys->p_outqueue;
+
+    /* model the reorder buffer */
+    block_t *p_block = dirac_Reorder( p_dec, p_block_in, u_picnum );
+    if( !p_block )
+        return 0;
+
+    /* A future ehancement is to stop modeling the reorder buffer as soon as
+     * the first packet is output -- interpolate the past and freewheel for
+     * the future */
+
+    dirac_FindPictureNumber( &u_picnum, p_block );
+    if( p_sys->b_tg_last_picnum )
+    {
+        if( dirac_PictureNbeforeM( u_picnum, p_sys->u_tg_last_picnum ) )
+        {
+            msg_Warn( p_dec, "stream jumped? %d < %d: resetting"
+                    , u_picnum, p_sys->u_tg_last_picnum );
+            /* pictures only emerge from the reorder buffer in sequence
+             * if a stream suddenly jumped backwards without a signaling
+             * a discontinuity, some pictures will get stuck in the RoB.
+             * flush the RoB. */
+            /* this could be a bit less indiscriminate */
+            block_ChainRelease( p_sys->p_outqueue );
+            p_sys->p_outqueue = dirac_EmitEOS( p_dec, 0 );
+            if( p_sys->p_outqueue )
+                p_sys->p_outqueue->i_flags = BLOCK_FLAG_DISCONTINUITY | NON_DATED;
+            /* return 2, so as not to reset the b_dts flags -- needed if
+             * using the rawdirac demuxer with broken stream */
+            return 2;
+        }
+    }
+    p_sys->b_tg_last_picnum = true;
+    p_sys->u_tg_last_picnum = u_picnum;
+
+    if( !p_sys->b_pts )
+    {
+        /* some demuxers (eg, AVI) will provide a series of fake dts values,
+         * which are actually inorder pts values (ie, what should be seen at
+         * the output of a decoder.  A main reason for simulating the reorder
+         * buffer is to turn the inorder fakedts into an out-of-order pts */
+        p_block->i_pts = p_sys->p_out_dts->i_dts;
+        p_block->i_flags |= VALID_PTS;
+        p_sys->p_out_dts->i_flags &= ~VALID_DTS;
+    }
+
+    /* If pts was copied from dts, the dts needs to be corrected to account for reordering*/
+    /* If dts has never been seen, the same needs to happen */
+    p_sys->p_out_dts->i_dts = p_block->i_pts - p_sys->i_pts_offset;
+    p_sys->p_out_dts->i_flags |= VALID_DTS;
+
+    /* move dts pointer */
+    if( p_sys->p_out_dts )
+        p_sys->p_out_dts = p_sys->p_out_dts->p_next;
+
+    return 0;
+}
+
+static block_t *dirac_BuildEncapsulationUnit( decoder_t *p_dec, block_t *p_block )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+
+    block_ChainLastAppend( &p_sys->pp_eu_last, p_block );
+
+    if(!(p_sys->p_eu->i_flags & (VALID_DTS|VALID_PTS)))
+    {
+        /* earliest block with pts/dts gets to set the pts/dts for the
+         * encapsulation unit as a whole */
+        if(p_block->i_flags & (VALID_DTS|VALID_PTS))
+        {
+            p_sys->p_eu->i_flags |= p_block->i_flags;
+            p_sys->p_eu->i_pts = p_block->i_pts;
+            p_sys->p_eu->i_dts = p_block->i_dts;
+        }
+    }
+
+    if( 0x08 & p_block->p_buffer[4] )
+    {
+        /* p_block is a picture -- it ends the 'encapsulation unit' */
+        if( 0x3 & p_block->p_buffer[4] )
+        {
+            /* if this picture is not an I frame, ensure that the
+             * random access point flags are not set */
+            p_sys->p_eu->i_flags &= ~BLOCK_FLAG_TYPE_I;
+        }
+    }
+    else if( 0x10 == p_block->p_buffer[4] )
+    {
+        /* p_block is an EOS packet */
+        /* for the moment, let this end an encapsulation unit */
+        /* seeing an eos packet requires a flush of the packetizer
+         * this is detected by the caller of this function */
+        p_sys->b_seen_seq_hdr = false;
+#if 0
+        /* let anything down streem know too */
+        /*
+        Actually, this is a bad idea:
+         - It sets the discontinuity for every dirac EOS packet
+           which doesnt imply a time discontinuity.
+         - When the syncronizer detects a real discontinuity, it
+           should copy the flags through.
+        p_sys->p_eu->i_flags |= BLOCK_FLAG_DISCONTINUITY;
+        */
+#endif
+    }
+    else if( 0 == p_block->p_buffer[4] )
+    {
+        if( !dirac_UnpackSeqHdr( &p_sys->seq_hdr, p_block ) )
+        {
+            /* couldn't parse the sequence header */
+            return NULL;
+        }
+        p_sys->b_seen_seq_hdr = true;
+
+       /* a sequence header followed by an I frame is a random
+        * access point; assume that this is the case */
+        p_sys->p_eu->i_flags |= BLOCK_FLAG_TYPE_I;
+
+        es_format_t *p_es = &p_dec->fmt_out;
+
+        p_es->video.i_width  = p_sys->seq_hdr.u_width;
+        p_es->video.i_height = p_sys->seq_hdr.u_height;
+
+        vlc_ureduce( &p_es->video.i_frame_rate, &p_es->video.i_frame_rate_base
+                   , p_sys->seq_hdr.u_fps_num, p_sys->seq_hdr.u_fps_den, 0 );
+
+        /* when field coding, dts needs to be incremented in terms of field periods */
+        int u_pics_per_sec = p_sys->seq_hdr.u_fps_num;
+        if (p_sys->seq_hdr.u_picture_coding_mode == DIRAC_FIELD_CODING)
+        {
+            u_pics_per_sec *= 2;
+        }
+        date_Change( &p_sys->dts, u_pics_per_sec, p_sys->seq_hdr.u_fps_den );
+
+        /* TODO: set p_sys->reorder_buf.u_size_max */
+        p_sys->i_pts_offset = p_sys->reorder_buf.u_size_max
+                            * 1000000
+                            * p_es->video.i_frame_rate_base / p_es->video.i_frame_rate + 1;
+
+        /* stash a copy of the seqhdr
+         *  - required for ogg muxing
+         *  - useful for error checking
+         *  - it isn't allowed to change until an eos */
+        if( p_es->p_extra )
+            free( p_es->p_extra );
+        p_es->p_extra = calloc( 1, p_block->i_buffer + 13 );
+        if( !p_es->p_extra )
+        {
+            p_es->i_extra = 0;
+            return NULL;
+        }
+        p_es->i_extra = p_block->i_buffer;
+        memcpy( p_es->p_extra, p_block->p_buffer, p_block->i_buffer );
+        /* append EOS as per Ogg guidelines */
+        p_block = dirac_EmitEOS( p_dec, p_block->i_buffer );
+        if( p_block )
+        {
+            memcpy( p_es->p_extra + p_es->i_extra, p_block->p_buffer, 13 );
+            p_es->i_extra += 13;
+        }
+
+        return NULL;
+    }
+    else {
+        /* doesn't end an encapsulation unit */
+        return NULL;
+    }
+
+    if( p_sys->b_seen_seq_hdr )
+    {
+        /* gather up the encapsulation unit */
+        p_block = block_ChainGather( p_sys->p_eu );
+    }
+    else {
+        /* encapsulation unit has ended, but we can't timestamp it reliably */
+        block_ChainRelease( p_sys->p_eu );
+        p_block = NULL;
+    }
+    p_sys->p_eu = NULL;
+    p_sys->pp_eu_last = &p_sys->p_eu;
+    return p_block;
+}
+
+static bool dirac_UnpackParseInfo( parse_info_t *p_pi, block_bytestream_t *p_bs,
+                                   int i_offset )
+{
+    if( i_offset < 0 )
+        return false;
+
+    uint8_t p_d[13];
+    if( VLC_SUCCESS != block_PeekOffsetBytes( p_bs, i_offset, p_d, 13 ) )
+        return false;
+
+    if( p_d[0] != 'B' || p_d[1] != 'B' || p_d[2] != 'C' || p_d[3] != 'D' )
+        return false;
+
+    p_pi->i_parse_code = p_d[4];
+    p_pi->u_next_offset = p_d[5] << 24 | p_d[6] << 16 | p_d[7] << 8 | p_d[8];
+    p_pi->u_prev_offset = p_d[9] << 24 | p_d[10] << 16 | p_d[11] << 8 | p_d[12];
+    return true;
+}
+
+static uint32_t dirac_uint( bs_t *p_bs )
+{
+  uint32_t count = 0, value = 0;
+  while( !bs_eof( p_bs ) && !bs_read( p_bs, 1 ) )
+  {
+    count++;
+    value <<= 1;
+    value |= bs_read( p_bs, 1 );
+  }
+
+  return (1 << count) - 1 + value;
+}
+
+static int dirac_bool( bs_t *p_bs )
+{
+    return bs_read( p_bs, 1 );
+}
+
+/* read in useful bits from sequence header */
+static bool dirac_UnpackSeqHdr( struct seq_hdr_t *p_sh, block_t *p_block )
+{
+    bs_t bs;
+    bs_init( &bs, p_block->p_buffer, p_block->i_buffer );
+    bs_skip( &bs, 13*8 ); /* parse_info_header */
+    dirac_uint( &bs ); /* major_version */
+    dirac_uint( &bs ); /* minor_version */
+    dirac_uint( &bs ); /* profile */
+    dirac_uint( &bs ); /* level */
+
+    uint32_t u_video_format = dirac_uint( &bs ); /* index */
+    if( u_video_format > 20 )
+    {
+        /* dont know how to parse this header */
+        return false;
+    }
+
+    static const struct {
+        uint32_t u_w, u_h;
+    } dirac_size_tbl[] = {
+        {640,480}, {176,120}, {176,144}, {352,240}, {352,288}, {704,480},
+        {704,576}, {720,480}, {720,576}, {1280,720}, {1280,720}, {1920,1080},
+        {1920,1080}, {1920,1080}, {1920,1080}, {2048,1080}, {4096,2160},
+        {3840,2160}, {3840,2160}, {7680,4320}, {7680,4320},
+    };
+
+    p_sh->u_width = dirac_size_tbl[u_video_format].u_w;
+    p_sh->u_height = dirac_size_tbl[u_video_format].u_h;
+    if( dirac_bool( &bs ) )
+    {
+        p_sh->u_width = dirac_uint( &bs ); /* frame_width */
+        p_sh->u_height = dirac_uint( &bs ); /* frame_height */
+    }
+
+    if( dirac_bool( &bs ) )
+    {
+        dirac_uint( &bs ); /* chroma_format */
+    }
+
+    if( dirac_bool( &bs ) )
+    {
+        dirac_uint( &bs ); /* scan_format */
+    }
+
+    static const struct {
+        uint32_t u_n /* numerator */, u_d /* denominator */;
+    } dirac_frate_tbl[] = { /* table 10.3 */
+        {1, 1}, /* this value is not used */
+        {24000,1001}, {24,1}, {25,1}, {30000,1001}, {30,1},
+        {50,1}, {60000,1001}, {60,1}, {15000,1001}, {25,2},
+    };
+
+    const unsigned dirac_frate_tbl_size =
+        sizeof( dirac_frate_tbl ) / sizeof( *dirac_frate_tbl );
+
+    static const uint32_t dirac_vidfmt_frate[] = { /* table C.1 */
+        1, 9, 10, 9, 10, 9, 10, 4, 3, 7, 6, 4, 3, 7, 6, 2, 2, 7, 6, 7, 6,
+    };
+
+    p_sh->u_fps_num = dirac_frate_tbl[dirac_vidfmt_frate[u_video_format]].u_n;
+    p_sh->u_fps_den = dirac_frate_tbl[dirac_vidfmt_frate[u_video_format]].u_d;
+    if( dirac_bool( &bs ) )
+    {
+        uint32_t frame_rate_index = dirac_uint( &bs );
+        p_sh->u_fps_num = dirac_frate_tbl[frame_rate_index].u_n;
+        p_sh->u_fps_den = dirac_frate_tbl[frame_rate_index].u_d;
+        if( frame_rate_index >= dirac_frate_tbl_size )
+        {
+            /* invalid header */
+            return false;
+        }
+        if( frame_rate_index == 0 )
+        {
+            p_sh->u_fps_num = dirac_uint( &bs ); /* frame_rate_numerator */
+            p_sh->u_fps_den = dirac_uint( &bs ); /* frame_rate_denominator */
+        }
+    }
+
+    /* must have a valid framerate */
+    if( !p_sh->u_fps_num || !p_sh->u_fps_den )
+        return false;
+
+    if( dirac_bool( &bs ) )
+    {
+        uint32_t par_index = dirac_uint( &bs );
+        if( !par_index )
+        {
+            dirac_uint( &bs ); /* par_num */
+            dirac_uint( &bs ); /* par_den */
+        }
+    }
+
+    if( dirac_bool( &bs ) )
+    {
+        dirac_uint( &bs ); /* clean_width */
+        dirac_uint( &bs ); /* clean_height */
+        dirac_uint( &bs ); /* clean_left_offset */
+        dirac_uint( &bs ); /* clean_top_offset */
+    }
+
+    if( dirac_bool( &bs ) )
+    {
+        uint32_t signal_range_index = dirac_uint( &bs );
+        if( !signal_range_index )
+        {
+            dirac_uint( &bs ); /* luma_offset */
+            dirac_uint( &bs ); /* luma_excursion */
+            dirac_uint( &bs ); /* chroma_offset */
+            dirac_uint( &bs ); /* chroma_excursion */
+        }
+    }
+
+    if( dirac_bool( &bs ) )
+    {
+        uint32_t colour_spec_index = dirac_uint( &bs );
+        if( !colour_spec_index )
+        {
+            if( dirac_bool( &bs ) )
+            {
+                dirac_uint( &bs ); /* colour_primaries_index */
+            }
+            if( dirac_bool( &bs ) )
+            {
+                dirac_uint( &bs ); /* colour_matrix_index */
+            }
+            if( dirac_bool( &bs ) )
+            {
+                dirac_uint( &bs ); /* transfer_function_index */
+            }
+        }
+    }
+
+    p_sh->u_picture_coding_mode = dirac_uint( &bs );
+
+    return true;
+}
+
+static block_t *dirac_EmitEOS( decoder_t *p_dec, uint32_t i_prev_parse_offset )
+{
+    const uint8_t eos[] = { 'B','B','C','D',0x10,0,0,0,13,0,0,0,0 };
+    block_t *p_block = block_New( p_dec, 13 );
+    if( !p_block )
+        return NULL;
+    memcpy( p_block->p_buffer, eos, 13 );
+
+    SetDWBE( p_block->p_buffer + 10, i_prev_parse_offset );
+
+    return p_block;
+
+    (void) p_dec;
+}
+
+/**
+ * Destructively find and recover the earliest timestamp from start of
+ * bytestream, upto i_length.
+ */
+static void dirac_RecoverTimestamps ( decoder_t *p_dec, size_t i_length )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    block_t *p_block = p_sys->bytestream.p_block;
+
+    /* Find the block with first non-flushed data */
+    size_t i_offset = p_sys->bytestream.i_offset;
+    for(; p_block != NULL; p_block = p_block->p_next )
+    {
+        if( i_offset < p_block->i_buffer )
+            break;
+        i_offset -= p_block->i_buffer;
+    }
+
+    i_offset += i_length;
+    for(; p_block != NULL; p_block = p_block->p_next )
+    {
+        if( !(p_sys->i_sync_flags & (VALID_PTS|VALID_DTS)))
+        {
+            /* oldest timestamp wins */
+            p_sys->i_sync_flags = p_block->i_flags;
+            p_sys->i_sync_pts = p_block->i_pts;
+            p_sys->i_sync_dts = p_block->i_dts;
+        }
+        /* clear timestamps -- more than one data unit can come from a block */
+        p_block->i_flags = 0;
+        if( i_offset < p_block->i_buffer )
+            break;
+        i_offset -= p_block->i_buffer;
+    }
+}
+
+static block_t *dirac_DoSync( decoder_t *p_dec )
+{
+    decoder_sys_t *p_sys = p_dec->p_sys;
+    parse_info_t pu;
+
+    static const uint8_t p_parsecode[4] = {'B','B','C','D'};
+    do {
+        switch( p_sys->i_state )
+        {
+        case NOT_SYNCED: {
+            size_t i_start = p_sys->i_offset;
+            bool b_found_prefix = VLC_SUCCESS ==
+                block_FindStartcodeFromOffset( &p_sys->bytestream, &p_sys->i_offset, p_parsecode, 4 );
+
+            /* In searching for a start code prefix, a discontinuity
+             * sentinal block may be transversed, if so, a state reset
+             * is required -- this is handled as a side effect an EOS DU */
+            uint32_t i_flags = BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED;
+            size_t i_end = p_sys->i_offset;
+            if( VLC_SUCCESS == block_FindMaskedFlags( &p_sys->bytestream, &i_flags, &i_start, &i_end ) )
+            {
+                block_SkipBytes( &p_sys->bytestream, i_end );
+                block_BytestreamFlush( &p_sys->bytestream );
+                p_sys->i_offset = 0;
+                block_t *p_block = dirac_EmitEOS( p_dec, p_sys->u_last_npo );
+                p_block->i_flags = BLOCK_FLAG_DISCONTINUITY;
+                return p_block;
+            }
+            if( !b_found_prefix )
+            {
+                /* p_sys->i_offset will have been set to:
+                 *   end of bytestream - amount of prefix found
+                 * can resume search from this point when more data arrives */
+                return NULL;
+            }
+            /* candidate parse_code_prefix has been found at p_sys->i_offset */
+            if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_offset + 12, NULL, 0 ) )
+            {
+                /* insufficient data has been accumulated to fully extract
+                 * a parse_info header. exit for now in the hope of more
+                 * data later to retry at exactly the same point */
+                return NULL;
+            }
+            /* found, fall through */
+        }
+        case TRY_SYNC: { /* -> SYNCED | NOT_SYNCED */
+            parse_info_t pu1;
+            bool a = dirac_UnpackParseInfo( &pu1, &p_sys->bytestream, p_sys->i_offset );
+            bool b = dirac_UnpackParseInfo( &pu, &p_sys->bytestream, p_sys->i_offset - pu1.u_prev_offset );
+            if( !a || !b || (pu1.u_prev_offset != pu.u_next_offset) ) {
+                p_sys->i_state = NOT_SYNCED;
+                p_sys->i_offset++;
+                break; /* find somewhere else to try again */
+            }
+            p_sys->u_last_npo = pu.u_next_offset;
+            /* offset was pointing at pu1, rewind to point at pu */
+            p_sys->i_offset -= pu.u_next_offset;
+            p_sys->i_state = SYNCED;
+            break;
+        }
+        case SYNCED: { /* -> SYNCED (|SYNCED_INCOMPLETEDU) | NOT_SYNCED */
+            if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_offset + 12, NULL, 0 ) )
+            {
+                /* insufficient data has been accumulated to fully extract
+                 * a parse_info header, retry later */
+                return NULL;
+            }
+            bool a = dirac_UnpackParseInfo( &pu, &p_sys->bytestream, p_sys->i_offset );
+            if( !a || (p_sys->u_last_npo != pu.u_prev_offset) ) {
+                p_sys->i_state = NOT_SYNCED;
+                break;
+            }
+            p_sys->u_last_npo = pu.u_next_offset;
+            p_sys->i_state = SYNCED;
+            break;
+        }
+        case SYNCED_INCOMPLETEDU: /* -> SYNCED */
+            dirac_UnpackParseInfo( &pu, &p_sys->bytestream, p_sys->i_offset );
+            p_sys->i_state = SYNCED; /* try again */
+        }
+    } while( NOT_SYNCED == p_sys->i_state );
+
+    /*
+     * synced, attempt to extract a data unit
+     */
+
+    /* fixup for case where pu.next_parse_offset = 0 (eg, EOS) */
+    if (!pu.u_next_offset) {
+        pu.u_next_offset = 13;
+    }
+
+    /* Check that a discontinuity sentinal block isn't within the data
+     * that is going to be extracted */
+    uint32_t i_flags = BLOCK_FLAG_DISCONTINUITY | BLOCK_FLAG_CORRUPTED;
+    size_t i_offset = p_sys->i_offset;
+    size_t i_end = p_sys->i_offset + pu.u_next_offset;
+    if( VLC_SUCCESS == block_FindMaskedFlags( &p_sys->bytestream, &i_flags, &i_offset, &i_end ) )
+    {
+        /* flush everything upto the end of the discontinuity */
+        block_SkipBytes( &p_sys->bytestream, i_end );
+        block_BytestreamFlush( &p_sys->bytestream );
+        p_sys->i_offset = 0;
+        p_sys->i_state = NOT_SYNCED;
+        p_sys->i_sync_flags = p_sys->i_sync_pts = p_sys->i_sync_dts = 0;
+
+        if( BLOCK_FLAG_CORRUPTED & i_flags )
+        {
+            /* the sync algorithm above, in order to reduce delay, once synced
+             * doesn't wait for the next data unit to arrive to confirm sync.
+             * As a result, it will not notice a sync error until the next
+             * data unit. */
+            /* If there was corruption, do not produce an EOS data unit as this
+             * is very disruptive to the stream (and may make a larger error).
+             * Drop the corrupt data unit and force a resync */
+            return NULL;
+        }
+
+        /* emit a dirac EOS block which causes the packetizer to reset state */
+        block_t *p_block = dirac_EmitEOS( p_dec, pu.u_prev_offset );
+        if( p_block )
+            p_block->i_flags = BLOCK_FLAG_DISCONTINUITY;
+        return p_block;
+    }
+
+    /* if we can't peek at the last byte, we don't yet have the entire DU */
+    if( VLC_SUCCESS != block_PeekOffsetBytes( &p_sys->bytestream, p_sys->i_offset + pu.u_next_offset, NULL, 0 ) )
+    {
+        p_sys->i_state = SYNCED_INCOMPLETEDU;
+        return NULL;
+    }
+
+    /* recover any timestamps from the data that is about to be flushed */
+    dirac_RecoverTimestamps( p_dec, p_sys->i_offset );
+
+    /* flush everything upto the start of the DU */
+    block_SkipBytes( &p_sys->bytestream, p_sys->i_offset );
+    block_BytestreamFlush( &p_sys->bytestream );
+    p_sys->i_offset = 0;
+
+    /* setup the data unit buffer */
+    block_t *p_block = block_New( p_dec, pu.u_next_offset );
+    if( !p_block )
+        return NULL;
+
+    p_block->i_pts = p_sys->i_sync_pts;
+    p_block->i_dts = p_sys->i_sync_dts;
+    p_block->i_flags = p_sys->i_sync_flags;
+    p_sys->i_sync_flags = p_sys->i_sync_pts = p_sys->i_sync_dts = 0;
+
+    /* recover any new timestamps from the data that is about to be consumed */
+    dirac_RecoverTimestamps( p_dec, p_sys->i_offset );
+
+    block_GetBytes( &p_sys->bytestream, p_block->p_buffer, p_block->i_buffer );
+
+    return p_block;
+}
-- 
1.5.6.5




More information about the vlc-devel mailing list