/*****************************************************************************/
/* Copyright (c) 2009 NXP B.V.                                               */
/*                                                                           */
/* This library is free software; you can redistribute it and/or             */
/* modify it under the terms of the GNU Lesser General Public                */
/* License as published by the Free Software Foundation, using               */
/* version 2.1 of the License.                                               */
/*                                                                           */
/* This library is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU          */
/* Lesser General Public License for more details.                           */
/*                                                                           */
/* You should have received a copy of the GNU Lesser General Public          */
/* License along with this library; if not, write to the Free Software       */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA   */
/*                                                                           */
/*****************************************************************************/
/*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
1   20090205    laird       First Version
2   20090313    laird       Stopping wasteful memcpys for H264 and using writev
3   20090316    laird       Handle bad timestamps.
4   20090317    laird       Handle MPEG4 DSI properly and have preroll function
5   20090319    laird       Make sure we resend DSI after a seek operation.
6   20090326    laird       Fixing more issues.
7   20090330    laird       Some AVI files dont have h264 codec_data and dont need it.
9   20090406    laird       boolean not string (Doh!)
--------------------------------------------------------------------------------
    For consistency and standardisation retain the Section Separators.
*/

/**
 * SECTION:stb22xframedvideo
 *
 * The stb22xframedvideo element takes in ES data ideally framed.
 * It then adds the proprietary meta data - See Design In Guide for details
 * This allows the video decoder to play most input media formats.
 *
 * In some cases some extra data is passed to the decoders to make sure
 * they can decode (i.e H264 DSI).
 */

/**************************************************************
* INCLUDE FILES                                               *
***************************************************************/
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>

#ifdef HAVE_CONFIG_H
    #include <config.h>
#endif
#include <gst/gst.h>
#include <gst/gstmarshal.h>
#include <gst/video/video.h>
#include "gststb22xframedvideo.h"
#include "gststb22xframedvideo_h264.h"

/**************************************************************
* LOCAL MACROS                                                *
***************************************************************/

/**
 * Default values for plugin options.
 */
#define DEFAULT_SYNC FALSE
#define DEFAULT_DUMP FALSE
#define DEFAULT_MUTE FALSE
#define DEFAULT_STATE_ERROR STB22X_FRAMED_VIDEO_STATE_ERROR_NONE
#define GST_TYPE_STB22X_FRAMED_VIDEO_STATE_ERROR (gst_stb22x_framed_video_state_error_get_type())

/* The following are taken from VC1 spec.*/
#define VC1_START     0xC5FFFFFF;
#define VC1_MARKER1   0x00000004;
#define VC1_MARKER2   0x0000000C;
/* This just marks the profile and indicates not CBR.*/
#define VC1_LEVEL_CBR 0x80000000;
/* We dont need these so we set to default values.*/
#define VC1_HRD_RATE  0x00000000;
#define VC1_FRAMERATE 0xFFFFFFFF;

/**************************************************************
* LOCAL TYPEDEFS                                              *
***************************************************************/

/**
 * Enum of available properties on this component.
 */
enum
{
    PROP_0,
    PROP_STATE_ERROR,
    PROP_DUMP,
    PROP_DUMP_ES_FILENAME,
    PROP_SBMSINKNAME,
    PROP_MUTE
};

/**************************************************************
* STATIC FUNCTION PROTOTYPES                                  *
***************************************************************/
static void gst_stb22x_framed_video_finalise (GObject * object);

static void gst_stb22x_framed_video_set_property (GObject * object,
                                                  guint prop_id,
                                                  const GValue * value,
                                                  GParamSpec * pspec);

static void gst_stb22x_framed_video_get_property (GObject * object,
                                                  guint prop_id,
                                                  GValue * value,
                                                  GParamSpec * pspec);

static GstStateChangeReturn gst_stb22x_framed_video_change_state (GstElement * element,
                                                                  GstStateChange transition);

static GstFlowReturn gst_stb22x_framed_video_preroll (GstBaseSink * bsink,
                                                      GstBuffer * buffer);

static GstFlowReturn gst_stb22x_framed_video_render (GstBaseSink * bsink,
                                                     GstBuffer * buffer);

static gboolean gst_stb22x_framed_video_event (GstBaseSink * bsink,
                                               GstEvent * event);

static gboolean gst_stb22x_framed_video_start (GstBaseSink * bsink);

static gboolean gst_stb22x_framed_video_stop (GstBaseSink * bsink);

static gboolean gst_stb22x_framed_video_plugin_init (GstPlugin * plugin);

/**************************************************************
* STATIC DATA                                                 *
***************************************************************/

/**
 * Details for the IN Pin, i.e what encoded video formats do we support?
 */
static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE("sink",
                                                                    GST_PAD_SINK,
                                                                    GST_PAD_ALWAYS,
                                                                    GST_STATIC_CAPS
                                                                    ("video/x-divx, "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ], "
                                                                     "divxversion = (int) { 3, 4, 5 }; "
                                                                     "video/x-xvid, "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ]; "
                                                                     "video/x-msmpeg, "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ], "
                                                                     "msmpegversion = (int) { 41, 42, 43 }; "
                                                                     "video/mpeg, "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ], "
                                                                     "mpegversion = (int) { 1, 2, 4}, "
                                                                     "systemstream = (boolean) FALSE; "
                                                                     "video/x-h263, "
                                                                     "h263version = (string) \"h263\", "
                                                                     "variant = (string) \"itu\", "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ]; "
                                                                     "video/x-h264, "
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ]; "
                                                                     "video/x-wmv,"
                                                                     "width = (int) [ 16, 2048 ], "
                                                                     "height = (int) [ 16, 1088 ], "
                                                                     "wmvversion = (int) { 1, 2, 3}; "
                                                                     "video/x-flash-video, "
                                                                     "width = (int) [ 16, 720 ], "
                                                                     "height = (int) [ 16, 576 ]; "
                                                                     "video/x-vp6-flash, "
                                                                     "width = (int) [ 16, 720 ], "
                                                                     "height = (int) [ 16, 576 ]; "));

/**
 * DIV3 Header that is required by video decoder before each frame.
 */
static guint8 divx_start_code[] = {'P', 'I', 'C', 'T', 'H', '3', '1', '1'};

/**
 * WMV Header that is required by video decoder before each frame.
 */
static guint8 wmv_frame_length[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

/**
 * Declare a debug category for this component
 * Enabled by doing 'export GST_DEBUG=stb22x_framed_video:x' where x=1-5.
 */
GST_DEBUG_CATEGORY (gst_stb22x_framed_video_debug);
#define GST_CAT_DEFAULT gst_stb22x_framed_video_debug

/**
 * Standard GStreamer boilerplate code generation.
 */
GST_BOILERPLATE(GstStb22xFramedVideo, gst_stb22x_framed_video, GstVideoSink, GST_TYPE_VIDEO_SINK);

/**************************************************************
* FUNCTION IMPLEMENTATION                                     *
***************************************************************/
/* Local function to deal with parital writes:
   This code comes from:
       http://inn.eyrie.org/svn/branches/2.4/lib/xwrite.c

   Copyright (c) 2004, 2005, 2006, 2007, 2008, 2009
       by Internet Systems Consortium, Inc. ("ISC")
   Copyright (c) 1991, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
       2002, 2003 by The Internet Software Consortium and Rich Salz

   This code is derived from software contributed to the Internet Software
   Consortium by Rich Salz.

   Permission to use, copy, modify, and distribute this software for any
   purpose with or without fee is hereby granted, provided that the above
   copyright notice and this permission notice appear in all copies.

   THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
   REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
   SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
ssize_t xwritev(int fd, const struct iovec iov[], int iovcnt)
{
    ssize_t total, status = 0;
    size_t left, offset;
    int iovleft, i, count;
    struct iovec *tmpiov;

    if (iovcnt == 0)
        return (0);

    /* Get a count of the total number of bytes in the iov array. */
    for (total = 0, i = 0; i < iovcnt; i++)
        total += iov[i].iov_len;

    if (total == 0)
        return (0);

    /* First, try just writing it all out.  Most of the time this will
       succeed and save us lots of work.  Abort the write if we try ten times
       with no forward progress. */
    count = 0;
    do
    {
        if (++count > 10)
            break;
        status = writev(fd, iov, iovcnt);
        if (status > 0)
            count = 0;
    } while (status < 0 && errno == EINTR);
    if (status < 0)
        return (-1);
    if (status == total)
        return (total);

    /* If we fell through to here, the first write partially succeeded.
       Figure out how far through the iov array we got, and then duplicate
       the rest of it so that we can modify it to reflect how much we manage
       to write on successive tries. */
    offset = status;
    left = total - offset;
    for (i = 0; offset >= (size_t) iov[i].iov_len; i++)
        offset -= iov[i].iov_len;
    iovleft = iovcnt - i;
    tmpiov = malloc(iovleft * sizeof(struct iovec));
    memcpy(tmpiov, iov + i, iovleft * sizeof(struct iovec));

    /* status now contains the offset into the first iovec struct in tmpiov.
       Go into the write loop, trying to write out everything remaining at
       each point.  At the top of the loop, status will contain a count of
       bytes written out at the beginning of the set of iovec structs. */
    i = 0;
    do
    {
        if (++count > 10)
            break;

        /* Skip any leading data that has been written out. */
        for (; offset >= (size_t) tmpiov[i].iov_len && iovleft > 0; i++)
        {
            offset -= tmpiov[i].iov_len;
            iovleft--;
        }
        tmpiov[i].iov_base = (char *) tmpiov[i].iov_base + offset;
        tmpiov[i].iov_len -= offset;

        /* Write out what's left and return success if it's all written. */
        status = writev(fd, tmpiov + i, iovleft);
        if (status <= 0)
            offset = 0;
        else
        {
            offset = status;
            left -= offset;
            count = 0;
        }
    } while (left > 0 && (status >= 0 || errno == EINTR));

    /* We're either done or got an error; if we're done, left is now 0. */
    free(tmpiov);
    return(left == 0) ? total : -1;
}

static gboolean gst_stb22x_framed_video_write_data(GstStb22xFramedVideo *stb22x_framed_video,
                                                   GstBuffer *payload,
                                                   gboolean writingDSI)
{
    gint written;
    guint real_payload_size = 0;
    gint num_es_buffers = 0;
    guint64 buffer_time = 0;
    guint8* pIovMem;
    struct iovec *pIov = NULL;
    struct iovec *pIovFirstEntry = NULL;

    GST_LOG(" ->> Entering");

    /* Create my vectors, no more than 3 for anything other than H264 (H264 with codec_data).*/
    if(!writingDSI &&
       (stb22x_framed_video->currentCodec == GstStb22xFramedVideo_H264) &&
       (stb22x_framed_video->videoDSI != NULL))
    {
        gint diff = 0;
        num_es_buffers = gst_stb22x_framed_video_get_num_es_buffers(payload, stb22x_framed_video->nal_length);

        if(num_es_buffers > 0)
        {
            gint num_vectors = (num_es_buffers * 2) + 1;

            /* if we are doing h264 we need num_es_buffers*2 (Es start code + data) + NXP metadata.*/
            pIovMem = malloc((sizeof(struct iovec)) * num_vectors);
            pIov = (struct iovec *)pIovMem;
            pIovFirstEntry = pIov;
        }
        /* we take away the NAL Header length N times and we add the ES buffer size N times.
           The ES header = 3 bytes long and typically NAL LENGTH =4, so do ES - NAL length * n.
           This may be negative and we add onto buffer size.*/
        diff = (GST_BUFFER_SIZE(stb22x_framed_video->es_code_prefix) * num_es_buffers) - (num_es_buffers * stb22x_framed_video->nal_length);
        real_payload_size = GST_BUFFER_SIZE(payload) + diff;

        GST_DEBUG("Length = %d, ES header length = %d(%d), Payload size = %d",
                  GST_BUFFER_SIZE(payload),
                  GST_BUFFER_SIZE(stb22x_framed_video->es_code_prefix),
                  num_es_buffers,
                  real_payload_size);
    }
    else
    {
        /* For all other media types we need no more than metadata  + [optional DSI] + payload.*/
        pIovMem = malloc((sizeof(struct iovec)) * 3);
        pIov = (struct iovec *)pIovMem;
        pIovFirstEntry = pIov;

        real_payload_size = GST_BUFFER_SIZE(payload);
    }

    /* Fill in the meta data header values that need updating for
       every frame:
       - convert timestamps into PTS format.*/
    buffer_time = GST_BUFFER_TIMESTAMP (payload);

    if((buffer_time != GST_CLOCK_TIME_NONE) &&
       (!writingDSI))
    {
        stb22x_framed_video->metaData.timeValid = True;
        stb22x_framed_video->metaData.timeLo = (guint32) (GSTTIME_TO_MPEGTIME(buffer_time) & 0xFFFFFFFF);
        stb22x_framed_video->metaData.timeHi = ((guint32) (GSTTIME_TO_MPEGTIME(buffer_time) >> 32) & 0x1);
    }
    else
    {
        stb22x_framed_video->metaData.timeValid = False;
        stb22x_framed_video->metaData.timeLo = 0;
        stb22x_framed_video->metaData.timeHi = 0;
    }

    /* Fill in the paylaod size that we will send next.*/
    stb22x_framed_video->metaData.payLoadLength = real_payload_size;

    /* For DIVX3.11 We need to add the VideoDSI for every packet we are sending so
       increase size by the header size (12).*/
    if(stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Divx3)
    {
        stb22x_framed_video->metaData.payLoadLength += GST_BUFFER_SIZE(stb22x_framed_video->videoDSI);
    }

    /* For VP6 We need to add the size of each packet to the ES byte stream so increase size
       by 32bits.*/
    if(stb22x_framed_video->currentCodec == GstStb22xFramedVideo_VP6)
    {
        stb22x_framed_video->metaData.payLoadLength += sizeof(real_payload_size);
    }

    /* For WMV we need to add the size of each packet to the ES byte stream so increase size
       by 8 bytes*/
    if((stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Wmv) && (!writingDSI))
    {
        stb22x_framed_video->metaData.payLoadLength += sizeof(wmv_frame_length);
    }

    /* Now write the meta data and payload*/
    if (stb22x_framed_video->metaData.payLoadLength > 0)
    {
        gint num_vectors = 0;

        /* Write the metadata.*/
        pIov->iov_base = &(stb22x_framed_video->metaData);
        pIov->iov_len =  sizeof(stb22x_framed_video->metaData);
        /* Next Vector....*/
        pIov++;
        /* Increment our counter. */
        num_vectors++;

        /* We need to add the VideoDSI for every packet we are sending.*/
        if(stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Divx3)
        {
            guint8* sizePtr = (GST_BUFFER_DATA(stb22x_framed_video->videoDSI) + sizeof(divx_start_code));

            /* Need to copy size of payload into header. (BE format)*/
            sizePtr[0] = (guint8)((real_payload_size >> 24) & 0xFF);
            sizePtr[1] = (guint8)((real_payload_size >> 16) & 0xFF);
            sizePtr[2] = (guint8)((real_payload_size >> 8) & 0xFF);
            sizePtr[3] = (guint8)(real_payload_size & 0xFF);

            pIov->iov_base = GST_BUFFER_DATA(stb22x_framed_video->videoDSI);
            pIov->iov_len =  GST_BUFFER_SIZE(stb22x_framed_video->videoDSI);
            /* Next Vector....*/
            pIov++;
            /* Increment our counter. */
            num_vectors++;
        }

        /* For VP6 we must have the size of the frame (32bit) sent before the payload. */
        if(stb22x_framed_video->currentCodec == GstStb22xFramedVideo_VP6)
        {
            pIov->iov_base = &real_payload_size;
            pIov->iov_len =  sizeof(real_payload_size);
            /* Next Vector....*/
            pIov++;
            /* Increment our counter. */
            num_vectors++;
        }

        /* For WMV we must have the size of the frame (8 bytes) sent before the payload. */
        if((stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Wmv) && (!writingDSI))
        {
            guint8* bufferPtr = GST_BUFFER_DATA(payload);

            /* Zero the data first.*/
            memset(wmv_frame_length, sizeof(wmv_frame_length), 0);

            /* Now put the size in.*/
            wmv_frame_length[3] = (guint8)((real_payload_size >> 24) & 0xFF);
            wmv_frame_length[2] = (guint8)((real_payload_size >> 16) & 0xFF);
            wmv_frame_length[1] = (guint8)((real_payload_size >> 8) & 0xFF);
            wmv_frame_length[0] = (guint8)(real_payload_size & 0xFF);

            /* Do we have a VC1 start code or not?
               MSVD wants one if it does not have one. */
            if((bufferPtr[2] != 0x01) && (bufferPtr[3] != 0x0D))
            {
                /* We need to make sure we have a startcode in the final 4 bytes.*/
                wmv_frame_length[4] = 0x00;
                wmv_frame_length[5] = 0x00;
                wmv_frame_length[6] = 0x01;
                wmv_frame_length[7] = 0x0D;
            }

            pIov->iov_base = &wmv_frame_length;
            pIov->iov_len =  sizeof(wmv_frame_length);
            /* Next Vector....*/
            pIov++;
            /* Increment our counter. */
            num_vectors++;
        }

        /* If we are writing h264 es data and we had some codec_data then do something special here,
           else just write the payload.*/
        if(!writingDSI &&
           (stb22x_framed_video->currentCodec == GstStb22xFramedVideo_H264) &&
           (stb22x_framed_video->videoDSI != NULL))
        {
            gst_stb22x_framed_video_parse_and_create_buffer (payload,
                                                             stb22x_framed_video->es_code_prefix,
                                                             stb22x_framed_video->nal_length,
                                                             &pIov);

            /* We know that we will have double the number of ES buffers as vectors (es+payload)*N.*/
            num_vectors += (num_es_buffers * 2);
        }
        else
        {
            pIov->iov_base = GST_BUFFER_DATA (payload);
            pIov->iov_len =  real_payload_size;
            /* Next Vector....*/
            pIov++;
            /* Increment our counter. */
            num_vectors++;
        }

        written = xwritev(stb22x_framed_video->fd, pIovFirstEntry, num_vectors);
        if(written < 0)
        {
            /* An error occured can use errno. */
            GST_ERROR("Failed to write metadata to stb22x_framed_video (fd=%d)=> %s", stb22x_framed_video->fd, strerror(errno));
        }
        /* Are we debugging and capturing the debug Meta Data file?*/
        if((stb22x_framed_video->dump) && (stb22x_framed_video->dump_fd != -1))
        {
            written = xwritev(stb22x_framed_video->dump_fd, pIovFirstEntry, num_vectors);
            if(written < 0)
            {
                /* An error occured can use errno. */
                GST_ERROR("Failed to write metadata to stb22x_framed_video (fd=%d)=> %s", stb22x_framed_video->fd, strerror(errno));
            }
        }
    }

    GST_LOG(" <<- Leaving");
    return TRUE;
}

/**
 * Useful error strings
 */
static GType gst_stb22x_framed_video_state_error_get_type (void)
{
    static GType stb22x_framed_video_state_error_type = 0;
    static const GEnumValue stb22x_framed_video_state_error[] =
    {
        {STB22X_FRAMED_VIDEO_STATE_ERROR_NONE, "No state change errors", "none"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_NULL_READY, "Fail state change from NULL to READY", "null-to-ready"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_READY_PAUSED, "Fail state change from READY to PAUSED", "ready-to-paused"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_PAUSED_PLAYING, "Fail state change from PAUSED to PLAYING", "paused-to-playing"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_PLAYING_PAUSED, "Fail state change from PLAYING to PAUSED", "playing-to-paused"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_PAUSED_READY, "Fail state change from PAUSED to READY", "paused-to-ready"},
        {STB22X_FRAMED_VIDEO_STATE_ERROR_READY_NULL, "Fail state change from READY to NULL", "ready-to-null"},
        {0, NULL, NULL},
    };

    if (!stb22x_framed_video_state_error_type)
    {
        stb22x_framed_video_state_error_type = g_enum_register_static ("GstStb22xFramedVideoStateError",
                                                                       stb22x_framed_video_state_error);
    }
    return (stb22x_framed_video_state_error_type);
}

/**
 * Called on exit so make sure we tidy up after ourselves.
 * Close all open fds etc.
 */
static void gst_stb22x_framed_video_finalise (GObject * object)
{
    GstStb22xFramedVideo *stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (object);

    GST_LOG(" ->> Entering");

    /* Make sure we close the devices.*/
    if(stb22x_framed_video->dump_fd >= 0)
    {
        close(stb22x_framed_video->dump_fd);
        stb22x_framed_video->dump_fd = -1;
    }
    if(stb22x_framed_video->fd >= 0)
    {
        close(stb22x_framed_video->fd);
        stb22x_framed_video->fd = -1;
    }
    G_OBJECT_CLASS (parent_class)->finalize (object);

    GST_LOG(" <<- Leaving");
}

/**
 * Get the caps of this pin.
 */
static GstCaps *gst_stb22x_framed_video_getcaps(GstBaseSink * basesink)
{
    GstStb22xFramedVideo *stb22x_framed_video;

    stb22x_framed_video = GST_STB22X_FRAMED_VIDEO(basesink);

    return gst_caps_copy(gst_pad_get_pad_template_caps(GST_BASE_SINK_PAD(stb22x_framed_video)));
}

/**
 * Set the caps (and allow me to store the mime type of what is being played.
 */
static gboolean gst_stb22x_framed_video_setcaps(GstBaseSink * basesink, GstCaps * caps)
{
    GstStb22xFramedVideo *stb22x_framed_video;
    GstStructure *structure;
    GstCaps *intersection;
    const gchar *mime;
    gint width = 0;
    gint height = 0;

    GST_LOG(" ->> Entering");

    stb22x_framed_video = GST_STB22X_FRAMED_VIDEO(basesink);

    /* Set to Guard before starting.*/
    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;

    /* See if there are any common video formats in the list asked for and the list we have installed.*/
    intersection = gst_caps_intersect(gst_static_caps_get(&sinktemplate.static_caps), caps);
    /* If the lists when compared are empty then we have no matching formats so will
       exit with unsupported.*/
    if (gst_caps_is_empty(intersection))
    {
        /*Do nothing.*/
    }
    else
    {
        /* Tidy up......*/
        gst_caps_unref(intersection);

        structure = gst_caps_get_structure(caps, 0);
        mime = gst_structure_get_name(structure);
        /* Get width and height.*/
        gst_structure_get_int (structure, "width", &width);
        gst_structure_get_int (structure, "height", &height);

        GST_INFO("Trying to match mimetype %s", mime);

        /* Check version numbers.*/
        if (!strcmp("video/x-divx", mime))
        {
            int version = 0;
            gst_structure_get_int(structure, "divxversion", &version);

            GST_INFO("Trying to play DIVX version=%d", version);
            switch(version)
            {
                case 3:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Divx3;

                    stb22x_framed_video->videoDSI = gst_buffer_new_and_alloc((sizeof(divx_start_code) + 4));
                    memcpy(GST_BUFFER_DATA(stb22x_framed_video->videoDSI), divx_start_code, sizeof(divx_start_code));

                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_divx3");
                    break;

                case 4:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Divx4;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;

                case 5:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Divx5;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;

                default:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    break;
            }
        }
        else if (!strcmp("video/x-xvid", mime))
        {
            GST_INFO("Trying to play XVID");
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Xvid;
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_xvid");
        }
        else if (!strcmp("video/x-msmpeg", mime))
        {
            int version = 0;
            gst_structure_get_int(structure, "msmpegversion", &version);

            GST_INFO("Trying to play MS-MPEG version=%d", version);
            switch(version)
            {
                case 41:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;

                case 42:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;

                case 43:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;

                default:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
                    break;
            }
        }
        else if (!strcmp("video/mpeg", mime))
        {
            int version = 0;
            gst_structure_get_int(structure, "mpegversion", &version);

            GST_INFO("Trying to play MPEG version=%d", version);
            switch(version)
            {
                case 1:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Mpeg1;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg");
                    break;

                case 2:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Mpeg2;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg2");
                    break;

                case 4:
                {
                    const GValue *value;

                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Mpeg4;
                    (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");

                    value = gst_structure_get_value(structure, "codec_data");
                    if(!value)
                    {
                        GST_INFO("No codec_data for MPEG4 playback was found\n");
                    }
                    else
                    {
                        stb22x_framed_video->videoDSI = gst_value_get_buffer(value);
                        stb22x_framed_video->videoDSISent = FALSE;

                        GST_INFO("MPEG4 codec_data is %d bytes long\n", GST_BUFFER_SIZE(stb22x_framed_video->videoDSI));
                    }
                    break;
                }
                default:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    break;
            }
        }
        else if (!strcmp("video/x-h263", mime))
        {
            const gchar* h263version = gst_structure_get_string(structure, "h263version");
            const gchar* h263variant = gst_structure_get_string(structure, "variant");

            GST_INFO("Trying to play H263 Version '%s' variant '%s'\n",
                     (h263version ? h263version : "unknown"),
                     (h263variant ? h263variant : "unknown"));

            /* We just tell MSVD its MPEG4 and it seems to work.*/
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Mpeg4;
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_mpeg4");
        }
        else if (!strcmp("video/x-h264", mime))
        {
            const GValue *value;

            GST_INFO("Trying to play H264");
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_H264;
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_h264");

            value = gst_structure_get_value(structure, "codec_data");
            if(!value)
            {
                GST_ERROR("No codec_data and we are trying to play H264\n");
            }
            else
            {
                GstBuffer *demuxData;

                demuxData = gst_value_get_buffer(value);
                stb22x_framed_video->videoDSISent = FALSE;

                GST_INFO("Is this valid qt header?");
                if(gst_stb22x_framed_video_valid_quicktime_header(demuxData))
                {
                    GST_INFO(" - Yes");

                    stb22x_framed_video->nal_length = gst_stb22x_framed_video_get_nal_length(demuxData);
                    stb22x_framed_video->videoDSI = gst_stb22x_framed_video_get_sps_pps_data(demuxData);
                    stb22x_framed_video->es_code_prefix = gst_stb22x_framed_video_get_es_prefix_code();
                }
                else
                {
                    GST_INFO(" - No");
                }
                gst_buffer_unref(demuxData);
            }
        }
        else if (!strcmp("video/x-wmv", mime))
        {
            const GValue *value;
            int version = 0;
            int width = 0;
            int height = 0;
            guint32 fourcc;

            gst_structure_get_int(structure, "wmvversion", &version);
            gst_structure_get_int(structure, "width", &width);
            gst_structure_get_int(structure, "height", &height);
            gst_structure_get_fourcc (structure, "format", &fourcc);

            GST_INFO("Trying to play WMV version=%d fourcc=%" GST_FOURCC_FORMAT "\n", version, GST_FOURCC_ARGS (fourcc));
            switch(version)
            {
                case 1:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Wmv;
                    break;

                case 2:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Wmv;
                    break;

                case 3:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Wmv;
                    break;

                default:
                    stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
                    break;
            }

            /* We need codec_data for WMV.
               This needs to be converted into a VC1 header and I don't know how to do this.*/
            value = gst_structure_get_value(structure, "codec_data");
            if(!value)
            {
                GST_ERROR("No codec_data and we are trying to play WMV\n");
            }
            else
            {
                GstBuffer* codec_data = gst_value_get_buffer(value);

                if(GST_BUFFER_SIZE(codec_data) > 4)
                {
                    /* This is a VC1 stream I think so just use this data.*/
                    stb22x_framed_video->videoDSI = gst_value_get_buffer(value);
                }
                else
                {
                    guint8* ptr;
                    guint32* ptr32;

                    /* We need to make a VCI header.  It will be 36 bytes long.*/
                    stb22x_framed_video->videoDSI = gst_buffer_new_and_alloc(36);
                    ptr = GST_BUFFER_DATA(stb22x_framed_video->videoDSI);
                    ptr32 = (guint32*)GST_BUFFER_DATA(stb22x_framed_video->videoDSI);
                    /* Marker*/
                     *ptr32 = VC1_START;
                    ptr32++;
                    /* Marker.*/
                    *ptr32 = VC1_MARKER1;
                    ptr32++;
                    /* STRUCT_C or codec_data.*/
                    memcpy(ptr32, GST_BUFFER_DATA(codec_data), GST_BUFFER_SIZE(codec_data));
                    ptr32++;
                    /* Now put height in.*/
                    ptr[15] = (guint8)((height >> 24) & 0xFF);
                    ptr[14] = (guint8)((height >> 16) & 0xFF);
                    ptr[13] = (guint8)((height >> 8) & 0xFF);
                    ptr[12] = (guint8)(height & 0xFF);
                    ptr32++;
                    /* Now put width in.*/
                    ptr[19] = (guint8)((width >> 24) & 0xFF);
                    ptr[18] = (guint8)((width >> 16) & 0xFF);
                    ptr[17] = (guint8)((width >> 8) & 0xFF);
                    ptr[16] = (guint8)(width & 0xFF);
                    ptr32++;
                    /* Marker.*/
                    *ptr32 = VC1_MARKER2;
                    ptr32++;
                    /* Level:3 | CBR:1: RES1:4 | HRD_BUFFER:24.*/
                    /* zero the 32 bits and then set low byte to be 0x80.*/
                    *ptr32 = VC1_LEVEL_CBR;
                    ptr32++;
                    /* HRD_RATE.*/
                    *ptr32 = VC1_HRD_RATE;
                    ptr32++;
                    /* FRAMERATE.*/
                    *ptr32 = VC1_FRAMERATE;
                    ptr32++;
                }
                stb22x_framed_video->videoDSISent = FALSE;
            }
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_wmv");
        }
        else if (!strcmp("video/x-vp6-flash", mime))
        {
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_VP6;
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_vp6");
        }
        else if (!strcmp("video/x-flash-video", mime))
        {
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_H263;
            (void)snprintf(stb22x_framed_video->dump_filename, GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH, "/opt/nxp/share/media/videodump.es_v_h263flv");
        }
        else
        {
            GST_INFO("Unable to play %s", mime);
            /* Set to guard and then catch it.*/
            stb22x_framed_video->currentCodec = GstStb22xFramedVideo_Guard;
        }
    }

    /* We have been told what we are going to be receiving so now create a dump debug file.*/
    if((stb22x_framed_video->dump) && (stb22x_framed_video->currentCodec != GstStb22xFramedVideo_Guard))
    {
        GST_INFO("opening %s for ES dump\n", stb22x_framed_video->dump_filename);
        stb22x_framed_video->dump_fd = open (stb22x_framed_video->dump_filename, O_CREAT | O_WRONLY, 0644);
        if (stb22x_framed_video->dump_fd < 0)
        {
            GST_ERROR("Failed to open %s for writing", stb22x_framed_video->dump_filename);
        }
    }
    /* Fill in the meta data header with the standard values.*/
    stb22x_framed_video->metaData.magicStart = PH_STBESMETADATA_MAGIC;
    stb22x_framed_video->metaData.formatValid = True;
    stb22x_framed_video->metaData.format = CONVERT_TO_METADATA_VIDEOTYPE(stb22x_framed_video->currentCodec);
    stb22x_framed_video->metaData.flags = 0x0;
    stb22x_framed_video->metaData.dimensionsValid = True;
    stb22x_framed_video->metaData.width = width;
    stb22x_framed_video->metaData.height = height;
    stb22x_framed_video->metaData.magicEnd = PH_STBESMETADATA_MAGIC;

    GST_LOG(" <<- Leaving (Caps are %s)", ((stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Guard) ? "Not Supported" : "Supported"));
    return ((stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Guard) ? FALSE : TRUE);
}

/**
 * GStreamer registration function.
 * This registers the capabilities of the plugin (i.e Sink/Decoder etc)
 * and adds the sink pin.
 */
static void gst_stb22x_framed_video_base_init (gpointer g_class)
{
    GstElementClass *gst_element_class = GST_ELEMENT_CLASS (g_class);

    GST_LOG(" ->> Entering");

    gst_element_class_set_details_simple (gst_element_class,
                                          "Stb22x Framed Video",
                                          "Decoder/Video/Device/Codec/Sink",
                                          "Generic STB22x Video Sink",
                                          "NXP Semiconductors <ipstb-support@nxp.com>");

    gst_element_class_add_pad_template (gst_element_class, gst_static_pad_template_get (&sinktemplate));

    GST_LOG(" <<- Leaving");
}

/**
 * Setup the plugin with its callable properties and function ptrs.
 */
static void gst_stb22x_framed_video_class_init (GstStb22xFramedVideoClass * klass)
{
    GObjectClass *gobject_class;
    GstElementClass *gstelement_class;
    GstBaseSinkClass *gstbase_sink_class;

    GST_LOG(" ->> Entering");

    gobject_class = G_OBJECT_CLASS (klass);
    gstelement_class = GST_ELEMENT_CLASS (klass);
    gstbase_sink_class = (GstBaseSinkClass *) klass;

    parent_class = g_type_class_peek_parent (klass);

    gobject_class->finalize = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_finalise);
    gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_set_property);
    gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_get_property);

    g_object_class_install_property (gobject_class, PROP_STATE_ERROR,
                                     g_param_spec_enum ("state-error", "State Error",
                                                        "Generate a state change error", GST_TYPE_STB22X_FRAMED_VIDEO_STATE_ERROR,
                                                        DEFAULT_STATE_ERROR, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (gobject_class, PROP_DUMP,
                                     g_param_spec_boolean ("dump", "Dump", "Dump buffer contents to stdout",
                                                           DEFAULT_DUMP, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (gobject_class, PROP_DUMP_ES_FILENAME,
                                     g_param_spec_string ("dump_filename", "ES Meta Dump", "Set the filename of the dump file",
                                                          "/opt/nxp/share/media/video.es_v_h264", G_PARAM_READWRITE));

    g_object_class_install_property (gobject_class, PROP_SBMSINKNAME,
                                     g_param_spec_string ("sbmsinkname", "Sbm Sink Name", "Set the SBM sink name we are using",
                                                          "/dev/stbsbmsink0", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    g_object_class_install_property (gobject_class, PROP_MUTE,
                                     g_param_spec_boolean ("mute", "Mute", "Mute the output (throw away all data)",
                                                           DEFAULT_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

    /* Install the standard function ptrs.*/
    gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_change_state);
    gstbase_sink_class->event = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_event);
    gstbase_sink_class->preroll = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_preroll);
    gstbase_sink_class->render = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_render);
    gstbase_sink_class->get_caps = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_getcaps);
    gstbase_sink_class->set_caps = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_setcaps);
    gstbase_sink_class->start = GST_DEBUG_FUNCPTR(gst_stb22x_framed_video_start);
    gstbase_sink_class->stop = GST_DEBUG_FUNCPTR (gst_stb22x_framed_video_stop);

    GST_LOG(" <<- Leaving");
}

/**
 * Setup plugin with sensible defaults.
 */
static void gst_stb22x_framed_video_init (GstStb22xFramedVideo * stb22x_framed_video, GstStb22xFramedVideoClass * g_class)
{
    GST_LOG(" ->> Entering");

    stb22x_framed_video->dump = DEFAULT_DUMP;
    stb22x_framed_video->mute = DEFAULT_MUTE;
    GST_BASE_SINK (stb22x_framed_video)->sync = DEFAULT_SYNC;
    stb22x_framed_video->state_error = DEFAULT_STATE_ERROR;
    stb22x_framed_video->fd = -1;
    stb22x_framed_video->dump_fd = -1;
    stb22x_framed_video->videoDSI = NULL;
    stb22x_framed_video->videoDSISent = FALSE;
    stb22x_framed_video->sps_pps_data = NULL;
    stb22x_framed_video->es_code_prefix = NULL;
    stb22x_framed_video->nal_length = 0;

    /* Clear the meta data.*/
    memset(&stb22x_framed_video->metaData, 0, sizeof(stb22x_framed_video->metaData));

    GST_LOG(" <<- Leaving");
}

/**
 * Set the properties of this plugin.
 */
static void gst_stb22x_framed_video_set_property (GObject * object,
                                                  guint prop_id,
                                                  const GValue * value,
                                                  GParamSpec * pspec)
{
    GstStb22xFramedVideo *stb22x_framed_video;

    GST_LOG(" ->> Entering prop_id:%d", prop_id);

    stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (object);

    switch (prop_id)
    {
        case PROP_STATE_ERROR:
            stb22x_framed_video->state_error = g_value_get_enum (value);
            break;

        case PROP_DUMP:
            stb22x_framed_video->dump = g_value_get_boolean (value);
            break;

        case PROP_DUMP_ES_FILENAME:
            strncpy(stb22x_framed_video->dump_filename, g_value_get_string (value), GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH);
            break;

        case PROP_SBMSINKNAME:
            strncpy(stb22x_framed_video->fullDeviceName, g_value_get_string (value), GST_STB22X_FRAMED_VIDEO_FILE_NAME_LENGTH);
            break;

        case PROP_MUTE:
            stb22x_framed_video->mute = g_value_get_boolean (value);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }

    GST_LOG(" <<- Leaving");
}

/**
 * Get the value of the properties of this plugin.
 */
static void gst_stb22x_framed_video_get_property (GObject * object,
                                                  guint prop_id,
                                                  GValue * value,
                                                  GParamSpec * pspec)
{
    GstStb22xFramedVideo *stb22x_framed_video;

    GST_LOG(" ->> Entering prop_id=%d", prop_id);

    stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (object);

    switch (prop_id)
    {
        case PROP_STATE_ERROR:
            g_value_set_enum (value, stb22x_framed_video->state_error);
            break;

        case PROP_DUMP:
            g_value_set_boolean (value, stb22x_framed_video->dump);
            break;

        case PROP_DUMP_ES_FILENAME:
            GST_OBJECT_LOCK (stb22x_framed_video);
            g_value_set_string (value, stb22x_framed_video->dump_filename);
            GST_OBJECT_UNLOCK (stb22x_framed_video);
            break;

        case PROP_SBMSINKNAME:
            GST_OBJECT_LOCK (stb22x_framed_video);
            g_value_set_string (value, stb22x_framed_video->fullDeviceName);
            GST_OBJECT_UNLOCK (stb22x_framed_video);
            break;

        case PROP_MUTE:
            g_value_set_boolean (value, stb22x_framed_video->mute);
            break;

        default:
            G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
            break;
    }

    GST_LOG(" <<- Leaving");
}

/**
 * Plugin event handler.
 */
static gboolean gst_stb22x_framed_video_event (GstBaseSink * bsink, GstEvent * event)
{
    const GstStructure *s;
    gchar *sstr;
    GstStb22xFramedVideo *stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (bsink);

    GST_LOG(" ->> Entering");

    if ((s = gst_event_get_structure (event)))
        sstr = gst_structure_to_string (s);
    else
        sstr = g_strdup ("");

    if(GST_EVENT_TYPE(event) == GST_EVENT_NEWSEGMENT)
    {
        /* This means we should resend the DSI next time we want to render.*/
        stb22x_framed_video->videoDSISent = FALSE;
    }

    if (GST_BASE_SINK_CLASS (parent_class)->event)
    {
        GST_LOG(" ->> GST_BASE_SINK_CLASS");
        g_free (sstr);
        return (GST_BASE_SINK_CLASS (parent_class)->event (bsink, event));
    }
    else
    {
        GST_LOG(" ->> !GST_BASE_SINK_CLASS event   ******* E (type: %d, %s) %p", GST_EVENT_TYPE (event), sstr, event);
        g_free (sstr);
        return (TRUE);
    }
}

/**
 * Open our ES device so that we can feed it data.
 */
static gboolean gst_stb22x_framed_video_start (GstBaseSink * bsink)
{
    gboolean ret = FALSE;
    GstStb22xFramedVideo *stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (bsink);

    GST_LOG(" ->> Entering");

    if ((stb22x_framed_video->fullDeviceName == NULL) || (stb22x_framed_video->fullDeviceName[0] == '\0'))
    {
        GST_ERROR("No file name specified for reading");
        ret = FALSE;
    }
    else
    {
        GST_INFO_OBJECT (stb22x_framed_video, "opening file %s", stb22x_framed_video->fullDeviceName);

        /* open the file */
        stb22x_framed_video->fd = open (stb22x_framed_video->fullDeviceName, O_WRONLY);
        if (stb22x_framed_video->fd < 0)
        {
            GST_ERROR("Failed to open %s for reading", stb22x_framed_video->fullDeviceName);
            ret = FALSE;
        }
        else
        {
            ret = TRUE;
        }
    }
    GST_LOG(" <<- Leaving");
    return ret;
}

/**
 * Close ES device and possibly the dump file.
 */
static gboolean gst_stb22x_framed_video_stop (GstBaseSink * bsink)
{
    GstStb22xFramedVideo *stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (bsink);

    GST_LOG(" ->> Entering");

    /* close the files */
    if(stb22x_framed_video->fd >= 0)
    {
        close (stb22x_framed_video->fd);
        stb22x_framed_video->fd = -1;
    }
    if(stb22x_framed_video->dump_fd >= 0)
    {
        close (stb22x_framed_video->dump_fd);
        stb22x_framed_video->dump_fd = -1;
    }
    GST_LOG(" <<- Leaving");

    return (TRUE);
}

/**
 * Preroll function is required as it prerolls one buffer of data
 * If I do not implement this I will get a repeated first frame.
 */
static GstFlowReturn gst_stb22x_framed_video_preroll (GstBaseSink * bsink,
                                                      GstBuffer * buffer)
{
    GST_LOG(" ->> Entering");

    GST_LOG(" <<- Leaving");
    return (GST_FLOW_OK);
}

/**
 * Write the input data to the ES device.
 */
static GstFlowReturn gst_stb22x_framed_video_render (GstBaseSink * bsink,
                                                     GstBuffer * buffer)
{
    guint size;
    guint8 *data;
    guint64 buffer_time = 0;
    GstStb22xFramedVideo *stb22x_framed_video;

    GST_LOG(" ->> Entering");

    size = GST_BUFFER_SIZE (buffer);
    data = GST_BUFFER_DATA (buffer);
    stb22x_framed_video = GST_STB22X_FRAMED_VIDEO_CAST (bsink);
    buffer_time = GST_BUFFER_TIMESTAMP (buffer);

    /* Only put data into sink if not muted.*/
    if(!stb22x_framed_video->mute)
    {
        /* Send the DSI header if != NULL.*/
        if((stb22x_framed_video->videoDSI != NULL) &&
           (!stb22x_framed_video->videoDSISent) &&
           ((stb22x_framed_video->currentCodec == GstStb22xFramedVideo_H264) ||
            (stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Mpeg4) ||
            (stb22x_framed_video->currentCodec == GstStb22xFramedVideo_Wmv)))
        {
            if(GST_BUFFER_SIZE(stb22x_framed_video->videoDSI) > 0)
            {
                GST_INFO(" Writing One time DSI");
                gst_stb22x_framed_video_write_data(stb22x_framed_video, stb22x_framed_video->videoDSI, TRUE);
                stb22x_framed_video->videoDSISent = TRUE;
            }
        }
        gst_stb22x_framed_video_write_data(stb22x_framed_video, buffer, FALSE);
    }

    GST_LOG(" <<- Leaving");
    return (GST_FLOW_OK);
}

/**
 * Plugin State handler - handles state changes PAUSE->PLAY etc.
 */
static GstStateChangeReturn gst_stb22x_framed_video_change_state (GstElement * element,
                                                                  GstStateChange transition)
{
    GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
    GstStb22xFramedVideo *stb22x_framed_video = GST_STB22X_FRAMED_VIDEO (element);

    GST_LOG(" ->> Entering");

    switch (transition)
    {
        case GST_STATE_CHANGE_NULL_TO_READY:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_NULL_READY)
                goto error;
            break;

        case GST_STATE_CHANGE_READY_TO_PAUSED:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_READY_PAUSED)
                goto error;
            break;

        case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_PAUSED_PLAYING)
                goto error;
            break;

        default:
            break;
    }

    ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);

    switch (transition)
    {
        case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_PLAYING_PAUSED)
                goto error;
            break;

        case GST_STATE_CHANGE_PAUSED_TO_READY:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_PAUSED_READY)
                goto error;
            break;

        case GST_STATE_CHANGE_READY_TO_NULL:
            if (stb22x_framed_video->state_error == STB22X_FRAMED_VIDEO_STATE_ERROR_READY_NULL)
                goto error;
            GST_OBJECT_LOCK (stb22x_framed_video);
            GST_OBJECT_UNLOCK (stb22x_framed_video);
            break;

        default:
            break;
    }

    GST_LOG(" <<- Leaving");
    return (ret);

    /* ERROR */
error:
    GST_ELEMENT_ERROR (element,
                       CORE,
                       STATE_CHANGE,
                       (NULL), ("Erroring out on state change as requested"));

    GST_LOG(" <<- Leaving");
    return (GST_STATE_CHANGE_FAILURE);
}

/* ============================================================= */
/*                       Public Methods                          */
/* ============================================================= */
/**
 * Register the plugin and make it the most important sink there is.
 */
static gboolean gst_stb22x_framed_video_plugin_init (GstPlugin * plugin)
{
    if (!gst_element_register (plugin,
                               "stb22xframedvideo",
                               GST_RANK_PRIMARY,
                               GST_TYPE_STB22X_FRAMED_VIDEO))
        return FALSE;

    GST_DEBUG_CATEGORY_INIT (gst_stb22x_framed_video_debug, "stb22xframedvideo", 0, "stb22xframedvideo element");
    return TRUE;
}

/**
 * GST_PLUGIN_DEFINE:
 * @major: major version number of the gstreamer-core that plugin was compiled for
 * @minor: minor version number of the gstreamer-core that plugin was compiled for
 * @name: short, but unique name of the plugin
 * @description: information about the purpose of the plugin
 * @init: function pointer to the plugin_init method with the signature of <code>static gboolean plugin_init (GstPlugin * plugin)</code>.
 * @version: full version string (e.g. VERSION from config.h)
 * @license: under which licence the package has been released, e.g. GPL, LGPL.
 * @package: the package-name (e.g. PACKAGE_NAME from config.h)
 * @origin: a description from where the package comes from (e.g. the homepage URL)
 *
 * This macro needs to be used to define the entry point and meta data of a
 * plugin. One would use this macro to export a plugin, so that it can be used
 * by other applications
 */
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
                   GST_VERSION_MINOR,
                   "stb22xframedvideo",
                   "Take ES input data and add Metadata for STB225 Playback",
                   gst_stb22x_framed_video_plugin_init,
                   VERSION,
                   "LGPL",
                   PACKAGE_NAME,
                   "NXP Semiconductors <ipstb-support@nxp.com>")

