diff mbox

[FFmpeg-devel] avcodec: Implement DM PAR Muxer/Demuxer

Message ID CACaFbZ+gEndmLUg_kZkc+XChupadCYDS99Cv_HoPXvZ66sGNWg@mail.gmail.com
State New
Headers show

Commit Message

Tom Needham July 5, 2019, 8:44 p.m. UTC
Samples are Available from

https://transfernow.net/131xk9g4u0jt

Signed-off-by: Thomas Needham <06needhamt@gmail.com>
---
 Changelog                     |    1 +
 configure                     |    7 +
 libavformat/Makefile          |   10 +
 libavformat/adaudio.c         |  137 ++++
 libavformat/adbinary.c        |  609 ++++++++++++++++
 libavformat/adcommon.c        | 1106 +++++++++++++++++++++++++++++
 libavformat/adffmpeg_errors.h |   94 +++
 libavformat/adjfif.c          |  551 ++++++++++++++
 libavformat/adjfif.h          |   41 ++
 libavformat/admime.c          |  822 +++++++++++++++++++++
 libavformat/adpic.h           |  116 +++
 libavformat/adraw.c           |  131 ++++
 libavformat/allformats.c      |    7 +
 libavformat/ds.c              | 1262 +++++++++++++++++++++++++++++++++
 libavformat/ds.h              |  135 ++++
 libavformat/ds_exports.h      |  173 +++++
 libavformat/dsenc.c           |  488 +++++++++++++
 libavformat/dsenc.h           |   35 +
 libavformat/dspic.c           |  317 +++++++++
 libavformat/libpar.c          | 1030 +++++++++++++++++++++++++++
 libavformat/libpar.h          |   40 ++
 libavformat/netvu.c           |  214 ++++++
 libavformat/netvu.h           |   21 +
 libavformat/version.h         |    4 +-
 24 files changed, 7349 insertions(+), 2 deletions(-)
 create mode 100644 libavformat/adaudio.c
 create mode 100644 libavformat/adbinary.c
 create mode 100644 libavformat/adcommon.c
 create mode 100644 libavformat/adffmpeg_errors.h
 create mode 100644 libavformat/adjfif.c
 create mode 100644 libavformat/adjfif.h
 create mode 100644 libavformat/admime.c
 create mode 100644 libavformat/adpic.h
 create mode 100644 libavformat/adraw.c
 create mode 100644 libavformat/ds.c
 create mode 100644 libavformat/ds.h
 create mode 100644 libavformat/ds_exports.h
 create mode 100644 libavformat/dsenc.c
 create mode 100644 libavformat/dsenc.h
 create mode 100644 libavformat/dspic.c
 create mode 100644 libavformat/libpar.c
 create mode 100644 libavformat/libpar.h
 create mode 100644 libavformat/netvu.c
 create mode 100644 libavformat/netvu.h

 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
                                                LIBAVFORMAT_VERSION_MINOR, \

Comments

Andreas Rheinhardt July 5, 2019, 10:54 p.m. UTC | #1
Tom Needham:
> Samples are Available from
> 
> https://transfernow.net/131xk9g4u0jt
> 
> Signed-off-by: Thomas Needham <06needhamt@gmail.com>
> ---
>  Changelog                     |    1 +
>  configure                     |    7 +
>  libavformat/Makefile          |   10 +
>  libavformat/adaudio.c         |  137 ++++
>  libavformat/adbinary.c        |  609 ++++++++++++++++
>  libavformat/adcommon.c        | 1106 +++++++++++++++++++++++++++++
>  libavformat/adffmpeg_errors.h |   94 +++
>  libavformat/adjfif.c          |  551 ++++++++++++++
>  libavformat/adjfif.h          |   41 ++
>  libavformat/admime.c          |  822 +++++++++++++++++++++
>  libavformat/adpic.h           |  116 +++
>  libavformat/adraw.c           |  131 ++++
>  libavformat/allformats.c      |    7 +
>  libavformat/ds.c              | 1262 +++++++++++++++++++++++++++++++++
>  libavformat/ds.h              |  135 ++++
>  libavformat/ds_exports.h      |  173 +++++
>  libavformat/dsenc.c           |  488 +++++++++++++
>  libavformat/dsenc.h           |   35 +
>  libavformat/dspic.c           |  317 +++++++++
>  libavformat/libpar.c          | 1030 +++++++++++++++++++++++++++
>  libavformat/libpar.h          |   40 ++
>  libavformat/netvu.c           |  214 ++++++
>  libavformat/netvu.h           |   21 +
>  libavformat/version.h         |    4 +-
>  24 files changed, 7349 insertions(+), 2 deletions(-)
>  create mode 100644 libavformat/adaudio.c
>  create mode 100644 libavformat/adbinary.c
>  create mode 100644 libavformat/adcommon.c
>  create mode 100644 libavformat/adffmpeg_errors.h
>  create mode 100644 libavformat/adjfif.c
>  create mode 100644 libavformat/adjfif.h
>  create mode 100644 libavformat/admime.c
>  create mode 100644 libavformat/adpic.h
>  create mode 100644 libavformat/adraw.c
>  create mode 100644 libavformat/ds.c
>  create mode 100644 libavformat/ds.h
>  create mode 100644 libavformat/ds_exports.h
>  create mode 100644 libavformat/dsenc.c
>  create mode 100644 libavformat/dsenc.h
>  create mode 100644 libavformat/dspic.c
>  create mode 100644 libavformat/libpar.c
>  create mode 100644 libavformat/libpar.h
>  create mode 100644 libavformat/netvu.c
>  create mode 100644 libavformat/netvu.h
> 
> diff --git a/Changelog b/Changelog
> index 86167b76a1..41d12c092e 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -35,6 +35,7 @@ version <next>:
>  - IFV demuxer
>  - derain filter
>  - deesser filter
> +- AD Holdings PAR Muxer and Demuxer
> 
> 
>  version 4.1:
> diff --git a/configure b/configure
> index 7cea9d4d73..39c4356c00 100755
> --- a/configure
> +++ b/configure
> @@ -317,6 +317,7 @@ External library support:
>    --enable-vapoursynth     enable VapourSynth demuxer [no]
>    --disable-xlib           disable xlib [autodetect]
>    --disable-zlib           disable zlib [autodetect]
> +  --enable-libparreader    enable PAR (de)muxing via libparreader [no]
> 
>    The following libraries provide various hardware acceleration features:
>    --disable-amf            disable AMF video encoding code [autodetect]
> @@ -1720,6 +1721,7 @@ EXTERNAL_LIBRARY_NONFREE_LIST="
>      libfdk_aac
>      openssl
>      libtls
> +    libparreader
>  "
> 
>  EXTERNAL_LIBRARY_VERSION3_LIST="
> @@ -2768,6 +2770,8 @@ on2avc_decoder_select="mdct"
>  opus_decoder_deps="swresample"
>  opus_decoder_select="mdct15"
>  opus_encoder_select="audio_frame_queue mdct15"
> +libparreader_demuxer_deps="libparreader"
> +libparreader_muxer_deps="libparreader"
>  png_decoder_deps="zlib"
>  png_encoder_deps="zlib"
>  png_encoder_select="llvidencdsp"
> @@ -6136,7 +6140,10 @@ for func in $COMPLEX_FUNCS; do
>  done
> 
>  # these are off by default, so fail if requested and not available
> +
>  enabled cuda_nvcc         && { check_nvcc || die "ERROR: failed checking
> for nvcc."; }
> +enabled libparreader  && require libparreader "parreader.h
> parreader_types.h" parReader_getPicStructSize -lparreader
> +enabled cuda_sdk          && require cuda_sdk cuda.h cuCtxCreate -lcuda
>  enabled chromaprint       && require chromaprint chromaprint.h
> chromaprint_get_version -lchromaprint
>  enabled decklink          && { require_headers DeckLinkAPI.h &&
>                                 { test_cpp_condition DeckLinkAPIVersion.h
> "BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a090500" || die "ERROR: Decklink API
> version must be >= 10.9.5."; } }
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index a434b005a4..363159009d 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -569,11 +569,21 @@ OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER)      +=
> yuv4mpegdec.o
>  OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER)        += yuv4mpegenc.o
> 
>  # external library muxers/demuxers
> +OBJS-$(CONFIG_ADAUDIO_DEMUXER)           += adaudio.o
> +OBJS-$(CONFIG_ADBINARY_DEMUXER)          += adbinary.o adcommon.o adjfif.o
> +OBJS-$(CONFIG_ADMIME_DEMUXER)            += admime.o adcommon.o adjfif.o
> +OBJS-$(CONFIG_ADRAW_DEMUXER)             += adraw.o adcommon.o adjfif.o
>  OBJS-$(CONFIG_AVISYNTH_DEMUXER)          += avisynth.o
>  OBJS-$(CONFIG_CHROMAPRINT_MUXER)         += chromaprint.o
> +OBJS-$(CONFIG_DM_PROTOCOL)               += dsenc.o ds.o
> +OBJS-$(CONFIG_DSPIC_DEMUXER)             += ds.o dspic.o adcommon.o
>  OBJS-$(CONFIG_LIBGME_DEMUXER)            += libgme.o
>  OBJS-$(CONFIG_LIBMODPLUG_DEMUXER)        += libmodplug.o
>  OBJS-$(CONFIG_LIBOPENMPT_DEMUXER)        += libopenmpt.o
> +OBJS-$(CONFIG_LIBPARREADER_DEMUXER)      += libpar.o adcommon.o
> +OBJS-$(CONFIG_LIBPARREADER_MUXER)     += libpar.o adcommon.o
> +OBJS-$(CONFIG_LIBMODPLUG_DEMUXER)        += libmodplug.o
> +OBJS-$(CONFIG_NETVU_PROTOCOL)            += netvu.o
>  OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER)       += vapoursynth.o
> 
>  # protocols I/O
> diff --git a/libavformat/adaudio.c b/libavformat/adaudio.c
> new file mode 100644
> index 0000000000..0c28497953
> --- /dev/null
> +++ b/libavformat/adaudio.c
> @@ -0,0 +1,137 @@
> +/*
> + * AD-Holdings demuxer for AD audio stream format
> + * Copyright (c) 2006-2010 AD-Holdings plc
> + * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg 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; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg 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 FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * AD-Holdings demuxer for AD audio stream format
> + */
> +
> +#include "avformat.h"
> +#include "ds_exports.h"
> +#include "adpic.h"
> +
> +
> +#define SIZEOF_RTP_HEADER       12
> +
> +
> +static int adaudio_probe(AVProbeData *p);
> +static int adaudio_read_header(AVFormatContext *s);
> +static int adaudio_read_packet(struct AVFormatContext *s, AVPacket *pkt);
> +
Unnecessary forward declarations
> +
> +static int adaudio_probe(AVProbeData *p)
> +{
> +    if( p->buf_size < 4 )
> +        return 0;
> +
> +    /* Check the value of the first byte and the payload byte */
> +    if(
> +        p->buf[0] == 0x80 &&
> +        (
> +            p->buf[1] == RTP_PAYLOAD_TYPE_8000HZ_ADPCM ||
> +            p->buf[1] == RTP_PAYLOAD_TYPE_11025HZ_ADPCM ||
> +            p->buf[1] == RTP_PAYLOAD_TYPE_16000HZ_ADPCM ||
> +            p->buf[1] == RTP_PAYLOAD_TYPE_22050HZ_ADPCM
> +        )
> +    ) {
This is not K&R.
> +        return AVPROBE_SCORE_MAX;
> +    }
> +
> +    return 0;
> +}
> +
> +static int adaudio_read_header(AVFormatContext *s)
> +{
> +    s->ctx_flags |= AVFMTCTX_NOHEADER;
> +
> +    return 0;
> +}
> +
> +static int adaudio_read_packet(struct AVFormatContext *s, AVPacket *pkt)
"struct" is unnecessary.
> +{
> +    AVIOContext *         ioContext = s->pb;
CamelCase is illegal in FFmpeg except for type names. Use underscores.
> +    int                   retVal = AVERROR(EIO);
> +    int                   packetSize = 0;
> +    int                   sampleSize = 0;
> +    AVStream *            st = NULL;
> +    int                   isPacketAlloced = 0;
> +#ifdef AD_SIDEDATA_IN_PRIV
> +    struct ADFrameData *    frameData = NULL;
> +#endif
If you are adding new elements to AVPacket
> +
> +    /* Get the next packet */
> +    if( (packetSize = ioContext->read_packet( ioContext->opaque,
> ioContext->buf_ptr, ioContext->buffer_size )) > 0 ) {
Who says that ioContext has a read_packet function at all? Why are you
relying on its buffer_size having the right value?
Furthermore, the style with a space after opening parentheses and
before closing parentheses is uncommon here.
> +        /* Validate the 12 byte RTP header as best we can */
> +        if( ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_8000HZ_ADPCM ||
> +            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_11025HZ_ADPCM ||
> +            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_16000HZ_ADPCM ||
> +            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_22050HZ_ADPCM
> +          ) {
> +            /* Calculate the size of the sample data */
> +            sampleSize = packetSize - SIZEOF_RTP_HEADER;
> +
> +            /* Create a new AVPacket */
> +            if( av_new_packet( pkt, sampleSize ) >= 0 ) {
Instead of checking for this to be >= 0, you should rather save the
return value and check it for errors; if an error is indicated, you
return with the right errors (you may use gotos in order to integrate
freeing data for multiple errors). Initializing retVal to AVERROR(EIO)
is unnecessary.
> +                isPacketAlloced = 1;If you do the above, this variable becomes unnecessary, too.
> +
> +                /* Copy data into packet */
> +                audiodata_network2host(pkt->data,
> &ioContext->buf_ptr[SIZEOF_RTP_HEADER], sampleSize);
> +
> +                /* Configure stream info */
> +                if( (st = ad_get_audio_stream(s, NULL)) != NULL ) {
If I see this correctly, then ad_get_audio_stream will always
dereference its second argument unless there are no audio streams
already in s and unless a call to avformat_new_stream fails. So you
have a NULL pointer dereference. Did you really test this patch?
> +                    pkt->stream_index = st->index;
> +                    pkt->duration =  ((int)(AV_TIME_BASE * 1.0));
> +
> +#ifdef AD_SIDEDATA_IN_PRIV
It seems that you are basing this patch on others that modify core
components and structures like AVPacket; but you have not released
said modifications, so that you effectively propose that your private
branch (that is useful only to you as the others don't have it) should
be merged into FFmpeg. Good luck with that.
> +                    if( (frameData = av_malloc(sizeof(*frameData))) !=
> NULL )  {
> +                        /* Set the frame info up */
> +                        frameData->frameType = RTPAudio;
> +                        frameData->frameData =
> (void*)(&ioContext->buf_ptr[1]);
Sample rate changes can already be communicated via sidedata of the
AV_PKT_DATA_PARAM_CHANGE type. It is of course unnecessary to add this
if the parameters don't really change.
> +                        frameData->additionalData = NULL;
> +
> +                        pkt->priv = (void*)frameData;
> +                        retVal = 0;
> +                    }
> +                    else
> +                        retVal = AVERROR(ENOMEM);
> +#endif
> +                }
> +            }
> +        }
> +    }
> +
> +    /* Check whether we need to release the packet data we allocated */
> +    if( retVal < 0 && isPacketAlloced != 0 ) {
> +        av_free_packet( pkt );Why not av_packet_unref?
> +    }
> +
> +    return retVal;
> +}
> +
> +
I'll stop here.

- Andreas
Hendrik Leppkes July 5, 2019, 11:14 p.m. UTC | #2
On Fri, Jul 5, 2019 at 10:53 PM Tom Needham <06needhamt@gmail.com> wrote:
>
> Samples are Available from
>
> https://transfernow.net/131xk9g4u0jt
>
> Signed-off-by: Thomas Needham <06needhamt@gmail.com>
> ---
>  Changelog                     |    1 +
>  configure                     |    7 +
>  libavformat/Makefile          |   10 +
>  libavformat/adaudio.c         |  137 ++++
>  libavformat/adbinary.c        |  609 ++++++++++++++++
>  libavformat/adcommon.c        | 1106 +++++++++++++++++++++++++++++
>  libavformat/adffmpeg_errors.h |   94 +++
>  libavformat/adjfif.c          |  551 ++++++++++++++
>  libavformat/adjfif.h          |   41 ++
>  libavformat/admime.c          |  822 +++++++++++++++++++++
>  libavformat/adpic.h           |  116 +++
>  libavformat/adraw.c           |  131 ++++
>  libavformat/allformats.c      |    7 +
>  libavformat/ds.c              | 1262 +++++++++++++++++++++++++++++++++
>  libavformat/ds.h              |  135 ++++
>  libavformat/ds_exports.h      |  173 +++++
>  libavformat/dsenc.c           |  488 +++++++++++++
>  libavformat/dsenc.h           |   35 +
>  libavformat/dspic.c           |  317 +++++++++
>  libavformat/libpar.c          | 1030 +++++++++++++++++++++++++++
>  libavformat/libpar.h          |   40 ++
>  libavformat/netvu.c           |  214 ++++++
>  libavformat/netvu.h           |   21 +
>  libavformat/version.h         |    4 +-
>  24 files changed, 7349 insertions(+), 2 deletions(-)

Before anyone spends a huge amount of time reviewing 24(!) files and
over 7000(!) lines of code..

This is an implementation of a proprietary format based on a non-free
library (and for some reason despite using a library still needs over
7000 lines of code?)
We don't really accept non-free proprietary library wrappers like this
into ffmpeg, the only possible exceptions not applying here
(open-source with incompatible license, or hardware libraries, and the
second one is even being debated on still).

Outside of that, the code looks like it needs a serious overhaul, and
the submitter does not appear to be the author of most of it, judging
from the comments in the headers, which makes authorship problematic
at best.

- Hendrik
Paul B Mahol July 7, 2019, 9:54 a.m. UTC | #3
On 7/5/19, Tom Needham <06needhamt@gmail.com> wrote:
> Samples are Available from
>
> https://transfernow.net/131xk9g4u0jt
>

Is there native Windows player for those files?
Tom Needham July 8, 2019, 6:44 a.m. UTC | #4
Hi Paul

The only windows player I am aware of can be downloaded from here.

https://www.dedicatedmicros-support.com/software_release/download.php?file=5357

Also, there is a tool that directly converts these PAR files into H264
format which can be obtained from here.

https://www.dedicatedmicros-support.com/software_release/download.php?file=3344

And finally the library I used to create the wrapper can be downloaded from
here.

https://www.dedicatedmicros-support.com/software_release/download.php?file=3347

Thanks

<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=oa-4885-a>
Virus-free.
www.avg.com
<http://www.avg.com/email-signature?utm_medium=email&utm_source=link&utm_campaign=sig-email&utm_content=webmail&utm_term=oa-4885-a>
<#DAB4FAD8-2DD7-40BB-A1B8-4E2AA1F9FDF2>

On Sun, 7 Jul 2019 at 10:55, Paul B Mahol <onemda@gmail.com> wrote:

> On 7/5/19, Tom Needham <06needhamt@gmail.com> wrote:
> > Samples are Available from
> >
> > https://transfernow.net/131xk9g4u0jt
> >
>
> Is there native Windows player for those files?
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel@ffmpeg.org
> https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>
> To unsubscribe, visit link above, or email
> ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
diff mbox

Patch

diff --git a/Changelog b/Changelog
index 86167b76a1..41d12c092e 100644
--- a/Changelog
+++ b/Changelog
@@ -35,6 +35,7 @@  version <next>:
 - IFV demuxer
 - derain filter
 - deesser filter
+- AD Holdings PAR Muxer and Demuxer


 version 4.1:
diff --git a/configure b/configure
index 7cea9d4d73..39c4356c00 100755
--- a/configure
+++ b/configure
@@ -317,6 +317,7 @@  External library support:
   --enable-vapoursynth     enable VapourSynth demuxer [no]
   --disable-xlib           disable xlib [autodetect]
   --disable-zlib           disable zlib [autodetect]
+  --enable-libparreader    enable PAR (de)muxing via libparreader [no]

   The following libraries provide various hardware acceleration features:
   --disable-amf            disable AMF video encoding code [autodetect]
@@ -1720,6 +1721,7 @@  EXTERNAL_LIBRARY_NONFREE_LIST="
     libfdk_aac
     openssl
     libtls
+    libparreader
 "

 EXTERNAL_LIBRARY_VERSION3_LIST="
@@ -2768,6 +2770,8 @@  on2avc_decoder_select="mdct"
 opus_decoder_deps="swresample"
 opus_decoder_select="mdct15"
 opus_encoder_select="audio_frame_queue mdct15"
+libparreader_demuxer_deps="libparreader"
+libparreader_muxer_deps="libparreader"
 png_decoder_deps="zlib"
 png_encoder_deps="zlib"
 png_encoder_select="llvidencdsp"
@@ -6136,7 +6140,10 @@  for func in $COMPLEX_FUNCS; do
 done

 # these are off by default, so fail if requested and not available
+
 enabled cuda_nvcc         && { check_nvcc || die "ERROR: failed checking
for nvcc."; }
+enabled libparreader  && require libparreader "parreader.h
parreader_types.h" parReader_getPicStructSize -lparreader
+enabled cuda_sdk          && require cuda_sdk cuda.h cuCtxCreate -lcuda
 enabled chromaprint       && require chromaprint chromaprint.h
chromaprint_get_version -lchromaprint
 enabled decklink          && { require_headers DeckLinkAPI.h &&
                                { test_cpp_condition DeckLinkAPIVersion.h
"BLACKMAGIC_DECKLINK_API_VERSION >= 0x0a090500" || die "ERROR: Decklink API
version must be >= 10.9.5."; } }
diff --git a/libavformat/Makefile b/libavformat/Makefile
index a434b005a4..363159009d 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -569,11 +569,21 @@  OBJS-$(CONFIG_YUV4MPEGPIPE_DEMUXER)      +=
yuv4mpegdec.o
 OBJS-$(CONFIG_YUV4MPEGPIPE_MUXER)        += yuv4mpegenc.o

 # external library muxers/demuxers
+OBJS-$(CONFIG_ADAUDIO_DEMUXER)           += adaudio.o
+OBJS-$(CONFIG_ADBINARY_DEMUXER)          += adbinary.o adcommon.o adjfif.o
+OBJS-$(CONFIG_ADMIME_DEMUXER)            += admime.o adcommon.o adjfif.o
+OBJS-$(CONFIG_ADRAW_DEMUXER)             += adraw.o adcommon.o adjfif.o
 OBJS-$(CONFIG_AVISYNTH_DEMUXER)          += avisynth.o
 OBJS-$(CONFIG_CHROMAPRINT_MUXER)         += chromaprint.o
+OBJS-$(CONFIG_DM_PROTOCOL)               += dsenc.o ds.o
+OBJS-$(CONFIG_DSPIC_DEMUXER)             += ds.o dspic.o adcommon.o
 OBJS-$(CONFIG_LIBGME_DEMUXER)            += libgme.o
 OBJS-$(CONFIG_LIBMODPLUG_DEMUXER)        += libmodplug.o
 OBJS-$(CONFIG_LIBOPENMPT_DEMUXER)        += libopenmpt.o
+OBJS-$(CONFIG_LIBPARREADER_DEMUXER)      += libpar.o adcommon.o
+OBJS-$(CONFIG_LIBPARREADER_MUXER)     += libpar.o adcommon.o
+OBJS-$(CONFIG_LIBMODPLUG_DEMUXER)        += libmodplug.o
+OBJS-$(CONFIG_NETVU_PROTOCOL)            += netvu.o
 OBJS-$(CONFIG_VAPOURSYNTH_DEMUXER)       += vapoursynth.o

 # protocols I/O
diff --git a/libavformat/adaudio.c b/libavformat/adaudio.c
new file mode 100644
index 0000000000..0c28497953
--- /dev/null
+++ b/libavformat/adaudio.c
@@ -0,0 +1,137 @@ 
+/*
+ * AD-Holdings demuxer for AD audio stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * AD-Holdings demuxer for AD audio stream format
+ */
+
+#include "avformat.h"
+#include "ds_exports.h"
+#include "adpic.h"
+
+
+#define SIZEOF_RTP_HEADER       12
+
+
+static int adaudio_probe(AVProbeData *p);
+static int adaudio_read_header(AVFormatContext *s);
+static int adaudio_read_packet(struct AVFormatContext *s, AVPacket *pkt);
+
+
+static int adaudio_probe(AVProbeData *p)
+{
+    if( p->buf_size < 4 )
+        return 0;
+
+    /* Check the value of the first byte and the payload byte */
+    if(
+        p->buf[0] == 0x80 &&
+        (
+            p->buf[1] == RTP_PAYLOAD_TYPE_8000HZ_ADPCM ||
+            p->buf[1] == RTP_PAYLOAD_TYPE_11025HZ_ADPCM ||
+            p->buf[1] == RTP_PAYLOAD_TYPE_16000HZ_ADPCM ||
+            p->buf[1] == RTP_PAYLOAD_TYPE_22050HZ_ADPCM
+        )
+    ) {
+        return AVPROBE_SCORE_MAX;
+    }
+
+    return 0;
+}
+
+static int adaudio_read_header(AVFormatContext *s)
+{
+    s->ctx_flags |= AVFMTCTX_NOHEADER;
+
+    return 0;
+}
+
+static int adaudio_read_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+    AVIOContext *         ioContext = s->pb;
+    int                   retVal = AVERROR(EIO);
+    int                   packetSize = 0;
+    int                   sampleSize = 0;
+    AVStream *            st = NULL;
+    int                   isPacketAlloced = 0;
+#ifdef AD_SIDEDATA_IN_PRIV
+    struct ADFrameData *    frameData = NULL;
+#endif
+
+    /* Get the next packet */
+    if( (packetSize = ioContext->read_packet( ioContext->opaque,
ioContext->buf_ptr, ioContext->buffer_size )) > 0 ) {
+        /* Validate the 12 byte RTP header as best we can */
+        if( ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_8000HZ_ADPCM ||
+            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_11025HZ_ADPCM ||
+            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_16000HZ_ADPCM ||
+            ioContext->buf_ptr[1] == RTP_PAYLOAD_TYPE_22050HZ_ADPCM
+          ) {
+            /* Calculate the size of the sample data */
+            sampleSize = packetSize - SIZEOF_RTP_HEADER;
+
+            /* Create a new AVPacket */
+            if( av_new_packet( pkt, sampleSize ) >= 0 ) {
+                isPacketAlloced = 1;
+
+                /* Copy data into packet */
+                audiodata_network2host(pkt->data,
&ioContext->buf_ptr[SIZEOF_RTP_HEADER], sampleSize);
+
+                /* Configure stream info */
+                if( (st = ad_get_audio_stream(s, NULL)) != NULL ) {
+                    pkt->stream_index = st->index;
+                    pkt->duration =  ((int)(AV_TIME_BASE * 1.0));
+
+#ifdef AD_SIDEDATA_IN_PRIV
+                    if( (frameData = av_malloc(sizeof(*frameData))) !=
NULL )  {
+                        /* Set the frame info up */
+                        frameData->frameType = RTPAudio;
+                        frameData->frameData =
(void*)(&ioContext->buf_ptr[1]);
+                        frameData->additionalData = NULL;
+
+                        pkt->priv = (void*)frameData;
+                        retVal = 0;
+                    }
+                    else
+                        retVal = AVERROR(ENOMEM);
+#endif
+                }
+            }
+        }
+    }
+
+    /* Check whether we need to release the packet data we allocated */
+    if( retVal < 0 && isPacketAlloced != 0 ) {
+        av_free_packet( pkt );
+    }
+
+    return retVal;
+}
+
+
+AVInputFormat ff_adaudio_demuxer = {
+    .name           = "adaudio",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings audio format"),
+    .read_probe     = adaudio_probe,
+    .read_header    = adaudio_read_header,
+    .read_packet    = adaudio_read_packet,
+};
diff --git a/libavformat/adbinary.c b/libavformat/adbinary.c
new file mode 100644
index 0000000000..5808d41c8f
--- /dev/null
+++ b/libavformat/adbinary.c
@@ -0,0 +1,609 @@ 
+/*
+ * AD-Holdings demuxer for AD stream format (binary)
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * AD-Holdings demuxer for AD stream format (binary)
+ */
+
+#include <strings.h>
+
+#include "avformat.h"
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+
+#include "adpic.h"
+#include "adffmpeg_errors.h"
+
+
+enum pkt_offsets { PKT_DATATYPE,
+                   PKT_DATACHANNEL,
+                   PKT_SIZE_BYTE_0,
+                   PKT_SIZE_BYTE_1,
+                   PKT_SIZE_BYTE_2,
+                   PKT_SIZE_BYTE_3,
+                   PKT_SEPARATOR_SIZE
+                 };
+
+typedef struct  {    // PRC 002
+    uint32_t t;
+    uint16_t ms;
+    uint16_t mode;
+} MinimalAudioHeader;
+
+
+static void audioheader_network2host(struct NetVuAudioData *dst, const
uint8_t *src)
+{
+    dst->version              = AV_RB32(src);
+    dst->mode                 = AV_RB32(src + 4);
+    dst->channel              = AV_RB32(src + 8);
+    dst->sizeOfAdditionalData = AV_RB32(src + 12);
+    dst->sizeOfAudioData      = AV_RB32(src + 16);
+    dst->seconds              = AV_RB32(src + 20);
+    dst->msecs                = AV_RB32(src + 24);
+    if ((void*)dst != (const void*)src) // Copy additionalData pointer if
needed
+        memcpy(&dst->additionalData, src + 28, sizeof(unsigned char *));
+}
+
+/**
+ * MPEG4 or H264 video frame with a Netvu header
+ */
+static int adbinary_mpeg(AVFormatContext *s,
+                         AVPacket *pkt,
+                         struct NetVuImageData *vidDat,
+                         char **txtDat)
+{
+    static const int hdrSize = NetVuImageDataHeaderSize;
+    AVIOContext *pb = s->pb;
+    int textSize = 0;
+    int n, status, errorVal = 0;
+
+    n = avio_read(pb, (uint8_t *)vidDat, hdrSize);
+    if (n < hdrSize) {
+        av_log(s, AV_LOG_ERROR, "%s: short of data reading header, "
+                                "expected %d, read %d\n",
+               __func__, hdrSize, n);
+        return ADFFMPEG_AD_ERROR_MPEG4_PIC_BODY;
+    }
+    ad_network2host(vidDat, (uint8_t *)vidDat);
+    if (!pic_version_valid(vidDat->version)) {
+        av_log(s, AV_LOG_ERROR, "%s: invalid pic version 0x%08X\n",
__func__,
+               vidDat->version);
+        return ADFFMPEG_AD_ERROR_MPEG4_PIC_VERSION_VALID;
+    }
+
+    // Get the additional text block
+    textSize = vidDat->start_offset;
+    *txtDat = av_malloc(textSize + 1);
+
+    if (*txtDat == NULL)  {
+        av_log(s, AV_LOG_ERROR, "%s: Failed to allocate memory for
text\n", __func__);
+        return AVERROR(ENOMEM);
+    }
+
+    // Copy the additional text block
+    n = avio_get_str(pb, textSize, *txtDat, textSize+1);
+    if (n < textSize)
+        avio_skip(pb, textSize - n);
+
+    status = av_get_packet(pb, pkt, vidDat->size);
+    if (status < 0)  {
+        av_log(s, AV_LOG_ERROR, "%s: av_get_packet (size %d) failed,
status %d\n",
+               __func__, vidDat->size, status);
+        return ADFFMPEG_AD_ERROR_MPEG4_NEW_PACKET;
+    }
+
+    if ( (vidDat->vid_format == PIC_MODE_MPEG4_411_I) ||
(vidDat->vid_format == PIC_MODE_MPEG4_411_GOV_I) )
+        pkt->flags |= AV_PKT_FLAG_KEY;
+
+    return errorVal;
+}
+
+/**
+ * MPEG4 or H264 video frame with a minimal header
+ */
+static int adbinary_mpeg_minimal(AVFormatContext *s,
+                                AVPacket *pkt, int size, int channel,
+                                struct NetVuImageData *vidDat, char
**text_data,
+                                int adDataType)
+{
+    static const int titleLen  = sizeof(vidDat->title) /
sizeof(vidDat->title[0]);
+    AdContext*       adContext = s->priv_data;
+    AVIOContext *    pb        = s->pb;
+    int              dataSize  = size - (4 + 2);
+    int              errorVal  = 0;
+
+    // Get the minimal video header and copy into generic video data
structure
+    memset(vidDat, 0, sizeof(struct NetVuImageData));
+    vidDat->session_time  = avio_rb32(pb);
+    vidDat->milliseconds  = avio_rb16(pb);
+
+    if ( pb->error || (vidDat->session_time == 0) )  {
+        av_log(s, AV_LOG_ERROR, "%s: Reading header, errorcode %d\n",
+               __func__, pb->error);
+        return ADFFMPEG_AD_ERROR_MPEG4_MINIMAL_GET_BUFFER;
+    }
+    vidDat->version = PIC_VERSION;
+    vidDat->cam = channel + 1;
+    vidDat->utc_offset = adContext->utc_offset;
+    snprintf(vidDat->title, titleLen, "Camera %d", vidDat->cam);
+
+    // Now get the main frame data into a new packet
+    errorVal = av_get_packet(pb, pkt, dataSize);
+    if( errorVal < 0 )  {
+        av_log(s, AV_LOG_ERROR, "%s: av_get_packet (size %d) failed,
status %d\n",
+               __func__, dataSize, errorVal);
+        return ADFFMPEG_AD_ERROR_MPEG4_MINIMAL_NEW_PACKET;
+    }
+
+    if (adContext->streamDatatype == 0)  {
+        //if (adDataType == AD_DATATYPE_MININAL_H264)
+        //    adContext->streamDatatype = PIC_MODE_H264I;
+        //else
+        adContext->streamDatatype = mpegOrH264(AV_RB32(pkt->data));
+    }
+    vidDat->vid_format = adContext->streamDatatype;
+
+    return errorVal;
+}
+
+/**
+ * Audio frame with a Netvu header
+ */
+static int ad_read_audio(AVFormatContext *s,
+                         AVPacket *pkt, int size,
+                         struct NetVuAudioData *data,
+                         enum AVCodecID codec_id)
+{
+    AVIOContext *pb = s->pb;
+    int status;
+
+    // Get the fixed size portion of the audio header
+    size = NetVuAudioDataHeaderSize - sizeof(unsigned char *);
+    if (avio_read( pb, (uint8_t*)data, size) != size)
+        return ADFFMPEG_AD_ERROR_AUDIO_ADPCM_GET_BUFFER;
+
+    // endian fix it...
+    audioheader_network2host(data, (uint8_t*)data);
+
+    // Now get the additional bytes
+    if( data->sizeOfAdditionalData > 0 ) {
+        data->additionalData = av_malloc( data->sizeOfAdditionalData );
+        if( data->additionalData == NULL )
+            return AVERROR(ENOMEM);
+
+        if (avio_read( pb, data->additionalData,
data->sizeOfAdditionalData) != data->sizeOfAdditionalData)
+            return ADFFMPEG_AD_ERROR_AUDIO_ADPCM_GET_BUFFER2;
+    }
+    else
+        data->additionalData = NULL;
+
+    status = av_get_packet(pb, pkt, data->sizeOfAudioData);
+    if (status  < 0)  {
+        av_log(s, AV_LOG_ERROR, "%s: av_get_packet (size %d) failed,
status %d\n",
+               __func__, data->sizeOfAudioData, status);
+        return ADFFMPEG_AD_ERROR_AUDIO_ADPCM_MIME_NEW_PACKET;
+    }
+
+    if (codec_id == AV_CODEC_ID_ADPCM_IMA_WAV)
+        audiodata_network2host(pkt->data, pkt->data,
data->sizeOfAudioData);
+
+    return status;
+}
+
+/**
+ * Audio frame with a minimal header
+ */
+static int adbinary_audio_minimal(AVFormatContext *s,
+                                  AVPacket *pkt, int size,
+                                  struct NetVuAudioData *data)
+{
+    AVIOContext *pb = s->pb;
+    int dataSize = size - (4 + 2 + 2);
+    int status;
+
+    // Get the minimal audio header and copy into generic audio data
structure
+    memset(data, 0, sizeof(struct NetVuAudioData));
+    data->seconds = avio_rb32(pb);
+    data->msecs   = avio_rb16(pb);
+    data->mode    = avio_rb16(pb);
+    if ( pb->error || (data->seconds == 0) )  {
+        av_log(s, AV_LOG_ERROR, "%s: Reading header, errorcode %d\n",
+               __func__, pb->error);
+        return ADFFMPEG_AD_ERROR_MINIMAL_AUDIO_ADPCM_GET_BUFFER;
+    }
+
+    // Now get the main frame data into a new packet
+    status = av_get_packet(pb, pkt, dataSize);
+    if (status < 0)  {
+        av_log(s, AV_LOG_ERROR, "%s: av_get_packet (size %d) failed,
status %d\n",
+               __func__, data->sizeOfAudioData, status);
+        return ADFFMPEG_AD_ERROR_MINIMAL_AUDIO_ADPCM_NEW_PACKET;
+    }
+
+    audiodata_network2host(pkt->data, pkt->data, dataSize);
+
+    return status;
+}
+
+
+
+/**
+ * Identify if the stream as an AD binary stream
+ */
+static int adbinary_probe(AVProbeData *p)
+{
+    int score = 0;
+    unsigned char *dataPtr;
+    uint32_t dataSize;
+    int bufferSize = p->buf_size;
+    uint8_t *bufPtr = p->buf;
+
+    // Netvu protocol can only send adbinary or admime
+    if ( (p->filename) && (av_stristart(p->filename, "netvu://", NULL) ==
1))
+        score += AVPROBE_SCORE_MAX / 4;
+
+    while ((bufferSize >= PKT_SEPARATOR_SIZE) && (score <
AVPROBE_SCORE_MAX))  {
+        dataSize =  (bufPtr[PKT_SIZE_BYTE_0] << 24) +
+                    (bufPtr[PKT_SIZE_BYTE_1] << 16) +
+                    (bufPtr[PKT_SIZE_BYTE_2] << 8 ) +
+                    (bufPtr[PKT_SIZE_BYTE_3]);
+
+        // Sanity check on dataSize
+        if ((dataSize < 6) || (dataSize > 0x1000000))
+            return 0;
+
+        // Maximum of 32 cameras can be connected to a system
+        if (bufPtr[PKT_DATACHANNEL] > 32)
+            return 0;
+
+        dataPtr = &bufPtr[PKT_SEPARATOR_SIZE];
+        bufferSize -= PKT_SEPARATOR_SIZE;
+
+        switch (bufPtr[PKT_DATATYPE])  {
+            case AD_DATATYPE_JPEG:
+            case AD_DATATYPE_MPEG4I:
+            case AD_DATATYPE_MPEG4P:
+            case AD_DATATYPE_H264I:
+            case AD_DATATYPE_H264P:
+                if (bufferSize >= NetVuImageDataHeaderSize) {
+                    struct NetVuImageData test;
+                    ad_network2host(&test, dataPtr);
+                    if (pic_version_valid(test.version))  {
+                        av_log(NULL, AV_LOG_DEBUG, "%s: Detected video
packet\n", __func__);
+                        score += AVPROBE_SCORE_MAX;
+                    }
+                }
+                break;
+            case AD_DATATYPE_JFIF:
+                if (bufferSize >= 2)  {
+                    if ( (*dataPtr == 0xFF) && (*(dataPtr + 1) == 0xD8) )
 {
+                        av_log(NULL, AV_LOG_DEBUG, "%s: Detected JFIF
packet\n", __func__);
+                        score += AVPROBE_SCORE_MAX;
+                    }
+                }
+                break;
+            case AD_DATATYPE_AUDIO_ADPCM:
+                if (bufferSize >= NetVuAudioDataHeaderSize)  {
+                    struct NetVuAudioData test;
+                    audioheader_network2host(&test, dataPtr);
+                    if (test.version == AUD_VERSION)  {
+                        av_log(NULL, AV_LOG_DEBUG, "%s: Detected audio
packet\n", __func__);
+                        score += AVPROBE_SCORE_MAX;
+                    }
+                }
+                break;
+            case AD_DATATYPE_AUDIO_RAW:
+                // We don't handle this format
+                av_log(NULL, AV_LOG_DEBUG, "%s: Detected raw audio packet
(unsupported)\n", __func__);
+                break;
+            case AD_DATATYPE_MINIMAL_MPEG4:
+                if (bufferSize >= 10)  {
+                    uint32_t sec  = AV_RB32(dataPtr);
+                    uint32_t vos  = AV_RB32(dataPtr + 6);
+
+                    if (mpegOrH264(vos) == PIC_MODE_MPEG4_411)  {
+                        // Check for MPEG4 start code in data along with a
timestamp
+                        // of 1980 or later.  Crappy test, but there isn't
much data
+                        // to go on.  Should be able to use milliseconds
<= 1000 but
+                        // servers often send larger values than this,
+                        // nonsensical as that is
+                        if (sec > 315532800)  {
+                            if ((vos >= 0x1B0) && (vos <= 0x1B6)) {
+                                av_log(NULL, AV_LOG_DEBUG, "%s: Detected
minimal MPEG4 packet %u\n", __func__, dataSize);
+                                score += AVPROBE_SCORE_MAX / 4;
+                            }
+                        }
+                        break;
+                    }
+                }
+                // Servers can send h264 identified as MPEG4, so fall
through
+                // to next case
+            //case(AD_DATATYPE_MINIMAL_H264):
+                if (bufferSize >= 10)  {
+                    uint32_t sec  = AV_RB32(dataPtr);
+                    uint32_t vos  = AV_RB32(dataPtr + 6);
+                    // Check for h264 start code in data along with a
timestamp
+                    // of 1980 or later.  Crappy test, but there isn't
much data
+                    // to go on.  Should be able to use milliseconds <=
1000 but
+                    // servers often send larger values than this,
+                    // nonsensical as that is
+                    if (sec > 315532800)  {
+                        if (vos == 0x01) {
+                            av_log(NULL, AV_LOG_DEBUG, "%s: Detected
minimal h264 packet %u\n", __func__, dataSize);
+                            score += AVPROBE_SCORE_MAX / 4;
+                        }
+                    }
+                }
+                break;
+            case AD_DATATYPE_MINIMAL_AUDIO_ADPCM:
+                if (bufferSize >= 8)  {
+                    MinimalAudioHeader test;
+                    test.t     = AV_RB32(dataPtr);
+                    test.ms    = AV_RB16(dataPtr + 4);
+                    test.mode  = AV_RB16(dataPtr + 6);
+
+                    switch(test.mode)  {
+                        case RTP_PAYLOAD_TYPE_8000HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_11025HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_16000HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_22050HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_32000HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_44100HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_48000HZ_ADPCM:
+                        case RTP_PAYLOAD_TYPE_8000HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_11025HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_16000HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_22050HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_32000HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_44100HZ_PCM:
+                        case RTP_PAYLOAD_TYPE_48000HZ_PCM:
+                            av_log(NULL, AV_LOG_DEBUG, "%s: Detected
minimal audio packet\n", __func__);
+                            score += AVPROBE_SCORE_MAX / 4;
+                    }
+                }
+                break;
+            case AD_DATATYPE_LAYOUT:
+                av_log(NULL, AV_LOG_DEBUG, "%s: Detected layout packet\n",
__func__);
+                break;
+            case AD_DATATYPE_INFO:
+                if ( (bufferSize >= 1) && (dataPtr[0] == 0) || (dataPtr[0]
== 1) )  {
+                    av_log(NULL, AV_LOG_DEBUG, "%s: Detected info packet
(%d)\n", __func__, dataPtr[0]);
+                    if ((bufferSize >= 5) && (strncmp(&dataPtr[1], "SITE",
4) == 0))
+                        score += AVPROBE_SCORE_MAX;
+                    else if ((bufferSize >= 15) && (strncmp(&dataPtr[1],
"(JPEG)TARGSIZE", 14) == 0))
+                        score += AVPROBE_SCORE_MAX;
+                    else
+                        score += 5;
+                }
+                break;
+            case AD_DATATYPE_XML_INFO:
+                if (bufferSize >= dataSize)  {
+                    const char *infoString = "<infoList>";
+                    int infoStringLen = strlen(infoString);
+                    if ( (infoStringLen <= dataSize) &&
(av_strncasecmp(dataPtr, infoString, infoStringLen) == 0) )  {
+                        av_log(NULL, AV_LOG_DEBUG, "%s: Detected xml info
packet\n", __func__);
+                        score += AVPROBE_SCORE_MAX;
+                    }
+                }
+                break;
+            case AD_DATATYPE_BMP:
+                av_log(NULL, AV_LOG_DEBUG, "%s: Detected bmp packet\n",
__func__);
+                break;
+            case AD_DATATYPE_PBM:
+                if (bufferSize >= 3)  {
+                    if ((dataPtr[0] == 'P') && (dataPtr[1] >= '1') &&
(dataPtr[1] <= '6'))  {
+                        if (dataPtr[2] == 0x0A)  {
+                            score += AVPROBE_SCORE_MAX;
+                            av_log(NULL, AV_LOG_DEBUG, "%s: Detected pbm
packet\n", __func__);
+                        }
+                    }
+                }
+                break;
+            case AD_DATATYPE_SVARS_INFO:
+                if ( (bufferSize >= 1) && (dataPtr[0] == 0) || (dataPtr[0]
== 1) )  {
+                    av_log(NULL, AV_LOG_DEBUG, "%s: Detected svars info
packet (%d)\n", __func__, dataPtr[0]);
+                    score += 5;
+                }
+                break;
+            default:
+                av_log(NULL, AV_LOG_DEBUG, "%s: Detected unknown packet
type\n", __func__);
+                break;
+        }
+
+        if (dataSize <= bufferSize)  {
+            bufferSize -= dataSize;
+            bufPtr = dataPtr + dataSize;
+        }
+        else  {
+            bufferSize = 0;
+            bufPtr = p->buf;
+        }
+    }
+
+    if (score > AVPROBE_SCORE_MAX)
+        score = AVPROBE_SCORE_MAX;
+
+    av_log(NULL, AV_LOG_DEBUG, "%s: Score %d\n", __func__, score);
+
+    return score;
+}
+
+static int adbinary_read_header(AVFormatContext *s)
+{
+    AdContext *adContext = s->priv_data;
+    return ad_read_header(s, &adContext->utc_offset);
+}
+
+static int adbinary_read_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+    AVIOContext *       pb        = s->pb;
+    void *              payload   = NULL;
+    char *              txtDat    = NULL;
+    int                 errorVal  = -1;
+    unsigned char *     tempbuf   = NULL;
+    enum AVMediaType    mediaType = AVMEDIA_TYPE_UNKNOWN;
+    enum AVCodecID      codecId   = AV_CODEC_ID_NONE;
+    int                 data_type, data_channel;
+    unsigned int        size;
+    uint8_t             temp[6];
+
+    // First read the 6 byte separator
+    if (avio_read(pb, temp, 6) >= 6)  {
+        data_type    = temp[0];
+        data_channel = temp[1];
+        size         = AV_RB32(temp + 2);
+        if (data_type >= AD_DATATYPE_MAX)  {
+            av_log(s, AV_LOG_WARNING, "%s: No handler for data_type = %d",
__func__, data_type);
+            return ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR;
+        }
+        //if (data_channel >= 32)  {
+        //    av_log(s, AV_LOG_WARNING, "%s: Channel number %d too high",
__func__, data_channel);
+        //    return ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR;
+        //}
+        if (size >= 0x1000000)  {
+            av_log(s, AV_LOG_WARNING, "%s: Packet too large, %d bytes",
__func__, size);
+            return ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR;
+        }
+    }
+    else
+        return ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR;
+
+    if (size == 0)  {
+        if(pb->eof_reached)
+            errorVal = AVERROR_EOF;
+        else {
+            av_log(s, AV_LOG_ERROR, "%s: Reading separator, error code
%d\n",
+                   __func__, pb->error);
+            errorVal = ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR;
+        }
+        return errorVal;
+    }
+
+    // Prepare for video or audio read
+    errorVal = initADData(data_type, &mediaType, &codecId, &payload);
+    if (errorVal >= 0)  {
+        // Proceed based on the type of data in this frame
+        switch(data_type) {
+            case AD_DATATYPE_JPEG:
+                errorVal = ad_read_jpeg(s, pkt, payload, &txtDat);
+                break;
+            case AD_DATATYPE_JFIF:
+                errorVal = ad_read_jfif(s, pkt, 0, size, payload, &txtDat);
+                break;
+            case AD_DATATYPE_MPEG4I:
+            case AD_DATATYPE_MPEG4P:
+            case AD_DATATYPE_H264I:
+            case AD_DATATYPE_H264P:
+                errorVal = adbinary_mpeg(s, pkt, payload, &txtDat);
+                break;
+            case AD_DATATYPE_MINIMAL_MPEG4:
+            //case(AD_DATATYPE_MINIMAL_H264):
+                errorVal = adbinary_mpeg_minimal(s, pkt, size,
data_channel,
+                                                 payload, &txtDat,
data_type);
+                break;
+            case AD_DATATYPE_MINIMAL_AUDIO_ADPCM:
+                errorVal = adbinary_audio_minimal(s, pkt, size, payload);
+                break;
+            case AD_DATATYPE_AUDIO_ADPCM:
+                errorVal = ad_read_audio(s, pkt, size, payload,
AV_CODEC_ID_ADPCM_IMA_WAV);
+                break;
+            case AD_DATATYPE_INFO:
+            case AD_DATATYPE_XML_INFO:
+            case AD_DATATYPE_SVARS_INFO:
+                // May want to handle INFO, XML_INFO and SVARS_INFO
separately in future
+                errorVal = ad_read_info(s, pkt, size);
+                break;
+            case AD_DATATYPE_LAYOUT:
+                errorVal = ad_read_layout(s, pkt, size);
+                break;
+            case AD_DATATYPE_BMP:
+                // av_dlog(s, "Bitmap overlay\n");
+                tempbuf = av_malloc(size);
+                if (tempbuf)  {
+                    avio_read(pb, tempbuf, size);
+                    av_free(tempbuf);
+                }
+                else
+                    return AVERROR(ENOMEM);
+                return ADFFMPEG_AD_ERROR_DEFAULT;
+            case AD_DATATYPE_PBM:
+                errorVal = ad_read_overlay(s, pkt, data_channel, size,
&txtDat);
+                break;
+            default:
+                av_log(s, AV_LOG_WARNING, "%s: No handler for data_type =
%d  "
+                       "Surrounding bytes = %02x%02x%08x\n",
+                       __func__, data_type, data_type, data_channel, size);
+
+                // Would like to use avio_skip, but that needs seek
support,
+                // so just read the data into a buffer then throw it away
+                tempbuf = av_malloc(size);
+                avio_read(pb, tempbuf, size);
+                av_free(tempbuf);
+
+                return ADFFMPEG_AD_ERROR_DEFAULT;
+        }
+    }
+
+    if (errorVal >= 0)  {
+        errorVal = ad_read_packet(s, pkt, data_channel, mediaType,
codecId, payload, txtDat);
+    }
+    else  {
+        av_log(s, AV_LOG_ERROR, "%s: Error %d creating packet\n",
__func__, errorVal);
+
+#ifdef AD_SIDEDATA_IN_PRIV
+        // If there was an error, release any memory that has been
allocated
+        if (payload != NULL)
+            av_freep(&payload);
+
+        if( txtDat != NULL )
+            av_freep(&txtDat);
+#endif
+    }
+
+#ifndef AD_SIDEDATA_IN_PRIV
+    if (payload != NULL)
+        av_freep(&payload);
+
+    if( txtDat != NULL )
+        av_freep(&txtDat);
+#endif
+
+    return errorVal;
+}
+
+static int adbinary_read_close(AVFormatContext *s)
+{
+    return 0;
+}
+
+
+AVInputFormat ff_adbinary_demuxer = {
+    .name           = "adbinary",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings video format
(binary)"),
+    .priv_data_size = sizeof(AdContext),
+    .read_probe     = adbinary_probe,
+    .read_header    = adbinary_read_header,
+    .read_packet    = adbinary_read_packet,
+    .read_close     = adbinary_read_close,
+    .flags          = AVFMT_TS_DISCONT | AVFMT_VARIABLE_FPS |
AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/adcommon.c b/libavformat/adcommon.c
new file mode 100644
index 0000000000..76a97d4c56
--- /dev/null
+++ b/libavformat/adcommon.c
@@ -0,0 +1,1106 @@ 
+/*
+ * AD-Holdings common functions for demuxers
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#include <strings.h>
+
+#include "internal.h"
+#include "url.h"
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+
+#include "adffmpeg_errors.h"
+#include "adpic.h"
+#include "adjfif.h"
+#include "netvu.h"
+
+
+static const AVRational MilliTB = {1, 1000};
+
+
+int ad_read_header(AVFormatContext *s, int *utcOffset)
+{
+    AVIOContext*    pb          = s->pb;
+    URLContext*     urlContext  = pb->opaque;
+    NetvuContext*   nv          = NULL;
+
+    if (urlContext && urlContext->is_streamed)  {
+        if ( av_stristart(urlContext->filename, "netvu://", NULL) == 1)
+            nv = urlContext->priv_data;
+    }
+    if (nv)  {
+        int ii;
+        char temp[12];
+
+        if (utcOffset)
+            *utcOffset = nv->utc_offset;
+
+        for(ii = 0; ii < NETVU_MAX_HEADERS; ii++)  {
+            av_dict_set(&s->metadata, nv->hdrNames[ii], nv->hdrs[ii], 0);
+        }
+        if ( (nv->utc_offset >= 0) && (nv->utc_offset <= 1440) )  {
+            snprintf(temp, sizeof(temp), "%d", nv->utc_offset);
+            av_dict_set(&s->metadata, "timezone", temp, 0);
+        }
+    }
+
+    s->ctx_flags |= AVFMTCTX_NOHEADER;
+    return 0;
+}
+
+void ad_network2host(struct NetVuImageData *pic, uint8_t *data)
+{
+    pic->version                = AV_RB32(data + 0);
+    pic->mode                   = AV_RB32(data + 4);
+    pic->cam                    = AV_RB32(data + 8);
+    pic->vid_format             = AV_RB32(data + 12);
+    pic->start_offset           = AV_RB32(data + 16);
+    pic->size                   = AV_RB32(data + 20);
+    pic->max_size               = AV_RB32(data + 24);
+    pic->target_size            = AV_RB32(data + 28);
+    pic->factor                 = AV_RB32(data + 32);
+    pic->alm_bitmask_hi         = AV_RB32(data + 36);
+    pic->status                 = AV_RB32(data + 40);
+    pic->session_time           = AV_RB32(data + 44);
+    pic->milliseconds           = AV_RB32(data + 48);
+    if ((uint8_t *)pic != data)  {
+        memcpy(pic->res,    data + 52, 4);
+        memcpy(pic->title,  data + 56, 31);
+        memcpy(pic->alarm,  data + 87, 31);
+    }
+    pic->format.src_pixels      = AV_RB16(data + 118);
+    pic->format.src_lines       = AV_RB16(data + 120);
+    pic->format.target_pixels   = AV_RB16(data + 122);
+    pic->format.target_lines    = AV_RB16(data + 124);
+    pic->format.pixel_offset    = AV_RB16(data + 126);
+    pic->format.line_offset     = AV_RB16(data + 128);
+    if ((uint8_t *)pic != data)
+        memcpy(pic->locale, data + 130, 30);
+    pic->utc_offset             = AV_RB32(data + 160);
+    pic->alm_bitmask            = AV_RB32(data + 164);
+}
+
+static AVStream * netvu_get_stream(AVFormatContext *s, struct
NetVuImageData *p)
+{
+    time_t dateSec;
+    char dateStr[18];
+    AVStream *stream = ad_get_vstream(s,
+                                      p->format.target_pixels,
+                                      p->format.target_lines,
+                                      p->cam,
+                                      p->vid_format,
+                                      p->title);
+    stream->start_time = p->session_time * 1000LL + p->milliseconds;
+    dateSec = p->session_time;
+    strftime(dateStr, sizeof(dateStr), "%Y-%m-%d %H:%MZ",
gmtime(&dateSec));
+    av_dict_set(&stream->metadata, "date", dateStr, 0);
+    return stream;
+}
+
+int ad_adFormatToCodecId(AVFormatContext *s, int32_t adFormat)
+{
+    int codec_id = AV_CODEC_ID_NONE;
+
+    switch(adFormat) {
+        case PIC_MODE_JPEG_422:
+        case PIC_MODE_JPEG_411:
+            codec_id = AV_CODEC_ID_MJPEG;
+            break;
+
+        case PIC_MODE_MPEG4_411:
+        case PIC_MODE_MPEG4_411_I:
+        case PIC_MODE_MPEG4_411_GOV_P:
+        case PIC_MODE_MPEG4_411_GOV_I:
+            codec_id = AV_CODEC_ID_MPEG4;
+            break;
+
+        case PIC_MODE_H264I:
+        case PIC_MODE_H264P:
+        case PIC_MODE_H264J:
+            codec_id = AV_CODEC_ID_H264;
+            break;
+
+        default:
+            av_log(s, AV_LOG_WARNING,
+                   "ad_get_stream: unrecognised vid_format %d\n",
+                   adFormat);
+            codec_id = AV_CODEC_ID_NONE;
+    }
+    return codec_id;
+}
+
+AVStream * ad_get_vstream(AVFormatContext *s, uint16_t w, uint16_t h,
uint8_t cam, int32_t format, const char *title)
+{
+    uint8_t codec_type = 0;
+    int codec_id, id;
+    int i, found;
+    char textbuffer[4];
+    AVStream *st;
+
+    codec_id = ad_adFormatToCodecId(s, format);
+    if (codec_id == AV_CODEC_ID_MJPEG)
+        codec_type = 0;
+    else if (codec_id == AV_CODEC_ID_MPEG4)
+        codec_type = 1;
+    else if (codec_id)
+        codec_type = 2;
+
+    id = ((codec_type & 0x0003) << 29) |
+         (((cam - 1)  & 0x001F) << 24) |
+         (((w >> 4)   & 0x0FFF) << 12) |
+         (((h >> 4)   & 0x0FFF) << 0);
+
+    found = FALSE;
+    for (i = 0; i < s->nb_streams; i++) {
+        st = s->streams[i];
+        if (st->id == id) {
+            found = TRUE;
+            break;
+        }
+    }
+    if (!found) {
+        st = avformat_new_stream(s, NULL);
+        if (st) {
+            st->id = id;
+            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+            st->codec->codec_id = codec_id;
+            st->codec->width = w;
+            st->codec->height = h;
+            st->index = i;
+
+            // Set pixel aspect ratio, display aspect is (sar * width /
height)
+            // May get overridden by codec
+            if( (st->codec->width > 360) && (st->codec->height < 480) )
+                st->sample_aspect_ratio = (AVRational) { 1, 2 };
+            else
+                st->sample_aspect_ratio = (AVRational) { 1, 1 };
+
+            // Use milliseconds as the time base
+            st->r_frame_rate = MilliTB;
+            avpriv_set_pts_info(st, 32, MilliTB.num, MilliTB.den);
+            st->codec->time_base = MilliTB;
+
+            if (title)
+                av_dict_set(&st->metadata, "title", title, 0);
+            snprintf(textbuffer, sizeof(textbuffer), "%u", cam);
+            av_dict_set(&st->metadata, "track", textbuffer, 0);
+
+            av_dict_set(&st->metadata, "type", "camera", 0);
+        }
+    }
+    return st;
+}
+
+static unsigned int RSHash(int camera, const char *name, unsigned int len)
+{
+    unsigned int b    = 378551;
+    unsigned int a    = (camera << 16) + (camera << 8) + camera;
+    unsigned int hash = 0;
+    unsigned int i    = 0;
+
+    for(i = 0; i < len; name++, i++)  {
+        hash = hash * a + (*name);
+        a    = a * b;
+    }
+    return hash;
+}
+
+static AVStream * ad_get_overlay_stream(AVFormatContext *s, int channel,
const char *title)
+{
+    static const int codec_id = AV_CODEC_ID_PBM;
+    unsigned int id;
+    int i, found;
+    AVStream *st;
+
+    id = RSHash(channel+1, title, strlen(title));
+
+    found = FALSE;
+    for (i = 0; i < s->nb_streams; i++) {
+        st = s->streams[i];
+        if ((st->codec->codec_id == codec_id) && (st->id == id)) {
+            found = TRUE;
+            break;
+        }
+    }
+    if (!found) {
+        st = avformat_new_stream(s, NULL);
+        if (st) {
+            st->id = id;
+            st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+            st->codec->codec_id = codec_id;
+            st->index = i;
+
+            // Use milliseconds as the time base
+            st->r_frame_rate = MilliTB;
+            avpriv_set_pts_info(st, 32, MilliTB.num, MilliTB.den);
+            st->codec->time_base = MilliTB;
+
+            av_dict_set(&st->metadata, "title", title, 0);
+            av_dict_set(&st->metadata, "type", "mask", 0);
+        }
+    }
+    return st;
+}
+
+AVStream * ad_get_audio_stream(AVFormatContext *s, struct NetVuAudioData*
audioHeader)
+{
+    int i, found;
+    AVStream *st;
+
+    found = FALSE;
+    for( i = 0; i < s->nb_streams; i++ ) {
+        st = s->streams[i];
+        if ( (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) && (st->id ==
audioHeader->channel) ) {
+            found = TRUE;
+            break;
+        }
+    }
+
+    // Did we find our audio stream? If not, create a new one
+    if( !found ) {
+        st = avformat_new_stream(s, NULL);
+        if (st) {
+            st->id = audioHeader->channel;
+            st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+            st->codec->channels = 1;
+            st->codec->block_align = 0;
+
+            if (audioHeader)  {
+                switch(audioHeader->mode)  {
+                    default:
+                    case(RTP_PAYLOAD_TYPE_8000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_16000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_44100HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_11025HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_22050HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_32000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_48000HZ_ADPCM):
+                        st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                        st->codec->bits_per_coded_sample = 4;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_8000HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_16000HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_44100HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_11025HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_22050HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_32000HZ_PCM):
+                    case(RTP_PAYLOAD_TYPE_48000HZ_PCM):
+                        st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                        st->codec->bits_per_coded_sample = 16;
+                        break;
+                }
+                switch(audioHeader->mode)  {
+                    default:
+                    case(RTP_PAYLOAD_TYPE_8000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_8000HZ_PCM):
+                        st->codec->sample_rate = 8000;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_16000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_16000HZ_PCM):
+                        st->codec->sample_rate = 16000;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_44100HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_44100HZ_PCM):
+                        st->codec->sample_rate = 441000;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_11025HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_11025HZ_PCM):
+                        st->codec->sample_rate = 11025;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_22050HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_22050HZ_PCM):
+                        st->codec->sample_rate = 22050;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_32000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_32000HZ_PCM):
+                        st->codec->sample_rate = 32000;
+                        break;
+                    case(RTP_PAYLOAD_TYPE_48000HZ_ADPCM):
+                    case(RTP_PAYLOAD_TYPE_48000HZ_PCM):
+                        st->codec->sample_rate = 48000;
+                        break;
+                }
+            }
+            else  {
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 8000;
+            }
+            avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+            st->index = i;
+        }
+    }
+
+    return st;
+}
+
+/**
+ * Returns the data stream associated with the current connection.
+ *
+ * If there isn't one already, a new one will be created and added to the
+ * AVFormatContext passed in.
+ *
+ * \param s Pointer to AVFormatContext
+ * \return Pointer to the data stream on success, NULL on failure
+ */
+static AVStream * ad_get_data_stream(AVFormatContext *s, enum AVCodecID
codecId)
+{
+    int i, found = FALSE;
+    AVStream *st = NULL;
+
+    for (i = 0; i < s->nb_streams && !found; i++ ) {
+        st = s->streams[i];
+        if (st->id == codecId)
+            found = TRUE;
+    }
+
+    // Did we find our data stream? If not, create a new one
+    if( !found ) {
+        st = avformat_new_stream(s, NULL);
+        if (st) {
+            st->id = codecId;
+            st->codec->codec_type = AVMEDIA_TYPE_SUBTITLE;
+            st->codec->codec_id = AV_CODEC_ID_TEXT;
+
+            // Use milliseconds as the time base
+            //st->r_frame_rate = MilliTB;
+            avpriv_set_pts_info(st, 32, MilliTB.num, MilliTB.den);
+            //st->codec->time_base = MilliTB;
+
+            st->index = i;
+        }
+    }
+    return st;
+}
+
+//static AVStream *ad_get_stream(AVFormatContext *s, enum AVMediaType
media,
+//                               enum AVCodecID codecId, void *data)
+//{
+//    switch (media)  {
+//        case(AVMEDIA_TYPE_VIDEO):
+//            if (codecId == AV_CODEC_ID_PBM)
+//                return ad_get_overlay_stream(s, (const char *)data);
+//            else
+//                return netvu_get_stream(s, (struct NetVuImageData
*)data);
+//            break;
+//        case(AVMEDIA_TYPE_AUDIO):
+//            return ad_get_audio_stream(s, (struct NetVuAudioData *)data);
+//            break;
+//        case(AVMEDIA_TYPE_DATA):
+//            return ad_get_data_stream(s);
+//            break;
+//        default:
+//            break;
+//    }
+//    return NULL;
+//}
+
+#ifdef AD_SIDEDATA_IN_PRIV
+static void ad_release_packet( AVPacket *pkt )
+{
+    if (pkt == NULL)
+        return;
+
+    if (pkt->priv)  {
+        // Have a look what type of frame we have and then free as
appropriate
+        struct ADFrameData *frameData = (struct ADFrameData *)pkt->priv;
+        struct NetVuAudioData *audHeader;
+
+        switch(frameData->frameType)  {
+            case(NetVuAudio):
+                audHeader = (struct NetVuAudioData *)frameData->frameData;
+                if( audHeader->additionalData )
+                    av_free( audHeader->additionalData );
+            case(NetVuVideo):
+            case(DMVideo):
+            case(DMNudge):
+            case(NetVuDataInfo):
+            case(NetVuDataLayout):
+            case(RTPAudio):
+                av_freep(&frameData->frameData);
+                av_freep(&frameData->additionalData);
+                break;
+            default:
+                // Error, unrecognised frameType
+                break;
+        }
+        av_freep(&pkt->priv);
+    }
+
+    // Now use the default routine to release the rest of the packet's
resources
+    av_destruct_packet( pkt );
+}
+#endif
+
+#ifdef AD_SIDEDATA_IN_PRIV
+int ad_new_packet(AVPacket *pkt, int size)
+{
+    int retVal = av_new_packet( pkt, size );
+    pkt->priv = NULL;
+    if( retVal >= 0 ) {
+        // Give the packet its own destruct function
+        pkt->destruct = ad_release_packet;
+    }
+
+    return retVal;
+}
+
+static void ad_keyvalsplit(const char *line, char *key, char *val)
+{
+    int ii, jj = 0;
+    int len = strlen(line);
+    int inKey, inVal;
+
+    inKey = 1;
+    inVal = 0;
+    for (ii = 0; ii < len; ii++)  {
+        if (inKey)  {
+            if (line[ii] == ':')  {
+                key[jj++] = '\0';
+                inKey = 0;
+                inVal = 1;
+                jj = 0;
+            }
+            else  {
+                key[jj++] = line[ii];
+            }
+        }
+        else if (inVal)  {
+            val[jj++] = line[ii];
+        }
+    }
+    val[jj++] = '\0';
+}
+
+static int ad_splitcsv(const char *csv, int *results, int maxElements, int
base)
+{
+    int ii, jj, ee;
+    char element[8];
+    int len = strlen(csv);
+
+    for (ii = 0, jj = 0, ee = 0; ii < len; ii++)  {
+        if ((csv[ii] == ',') || (csv[ii] == ';') || (ii == (len -1)))  {
+            element[jj++] = '\0';
+            if (base == 10)
+                sscanf(element, "%d", &results[ee++]);
+            else
+                sscanf(element, "%x", &results[ee++]);
+            if (ee >= maxElements)
+                break;
+            jj = 0;
+        }
+        else
+            element[jj++] = csv[ii];
+    }
+    return ee;
+}
+
+static void ad_parseVSD(const char *vsd, struct ADFrameData *frame)
+{
+    char key[64], val[128];
+    ad_keyvalsplit(vsd, key, val);
+
+    if ((strlen(key) == 2) && (key[0] == 'M'))  {
+        switch(key[1])  {
+            case('0'):
+                ad_splitcsv(val, frame->vsd[VSD_M0], VSDARRAYLEN, 10);
+                break;
+            case('1'):
+                ad_splitcsv(val, frame->vsd[VSD_M1], VSDARRAYLEN, 10);
+                break;
+            case('2'):
+                ad_splitcsv(val, frame->vsd[VSD_M2], VSDARRAYLEN, 10);
+                break;
+            case('3'):
+                ad_splitcsv(val, frame->vsd[VSD_M3], VSDARRAYLEN, 10);
+                break;
+            case('4'):
+                ad_splitcsv(val, frame->vsd[VSD_M4], VSDARRAYLEN, 10);
+                break;
+            case('5'):
+                ad_splitcsv(val, frame->vsd[VSD_M5], VSDARRAYLEN, 10);
+                break;
+            case('6'):
+                ad_splitcsv(val, frame->vsd[VSD_M6], VSDARRAYLEN, 10);
+                break;
+        }
+    }
+    else if ((strlen(key) == 3) && (av_strncasecmp(key, "FM0", 3) == 0))  {
+        ad_splitcsv(val, frame->vsd[VSD_FM0], VSDARRAYLEN, 10);
+    }
+    else if ((strlen(key) == 1) && (key[0] == 'F')) {
+        ad_splitcsv(val, frame->vsd[VSD_F], VSDARRAYLEN, 16);
+    }
+    else if ((strlen(key) == 3) && (av_strncasecmp(key, "EM0", 3) == 0))  {
+        ad_splitcsv(val, frame->vsd[VSD_EM0], VSDARRAYLEN, 10);
+    }
+    else
+        av_log(NULL, AV_LOG_DEBUG, "Unknown VSD key: %s:  Val: %s", key,
val);
+}
+
+static void ad_parseLine(AVFormatContext *s, const char *line, struct
ADFrameData *frame)
+{
+    char key[32], val[128];
+    ad_keyvalsplit(line, key, val);
+
+    if (av_strncasecmp(key, "Active-zones", 12) == 0)  {
+        sscanf(val, "%d", &frame->activeZones);
+    }
+    else if (av_strncasecmp(key, "FrameNum", 8) == 0)  {
+        sscanf(val, "%u", &frame->frameNum);
+    }
+//    else if (av_strncasecmp(key, "Site-ID", 7) == 0)  {
+//    }
+    else if (av_strncasecmp(key, "ActMask", 7) == 0)  {
+        for (int ii = 0, jj = 0; (ii < ACTMASKLEN) && (jj < strlen(val));
ii++)  {
+            sscanf(&val[jj], "0x%04hx", &frame->activityMask[ii]);
+            jj += 7;
+        }
+    }
+    else if (av_strncasecmp(key, "VSD", 3) == 0)  {
+        ad_parseVSD(val, frame);
+    }
+}
+
+static void ad_parseText(AVFormatContext *s, struct ADFrameData *frameData)
+{
+    const char *src = frameData->additionalData;
+    int len = strlen(src);
+    char line[512];
+    int ii, jj = 0;
+
+    for (ii = 0; ii < len; ii++)  {
+        if ( (src[ii] == '\r') || (src[ii] == '\n') )  {
+            line[jj++] = '\0';
+            if (strlen(line) > 0)
+                ad_parseLine(s, line, frameData);
+            jj = 0;
+        }
+        else
+            line[jj++] = src[ii];
+    }
+}
+#endif
+
+int initADData(int data_type, enum AVMediaType *mediaType, enum AVCodecID
*codecId, void **payload)
+{
+    switch(data_type)  {
+        case(AD_DATATYPE_JPEG):
+        case(AD_DATATYPE_JFIF):
+        case(AD_DATATYPE_MPEG4I):
+        case(AD_DATATYPE_MPEG4P):
+        case(AD_DATATYPE_H264I):
+        case(AD_DATATYPE_H264P):
+        case(AD_DATATYPE_MINIMAL_MPEG4):
+        //case(AD_DATATYPE_MINIMAL_H264):
+            *payload = av_mallocz( sizeof(struct NetVuImageData) );
+            if( *payload == NULL )
+                return AVERROR(ENOMEM);
+            *mediaType = AVMEDIA_TYPE_VIDEO;
+            switch(data_type)  {
+                case(AD_DATATYPE_JPEG):
+                case(AD_DATATYPE_JFIF):
+                    *codecId = AV_CODEC_ID_MJPEG;
+                    break;
+                case(AD_DATATYPE_MPEG4I):
+                case(AD_DATATYPE_MPEG4P):
+                case(AD_DATATYPE_MINIMAL_MPEG4):
+                    *codecId = AV_CODEC_ID_MPEG4;
+                    break;
+                case(AD_DATATYPE_H264I):
+                case(AD_DATATYPE_H264P):
+                //case(AD_DATATYPE_MINIMAL_H264):
+                    *codecId = AV_CODEC_ID_H264;
+                    break;
+            }
+            break;
+        case(AD_DATATYPE_AUDIO_ADPCM):
+        case(AD_DATATYPE_MINIMAL_AUDIO_ADPCM):
+            *payload = av_malloc( sizeof(struct NetVuAudioData) );
+            if( *payload == NULL )
+                return AVERROR(ENOMEM);
+            *mediaType = AVMEDIA_TYPE_AUDIO;
+            *codecId = AV_CODEC_ID_ADPCM_IMA_WAV;
+            break;
+        case(AD_DATATYPE_INFO):
+        case(AD_DATATYPE_XML_INFO):
+        case(AD_DATATYPE_LAYOUT):
+        case(AD_DATATYPE_SVARS_INFO):
+            *mediaType = AVMEDIA_TYPE_DATA;
+            *codecId = AV_CODEC_ID_FFMETADATA;
+            break;
+        case(AD_DATATYPE_BMP):
+            *mediaType = AVMEDIA_TYPE_VIDEO;
+            *codecId = AV_CODEC_ID_BMP;
+            break;
+        case(AD_DATATYPE_PBM):
+            *mediaType = AVMEDIA_TYPE_VIDEO;
+            *codecId = AV_CODEC_ID_PBM;
+            break;
+        default:
+            *mediaType = AVMEDIA_TYPE_UNKNOWN;
+            *codecId = AV_CODEC_ID_NONE;
+    }
+    return 0;
+}
+
+#if CONFIG_ADBINARY_DEMUXER || CONFIG_ADMIME_DEMUXER
+int ad_read_jpeg(AVFormatContext *s, AVPacket *pkt, struct NetVuImageData
*video_data,
+                 char **text_data)
+{
+    static const int nviSize = NetVuImageDataHeaderSize;
+    AVIOContext *pb = s->pb;
+    int hdrSize;
+    char jfif[2048], *ptr;
+    int n, textSize, errorVal = 0;
+    int status;
+
+    // Check if we've already read a NetVuImageData header
+    // (possible if this is called by adraw demuxer)
+    if (video_data && (!pic_version_valid(video_data->version)))  {
+        // Read the pic structure
+        if ((n = avio_read(pb, (uint8_t*)video_data, nviSize)) != nviSize)
 {
+            av_log(s, AV_LOG_ERROR, "%s: Short of data reading "
+                                    "struct NetVuImageData, expected %d,
read %d\n",
+                                    __func__, nviSize, n);
+            return ADFFMPEG_AD_ERROR_JPEG_IMAGE_DATA_READ;
+        }
+
+        // Endian convert if necessary
+        ad_network2host(video_data, (uint8_t *)video_data);
+    }
+
+    if ((video_data==NULL) || !pic_version_valid(video_data->version))  {
+        av_log(s, AV_LOG_ERROR, "%s: invalid struct NetVuImageData version
"
+                                "0x%08X\n", __func__, video_data->version);
+        return ADFFMPEG_AD_ERROR_JPEG_PIC_VERSION;
+    }
+
+    // Get the additional text block
+    textSize = video_data->start_offset;
+    *text_data = av_malloc(textSize + 1);
+    if( *text_data == NULL )  {
+        av_log(s, AV_LOG_ERROR, "%s: text_data allocation failed "
+                                "(%d bytes)", __func__, textSize + 1);
+        return AVERROR(ENOMEM);
+    }
+
+    // Copy the additional text block
+    if( (n = avio_read( pb, *text_data, textSize )) != textSize )  {
+        av_log(s, AV_LOG_ERROR, "%s: short of data reading text block"
+                                " data, expected %d, read %d\n",
+                                __func__, textSize, n);
+        return ADFFMPEG_AD_ERROR_JPEG_READ_TEXT_BLOCK;
+    }
+
+    // Somtimes the buffer seems to end with a NULL terminator, other
times it
+    // doesn't.  Adding a terminator here regardless
+    (*text_data)[textSize] = '\0';
+
+    // Use the struct NetVuImageData struct to build a JFIF header
+    if ((hdrSize = build_jpeg_header( jfif, video_data, 2048)) <= 0)  {
+        av_log(s, AV_LOG_ERROR, "%s: build_jpeg_header failed\n",
__func__);
+        return ADFFMPEG_AD_ERROR_JPEG_HEADER;
+    }
+    // We now know the packet size required for the image, allocate it.
+    if ((status = ad_new_packet(pkt, hdrSize + video_data->size + 2)) < 0)
 {
+        av_log(s, AV_LOG_ERROR, "%s: ad_new_packet %d failed, "
+                                "status %d\n", __func__,
+                                hdrSize + video_data->size + 2, status);
+        return ADFFMPEG_AD_ERROR_JPEG_NEW_PACKET;
+    }
+    ptr = pkt->data;
+    // Copy the JFIF header into the packet
+    memcpy(ptr, jfif, hdrSize);
+    ptr += hdrSize;
+    // Now get the compressed JPEG data into the packet
+    if ((n = avio_read(pb, ptr, video_data->size)) != video_data->size) {
+        av_log(s, AV_LOG_ERROR, "%s: short of data reading pic body, "
+                                "expected %d, read %d\n", __func__,
+                                video_data->size, n);
+        return ADFFMPEG_AD_ERROR_JPEG_READ_BODY;
+    }
+    ptr += video_data->size;
+    // Add the EOI marker
+    *ptr++ = 0xff;
+    *ptr++ = 0xd9;
+
+    return errorVal;
+}
+
+int ad_read_jfif(AVFormatContext *s, AVPacket *pkt, int imgLoaded, int
size,
+                 struct NetVuImageData *video_data, char **text_data)
+{
+    int n, status, errorVal = 0;
+    AVIOContext *pb = s->pb;
+
+    if(!imgLoaded) {
+        if ((status = ad_new_packet(pkt, size)) < 0) { // PRC 003
+            av_log(s, AV_LOG_ERROR, "ad_read_jfif: ad_new_packet %d
failed, status %d\n", size, status);
+            return ADFFMPEG_AD_ERROR_JFIF_NEW_PACKET;
+        }
+
+        if ((n = avio_read(pb, pkt->data, size)) < size) {
+            av_log(s, AV_LOG_ERROR, "ad_read_jfif: short of data reading
jfif image, expected %d, read %d\n", size, n);
+            return ADFFMPEG_AD_ERROR_JFIF_GET_BUFFER;
+        }
+    }
+    if ( parse_jfif(s, pkt->data, video_data, size, text_data ) <= 0) {
+        av_log(s, AV_LOG_ERROR, "ad_read_jfif: parse_jfif failed\n");
+        return ADFFMPEG_AD_ERROR_JFIF_MANUAL_SIZE;
+    }
+    return errorVal;
+}
+
+/**
+ * Data info extraction. A DATA_INFO frame simply contains a
+ * type byte followed by a text block. Due to its simplicity, we'll
+ * just extract the whole block into the AVPacket's data structure and
+ * let the client deal with checking its type and parsing its text
+ *
+ * \todo Parse the string and use the information to add metadata and/or
update
+ *       the struct NetVuImageData struct
+ *
+ * Example strings, taken from a minimal mp4 stream: (Prefixed by a zero
byte)
+ * SITE DVIP3S;CAM 1:TV;(JPEG)TARGSIZE 0:(MPEG)BITRATE 262144;IMAGESIZE
0,0:704,256;
+ * SITE DVIP3S;CAM 2:Directors office;(JPEG)TARGSIZE 0:(MPEG)BITRATE
262144;IMAGESIZE 0,0:704,256;
+ * SITE DVIP3S;CAM 3:Development;(JPEG)TARGSIZE 0:(MPEG)BITRATE
262144;IMAGESIZE 0,0:704,256;
+ * SITE DVIP3S;CAM 4:Rear road;(JPEG)TARGSIZE 0:(MPEG)BITRATE
262144;IMAGESIZE 0,0:704,256;
+ */
+int ad_read_info(AVFormatContext *s, AVPacket *pkt, int size)
+{
+    int n, status, errorVal = 0;
+    AVIOContext *pb = s->pb;
+    //uint8_t dataDatatype;
+
+    // Allocate a new packet
+    if( (status = ad_new_packet( pkt, size )) < 0 )
+        return ADFFMPEG_AD_ERROR_INFO_NEW_PACKET;
+
+    //dataDatatype = avio_r8(pb);
+    //--size;
+
+    // Get the data
+    if( (n = avio_read( pb, pkt->data, size)) != size )
+        return ADFFMPEG_AD_ERROR_INFO_GET_BUFFER;
+
+    return errorVal;
+}
+
+int ad_read_layout(AVFormatContext *s, AVPacket *pkt, int size)
+{
+    int n, status, errorVal = 0;
+    AVIOContext *pb = s->pb;
+
+    // Allocate a new packet
+    if( (status = ad_new_packet( pkt, size )) < 0 )
+        return ADFFMPEG_AD_ERROR_LAYOUT_NEW_PACKET;
+
+    // Get the data
+    if( (n = avio_read( pb, pkt->data, size)) != size )
+        return ADFFMPEG_AD_ERROR_LAYOUT_GET_BUFFER;
+
+    return errorVal;
+}
+
+int ad_read_overlay(AVFormatContext *s, AVPacket *pkt, int channel, int
insize, char **text_data)
+{
+    AdContext* adContext = s->priv_data;
+    AVIOContext *pb      = s->pb;
+    AVStream *st         = NULL;
+    uint8_t *inbuf       = NULL;
+    int n, w, h;
+    char *comment = NULL;
+
+    inbuf = av_malloc(insize);
+    n = avio_read(pb, inbuf, insize);
+    if (n != insize)  {
+        av_log(s, AV_LOG_ERROR, "%s: short of data reading pbm data body,
expected %d, read %d\n", __func__, insize, n);
+        return ADFFMPEG_AD_ERROR_OVERLAY_GET_BUFFER;
+    }
+
+    pkt->size = ad_pbmDecompress(&comment, &inbuf, insize, pkt, &w, &h);
+    if (pkt->size <= 0) {
+ av_log(s, AV_LOG_ERROR, "ADPIC: ad_pbmDecompress failed\n");
+ return ADFFMPEG_AD_ERROR_OVERLAY_PBM_READ;
+ }
+
+    if (text_data)  {
+        int len = 12 + strlen(comment);
+        *text_data = av_malloc(len);
+        snprintf(*text_data, len-1, "Camera %u: %s", channel+1, comment);
+
+        st = ad_get_overlay_stream(s, channel, *text_data);
+        st->codec->width = w;
+        st->codec->height = h;
+    }
+
+    av_free(comment);
+
+    if (adContext)
+        pkt->dts = pkt->pts = adContext->lastVideoPTS;
+
+    return 0;
+}
+#endif
+
+
+int ad_pbmDecompress(char **comment, uint8_t **src, int size, AVPacket
*pkt, int *width, int *height)
+{
+    static const uint8_t pbm[3] = { 0x50, 0x34, 0x0A };
+    static const uint8_t rle[4] = { 0x52, 0x4C, 0x45, 0x20 };
+    int isrle                   = 0;
+    const uint8_t *ptr          = *src;
+    const uint8_t *endPtr       = (*src) + size;
+    uint8_t *dPtr               = NULL;
+    int strSize                 = 0;
+    const char *strPtr          = NULL;
+    const char *endStrPtr       = NULL;
+    unsigned int elementsRead;
+
+    if ((size >= sizeof(pbm)) && (ptr[0] == 'P') && (ptr[1] >= '1') &&
(ptr[1] <= '6') && (ptr[2] == 0x0A) )
+        ptr += sizeof(pbm);
+    else
+        return -1;
+
+    while ( (ptr < endPtr) && (*ptr == '#') )  {
+        ++ptr;
+
+        if ( ((endPtr - ptr) > sizeof(rle)) && (memcmp(ptr, rle,
sizeof(rle)) == 0) )  {
+            isrle = 1;
+            ptr += sizeof(rle);
+        }
+
+        strPtr = ptr;
+        while ( (ptr < endPtr) && (*ptr != 0x0A) )  {
+            ++ptr;
+        }
+        endStrPtr = ptr;
+        strSize = endStrPtr - strPtr;
+
+        if (comment)  {
+            if (*comment)
+                av_free(*comment);
+            *comment = av_malloc(strSize + 1);
+
+            memcpy(*comment, strPtr, strSize);
+            (*comment)[strSize] = '\0';
+        }
+        ++ptr;
+    }
+
+    elementsRead = sscanf(ptr, "%d", width);
+    ptr += sizeof(*width) * elementsRead;
+    elementsRead = sscanf(ptr, "%d", height);
+    ptr += sizeof(*height) * elementsRead;
+
+    if (isrle)  {
+        // Data is Runlength Encoded, alloc a new buffer and decode into it
+        int len;
+        uint8_t val;
+        unsigned int headerSize   = (ptr - *src) - sizeof(rle);
+        unsigned int headerP1Size = sizeof(pbm) + 1;
+        unsigned int headerP2Size = headerSize - headerP1Size;
+        unsigned int dataSize = ((*width) * (*height)) / 8;
+
+        ad_new_packet(pkt, headerSize + dataSize);
+        dPtr = pkt->data;
+
+        memcpy(dPtr, *src, headerP1Size);
+        dPtr += headerP1Size;
+        if (strPtr)  {
+            memcpy(dPtr, strPtr, headerP2Size);
+            dPtr += headerP2Size;
+        }
+
+        // Decompress loop
+        while (ptr < endPtr)  {
+            len = *ptr++;
+            val = *ptr++;
+            do  {
+ len--;
+ *dPtr++ = val;
+ } while(len>0);
+        }
+
+        // Free compressed data
+        av_freep(src);
+
+        return headerSize + dataSize;
+    }
+    else  {
+        ad_new_packet(pkt, size);
+        memcpy(pkt->data, *src, size);
+        av_freep(src);
+        return size;
+    }
+}
+
+static int addSideData(AVFormatContext *s, AVPacket *pkt,
+                       enum AVMediaType media, unsigned int size,
+                       void *data, const char *text)
+{
+#if defined(AD_SIDEDATA_IN_PRIV)
+    struct ADFrameData *frameData = av_mallocz(sizeof(*frameData));
+    if( frameData == NULL )
+        return AVERROR(ENOMEM);
+
+    if (media == AVMEDIA_TYPE_VIDEO)
+        frameData->frameType = NetVuVideo;
+    else
+        frameData->frameType = NetVuAudio;
+
+    frameData->frameData = data;
+    frameData->additionalData = (unsigned char *)text;
+
+    if (text != NULL)
+        ad_parseText(s, frameData);
+    pkt->priv = frameData;
+#elif defined(AD_SIDEDATA)
+    uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_AD_FRAME,
size);
+    if (side)
+        memcpy(side, data, size);
+
+    if (text)  {
+        size = strlen(text) + 1;
+        side = av_packet_new_side_data(pkt, AV_PKT_DATA_AD_TEXT, size);
+        if (side)
+            memcpy(side, text, size);
+    }
+#endif
+    return 0;
+}
+
+int ad_read_packet(AVFormatContext *s, AVPacket *pkt, int channel,
+                   enum AVMediaType media, enum AVCodecID codecId,
+                   void *data, char *text)
+{
+    AdContext *adContext          = s->priv_data;
+    AVStream *st                  = NULL;
+
+    if ((media == AVMEDIA_TYPE_VIDEO) && (codecId == AV_CODEC_ID_PBM))  {
+        // Get or create a data stream
+        if ( (st = ad_get_overlay_stream(s, channel, text)) == NULL ) {
+            av_log(s, AV_LOG_ERROR, "%s: ad_get_overlay_stream failed\n",
__func__);
+            return ADFFMPEG_AD_ERROR_GET_OVERLAY_STREAM;
+        }
+    }
+    else if (media == AVMEDIA_TYPE_VIDEO)  {
+        // At this point We have a legal NetVuImageData structure which we
use
+        // to determine which codec stream to use
+        struct NetVuImageData *video_data = (struct NetVuImageData *)data;
+        if ( (st = netvu_get_stream( s, video_data)) == NULL ) {
+            av_log(s, AV_LOG_ERROR, "ad_read_packet: Failed get_stream for
video\n");
+            return ADFFMPEG_AD_ERROR_GET_STREAM;
+        }
+        else  {
+            if (video_data->session_time > 0)  {
+                pkt->pts = video_data->session_time;
+                pkt->pts *= 1000ULL;
+                pkt->pts += video_data->milliseconds % 1000;
+            }
+            else
+                pkt->pts = AV_NOPTS_VALUE;
+            if (adContext)
+                adContext->lastVideoPTS = pkt->pts;
+
+            // Servers occasionally send insane timezone data, which can
screw
+            // up clients.  Check for this and set to 0
+            if (abs(video_data->utc_offset) > 1440)  {
+                av_log(s, AV_LOG_INFO,
+                       "ad_read_packet: Invalid utc_offset of %d, "
+                       "setting to zero\n", video_data->utc_offset);
+                video_data->utc_offset = 0;
+            }
+
+            if (adContext && (!adContext->metadataSet))  {
+                char utcOffsetStr[12];
+                snprintf(utcOffsetStr, sizeof(utcOffsetStr), "%d",
video_data->utc_offset);
+                av_dict_set(&s->metadata, "locale", video_data->locale, 0);
+                av_dict_set(&s->metadata, "timezone", utcOffsetStr, 0);
+                adContext->metadataSet = 1;
+            }
+
+            addSideData(s, pkt, media, sizeof(struct NetVuImageData),
data, text);
+        }
+    }
+    else if (media == AVMEDIA_TYPE_AUDIO) {
+        // Get the audio stream
+        struct NetVuAudioData *audHdr = (struct NetVuAudioData *)data;
+        if ( (st = ad_get_audio_stream( s, audHdr )) == NULL ) {
+            av_log(s, AV_LOG_ERROR, "ad_read_packet: ad_get_audio_stream
failed\n");
+            return ADFFMPEG_AD_ERROR_GET_AUDIO_STREAM;
+        }
+        else  {
+            if (audHdr->seconds > 0)  {
+                int64_t milliseconds = audHdr->seconds * 1000ULL +
(audHdr->msecs % 1000);
+                pkt->pts = av_rescale_q(milliseconds, MilliTB,
st->time_base);
+            }
+            else
+                pkt->pts = AV_NOPTS_VALUE;
+
+            addSideData(s, pkt, media, sizeof(struct NetVuAudioData),
audHdr, text);
+        }
+    }
+    else if (media == AVMEDIA_TYPE_DATA) {
+        // Get or create a data stream
+        if ( (st = ad_get_data_stream(s, codecId)) == NULL ) {
+            av_log(s, AV_LOG_ERROR, "%s: ad_get_data_stream failed\n",
__func__);
+            return ADFFMPEG_AD_ERROR_GET_INFO_LAYOUT_STREAM;
+        }
+    }
+
+    if (st)  {
+        pkt->stream_index = st->index;
+
+        pkt->duration = 1;
+        pkt->pos = -1;
+    }
+
+    return 0;
+}
+
+void audiodata_network2host(uint8_t *dest, const uint8_t *src, int size)
+{
+    const uint8_t *dataEnd = src + size;
+
+    uint16_t predictor = AV_RB16(src);
+    src += 2;
+
+    AV_WL16(dest, predictor);
+    dest += 2;
+
+    *dest++ = *src++;
+    *dest++ = *src++;
+
+    for (;src < dataEnd; src++, dest++)  {
+        *dest = (((*src) & 0xF0) >> 4) | (((*src) & 0x0F) << 4);
+    }
+}
+
+int mpegOrH264(unsigned int startCode)
+{
+    if ( (startCode & 0xFFFFFF00) == 0x00000100)
+        return PIC_MODE_MPEG4_411;
+    else
+        return PIC_MODE_H264I;
+}
diff --git a/libavformat/adffmpeg_errors.h b/libavformat/adffmpeg_errors.h
new file mode 100644
index 0000000000..97f44676d9
--- /dev/null
+++ b/libavformat/adffmpeg_errors.h
@@ -0,0 +1,94 @@ 
+#ifndef __ADFFMPEG_ERRORS_H__
+#define __ADFFMPEG_ERRORS_H__
+
+enum ADHErrorCode
+{
+    ADFFMPEG_ERROR_NONE                               = 0,
+
+    ADFFMPEG_DS_ERROR                                 = -99,
+    ADFFMPEG_DS_ERROR_AUTH_REQUIRED                   = ADFFMPEG_DS_ERROR
-1,
+    ADFFMPEG_DS_ERROR_DNS_HOST_RESOLUTION_FAILURE     = ADFFMPEG_DS_ERROR
-2,
+    ADFFMPEG_DS_ERROR_HOST_UNREACHABLE                = ADFFMPEG_DS_ERROR
-3,
+    ADFFMPEG_DS_ERROR_INVALID_CREDENTIALS             = ADFFMPEG_DS_ERROR
-4,
+    ADFFMPEG_DS_ERROR_SOCKET                          = ADFFMPEG_DS_ERROR
-5,
+    ADFFMPEG_DS_ERROR_CREAT_CONECTION_TIMEOUT         = ADFFMPEG_DS_ERROR
-6,
+
+    ADFFMPEG_AD_ERROR                                 = -200,
+
+    ADFFMPEG_AD_ERROR_UNKNOWN                         = ADFFMPEG_AD_ERROR
-1,
+    ADFFMPEG_AD_ERROR_READ_6_BYTE_SEPARATOR           = ADFFMPEG_AD_ERROR
-2,
+    ADFFMPEG_AD_ERROR_NEW_PACKET                      = ADFFMPEG_AD_ERROR
-3,
+    ADFFMPEG_AD_ERROR_PARSE_MIME_HEADER               = ADFFMPEG_AD_ERROR
-4,
+    //ADFFMPEG_AD_ERROR_NETVU_IMAGE_DATA                =
ADFFMPEG_AD_ERROR -5,
+    //ADFFMPEG_AD_ERROR_NETVU_AUDIO_DATA                =
ADFFMPEG_AD_ERROR -6,
+
+    ADFFMPEG_AD_ERROR_JPEG_IMAGE_DATA_READ            = ADFFMPEG_AD_ERROR
-7,
+    ADFFMPEG_AD_ERROR_JPEG_PIC_VERSION                = ADFFMPEG_AD_ERROR
-8,
+    //ADFFMPEG_AD_ERROR_JPEG_ALLOCATE_TEXT_BLOCK        =
ADFFMPEG_AD_ERROR -9,
+    ADFFMPEG_AD_ERROR_JPEG_READ_TEXT_BLOCK            = ADFFMPEG_AD_ERROR
-10,
+    ADFFMPEG_AD_ERROR_JPEG_HEADER                     = ADFFMPEG_AD_ERROR
-11,
+    ADFFMPEG_AD_ERROR_JPEG_NEW_PACKET                 = ADFFMPEG_AD_ERROR
-12,
+    ADFFMPEG_AD_ERROR_JPEG_READ_BODY                  = ADFFMPEG_AD_ERROR
-13,
+
+    ADFFMPEG_AD_ERROR_JFIF_NEW_PACKET                 = ADFFMPEG_AD_ERROR
-14,
+    ADFFMPEG_AD_ERROR_JFIF_GET_BUFFER                 = ADFFMPEG_AD_ERROR
-15,
+    ADFFMPEG_AD_ERROR_JFIF_MANUAL_SIZE                = ADFFMPEG_AD_ERROR
-16,
+
+    ADFFMPEG_AD_ERROR_MPEG4_MIME_NEW_PACKET           = ADFFMPEG_AD_ERROR
-17,
+    ADFFMPEG_AD_ERROR_MPEG4_MIME_GET_BUFFER           = ADFFMPEG_AD_ERROR
-18,
+    ADFFMPEG_AD_ERROR_MPEG4_MIME_PARSE_HEADER         = ADFFMPEG_AD_ERROR
-19,
+    ADFFMPEG_AD_ERROR_MPEG4_MIME_PARSE_TEXT_DATA      = ADFFMPEG_AD_ERROR
-20,
+    ADFFMPEG_AD_ERROR_MPEG4_MIME_GET_TEXT_BUFFER      = ADFFMPEG_AD_ERROR
-21,
+    //ADFFMPEG_AD_ERROR_MPEG4_MIME_ALLOCATE_TEXT_BUFFER =
ADFFMPEG_AD_ERROR -22,
+
+    ADFFMPEG_AD_ERROR_MPEG4_GET_BUFFER                = ADFFMPEG_AD_ERROR
-23,
+    ADFFMPEG_AD_ERROR_MPEG4_PIC_VERSION_VALID         = ADFFMPEG_AD_ERROR
-24,
+    //ADFFMPEG_AD_ERROR_MPEG4_ALLOCATE_TEXT_BUFFER      =
ADFFMPEG_AD_ERROR -25,
+    ADFFMPEG_AD_ERROR_MPEG4_GET_TEXT_BUFFER           = ADFFMPEG_AD_ERROR
-26,
+    ADFFMPEG_AD_ERROR_MPEG4_NEW_PACKET                = ADFFMPEG_AD_ERROR
-27,
+    ADFFMPEG_AD_ERROR_MPEG4_PIC_BODY                  = ADFFMPEG_AD_ERROR
-28,
+
+    ADFFMPEG_AD_ERROR_MPEG4_MINIMAL_GET_BUFFER        = ADFFMPEG_AD_ERROR
-29,
+    ADFFMPEG_AD_ERROR_MPEG4_MINIMAL_NEW_PACKET        = ADFFMPEG_AD_ERROR
-30,
+    ADFFMPEG_AD_ERROR_MPEG4_MINIMAL_NEW_PACKET2       = ADFFMPEG_AD_ERROR
-31,
+
+    ADFFMPEG_AD_ERROR_MINIMAL_AUDIO_ADPCM_GET_BUFFER  = ADFFMPEG_AD_ERROR
-32,
+    ADFFMPEG_AD_ERROR_MINIMAL_AUDIO_ADPCM_NEW_PACKET  = ADFFMPEG_AD_ERROR
-33,
+    ADFFMPEG_AD_ERROR_MINIMAL_AUDIO_ADPCM_GET_BUFFER2 = ADFFMPEG_AD_ERROR
-34,
+
+    ADFFMPEG_AD_ERROR_AUDIO_ADPCM_GET_BUFFER          = ADFFMPEG_AD_ERROR
-35,
+    //ADFFMPEG_AD_ERROR_AUDIO_ADPCM_ALLOCATE_ADDITIONAL =
ADFFMPEG_AD_ERROR -36,
+    ADFFMPEG_AD_ERROR_AUDIO_ADPCM_GET_BUFFER2         = ADFFMPEG_AD_ERROR
-37,
+
+    ADFFMPEG_AD_ERROR_AUDIO_ADPCM_MIME_NEW_PACKET     = ADFFMPEG_AD_ERROR
-38,
+    ADFFMPEG_AD_ERROR_AUDIO_ADPCM_MIME_GET_BUFFER     = ADFFMPEG_AD_ERROR
-39,
+
+    ADFFMPEG_AD_ERROR_INFO_NEW_PACKET                 = ADFFMPEG_AD_ERROR
-40,
+    ADFFMPEG_AD_ERROR_INFO_GET_BUFFER                 = ADFFMPEG_AD_ERROR
-41,
+
+    ADFFMPEG_AD_ERROR_LAYOUT_NEW_PACKET               = ADFFMPEG_AD_ERROR
-42,
+    ADFFMPEG_AD_ERROR_LAYOUT_GET_BUFFER               = ADFFMPEG_AD_ERROR
-43,
+
+    ADFFMPEG_AD_ERROR_DEFAULT                         = ADFFMPEG_AD_ERROR
-44,
+
+    ADFFMPEG_AD_ERROR_GET_STREAM                      = ADFFMPEG_AD_ERROR
-45,
+    ADFFMPEG_AD_ERROR_GET_AUDIO_STREAM                = ADFFMPEG_AD_ERROR
-46,
+    ADFFMPEG_AD_ERROR_GET_INFO_LAYOUT_STREAM          = ADFFMPEG_AD_ERROR
-47,
+    //ADFFMPEG_AD_ERROR_END_OF_STREAM                   =
ADFFMPEG_AD_ERROR -48,
+
+    //ADFFMPEG_AD_ERROR_FAILED_TO_PARSE_INFOLIST        =
ADFFMPEG_AD_ERROR -49,
+    ADFFMPEG_AD_ERROR_OVERLAY_GET_BUFFER              = ADFFMPEG_AD_ERROR
-50,
+    ADFFMPEG_AD_ERROR_OVERLAY_PBM_READ                = ADFFMPEG_AD_ERROR
-51,
+    ADFFMPEG_AD_ERROR_GET_OVERLAY_STREAM              = ADFFMPEG_AD_ERROR
-52,
+
+    ADFFMPEG_AD_ERROR_LAST                            = ADFFMPEG_AD_ERROR
-53,
+
+ ADH_GRAPH_RELOAD_NEEDED                           = 0x8001,
+
+#ifdef __midl
+} ADHErrorCode;
+#else
+};
+#endif
+
+#endif
diff --git a/libavformat/adjfif.c b/libavformat/adjfif.c
new file mode 100644
index 0000000000..50df8257fe
--- /dev/null
+++ b/libavformat/adjfif.c
@@ -0,0 +1,551 @@ 
+/*
+ * Helper functions for converting between raw JPEG data with
+ * NetVuImageData header and full JFIF image (and vice-versa)
+ *
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * Helper functions for converting between raw JPEG data with
+ * NetVuImageData header and full JFIF image (and vice-versa)
+ */
+
+#include <time.h>
+#include "internal.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/avstring.h"
+
+#include "adjfif.h"
+#include "adpic.h"
+
+static int find_q(unsigned char *qy);
+static void parse_comment(char *text, int text_len, struct NetVuImageData
*pic,
+                          char **additionalText );
+static void calcQtabs(void);
+
+
+static const char comment_version[] = "Version: 00.02\r\n";
+static const char comment_version_0_1[] = "Version: 00.01\r\n";
+static const char camera_title[] = "Name: ";
+static const char camera_number[] = "Number: ";
+static const char image_date[] = "Date: ";
+static const char image_time[] = "Time: ";
+static const char image_ms[] = "MSec: ";
+static const char q_factor[] = "Q-Factor: ";
+static const char alarm_comment[] = "Alarm-text: ";
+static const char active_alarms[] = "Active-alarms: ";
+static const char active_detectors[] = "Active-detectors: ";
+static const char script_msg[] = "Comments: ";
+static const char time_zone[] = "Locale: ";
+static const char utc_offset[] = "UTCoffset: ";
+
+static const uint8_t jfif_header[] = {
+    0xFF, 0xD8,                     // SOI
+    0xFF, 0xE0,                     // APP0
+    0x00, 0x10,                     // APP0 header size (including
+                                    // this field, but excluding preceding)
+    0x4A, 0x46, 0x49, 0x46, 0x00,   // ID string 'JFIF\0'
+    0x01, 0x02,                     // version
+    0x02,                           // density units (0 - none, 1 - PPI, 2
- PPCM)
+    0x00, 0x19,                     // X density
+    0x00, 0x19,                     // Y density
+    0x00,                           // X thumbnail size
+    0x00,                           // Y thumbnail size
+};
+
+static const unsigned char sof_422_header[] = {
+    0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0x00, 0x01,
+    0x60, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01,
+    0x03, 0x11, 0x01
+};
+static const unsigned char sof_411_header[] = {
+    0xFF, 0xC0, 0x00, 0x11, 0x08, 0x01, 0x00, 0x01,
+    0x60, 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01,
+    0x03, 0x11, 0x01
+};
+
+static const unsigned char huf_header[] = {
+    0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+    0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02,
+    0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05,
+    0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02,
+    0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31,
+    0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71,
+    0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42,
+    0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33,
+    0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18,
+    0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A,
+    0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43,
+    0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53,
+    0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63,
+    0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73,
+    0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83,
+    0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92,
+    0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
+    0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9,
+    0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8,
+    0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+    0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
+    0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4,
+    0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2,
+    0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
+    0xFF, 0xC4, 0x00, 0x1F, 0x01, 0x00, 0x03, 0x01,
+    0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02,
+    0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
+    0x0B, 0xFF, 0xC4, 0x00, 0xB5, 0x11, 0x00, 0x02,
+    0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05,
+    0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01,
+    0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06,
+    0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22,
+    0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1,
+    0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62,
+    0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25,
+    0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28,
+    0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
+    0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
+    0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A,
+    0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
+    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A,
+    0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+    0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+    0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+    0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
+    0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
+    0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4,
+    0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3,
+    0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2,
+    0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA
+};
+
+static const unsigned char sos_header[] = {
+    0xFF, 0xDA, 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02,
+    0x11, 0x03, 0x11, 0x00, 0x3F, 0x00
+};
+
+unsigned short Yvis[64] = {
+     16,  11,  12,  14,  12,  10,  16,  14,
+     13,  14,  18,  17,  16,  19,  24,  40,
+     26,  24,  22,  22,  24,  49,  35,  37,
+     29,  40,  58,  51,  61,  60,  57,  51,
+     56,  55,  64,  72,  92,  78,  64,  68,
+     87,  69,  55,  56,  80, 109,  81,  87,
+     95,  98, 103, 104, 103,  62,  77, 113,
+    121, 112, 100, 120,  92, 101, 103,  99
+};
+
+unsigned short UVvis[64] = {
+    17, 18, 18, 24, 21, 24, 47, 26,
+    26, 47, 99, 66, 56, 66, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99,
+    99, 99, 99, 99, 99, 99, 99, 99
+};
+
+unsigned char YQuantizationFactors[256][64],
UVQuantizationFactors[256][64];
+
+static int q_init;
+
+
+/**
+ * Build the correct JFIF headers & tables for the supplied image data
+ *
+ * \param jfif Pointer to output buffer
+ * \param pic  Pointer to NetVuImageData - includes Q factors, image size,
+ *             mode etc.
+ * \param max  Maximum size of header
+ * \return Total bytes in the JFIF image
+ */
+unsigned int build_jpeg_header(void *jfif, struct NetVuImageData *pic,
unsigned int max)
+{
+    volatile unsigned int count;
+    unsigned short    us1;
+    char  *bufptr = jfif;
+    char sof_copy[sizeof(sof_422_header)];
+
+
+    if (!q_init) {
+        calcQtabs();
+        q_init = 1;
+    }
+
+    // Add all the fixed length headers prior to building comment field
+    count = sizeof(jfif_header);
+    if (count > max)
+        return 0;
+
+    memcpy(bufptr, jfif_header, sizeof(jfif_header));
+
+    if( (pic->format.target_pixels > 360) && (pic->format.target_lines <
480) )
+        bufptr[17] = 0x32;
+
+    bufptr += sizeof(jfif_header);
+
+    // Q tables and markers
+    count += 138;
+    if (count > max)
+        return 0;
+    *bufptr++ = 0xff;
+    *bufptr++ = 0xdb;
+    *bufptr++ = 0x00;
+    *bufptr++ = 0x43;
+    *bufptr++ = 0x00;
+    for (us1 = 0; us1 < 64; us1++)
+        *bufptr++ = YQuantizationFactors[pic->factor][us1];
+    *bufptr++ = 0xff;
+    *bufptr++ = 0xdb;
+    *bufptr++ = 0x00;
+    *bufptr++ = 0x43;
+    *bufptr++ = 0x01;
+    for (us1 = 0; us1 < 64; us1++)
+        *bufptr++ = UVQuantizationFactors[pic->factor][us1];
+    count += (sizeof(sof_copy) + sizeof(huf_header) + sizeof(sos_header));
+    if (count > max)
+        return 0;
+    else {
+        unsigned short targ;
+        char *ptr1, *ptr2;
+        memcpy(sof_copy, (pic->vid_format == PIC_MODE_JPEG_411) ?
sof_411_header : sof_422_header, sizeof( sof_copy ) );
+        targ = AV_RB16(&pic->format.target_pixels); // Byte swap from
native to big-endian
+        ptr1 = (char *)&targ;
+        ptr2 = &sof_copy[7];
+        *ptr2++ = *ptr1++;
+        *ptr2 = *ptr1;
+
+        targ = AV_RB16(&pic->format.target_lines); // Byte swap from
native to big-endian
+        ptr1 = (char *)&targ;
+        ptr2 = &sof_copy[5];
+        *ptr2++ = *ptr1++;
+        *ptr2 = *ptr1;
+
+        memcpy(bufptr, sof_copy, sizeof(sof_copy));
+        bufptr += sizeof(sof_copy);
+
+        memcpy(bufptr, huf_header, sizeof(huf_header));
+        bufptr += sizeof(huf_header);
+
+        memcpy(bufptr, sos_header, sizeof(sos_header));
+        bufptr += sizeof(sos_header);
+    }
+
+    return bufptr - (char*)jfif;
+}
+
+/**
+ * Calculate the Q tables
+ *
+ * Updates the module Q factor tables
+ */
+static void calcQtabs(void)
+{
+    short i;
+    int uvfactor;
+    short factor;
+    short yval, uvval;
+    for (factor = 1; factor < 256; factor++ ) {
+        uvfactor = factor * 1;
+        if (uvfactor > 255)
+            uvfactor = 255;
+        for (i = 0; i < 64; i++) {
+            yval  = (short)(((Yvis[i] * factor) + 25) / 50);
+            uvval = (short)(((UVvis[i] * uvfactor) + 25) / 50);
+
+            if ( yval < 1 )
+                yval = 1;    // The DC and AC values cannot be
+            if ( uvval < 1)  //
+                uvval = 1;   // less than 1
+
+            if ( yval > 255 )
+                yval = 255;    // The DC and AC values cannot
+            if ( uvval > 255)  //
+                uvval = 255;   // be more than 255
+
+            YQuantizationFactors[factor][i]  = (uint8_t)yval;
+            UVQuantizationFactors[factor][i] = (uint8_t)uvval;
+        }
+    }
+}
+
+static int find_q(unsigned char *qy)
+{
+    int factor, smallest_err = 0, best_factor = 0;
+    unsigned char *q1, *q2, *qe;
+    unsigned char qtest[64];
+    int err_diff;
+
+    if (!q_init) {
+        calcQtabs();
+        q_init = 1;
+    }
+
+    memcpy(&qtest[0], qy, 64); // PRC 025
+
+    qe = &qtest[32];
+
+    for (factor = 1; factor < 256; factor++ ) {
+        q1 = &qtest[0];
+        q2 = &YQuantizationFactors[factor][0];
+        err_diff = 0;
+        while (q1 < qe) {
+            if (*q1 > *q2)
+                err_diff = *q1 - *q2;
+            else
+                err_diff = *q2 - *q1;
+
+            q1++;
+            q2++;
+        }
+        if (err_diff == 0) {
+            best_factor = factor;
+            smallest_err = 0;
+            break;
+        }
+        else if ( err_diff < smallest_err || best_factor == 0 ) {
+            best_factor = factor;
+            smallest_err = err_diff;
+        }
+    }
+    if (factor == 256) {
+        factor = best_factor;
+        av_log(NULL, AV_LOG_ERROR, "find_q: Unable to match Q setting
%d\n", best_factor );
+    }
+    return factor;
+}
+
+
+/**
+ * Analyses a JFIF header and fills out a NetVuImageData structure with
the info
+ *
+ * \param data        Input buffer
+ * \param pic         NetVuImageData structure
+ * \param imgSize     Total length of input buffer
+ * \param text        Buffer in which is placed text from the JFIF comment
+ *                    that doesn't have a specific field in NetVuImageData
+ * \return Length of JFIF header in bytes
+ */
+int parse_jfif(AVFormatContext *s, unsigned char *data, struct
NetVuImageData *pic,
+               int imgSize, char **text)
+{
+    int i, sos = FALSE;
+    unsigned short length, marker;
+    uint16_t xdensity = 0;
+    uint16_t ydensity = 0;
+    uint8_t *densityPtr = NULL;
+    unsigned int jfifMarker;
+
+    //av_log(s, AV_LOG_DEBUG, "parse_jfif: leading bytes 0x%02X, 0x%02X, "
+    //       "length=%d\n", data[0], data[1], imgSize);
+    memset(pic, 0, sizeof(*pic));
+    pic->version = PIC_VERSION;
+    pic->factor = -1;
+    pic->start_offset = 0;
+    i = 0;
+
+    if(data[i] != 0xff && data[i+1] != 0xd8) {
+        //there is a header so skip it
+        while( ((unsigned char)data[i] != 0xff) && (i < imgSize) )
+            i++;
+        //if ( i > 0 )
+        //    av_log(s, AV_LOG_DEBUG, "parse_jfif: %d leading bytes\n", i);
+
+        i++;
+        if ( (unsigned char) data[i] != 0xd8) {
+            //av_log(s, AV_LOG_ERROR, "parse_jfif: incorrect SOI
0xff%02x\n", data[i]);
+            return -1;
+        }
+        i++;
+    }
+    else {
+        //no header
+        i += 2;
+    }
+
+    while ( !sos && (i < imgSize) ) {
+        if (data[i] != 0xff)
+            continue;
+        marker = AV_RB16(&data[i]);
+        i += 2;
+        length = AV_RB16(&data[i]) - 2;
+        i += 2;
+
+        switch (marker) {
+            case 0xffe0 :    // APP0
+                jfifMarker = AV_RB32(&data[i]);
+                if ( (jfifMarker==0x4A464946) && (data[i+4]==0x00) )  {
+                    xdensity = AV_RB16(&data[i+8]);
+                    ydensity = AV_RB16(&data[i+10]);
+                    densityPtr = &data[i+8];
+                }
+                break;
+            case 0xffdb :    // Q table
+                if (!data[i])
+                    pic->factor =  find_q(&data[i+1]);
+                break;
+            case 0xffc0 :    // SOF
+                pic->format.target_lines  = AV_RB16(&data[i+1]);
+                pic->format.target_pixels = AV_RB16(&data[i+3]);
+                if (data[i+7] == 0x22)
+                    pic->vid_format = PIC_MODE_JPEG_411;
+                else if (data[i+7] == 0x21)
+                    pic->vid_format = PIC_MODE_JPEG_422;
+                else
+                    av_log(s, AV_LOG_WARNING, "%s: Unknown SOF format byte
0x%02X\n", __func__, data[i+7]);
+                break;
+            case 0xffda :    // SOS
+                sos = TRUE;
+                break;
+            case 0xfffe :    // Comment
+                parse_comment((char *)&data[i], length - 2, pic, text);
+                break;
+            default :
+                break;
+        }
+        i += length;
+
+        if ( densityPtr && (pic->format.target_lines > 0) && (xdensity ==
(ydensity*2)) )  {
+            if( (pic->format.target_pixels > 360) &&
(pic->format.target_lines < 480) )  {
+                // Server is sending wrong pixel aspect ratio, reverse it
+                //av_log(s, AV_LOG_DEBUG, "%s: Server is sending wrong
pixel "
+                //                        "aspect ratio. Old = %d:%d, New
= %d:%d"
+                //                        " Res = %dx%d\n",
+                //       __func__, xdensity, ydensity, ydensity, xdensity,
+                //       pic->format.target_pixels,
pic->format.target_lines);
+                AV_WB16(densityPtr, ydensity);
+                AV_WB16(densityPtr + 2, xdensity);
+            }
+            else  {
+                // Server is sending wrong pixel aspect ratio, set it to
1:1
+                AV_WB16(densityPtr, ydensity);
+            }
+            xdensity = AV_RB16(densityPtr);
+            ydensity = AV_RB16(densityPtr+2);
+        }
+    }
+    pic->size = imgSize - i - 2;     // 2 bytes for FFD9
+    return i;
+}
+
+static void parse_comment( char *text, int text_len, struct NetVuImageData
*pic, char **additionalText )
+{
+    char            result[512];
+    int             i = 0;
+    int             j = 0;
+    struct tm       t;
+    time_t          when = 1000000000;
+
+    // Calculate timezone offset of client, needed because mktime uses
local
+    // time and there is no ISO C equivalent for GMT.
+    struct tm       utc = *gmtime(&when);
+    struct tm       lcl = *localtime(&when);
+    int             delta_h = mktime(&utc) - mktime(&lcl);
+
+    memset(&t, 0, sizeof(t));
+
+    while( 1 ) {
+        j = 0;
+
+        // Check we haven't covered all the buffer already
+        if( i >= text_len || text[i] <= 0 )
+            break;
+
+        // Get the next line from the text block
+        while (text[i] && text[i] != '\n' && (i < text_len))  {
+            result[j++] = text[i++];
+            if ( j >= sizeof(result) )  {
+                --j;
+                break;
+            }
+        }
+        result[j] = '\0';
+
+        // Skip the \n
+        if( text[i] == '\n' ) {
+            if (j > 0)
+                result[j-1] = '\0';
+            else
+                result[0] = '\0';
+            i++;
+        }
+
+        // Changed this line so it doesn't include the \r\n from the end
of comment_version
+        if( !memcmp( result, comment_version, strlen(comment_version) - 2
) )
+            pic->version = 0xdecade11;
+        else if ( !memcmp( result, comment_version,
strlen(comment_version_0_1) - 2 ) )
+            pic->version = 0xdecade10;
+        else if( !memcmp( result, camera_title, strlen(camera_title) ) )  {
+            av_strlcpy( pic->title, &result[strlen(camera_title)],
sizeof(pic->title) );
+        }
+        else if( !memcmp( result, camera_number, strlen(camera_number) ) )
+            sscanf( &result[strlen(camera_number)], "%d", &pic->cam );
+        else if( !memcmp( result, image_date, strlen(image_date) ) ) {
+            sscanf( &result[strlen(image_date)], "%d/%d/%d", &t.tm_mday,
&t.tm_mon, &t.tm_year );
+            t.tm_year -= 1900;
+            t.tm_mon--;
+        }
+        else if( !memcmp( result, image_time, strlen(image_time) ) )
+            sscanf( &result[strlen(image_time)], "%d:%d:%d", &t.tm_hour,
&t.tm_min, &t.tm_sec );
+        else if( !memcmp( result, image_ms, strlen(image_ms) ) )
+            sscanf( &result[strlen(image_ms)], "%d", &pic->milliseconds );
+        else if( !memcmp( result, q_factor, strlen(q_factor) ) )
+            sscanf( &result[strlen(q_factor)], "%d", &pic->factor );
+        else if( !memcmp( result, alarm_comment, strlen(alarm_comment) ) )
+            av_strlcpy( pic->alarm, &result[strlen(alarm_comment)],
sizeof(pic->alarm) );
+        else if( !memcmp( result, active_alarms, strlen(active_alarms) ) )
+            sscanf( &result[strlen(active_alarms)], "%X",
&pic->alm_bitmask );
+        else if( !memcmp( result, active_detectors,
strlen(active_detectors) ) )
+            sscanf( &result[strlen(active_detectors)], "%X",
&pic->alm_bitmask_hi );
+        else if( !memcmp( result, script_msg, strlen(script_msg) ) ) {
+        }
+        else if( !memcmp( result, time_zone, strlen(time_zone) ) )
+            av_strlcpy( pic->locale, &result[strlen(time_zone)],
sizeof(pic->locale) );
+        else if( !memcmp( result, utc_offset, strlen(utc_offset) ) )
+            sscanf( &result[strlen(utc_offset)], "%d", &pic->utc_offset );
+        else {
+            // Any line we don't explicitly detect for extraction to the
pic
+            // struct, we must add to the additional text block
+            if ( (additionalText != NULL) && (strlen(result) > 0) )  {
+                int             strLen  = 0;
+                const char      lineEnd[3] = { '\r', '\n', '\0' };
+
+                // Get the length of the existing text block if it exists
+                if( *additionalText != NULL )
+                    strLen = strlen( *additionalText );
+
+                // Ok, now allocate some space to hold the new string
+                *additionalText = av_realloc( *additionalText, strLen +
strlen(result) + 3 );
+
+                // Copy the line into the text block
+                memcpy( &(*additionalText)[strLen], result, strlen(result)
);
+
+                // Add a \r\n and NULL termination
+                memcpy( &(*additionalText)[strLen + strlen(result)],
lineEnd, 3 );
+            }
+        }
+    }
+
+    pic->session_time = mktime(&t) - delta_h;
+}
diff --git a/libavformat/adjfif.h b/libavformat/adjfif.h
new file mode 100644
index 0000000000..b59c5823c7
--- /dev/null
+++ b/libavformat/adjfif.h
@@ -0,0 +1,41 @@ 
+/*
+ * Helper functions for converting between raw JPEG data with
+ * NetVuImageData header and full JFIF image (and vice-versa)
+ *
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * Helper functions for converting between raw JPEG data with
+ * NetVuImageData header and full JFIF image (and vice-versa)
+ */
+
+#ifndef AVFORMAT_ADJFIF_H
+#define AVFORMAT_ADJFIF_H
+
+#include "ds_exports.h"
+
+extern unsigned int build_jpeg_header(void *jfif, struct NetVuImageData
*pic,
+                                      unsigned int max);
+extern int parse_jfif(AVFormatContext *s, unsigned char *data,
+                      struct NetVuImageData *pic, int imgSize, char
**text);
+
+#endif
diff --git a/libavformat/admime.c b/libavformat/admime.c
new file mode 100644
index 0000000000..47d9ece2e4
--- /dev/null
+++ b/libavformat/admime.c
@@ -0,0 +1,822 @@ 
+/*
+ * AD-Holdings demuxer for AD stream format (binary)
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file AD-Holdings demuxer for AD stream format (binary)
+ */
+
+#include <strings.h>
+
+#include "avformat.h"
+#include "adffmpeg_errors.h"
+#include "adpic.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/avstring.h"
+
+
+#define TEMP_BUFFER_SIZE        1024
+#define MAX_IMAGE_SIZE          (256 * 1024)
+
+// This value is only used internally within the library DATA_PLAINTEXT
blocks
+// should not be exposed to the client
+#define DATA_PLAINTEXT          (AD_DATATYPE_MAX + 1)
+
+
+static const char *     BOUNDARY_PREFIX1 = "--0plm(";
+static const char *     BOUNDARY_PREFIX2 = "Í -0plm(";
+static const char *     BOUNDARY_PREFIX3 = "ÍÍ 0plm(";
+static const char *     BOUNDARY_PREFIX4 = "ÍÍÍ plm(";
+static const char *     BOUNDARY_PREFIX5 = "ÍÍÍÍ lm(";
+static const char *     BOUNDARY_PREFIX6 = "ÍÍÍÍÍ m(";
+static const char *     BOUNDARY_PREFIX7 = "/r--0plm(";
+static const char *     BOUNDARY_SUFFIX  =
":Server-Push:Boundary-String)1qaz";
+
+static const char *     MIME_TYPE_JPEG   = "image/jpeg";
+static const char *     MIME_TYPE_MP4    = "image/admp4";
+static const char *     MIME_TYPE_TEXT   = "text/plain";
+static const char *     MIME_TYPE_XML    = "text/xml";
+static const char *     MIME_TYPE_ADPCM  = "audio/adpcm";
+static const char *     MIME_TYPE_LAYOUT = "data/layout";
+static const char *     MIME_TYPE_PBM    = "image/pbm";
+static const char *     MIME_TYPE_H264   = "image/adh264";
+
+static const uint8_t rawJfifHeader[] = { 0xff, 0xd8, 0xff, 0xe0,
+                                         0x00, 0x10, 0x4a, 0x46,
+                                         0x49, 0x46, 0x00, 0x01 };
+
+
+
+/**
+ * Validates a multipart MIME boundary separator against the convention
used by
+ * NetVu video servers
+ *
+ * \param buf Buffer containing the boundary separator
+ * \param bufLen Size of the buffer
+ * \return 1 if boundary separator is valid, 0 if not
+ */
+static int is_valid_separator( unsigned char * buf, int bufLen )
+{
+    if (buf == NULL )
+        return FALSE;
+
+    if (bufLen < strlen(BOUNDARY_PREFIX1) + strlen(BOUNDARY_SUFFIX) )
+        return FALSE;
+
+    if ((strncmp(buf, BOUNDARY_PREFIX1, strlen(BOUNDARY_PREFIX1)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX2, strlen(BOUNDARY_PREFIX2)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX3, strlen(BOUNDARY_PREFIX3)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX4, strlen(BOUNDARY_PREFIX4)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX5, strlen(BOUNDARY_PREFIX5)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX6, strlen(BOUNDARY_PREFIX6)) == 0) ||
+        (strncmp(buf, BOUNDARY_PREFIX7, strlen(BOUNDARY_PREFIX7)) == 0)
 ) {
+        unsigned char *     b = &buf[strlen(BOUNDARY_PREFIX1)];
+
+        // Now we have a server type string. We must skip past this
+        while( !av_isspace(*b) && *b != ':' && (b - buf) < bufLen ) {
+            b++;
+        }
+
+        if (*b == ':' ) {
+            if ((b - buf) + strlen(BOUNDARY_SUFFIX)  <= bufLen ) {
+                if (strncmp(b, BOUNDARY_SUFFIX, strlen(BOUNDARY_SUFFIX))
== 0 )
+                    return TRUE;
+            }
+        }
+    }
+
+    return FALSE;
+}
+
+
+/**
+ * Parse a line of MIME data
+ */
+static int process_line(char *line, int *line_count, int *dataType,
+                        int *size, long *extra )
+{
+    char *tag, *p = NULL;
+    int         http_code = 0;
+
+    // end of header
+    if (line[0] == '\0')
+        return 0;
+
+    p = line;
+
+    // The boundry string is missing sometimes so check for the HTTP header
+    // and skip to line 1
+    if(*line_count == 0 && 'H' == *(p) && 'T' == *(p + 1) && 'T' == *(p +
2) && 'P' == *(p + 3))
+        *line_count = 1;
+
+    // The first valid line will be the boundary string - validate this
here
+    if (*line_count == 0 ) {
+        if (is_valid_separator( p, strlen(p) ) == FALSE )
+            return -1;
+    }
+    else if (*line_count == 1 ) { // Second line will contain the HTTP
status code
+        while (!av_isspace(*p) && *p != '\0')
+            p++;
+        while (av_isspace(*p))
+            p++;
+        http_code = strtol(p, NULL, 10);
+    }
+    else {
+        // Any other line we are just looking for particular headers
+        // if we find them, we fill in the appropriate output data
+        while (*p != '\0' && *p != ':')
+            p++;
+        if (*p != ':')
+            return 1;
+
+        *p = '\0';
+        tag = line;
+        p++;
+        while (av_isspace(*p))
+            p++;
+
+        if (!strcmp(tag, "Content-length")) {
+            *size = strtol(p, NULL, 10);
+
+            if (size == 0 )
+                return -1;
+        }
+
+        if (!strcmp(tag, "Content-type")) {
+            // Work out what type we actually have
+            if (av_strcasecmp(p, MIME_TYPE_JPEG ) == 0 )
+                *dataType = AD_DATATYPE_JFIF;
+            // Or if it starts image/mp4 - this covers all the supported
mp4
+            // variations (i and p frames)
+            else if(av_strncasecmp(p, MIME_TYPE_MP4, strlen(MIME_TYPE_MP4)
) == 0) {
+                // P for now - as they are both processed the same
subsequently
+                *dataType = AD_DATATYPE_MPEG4P;
+            }
+            else if (av_strcasecmp(p, MIME_TYPE_TEXT ) == 0 )
+                *dataType = DATA_PLAINTEXT;
+            else if (av_strcasecmp(p, MIME_TYPE_LAYOUT ) == 0 )
+                *dataType = AD_DATATYPE_LAYOUT;
+            else if (av_strcasecmp(p, MIME_TYPE_XML ) == 0 )
+                *dataType = AD_DATATYPE_XML_INFO;
+            else if(av_strncasecmp(p, MIME_TYPE_ADPCM,
strlen(MIME_TYPE_ADPCM)) == 0) {
+                *dataType = AD_DATATYPE_AUDIO_ADPCM;
+
+                // If we find audio in a mime header, we need to extract
the
+                // mode out. The header takes the form,
+                // Content-Type: audio/adpcm;rate=<mode>
+                while (*p != '\0' && *p != ';')
+                    p++;
+
+                if (*p != ';' )
+                    return 1;
+
+                p++;
+                while (av_isspace(*p))
+                    p++;
+
+                // p now pointing at the rate. Look for the first '='
+                while (*p != '\0' && *p != '=')
+                    p++;
+                if (*p != '=')
+                    return 1;
+
+                p++;
+
+                tag = p;
+
+                while( *p != '\0' && !av_isspace(*p) )
+                    p++;
+
+                if (*p != '\0' )
+                    *p = '\0';
+
+                *extra = strtol(tag, NULL, 10);
+
+                // Map the rate to a RTP payload value for consistancy -
+                // the other audio headers contain mode values in this
format
+                if (*extra == 8000 )
+                    *extra = RTP_PAYLOAD_TYPE_8000HZ_ADPCM;
+                else if (*extra == 11025 )
+                    *extra = RTP_PAYLOAD_TYPE_11025HZ_ADPCM;
+                else if (*extra == 16000 )
+                    *extra = RTP_PAYLOAD_TYPE_16000HZ_ADPCM;
+                else if (*extra == 22050 )
+                    *extra = RTP_PAYLOAD_TYPE_22050HZ_ADPCM;
+                else if (*extra == 32000 )
+                    *extra = RTP_PAYLOAD_TYPE_32000HZ_ADPCM;
+                else if (*extra == 44100 )
+                    *extra = RTP_PAYLOAD_TYPE_44100HZ_ADPCM;
+                else if (*extra == 48000 )
+                    *extra = RTP_PAYLOAD_TYPE_48000HZ_ADPCM;
+                else
+                    *extra = RTP_PAYLOAD_TYPE_8000HZ_ADPCM; // Default
+            }
+            else if (av_strcasecmp(p, MIME_TYPE_PBM ) == 0 )  {
+                *dataType = AD_DATATYPE_PBM;
+            }
+            else if(av_strncasecmp(p, MIME_TYPE_H264,
strlen(MIME_TYPE_H264) ) == 0) {
+                // P for now - as they are both processed the same
subsequently
+                *dataType = AD_DATATYPE_H264P;
+            }
+            else  {
+                *dataType = AD_DATATYPE_MAX;
+            }
+        }
+    }
+    return 1;
+}
+
+/**
+ * Read and process MIME header information
+ *
+ * \return Zero on successful decode, -2 if a raw JPEG, anything else
indicates
+ *         failure
+ */
+static int parse_mime_header(AVIOContext *pb, uint8_t *buffer, int
*bufSize,
+                             int *dataType, int *size, long *extra)
+{
+    unsigned char ch, *q = NULL;
+    int           err, lineCount = 0;
+    const int     maxBufSize = *bufSize;
+
+    *bufSize = 0;
+
+    // Check for JPEG header first
+    do {
+        if (avio_read(pb, &ch, 1) < 0)
+            break;
+        if (buffer && (ch == rawJfifHeader[*bufSize]))  {
+            buffer[*bufSize] = ch;
+            ++(*bufSize);
+            if ((*bufSize) == sizeof(rawJfifHeader))
+                return -2;
+        }
+        else
+            break;
+    } while( (*bufSize) < sizeof(rawJfifHeader));
+
+    q = buffer + *bufSize;
+    // Try and parse the header
+    for(;;) {
+        if (ch == '\n') {
+            // process line
+            if (q > buffer && q[-1] == '\r')
+                q--;
+            *q = '\0';
+
+            err = process_line( buffer, &lineCount, dataType, size, extra
);
+            // First line contains a \n
+            if (!(err == 0 && lineCount == 0) ) {
+                if (err < 0 )
+                    return err;
+
+                if (err == 0 )
+                    return 0;
+                lineCount++;
+            }
+
+            q = buffer;
+        }
+        else {
+            if ((q - buffer) < (maxBufSize - 1))
+                *q++ = ch;
+            else
+                return ADFFMPEG_AD_ERROR_PARSE_MIME_HEADER;
+        }
+
+        err = avio_read(pb, &ch, 1);
+        if (err < 0)  {
+            if (pb->eof_reached)
+                return err;
+            else
+                return ADFFMPEG_AD_ERROR_PARSE_MIME_HEADER;
+        }
+    }
+
+    return ADFFMPEG_AD_ERROR_PARSE_MIME_HEADER;
+}
+
+
+/**
+ * Parse a line of MIME data for MPEG video frames
+ */
+static int process_mp4data_line( char *line, int line_count,
+                                 struct NetVuImageData *vidDat, struct tm
*tim,
+                                 char ** txtDat )
+{
+    static const int titleLen = sizeof(vidDat->title) /
sizeof(vidDat->title[0]);
+    char        *tag = NULL, *p = NULL;
+    int         lineLen = 0;
+
+    // end of header
+    if (line[0] == '\0')
+        return 0;
+
+    p = line;
+
+
+    while (*p != '\0' && *p != ':')
+        p++;
+    if (*p != ':')
+        return 1;
+
+    tag = line;
+    p++;
+
+    while( *p != '\0' && *p == ' '  )  // While the current char is a space
+        p++;
+
+    if (*p == '\0')
+        return 1;
+    else {
+        char * temp = p;
+
+        // Get the length of the rest of the line
+        while( *temp != '\0' )
+            temp++;
+
+        lineLen = temp - line;
+    }
+
+    if (!memcmp( tag, "Number", strlen( "Number" ) ) )
+        vidDat->cam = strtol(p, NULL, 10);
+    else if (!memcmp( tag, "Name", strlen( "Name" ) ) )
+        memcpy( vidDat->title, p, FFMIN( titleLen, strlen(p) ) );
+    else if (!memcmp( tag, "Version", strlen( "Version" ) ) )  {
+        int verNum = strtod(p, NULL) * 100.0 - 1;
+        vidDat->version = 0xDECADE10 + verNum;
+    }
+    else if (!memcmp( tag, "Date", strlen( "Date" ) ) ) {
+        sscanf( p, "%d/%d/%d", &tim->tm_mday, &tim->tm_mon, &tim->tm_year
);
+        tim->tm_year -= 1900;
+        tim->tm_mon--;
+
+        if ((tim->tm_sec != 0) || (tim->tm_min != 0) || (tim->tm_hour !=
0))
+            vidDat->session_time = mktime(tim);
+    }
+    else if (!memcmp( tag, "Time", strlen( "Time" ) ) ) {
+        sscanf( p, "%d:%d:%d", &tim->tm_hour, &tim->tm_min, &tim->tm_sec );
+
+        if (tim->tm_year != 0)
+            vidDat->session_time = mktime(tim);
+    }
+    else if (!memcmp( tag, "MSec", strlen( "MSec" ) ) )
+        vidDat->milliseconds = strtol(p, NULL, 10);
+    else if (!memcmp( tag, "Locale", strlen( "Locale" ) ) )
+        memcpy( vidDat->locale, p, FFMIN( titleLen, strlen(p) ) );
+    else if (!memcmp( tag, "UTCoffset", strlen( "UTCoffset" ) ) )
+        vidDat->utc_offset = strtol(p, NULL, 10);
+    else {
+        // Any lines that aren't part of the pic struct,
+        // tag onto the additional text block
+        // \todo Parse out some of these and put them into metadata
+        if (txtDat != NULL && lineLen > 0 ) {
+#define LINE_END_LEN        3
+            int             strLen  = 0;
+            const char      lineEnd[LINE_END_LEN] = { '\r', '\n', '\0' };
+
+            // Get the length of the existing text block if it exists
+            if (*txtDat != NULL )
+                strLen = strlen( *txtDat );
+
+            // Ok, now allocate some space to hold the new string
+            *txtDat = av_realloc(*txtDat, strLen + lineLen + LINE_END_LEN);
+
+            // Copy the line into the text block
+            memcpy( &(*txtDat)[strLen], line, lineLen );
+
+            // Add a NULL terminator
+            memcpy( &(*txtDat)[strLen + lineLen], lineEnd, LINE_END_LEN );
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * Parse MIME data that is sent after each MPEG video frame
+ */
+static int parse_mp4_text_data( unsigned char *mp4TextData, int bufferSize,
+                                struct NetVuImageData *vidDat, char
**txtDat )
+{
+    unsigned char               buffer[TEMP_BUFFER_SIZE];
+    int                         ch, err;
+    unsigned char *             q = NULL;
+    int                         lineCount = 0;
+    unsigned char *             currentChar = mp4TextData;
+    struct tm                   tim;
+
+    memset( &tim, 0, sizeof(struct tm) );
+
+    // Try and parse the header
+    q = buffer;
+    for(;;) {
+        ch = *currentChar++;
+
+        if (ch < 0)
+            return 1;
+
+        if (ch == '\n') {
+            // process line
+            if (q > buffer && q[-1] == '\r')
+                q--;
+            *q = '\0';
+
+            err = process_mp4data_line(buffer, lineCount, vidDat, &tim,
txtDat);
+
+            if (err < 0 )
+                return err;
+
+            if (err == 0 )
+                return 0;
+
+            // Check we're not at the end of the buffer. If the following
+            // statement is true and we haven't encountered an error then
we've
+            // finished parsing the buffer
+            if (err == 1 ) {
+                // Not particularly happy with this code but it seems
there's
+                // little consistency in the way these buffers end. This
block
+                // catches all of those variations. The variations that
indicate
+                // the end of a MP4 MIME text block are:
+                //
+                // 1. The amount of buffer parsed successfully is equal to
the
+                //    total buffer size
+                //
+                // 2. The buffer ends with two NULL characters
+                //
+                // 3. The buffer ends with the sequence \r\n\0
+
+                if (currentChar - mp4TextData == bufferSize )
+                    return 0;
+
+                // CS - I *think* lines should end either when we've
processed
+                // all the buffer OR it's padded with 0s
+                // Is detection of a NULL character here sufficient?
+                if (*currentChar == '\0' )
+                    return 0;
+            }
+
+            lineCount++;
+            q = buffer;
+        }
+        else {
+            if ((q - buffer) < sizeof(buffer) - 1)
+                *q++ = ch;
+        }
+    }
+
+    return 1;
+}
+
+/**
+ * MPEG4 or H264 video frame with a MIME trailer
+ */
+static int admime_mpeg(AVFormatContext *s,
+                       AVPacket *pkt, int size, long *extra,
+                       struct NetVuImageData *vidDat, char **txtDat,
+                       int adDataType)
+{
+    AVIOContext *pb = s->pb;
+    AdContext* adContext = s->priv_data;
+    int errorVal = 0;
+    int mimeBlockType = 0;
+    uint8_t buf[TEMP_BUFFER_SIZE];
+    int bufSize = TEMP_BUFFER_SIZE;
+
+    // Fields are set manually from MIME data with these types so need
+    // to set everything to zero initially in case some values aren't
+    // available
+    memset(vidDat, 0, sizeof(struct NetVuImageData));
+
+    // Allocate a new packet to hold the frame's image data
+    if (ad_new_packet(pkt, size) < 0 )
+        return ADFFMPEG_AD_ERROR_MPEG4_MIME_NEW_PACKET;
+
+    // Now read the frame data into the packet
+    if (avio_read( pb, pkt->data, size ) != size )
+        return ADFFMPEG_AD_ERROR_MPEG4_MIME_GET_BUFFER;
+
+    if (adContext->streamDatatype == 0)  {
+        if (adDataType == AD_DATATYPE_H264I)
+            adContext->streamDatatype = PIC_MODE_H264I;
+        else if (adDataType == AD_DATATYPE_H264P)
+            adContext->streamDatatype = PIC_MODE_H264P;
+        else
+            adContext->streamDatatype = mpegOrH264(AV_RB32(pkt->data));
+    }
+    vidDat->vid_format = adContext->streamDatatype;
+
+    // Now we should have a text block following this which contains the
+    // frame data that we can place in a _image_data struct
+    if (parse_mime_header(pb, buf, &bufSize, &mimeBlockType, &size, extra
) != 0)
+        return ADFFMPEG_AD_ERROR_MPEG4_MIME_PARSE_HEADER;
+
+    // Validate the data type and then extract the text buffer
+    if (mimeBlockType == DATA_PLAINTEXT ) {
+        unsigned char *textBuffer = av_malloc( size );
+
+        if (textBuffer != NULL ) {
+            if (avio_read( pb, textBuffer, size ) == size ) {
+                // Now parse the text buffer and populate the
+                // _image_data struct
+                if (parse_mp4_text_data(textBuffer, size, vidDat, txtDat )
!= 0) {
+                    av_free( textBuffer );
+                    return ADFFMPEG_AD_ERROR_MPEG4_MIME_PARSE_TEXT_DATA;
+                }
+            }
+            else {
+                av_free( textBuffer );
+                return ADFFMPEG_AD_ERROR_MPEG4_MIME_GET_TEXT_BUFFER;
+            }
+
+            av_free( textBuffer );
+        }
+        else {
+            return AVERROR(ENOMEM);
+        }
+    }
+    return errorVal;
+}
+
+
+/**
+ * Audio frame
+ */
+static int ad_read_audio(AVFormatContext *s,
+                         AVPacket *pkt, int size, long extra,
+                         struct NetVuAudioData *audDat,
+                         enum AVCodecID codec_id)
+{
+    AVIOContext *pb = s->pb;
+
+    // No presentation information is sent with audio frames in a mime
+    // stream so there's not a lot we can do here other than ensure the
+    // struct contains the size of the audio data
+    audDat->sizeOfAudioData = size;
+    audDat->mode = extra;
+    audDat->seconds = 0;
+    audDat->msecs = 0;
+    audDat->channel = 0;
+    audDat->sizeOfAdditionalData = 0;
+    audDat->additionalData = NULL;
+
+    if (ad_new_packet(pkt, size) < 0)
+        return ADFFMPEG_AD_ERROR_AUDIO_ADPCM_MIME_NEW_PACKET;
+
+    // Now get the actual audio data
+    if (avio_read( pb, pkt->data, size) != size)
+        return ADFFMPEG_AD_ERROR_AUDIO_ADPCM_MIME_GET_BUFFER;
+
+    if (codec_id == AV_CODEC_ID_ADPCM_IMA_WAV)
+        audiodata_network2host(pkt->data, pkt->data, size);
+
+    return 0;
+}
+
+
+/**
+ * Invalid mime header.
+ *
+ * However sometimes the header is missing and then there is a valid image
so
+ * try and parse a frame out anyway.
+ */
+static int handleInvalidMime(AVFormatContext *s,
+                             uint8_t *preRead, int preReadSize, AVPacket
*pkt,
+                             int *data_type, int *size, int *imgLoaded)
+{
+    AVIOContext *pb = s->pb;
+    int errorVal = 0;
+    unsigned char chkByte;
+    int status, read, found = FALSE;
+    uint8_t imageData[MAX_IMAGE_SIZE];
+
+    for(read = 0; read < preReadSize; read++)  {
+        imageData[read] = preRead[read];
+    }
+
+    //Set the data type
+    *data_type = AD_DATATYPE_JFIF;
+
+    // Read more data till we find end of image marker
+    for (; !found && (pb->eof_reached==0) && (pb->error==0); read++) {
+        if (read >= MAX_IMAGE_SIZE)
+            return ADFFMPEG_AD_ERROR_PARSE_MIME_HEADER;
+
+        if (avio_read(pb, &chkByte, 1) < 0)
+            break;
+        imageData[read] = chkByte;
+        if(imageData[read - 1] == 0xFF && imageData[read] == 0xD9)
+            found = TRUE;
+    }
+
+    *size = read;
+    if ((status = ad_new_packet(pkt, *size)) < 0) {
+        av_log(s, AV_LOG_ERROR, "handleInvalidMime: ad_new_packet (size
%d)"
+                                " failed, status %d\n", *size, status);
+        return ADFFMPEG_AD_ERROR_NEW_PACKET;
+    }
+
+    memcpy(pkt->data, imageData, *size);
+    *imgLoaded = TRUE;
+
+    return errorVal;
+}
+
+
+/**
+ * Identify if the stream as an AD MIME stream
+ */
+static int admime_probe(AVProbeData *p)
+{
+    int offset = 0;
+    int ii, matchedBytes = 0;
+
+    if (p->buf_size <= sizeof(BOUNDARY_PREFIX1))
+        return 0;
+
+    // This is nasty but it's got to go here as we don't want to try and
deal
+    // with fixes for certain server nuances in the HTTP layer.
+    // DS2 servers seem to end their HTTP header section with the byte
sequence,
+    // 0x0d, 0x0a, 0x0d, 0x0a, 0x0a
+    // Eco9 server ends its HTTP headers section with the sequence,
+    // 0x0d, 0x0a, 0x0d, 0x0a, 0x0d, 0x0a
+    // Both of which are incorrect. We'll try and detect these cases here
and
+    // make adjustments to the buffers so that a standard validation
routine
+    // can be called...
+
+    if (p->buf[0] == 0x0a )                             // DS2 detection
+        offset = 1;
+    else if (p->buf[0] == 0x0d &&  p->buf[1] == 0x0a )  // Eco 9 detection
+        offset = 2;
+
+    // Now check whether we have the start of a MIME boundary separator
+    if (is_valid_separator( &p->buf[offset], p->buf_size - offset ) > 0 )
+        return AVPROBE_SCORE_MAX;
+
+    // If server is only sending a single frame (i.e. fields=1) then it
+    // sometimes just sends the raw JPEG with no MIME or other header,
check
+    // for this
+    if (p->buf_size >= sizeof(rawJfifHeader))  {
+        for(ii = 0; ii < sizeof(rawJfifHeader); ii++)  {
+            if (p->buf[ii] == rawJfifHeader[ii])
+                ++matchedBytes;
+        }
+        if (matchedBytes == sizeof(rawJfifHeader))
+            return AVPROBE_SCORE_MAX;
+    }
+
+    return 0;
+}
+
+static int admime_read_header(AVFormatContext *s)
+{
+    return ad_read_header(s, NULL);
+}
+
+
+static int admime_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    //AdContext*              adContext = s->priv_data;
+    AVIOContext *           pb = s->pb;
+    void *                  payload = NULL;
+    char *                  txtDat = NULL;
+    int                     data_type = AD_DATATYPE_MAX;
+    int                     size = -1;
+    long                    extra = 0;
+    int                     errorVal = ADFFMPEG_AD_ERROR_UNKNOWN;
+    enum AVMediaType        mediaType = AVMEDIA_TYPE_UNKNOWN;
+    enum AVCodecID          codecId   = AV_CODEC_ID_NONE;
+    int                     imgLoaded = FALSE;
+    uint8_t                 buf[TEMP_BUFFER_SIZE];
+    int                     bufSize = TEMP_BUFFER_SIZE;
+    unsigned char *         tempbuf = NULL;
+
+    errorVal = parse_mime_header(pb, buf, &bufSize, &data_type, &size,
&extra);
+    if(errorVal != 0 )  {
+        if (errorVal == -2)
+            errorVal = handleInvalidMime(s, buf, bufSize, pkt,
+                                         &data_type, &size, &imgLoaded);
+
+        if (errorVal < 0)  {
+            return errorVal;
+        }
+    }
+
+    // Prepare for video or audio read
+    errorVal = initADData(data_type, &mediaType, &codecId, &payload);
+    if (errorVal < 0)  {
+        if (payload != NULL )
+            av_free(payload);
+        return errorVal;
+    }
+
+    // Proceed based on the type of data in this frame
+    switch(data_type) {
+        case AD_DATATYPE_JPEG:
+            errorVal = ad_read_jpeg(s, pkt, payload, &txtDat);
+            break;
+        case AD_DATATYPE_JFIF:
+            errorVal = ad_read_jfif(s, pkt, imgLoaded, size, payload,
&txtDat);
+            break;
+        case AD_DATATYPE_MPEG4I:
+        case AD_DATATYPE_MPEG4P:
+        case AD_DATATYPE_H264I:
+        case AD_DATATYPE_H264P:
+            errorVal = admime_mpeg(s, pkt, size, &extra, payload, &txtDat,
data_type);
+            break;
+        case AD_DATATYPE_AUDIO_ADPCM:
+            errorVal = ad_read_audio(s, pkt, size, extra, payload,
AV_CODEC_ID_ADPCM_IMA_WAV);
+            break;
+        case AD_DATATYPE_INFO:
+        case AD_DATATYPE_XML_INFO:
+        case AD_DATATYPE_SVARS_INFO:
+            // May want to handle INFO, XML_INFO and SVARS_INFO separately
in future
+            errorVal = ad_read_info(s, pkt, size);
+            break;
+        case AD_DATATYPE_LAYOUT:
+            errorVal = ad_read_layout(s, pkt, size);
+            break;
+        case AD_DATATYPE_PBM:
+            errorVal = ad_read_overlay(s, pkt, 1, size, &txtDat);
+            break;
+        case AD_DATATYPE_BMP:
+        default: {
+            av_log(s, AV_LOG_WARNING, "admime_read_packet: No handler for "
+                   "data_type=%d\n", data_type);
+
+            // Would like to use avio_skip, but that needs seek support,
+            // so just read the data into a buffer then throw it away
+            tempbuf = av_malloc(size);
+            if (tempbuf)  {
+                avio_read(pb, tempbuf, size);
+                av_free(tempbuf);
+            }
+            else
+                return AVERROR(ENOMEM);
+
+            return ADFFMPEG_AD_ERROR_DEFAULT;
+        }
+        break;
+    }
+
+    if (errorVal >= 0)  {
+        errorVal = ad_read_packet(s, pkt, 1, mediaType, codecId, payload,
txtDat);
+    }
+    else  {
+        // av_dlog(s, "admime_read_packet: Error %d\n", errorVal);
+
+#ifdef AD_SIDEDATA_IN_PRIV
+        // If there was an error, release any memory that has been
allocated
+        if (payload != NULL)
+            av_free(payload);
+
+        if (txtDat != NULL)
+            av_free( txtDat );
+#endif
+    }
+
+#ifndef AD_SIDEDATA_IN_PRIV
+    if (payload != NULL)
+        av_freep(&payload);
+
+    if( txtDat != NULL )
+        av_freep(&txtDat);
+#endif
+
+    return errorVal;
+}
+
+static int admime_read_close(AVFormatContext *s)
+{
+    return 0;
+}
+
+
+AVInputFormat ff_admime_demuxer = {
+    .name           = "admime",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings video format
(MIME)"),
+    .priv_data_size = sizeof(AdContext),
+    .read_probe     = admime_probe,
+    .read_header    = admime_read_header,
+    .read_packet    = admime_read_packet,
+    .read_close     = admime_read_close,
+    .flags          = AVFMT_TS_DISCONT | AVFMT_VARIABLE_FPS |
AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/adpic.h b/libavformat/adpic.h
new file mode 100644
index 0000000000..c8a27a6d29
--- /dev/null
+++ b/libavformat/adpic.h
@@ -0,0 +1,116 @@ 
+/*
+ * Type information and function prototypes for common AD-Holdings demuxer
code
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * Type information and function prototypes for common AD-Holdings demuxer
code
+ */
+
+#ifndef AVFORMAT_ADPIC_H
+#define AVFORMAT_ADPIC_H
+
+#include "avformat.h"
+#include "ds_exports.h"
+
+
+#ifdef AD_SIDEDATA_IN_PRIV
+int ad_new_packet(AVPacket *pkt, int size);
+#else
+#define ad_new_packet av_new_packet
+#endif
+
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+/// These are the data types that are supported by the DS2 video servers
+enum ff_ad_data_type {  AD_DATATYPE_JPEG = 0,
+                        AD_DATATYPE_JFIF,
+                        AD_DATATYPE_MPEG4I,
+                        AD_DATATYPE_MPEG4P,
+                        AD_DATATYPE_AUDIO_ADPCM,
+                        AD_DATATYPE_AUDIO_RAW,
+                        AD_DATATYPE_MINIMAL_MPEG4,
+                        AD_DATATYPE_MINIMAL_AUDIO_ADPCM,
+                        AD_DATATYPE_LAYOUT,
+                        AD_DATATYPE_INFO,
+                        AD_DATATYPE_H264I,
+                        AD_DATATYPE_H264P,
+                        AD_DATATYPE_XML_INFO,
+                        AD_DATATYPE_BMP,
+                        AD_DATATYPE_PBM,
+                        AD_DATATYPE_SVARS_INFO,
+                        AD_DATATYPE_MAX
+                      };
+
+typedef struct {
+    int64_t lastVideoPTS;
+    int     utc_offset;     ///< Only used in minimal video case
+    int     metadataSet;
+    enum ff_ad_data_type streamDatatype;
+} AdContext;
+
+
+int ad_read_header(AVFormatContext *s, int *utcOffset);
+void ad_network2host(struct NetVuImageData *pic, uint8_t *data);
+int initADData(int data_type, enum AVMediaType *media, enum AVCodecID
*codecId, void **payload);
+int ad_read_jpeg(AVFormatContext *s, AVPacket *pkt, struct NetVuImageData
*vid, char **txt);
+int ad_read_jfif(AVFormatContext *s, AVPacket *pkt, int manual_size, int
size,
+                 struct NetVuImageData *video_data, char **text_data);
+int ad_read_info(AVFormatContext *s, AVPacket *pkt, int size);
+int ad_read_layout(AVFormatContext *s, AVPacket *pkt, int size);
+int ad_read_overlay(AVFormatContext *s, AVPacket *pkt, int channel, int
size, char **text_data);
+int ad_read_packet(AVFormatContext *s, AVPacket *pkt, int channel,
+                   enum AVMediaType mediaType, enum AVCodecID codecId,
+                   void *data, char *text_data);
+AVStream * ad_get_vstream(AVFormatContext *s, uint16_t w, uint16_t h,
+                          uint8_t cam, int format, const char *title);
+AVStream * ad_get_audio_stream(AVFormatContext *s, struct NetVuAudioData*
audioHeader);
+void audiodata_network2host(uint8_t *data, const uint8_t *src, int size);
+int ad_adFormatToCodecId(AVFormatContext *s, int32_t adFormat);
+int mpegOrH264(unsigned int startCode);
+int ad_pbmDecompress(char **comment, uint8_t **src, int size, AVPacket
*pkt, int *width, int *height);
+
+
+#define PIC_REVISION 1
+#define MIN_PIC_VERSION 0xDECADE10
+#define MAX_PIC_VERSION (MIN_PIC_VERSION + PIC_REVISION)
+#define PIC_VERSION (MIN_PIC_VERSION + PIC_REVISION)
+#define pic_version_valid(v) ( ( (v)>=MIN_PIC_VERSION ) && (
(v)<=MAX_PIC_VERSION ) )
+
+#define AUD_VERSION 0x00ABCDEF
+
+#define PIC_MODE_JPEG_422        0
+#define PIC_MODE_JPEG_411        1
+#define PIC_MODE_MPEG4_411       2
+#define PIC_MODE_MPEG4_411_I     3
+#define PIC_MODE_MPEG4_411_GOV_P 4
+#define PIC_MODE_MPEG4_411_GOV_I 5
+#define PIC_MODE_H264I           6
+#define PIC_MODE_H264P           7
+#define PIC_MODE_H264J           8
+
+#endif
diff --git a/libavformat/adraw.c b/libavformat/adraw.c
new file mode 100644
index 0000000000..c689ce4f56
--- /dev/null
+++ b/libavformat/adraw.c
@@ -0,0 +1,131 @@ 
+/*
+ * AD-Holdings demuxer for AD stream format (raw)
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * AD-Holdings demuxer for AD stream format (raw)
+ */
+
+#include "avformat.h"
+#include "adpic.h"
+
+
+/**
+ * Identify if the stream as an AD raw stream
+ */
+static int adraw_probe(AVProbeData *p)
+{
+    int bufferSize = p->buf_size;
+    uint8_t *bufPtr = p->buf;
+
+    while (bufferSize >= NetVuImageDataHeaderSize)  {
+        struct NetVuImageData test;
+        ad_network2host(&test, bufPtr);
+        if (pic_version_valid(test.version))  {
+            if (ad_adFormatToCodecId(NULL, test.vid_format) ==
AV_CODEC_ID_MJPEG)
+                return AVPROBE_SCORE_MAX / 2;
+        }
+        --bufferSize;
+        ++bufPtr;
+    }
+    return 0;
+}
+
+static int adraw_read_header(AVFormatContext *s)
+{
+    return ad_read_header(s, NULL);
+}
+
+static int adraw_read_packet(struct AVFormatContext *s, AVPacket *pkt)
+{
+    AVIOContext     *pb = s->pb;
+    uint8_t         *buf = NULL;
+    struct NetVuImageData  *vidDat = NULL;
+    char            *txtDat = NULL;
+    int             errVal = 0;
+    int             ii = 0;
+
+    vidDat = av_malloc(sizeof(struct NetVuImageData));
+    buf = av_malloc(sizeof(struct NetVuImageData));
+
+    if (!vidDat || !buf)
+        return AVERROR(ENOMEM);
+
+    // Scan for 0xDECADE11 marker
+    errVal = avio_read(pb, buf, sizeof(struct NetVuImageData));
+    while (errVal > 0)  {
+        ad_network2host(vidDat, buf);
+        if (pic_version_valid(vidDat->version))  {
+            break;
+        }
+        for(ii = 0; ii < (sizeof(struct NetVuImageData) - 1); ii++)  {
+            buf[ii] = buf[ii+1];
+        }
+        errVal = avio_read(pb, buf + sizeof(struct NetVuImageData) - 1, 1);
+    }
+    av_free(buf);
+
+    if (errVal > 0)  {
+        switch (ad_adFormatToCodecId(s, vidDat->vid_format))  {
+            case(AV_CODEC_ID_MJPEG):
+                errVal = ad_read_jpeg(s, pkt, vidDat, &txtDat);
+                break;
+            case(AV_CODEC_ID_MPEG4):
+            case(AV_CODEC_ID_H264):
+            default:
+                //errVal = adbinary_mpeg(s, pkt, vidDat, &txtDat);
+                av_log(s, AV_LOG_ERROR, "Unsupported format for adraw
demuxer: "
+                        "%d\n", vidDat->vid_format);
+                break;
+        }
+    }
+    if (errVal >= 0)  {
+        errVal = ad_read_packet(s, pkt, 1, AVMEDIA_TYPE_VIDEO,
AV_CODEC_ID_MJPEG,
+                                vidDat, txtDat);
+    }
+    else  {
+        // If there was an error, release any allocated memory
+        if( vidDat != NULL )
+            av_free( vidDat );
+
+        if( txtDat != NULL )
+            av_free( txtDat );
+    }
+
+    return errVal;
+}
+
+static int adraw_read_close(AVFormatContext *s)
+{
+    return 0;
+}
+
+
+AVInputFormat ff_adraw_demuxer = {
+    .name           = "adraw",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings video format
(raw)"),
+    .read_probe     = adraw_probe,
+    .read_header    = adraw_read_header,
+    .read_packet    = adraw_read_packet,
+    .read_close     = adraw_read_close,
+    .flags          = AVFMT_TS_DISCONT | AVFMT_VARIABLE_FPS |
AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index cd00834807..6988c87414 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -35,8 +35,12 @@  extern AVInputFormat  ff_ac3_demuxer;
 extern AVOutputFormat ff_ac3_muxer;
 extern AVInputFormat  ff_acm_demuxer;
 extern AVInputFormat  ff_act_demuxer;
+extern AVInputFormat  ff_adaudio_demuxer;
+extern AVInputFormat  ff_adbinary_demuxer;
 extern AVInputFormat  ff_adf_demuxer;
+extern AVInputFormat  ff_admime_demuxer;
 extern AVInputFormat  ff_adp_demuxer;
+extern AVInputFormat  ff_adraw_demuxer;
 extern AVInputFormat  ff_ads_demuxer;
 extern AVOutputFormat ff_adts_muxer;
 extern AVInputFormat  ff_adx_demuxer;
@@ -117,6 +121,7 @@  extern AVInputFormat  ff_dnxhd_demuxer;
 extern AVOutputFormat ff_dnxhd_muxer;
 extern AVInputFormat  ff_dsf_demuxer;
 extern AVInputFormat  ff_dsicin_demuxer;
+extern AVInputFormat  ff_dspic_demuxer;
 extern AVInputFormat  ff_dss_demuxer;
 extern AVInputFormat  ff_dts_demuxer;
 extern AVOutputFormat ff_dts_muxer;
@@ -494,6 +499,8 @@  extern AVOutputFormat ff_chromaprint_muxer;
 extern AVInputFormat  ff_libgme_demuxer;
 extern AVInputFormat  ff_libmodplug_demuxer;
 extern AVInputFormat  ff_libopenmpt_demuxer;
+extern AVInputFormat  ff_libparreader_demuxer;
+extern AVOutputFormat ff_libparreader_muxer;
 extern AVInputFormat  ff_vapoursynth_demuxer;

 #include "libavformat/muxer_list.c"
diff --git a/libavformat/ds.c b/libavformat/ds.c
new file mode 100644
index 0000000000..4765c51b67
--- /dev/null
+++ b/libavformat/ds.c
@@ -0,0 +1,1262 @@ 
+/*
+ * Protocol for AD-Holdings Digital Sprite stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#include <strings.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "url.h"
+#include "libavutil/avstring.h"
+#include "libavutil/bswap.h"
+
+#include "adffmpeg_errors.h"
+#include "dsenc.h"
+#include "ds.h"
+
+/* -------------------------------------- Constants
-------------------------------------- */
+#define DS_DEFAULT_PORT                             8234                /*
Deafult TCP and UDP port for comms */
+#define DS_HEADER_MAGIC_NUMBER                      0xFACED0FF
+#define MAX_USER_ID_LENGTH                          30
+#define ACCESS_KEY_LENGTH                           36
+#define MAC_ADDR_LENGTH                             16
+#define UNIT_NAME_LENGTH                            32
+
+#define DS_WRITE_BUFFER_SIZE                        1024
+#define DS_READ_BUFFER_SIZE                         1024
+
+#define DS_PLAYBACK_MODE_LIVE                       0x00
+#define DS_PLAYBACK_MODE_PLAY                       0x01
+
+#define DS_RESOLUTION_HI                            2
+#define DS_RESOLUTION_MED                           1
+#define DS_RESOLUTION_LOW                           0
+
+
+#define SET_FLAG_ZONE_UNKNOWN(flags)    ((flags) |= 0x80)
+
+/* -------------------------------------- Structures/types
-------------------------------------- */
+typedef struct _dsContext {
+    URLContext *        TCPContext; /* Context of the underlying TCP
network connection */
+} DSContext;
+
+typedef struct _networkMessage {
+    MessageHeader       header;
+    void *              body;
+} NetworkMessage;
+
+typedef enum _imgControlMsgType {
+    IMG_LIVE,
+    IMG_PLAY,
+    IMG_GOTO,
+    IMG_DATA,
+    IMG_STOP
+} ImgControlMsgType;
+
+typedef struct _clientConnectMsg {
+    unsigned long           udpPort;
+    long                    connectType;        /* ImgControlMsgType enum.
long used to avoid sizeof(enum) discrepancies */
+    char                    userID[MAX_USER_ID_LENGTH];
+    char                    accessKey[ACCESS_KEY_LENGTH];
+} ClientConnectMsg;
+#define VER_TCP_CLI_CONNECT                     0x00000003
+#define SIZEOF_TCP_CLI_CONNECT_IO               (MAX_USER_ID_LENGTH +
ACCESS_KEY_LENGTH + 8)      /* Size in bytes of the MessageHeader
structure. Can't use sizeof to read/write one of these to network as
structure packing may differ based on platform */
+
+typedef enum _netRejectReason {
+    REJECT_BUSY,
+    REJECT_INVALID_USER_ID,
+    REJECT_AUTHENTIFICATION_REQUIRED,
+    REJECT_AUTHENTIFICATION_INVALID,
+    REJECT_UNAUTHORISED,
+    REJECT_OTHER,
+    REJECT_PASSWORD_CHANGE_REQUIRED,
+    REJECT_OUT_OF_MEMORY,
+    REJECT_CORRUPT_USERS_FILES
+} NetRejectReason;
+
+typedef struct _srvConnectRejectMsg {
+    long                    reason;                /* enum
NET_REJECT_REASON */
+    long                    timestamp;
+    char                    macAddr[MAC_ADDR_LENGTH];
+    unsigned long           appVersion;
+    unsigned long           minViewerVersion;
+} SrvConnectRejectMsg;
+#define VER_TCP_SRV_CONNECT_REJECT          0x00000001
+
+typedef enum _realmType {
+    REALM_LIVE,
+    REALM_PLAYBACK,
+    REALM_TELEM,
+    REALM_EVENTS,
+    REALM_ADMIN,
+    REALM_PASSWORD,
+    REALM_PW_ONCE,
+    REALM_VIEW_ALL,
+    REALM_MCI,
+    REALM_FILE_EXPORT,
+    REALM_WEB,
+    REALM_POS,
+    NUM_FIXED_REALMS,
+} RealmType;
+
+typedef struct _srvConnectReplyMsg {
+    long                    numCameras;
+    long                    viewableCamMask;
+    long                    telemetryCamMask;
+    long                    failedCamMask;
+    long                    maxMsgInterval;
+    int64_t                    timestamp;                          /*
TODO: Verify - this was a 'hyper long' before. Assuming 64 bit value. Is
this correct? Is solution portable? */
+    char                    cameraTitles[16][28];
+    long                    unitType;
+    unsigned long           applicationVersion;
+    long                    videoStandard;
+    char                    macAddr[MAC_ADDR_LENGTH];
+    char                    unitName[UNIT_NAME_LENGTH];
+    long                    numFixedRealms;                        /*
Number of FIXED system realms */
+    unsigned long            realmFlags[NUM_FIXED_REALMS];        /*
Indicates if user is in realm. */
+    unsigned long           minimumViewerVersion;
+} SrvConnectReplyMsg;
+#define VER_TCP_SRV_CONNECT_REPLY           0x00000001
+
+typedef struct _srvFeatureConnectReplyMsg {
+    long                    numCameras;
+    long                    viewableCamMask;
+    long                    telemetryCamMask;
+    long                    failedCamMask;
+    long                    maxMsgInterval;
+    int64_t                    timestamp;                          /*
TODO: Verify - this was a 'hyper long' before. Assuming 64 bit value. Is
this correct? Is solution portable? */
+    char                    cameraTitles[16][28];
+    long                    unitType;
+    unsigned long           applicationVersion;
+    long                    videoStandard;
+    char                    macAddr[MAC_ADDR_LENGTH];
+    char                    unitName[UNIT_NAME_LENGTH];
+
+    unsigned long           minimumViewerVersion;
+    unsigned long             unitFeature01;
+    unsigned long             unitFeature02;
+    unsigned long             unitFeature03;
+    unsigned long             unitFeature04;
+    long                    numFixedRealms;                   /* Number of
FIXED system realms */
+    unsigned long            realmFlags[NUM_FIXED_REALMS];       /*
Indicates if user is in realm. */
+} SrvFeatureConnectReplyMsg;
+#define VER_TCP_SRV_FEATURE_CONNECT_REPLY   0x00000002
+
+typedef struct _cliImgLiveRequestMsg {
+    long                    cameraMask;
+    long                    resolution;
+} CliImgLiveRequestMsg;
+#define VER_TCP_CLI_IMG_LIVE_REQUEST        0x00000001
+#define SIZEOF_TCP_CLI_IMG_LIVE_REQUEST_IO  8            /* Size in bytes
of the MessageHeader structure. Can't use sizeof to read/write one of these
to network as structure packing may differ based on platform */
+
+typedef enum _vcrMode {
+    VM_PLAY,
+    VM_VIS_REW,
+    VM_VIS_FF,
+    VM_STOP,
+    VM_PLAY_SHUTTLE,
+    VM_FINISH
+} vcrMode;
+
+typedef struct _cliImgPlayRequestMsg {
+    long                    cameraMask;
+    long                    mode;                /*    (enum VCR_MODE) */
+    long                    pace;
+    int64_t                    fromTime;            /*        (time_u)
 */
+    int64_t                    toTime;                /*        (time_u)
 */
+} CliImgPlayRequestMsg;
+#define VER_TCP_CLI_IMG_PLAY_REQUEST        0x00000001
+#define SIZEOF_TCP_CLI_IMG_PLAY_REQUEST_IO  28            /* Size in bytes
of the MessageHeader structure. Can't use sizeof to read/write one of these
to network as structure packing may differ based on platform */
+
+
+/* -------------------------------------- Local function declarations
-------------------------------------- */
+static NetworkMessage *     CreateNetworkMessage( ControlMessageTypes
messageType, long channelID );
+static void                 FreeNetworkMessage( NetworkMessage **message );
+
+static int DSOpen( URLContext *h, const char *uri, int flags );
+static int DSRead( URLContext *h, uint8_t *buf, int size );
+static int DSWrite( URLContext *h, const uint8_t *buf, int size );
+static int DSClose( URLContext *h );
+static int DSConnect( URLContext *h, const char *path, const char
*hoststr, const char *auth );
+static inline int MessageSize( const NetworkMessage *message );
+
+static int ReceiveNetworkMessage( URLContext *h, NetworkMessage **message
);
+static int ReadNetworkMessageHeader( URLContext *h, MessageHeader *header
);
+static int ReadNetworkMessageBody( URLContext * h, NetworkMessage *message
);
+
+static int SendNetworkMessage( URLContext *h, NetworkMessage *message );
+static void HToNMessageHeader( MessageHeader *header, unsigned char *buf );
+
+static int ReadConnectRejectMessage( URLContext * h, NetworkMessage
*message );
+static int ReadConnectReplyMessage( URLContext * h, NetworkMessage
*message );
+static int ReadFeatureConnectReplyMessage( URLContext * h, NetworkMessage
*message );
+
+static int GetUserAndPassword( const char * auth, char *user, char
*password  );
+static int CrackURI( const char *path, int *streamType, int *res, int
*cam, time_t *from, time_t *to, int *rate, vcrMode *playMode );
+static int DSReadBuffer( URLContext *h, uint8_t *buffer, int size );
+static int64_t TimeTolong64( time_t time );
+static void NToHMessageHeader( MessageHeader *header );
+
+#if HAVE_BIGENDIAN
+#define HTON64(x)               x
+#define HTON32(x)               x
+#else
+#define HTON64(x)               (av_bswap64(x))
+#define HTON32(x)               (av_bswap32(x))
+#endif
+
+
+/****************************************************************************************************************
+ * Function: CreateNetworkMessage
+ * Desc: Allocates and initialises a NetworkMessage for sending to the
server based on the given type and channel.
+ *       It is the caller's responsibility to release the NetworkMessage
created by this function. This should be
+ *       done with the FreeNetworkMessage function.
+ * Params:
+ *  messageType - Type of the message to create
+ *  channelID - Channel for which the message is intended
+ * Return:
+ *   Pointer to new network message on success. NULL on failure
+
****************************************************************************************************************/
+static NetworkMessage * CreateNetworkMessage( ControlMessageTypes
messageType, long channelID )
+{
+    NetworkMessage *        newMessage = NULL;
+    int                     length = 0;
+    int                     version = 0;
+
+    /* Create a new message structure */
+    newMessage = av_mallocz( sizeof(NetworkMessage) );
+
+    if( newMessage != NULL ) {
+        /* Now allocate the body structure if we've been successful */
+        switch( messageType ) {
+                /* In normal cases, set whatever member variables we can
in here also */
+            case TCP_CLI_CONNECT: {
+                if( (newMessage->body = av_mallocz(
sizeof(ClientConnectMsg) )) != NULL ) {
+                    length = SIZEOF_TCP_CLI_CONNECT_IO;
+                    version = VER_TCP_CLI_CONNECT;
+                }
+                else
+                    goto fail;
+            }
+            break;
+
+            case TCP_CLI_IMG_LIVE_REQUEST: {
+                if( (newMessage->body = av_mallocz(
sizeof(CliImgLiveRequestMsg) )) != NULL ) {
+                    length = SIZEOF_TCP_CLI_IMG_LIVE_REQUEST_IO;
+                    version = VER_TCP_CLI_IMG_LIVE_REQUEST;
+                }
+                else
+                    goto fail;
+            }
+            break;
+
+            case TCP_CLI_IMG_PLAY_REQUEST: {
+                if( (newMessage->body = av_mallocz(
sizeof(CliImgPlayRequestMsg) )) != NULL ) {
+                    length = SIZEOF_TCP_CLI_IMG_PLAY_REQUEST_IO;
+                    version = VER_TCP_CLI_IMG_PLAY_REQUEST;
+                }
+                else
+                    goto fail;
+            }
+            break;
+
+            default: { /* Unknown (or unsupported) message type
encountered */
+                goto fail;
+            }
+            break;
+        }
+
+        /* Set whatever header values we can in here */
+        newMessage->header.messageType = messageType;
+        newMessage->header.magicNumber = DS_HEADER_MAGIC_NUMBER;
+        newMessage->header.channelID = channelID;
+        newMessage->header.sequence = 0; /* Currently unsupported at
server */
+        newMessage->header.checksum = 0; /* As suggested in protocol
documentation */
+        newMessage->header.messageVersion = version;
+
+        /* Set the length of the remaining data (size of the message
header - the magic number + size of the message body) */
+        newMessage->header.length = SIZEOF_MESSAGE_HEADER_IO -
sizeof(unsigned long) + length;
+    }
+
+    return newMessage;
+
+fail:
+    /* Release whatever may have been allocated */
+    FreeNetworkMessage( &newMessage );
+
+    return NULL;
+}
+
+/****************************************************************************************************************
+ * Function: FreeNetworkMessage
+ * Desc: Releases the resources associated with a network message created
with CreateNetworkMessage()
+ * Params:
+ *  message - Address of a pointer to a NetworkMessage struct allocated
with CreateNetworkMessage
+ * Return:
+
****************************************************************************************************************/
+static void FreeNetworkMessage( NetworkMessage **message )
+{
+    /* Simply cascade free all memory allocated */
+    if( *message ) {
+        if( (*message)->body ) {
+            av_free( (*message)->body );
+        }
+
+        av_free( *message );
+        *message = NULL;
+    }
+}
+
+/****************************************************************************************************************
+ * Function: DSOpen
+ * Desc: Opens a connection to a DM Digital Sprite video server and
executes the initial connect transaction
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  uri - The URI indicating the server's location
+ *  flags - Flags indicating the type of connection required (URL_WRONLY,
etc)
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int DSOpen( URLContext *h, const char *uri, int flags )
+{
+    char            hostname[1024], hoststr[1024];
+    char            auth[1024];
+    char            path1[1024];
+    char            buf[1024];
+    int             port, err;
+    const char *    path;
+    URLContext *    TCPContext = NULL;
+    DSContext *     s = NULL;
+
+    h->is_streamed = 1;
+
+    s = av_malloc( sizeof(DSContext) );
+    if (!s) {
+        return -ENOMEM;
+    }
+    h->priv_data = s;
+
+    /* Crack the URL */
+    av_url_split( NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname),
&port, path1, sizeof(path1), uri );
+
+    if (port > 0) {
+        snprintf( hoststr, sizeof(hoststr), "%s:%d", hostname, port );
+    }
+    else {
+        av_strlcpy( hoststr, hostname, sizeof(hoststr) );
+    }
+
+    /* Add the URL parameters (if any) */
+    if (path1[0] == '\0') {
+        path = "/";
+    }
+    else {
+        path = path1;
+    }
+
+    /* Assume default port for this protocol if one isn't supplied */
+    if (port < 0) {
+        port = DS_DEFAULT_PORT;
+    }
+
+    /* Form the appropriate TCP URL */
+    snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
+
+    /* Now open a connection to that TCP address */
+    if( (err = ffurl_open(&TCPContext, buf, AVIO_FLAG_READ_WRITE,
&TCPContext->interrupt_callback, NULL)) < 0 ) {
+        goto fail;
+    }
+
+    /* Save the TCP context */
+    s->TCPContext = TCPContext;
+
+    /* Now initiate connection using the DS protocol */
+    if( (err = DSConnect( h, path, hoststr, auth )) < 0 ) {
+        goto fail;
+    }
+
+    return 0;
+fail:
+    if( TCPContext ) {
+        ffurl_close( TCPContext );
+    }
+
+    av_free( s );
+
+    return err;
+}
+
+/****************************************************************************************************************
+ * Function: DSRead
+ * Desc: Reads data from the connection
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  buf - Buffer to which read data will be written
+ *  size - Number of bytes to read into buf
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int DSRead( URLContext *h, uint8_t *buf, int size )
+{
+    /* All we need to do in here is call the generic read function on our
underlying TCP connection */
+    DSContext *         context = (DSContext *)h->priv_data;
+
+    if( context != NULL )
+        return ffurl_read( context->TCPContext, buf, size );
+
+    return AVERROR(EIO);
+}
+
+/****************************************************************************************************************
+ * Function: DSWrite
+ * Desc: Writes data to the connection
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  buf - Buffer from which data will be written
+ *  size - Number of bytes to write from buf
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int DSWrite( URLContext *h, const uint8_t *buf, int size )
+{
+    /* All we need to do in here is call the generic write function on our
underlying TCP connection */
+    DSContext *     context = (DSContext *)h->priv_data;
+
+    if( context != NULL )
+        return ffurl_write( context->TCPContext, buf, size );
+
+    return AVERROR(EIO);
+}
+
+/****************************************************************************************************************
+ * Function: DSClose
+ * Desc: Closes the connection to the DM Digital Sprite video server
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int DSClose( URLContext *h )
+{
+    DSContext * context = (DSContext *)h->priv_data;
+
+    if( context != NULL ) {
+        ffurl_close( context->TCPContext );
+
+        av_free( context );
+    }
+
+    return 0;
+}
+
+/****************************************************************************************************************
+ * Function: MessageSize
+ * Desc: Calculates the size in bytes of the given NetworkMessage
+ * Params:
+ *  message - Pointer to the message for which the size is required
+ * Return:
+ *   The size of the message, -1 if no message passed
+
****************************************************************************************************************/
+static inline int MessageSize( const NetworkMessage *message )
+{
+    if( message )
+        return message->header.length + sizeof(unsigned long);
+
+    return -1;
+}
+
+/****************************************************************************************************************
+ * Function: DSConnect
+ * Desc: Attempts to connect to the DM Digital Sprite video server
according to its connection protocol
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  path - TODO: Need to confirm whether these params are actually needed
+ *  hoststr -
+ *  auth - Authentication credentials
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int DSConnect( URLContext *h, const char *path, const char
*hoststr, const char *auth )
+{
+    NetworkMessage *    sendMessage = NULL;
+    NetworkMessage *    recvMessage = NULL;
+    int                 retVal = 0;
+    int                 isConnecting = 1;
+    char *              encCredentials = NULL;
+    ClientConnectMsg *  connectMsg = NULL;
+    int                 channelID = -2;
+    int                 streamType = 0, res = 0, cam = 0;
+    time_t              from = 0, to = 0;
+    int                 rate = 0;
+    vcrMode             playMode = VM_PLAY;
+    char                user[MAX_USER_ID_LENGTH];
+    char                password[MAX_USER_ID_LENGTH];
+
+    /* Initialise the user and password fields */
+    memset( user, 0, sizeof(char) * MAX_USER_ID_LENGTH );
+    memset( password, 0, sizeof(char) * MAX_USER_ID_LENGTH );
+
+    /* Extract the username and password */
+    if( (retVal = GetUserAndPassword( auth, user, password )) == 0 ) {
+        /* Crack the URI to get the control parameters */
+        retVal = CrackURI( path, &streamType, &res, &cam, &from, &to,
&rate, &playMode );
+    }
+
+    while( isConnecting && retVal == 0 ) {
+        if( (sendMessage = CreateNetworkMessage( TCP_CLI_CONNECT,
channelID )) == NULL ) {
+            if( encCredentials != NULL )
+                av_free( encCredentials );
+
+            return AVERROR(ENOMEM);
+        }
+
+        /* Set up the connection request */
+        /* Set the message body up now */
+        connectMsg = (ClientConnectMsg *)sendMessage->body;
+
+        connectMsg->udpPort = DS_DEFAULT_PORT;
+
+        if( streamType == DS_PLAYBACK_MODE_PLAY )
+            connectMsg->connectType = IMG_PLAY;
+        else
+            connectMsg->connectType = IMG_LIVE;
+
+        if( encCredentials != NULL ) {
+            memcpy( connectMsg->userID, user, strlen(user) );
+            memcpy( connectMsg->accessKey, encCredentials,
strlen(encCredentials) );
+        }
+
+        /* Send the message to the server */
+        if( (retVal = SendNetworkMessage( h, sendMessage )) >= 0 ) {
+            /* Receive the response */
+            if( (retVal = ReceiveNetworkMessage( h, &recvMessage )) >= 0 )
{
+                switch( recvMessage->header.messageType ) {
+                    case TCP_SRV_CONNECT_REJECT: { /* We expect this first
time */
+                        /* Extract the info we need to encrypt */
+                        SrvConnectRejectMsg     * msg =
(SrvConnectRejectMsg *)recvMessage->body;
+
+                        /* What was the reason for the failure? */
+                        if( msg->reason ==
REJECT_AUTHENTIFICATION_REQUIRED ) {
+                            channelID = recvMessage->header.channelID;
+
+                            /* Encrypt the username / password */
+                            if( strlen( user ) > 0 && strlen( password ) >
0 ) {
+                                if( (encCredentials =
EncryptPasswordString( user, password, msg->timestamp, msg->macAddr,
msg->appVersion )) == NULL )
+                                    retVal = AVERROR(ENOMEM);
+                            }
+                            else {
+                                /* If we haven't got a user and password
string then we have to notify the client */
+                                retVal = ADFFMPEG_DS_ERROR_AUTH_REQUIRED;
+                            }
+                        }
+                        else if( msg->reason ==
REJECT_AUTHENTIFICATION_INVALID ) { /* Supplied credentials are invalid */
+                            retVal = ADFFMPEG_DS_ERROR_INVALID_CREDENTIALS;
+                        }
+                        else { /* Fail */
+                            retVal = AVERROR(EIO);
+                            isConnecting = 0;
+                        }
+                    }
+                    break;
+
+                    case TCP_SRV_FEATURE_CONNECT_REPLY:
+                    case TCP_SRV_CONNECT_REPLY: {
+                        /* Great, we're connected - we just need to send a
IMG_LIVE_REQUEST to the server to start the streaming */
+                        NetworkMessage *        imgRequestMsg = NULL;
+
+                        if( streamType == DS_PLAYBACK_MODE_LIVE ) {
+                            if( (imgRequestMsg = CreateNetworkMessage(
TCP_CLI_IMG_LIVE_REQUEST, channelID )) ) {
+                                CliImgLiveRequestMsg *      msgBody =
(CliImgLiveRequestMsg *)imgRequestMsg->body;
+
+                                msgBody->cameraMask = cam;
+                                msgBody->resolution = res;
+                            }
+                        }
+                        else if( streamType == DS_PLAYBACK_MODE_PLAY ) {
+                            if( (imgRequestMsg = CreateNetworkMessage(
TCP_CLI_IMG_PLAY_REQUEST, channelID )) ) {
+                                CliImgPlayRequestMsg *      msgBody =
(CliImgPlayRequestMsg *)imgRequestMsg->body;
+
+                                msgBody->cameraMask = cam;
+                                msgBody->fromTime = TimeTolong64( from );
+                                msgBody->toTime = TimeTolong64( to );
+                                msgBody->pace = rate;
+                                msgBody->mode = playMode;
+                            }
+                        }
+                        else
+                            retVal = AVERROR(EIO);
+
+                        if( retVal == 0 ) {
+                            /* Fire the request message off */
+                            retVal = SendNetworkMessage( h, imgRequestMsg
);
+                        }
+
+                        isConnecting = 0;
+                    }
+                    break;
+
+                    /* Anything other than a connect reply is failure */
+                    default: {
+                        retVal = -1;
+                        isConnecting = 0;
+                    }
+                    break;
+                }
+            }
+            else
+                isConnecting = 0;
+        }
+        else
+            isConnecting = 0;
+
+
+        /* We can release the messages now */
+        FreeNetworkMessage( &sendMessage );
+        FreeNetworkMessage( &recvMessage );
+    }
+
+    return retVal;
+}
+
+static int GetUserAndPassword( const char * auth, char *user, char
*password  )
+{
+    int             retVal = 0;
+    char *          authStr = NULL;
+    char *          token = NULL;
+    const char *    delim = ":";
+    int             count = 0;
+
+    if( auth != NULL ) {
+        if( strcmp( auth, "" ) != 0 ) {
+            retVal = AVERROR_INVALIDDATA;
+
+            /* Copy the string as strtok needs to modify as it goes */
+            if( (authStr = (char*)av_mallocz( strlen(auth) + 1 )) != NULL
) {
+                strcpy( authStr, auth );
+
+                /* Now split it into the user and password */
+                token = strtok( authStr, delim );
+
+                while( token != NULL ) {
+                    count++;
+
+                    if( count == 1 ) {
+                        if( strlen(token) <= MAX_USER_ID_LENGTH ) {
+                            strcpy( user, token );
+
+                            if( strlen(token) < MAX_USER_ID_LENGTH )
+                                user[strlen(token)] = '\0';
+                        }
+                    }
+                    else if( count == 2 ) {
+                        /* TODO: Verify whether checking against the
length of the max user id is ok. Ultimately, the password is hashed before
transmission
+                           so the length is not imperative here. Maybe
server defines a maximum length though? */
+                        if( strlen(token) <= MAX_USER_ID_LENGTH ) {
+                            strcpy( password, token );
+
+                            if( strlen(token) < MAX_USER_ID_LENGTH )
+                                password[strlen(token)] = '\0';
+
+                            retVal = 0;
+                        }
+                    }
+                    else { /* There shouldn't be more than 2 tokens,
better flag an error */
+                        retVal = AVERROR_INVALIDDATA;
+                    }
+
+                    token = strtok( NULL, delim );
+                }
+
+                av_free( authStr );
+                authStr = NULL;
+            }
+            else
+                retVal = AVERROR(ENOMEM);
+        }
+    }
+
+    return retVal;
+}
+
+static int CrackURI( const char *path, int *streamType, int *res, int
*cam, time_t *from, time_t *to, int *rate, vcrMode *playMode )
+{
+    int             retVal = AVERROR_INVALIDDATA;
+    const char *    delim = "?&";
+    char *          pathStr = NULL;
+    char *          token = NULL;
+
+    *res = DS_RESOLUTION_HI;
+    *streamType = DS_PLAYBACK_MODE_LIVE;
+    *cam = 1;
+
+    if( path != NULL ) {
+        /* Take a copy of the path string so that strtok can modify it */
+        if( (pathStr = (char*)av_mallocz( strlen(path) + 1 )) != NULL ) {
+            strcpy( pathStr, path );
+
+            retVal = 0;
+
+            token = strtok( pathStr, delim );
+
+            while( token != NULL ) {
+                char *          name = NULL;
+                char *          value = NULL;
+
+                /* Now look inside this token string for a name value pair
separated by an = */
+                if( (value = strstr(token, "=")) != NULL ) {
+                    value++;
+
+                    name = token;
+                    name[(value-1) - token] = '\0';
+
+                    if( name != NULL && value != NULL ) {
+                        /* Which parameter have we got? */
+                        if( av_strcasecmp(name, "res" ) == 0 ) {
+                            if( strcmp( value, "hi" ) == 0 )
+                                *res = DS_RESOLUTION_HI;
+                            else if( strcmp( value, "med" ) == 0 )
+                                *res = DS_RESOLUTION_MED;
+                            else if( strcmp( value, "low" ) == 0 )
+                                *res = DS_RESOLUTION_LOW;
+                        }
+                        else if( av_strcasecmp(name, "stream" ) == 0 ) {
+                            if( av_strcasecmp(value, "live" ) == 0 )
+                                *streamType = DS_PLAYBACK_MODE_LIVE;
+                            else if( av_strcasecmp(value, "play" ) == 0 )
+                                *streamType = DS_PLAYBACK_MODE_PLAY;
+                        }
+                        else if( av_strcasecmp(name, "cam" ) == 0 ) {
+                            *cam = atoi( value );
+                        }
+                        else if( av_strcasecmp(name, "from" ) == 0 ) {
+                            *from = (time_t)atoi( value );
+                        }
+                        else if( av_strcasecmp(name, "to" ) == 0 ) {
+                            *to = (time_t)atoi( value );
+                        }
+                        else if( av_strcasecmp(name, "rate" ) == 0 ) {
+                            *rate = atoi( value );
+                        }
+                        else if( av_strcasecmp(name, "mode" ) == 0 ) {
+                            if( av_strcasecmp(value, "play" ) == 0 )
+                                *playMode = VM_PLAY;
+                            else if( av_strcasecmp(value, "rwd" ) == 0 )
+                                *playMode = VM_VIS_REW;
+                            else if( av_strcasecmp(value, "fwd" ) == 0 )
+                                *playMode = VM_VIS_FF;
+                            else if( av_strcasecmp(value, "stop" ) == 0 )
+                                *playMode = VM_STOP;
+                            else if( av_strcasecmp(value, "shuttle" ) == 0
)
+                                *playMode = VM_PLAY_SHUTTLE;
+                            else if( av_strcasecmp(value, "finish" ) == 0 )
+                                *playMode = VM_FINISH;
+                        }
+                    }
+                }
+
+                token = strtok( NULL, delim );
+            }
+
+            av_free( pathStr );
+            pathStr = NULL;
+        }
+        else
+            retVal = AVERROR(ENOMEM);
+    }
+
+    return retVal;
+}
+
+/****************************************************************************************************************
+ * Function: ReceiveNetworkMessage
+ * Desc: Reads the next NetworkMessage from the connection. New message is
allocated and must be release by the
+ *       caller with FreeNetworkMessage()
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  message - Address of pointer to network message. This will be
allocated by this function
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int ReceiveNetworkMessage( URLContext *h, NetworkMessage **message )
+{
+    int     retVal = 0;
+
+    /* Allocate a new NetworkMessage struct */
+    *message = av_mallocz( sizeof(NetworkMessage) );
+
+    if( *message == NULL )
+        return AVERROR(ENOMEM);
+
+    if( (retVal = ReadNetworkMessageHeader( h, &(*message)->header )) != 0
) {
+        FreeNetworkMessage( message );
+        return retVal;
+    }
+
+    if( (retVal = ReadNetworkMessageBody( h, *message )) != 0 ) {
+        FreeNetworkMessage( message );
+        return retVal;
+    }
+
+    return 0;
+}
+
+static int DSReadBuffer( URLContext *h, uint8_t *buffer, int size )
+{
+    int         ret;
+    int         totalRead = 0;
+
+    if( buffer != NULL && size > 0 ) {
+        while( size - totalRead != 0 ) {
+            ret = DSRead( h, buffer, size - totalRead );
+
+            if( ret < 0 )
+                return ret;
+            else
+                totalRead += ret;
+        }
+    }
+
+    return totalRead;
+}
+
+static int ReadNetworkMessageHeader( URLContext *h, MessageHeader *header )
+{
+    /* Read the header in a piece at a time... */
+    if( DSReadBuffer( h, (uint8_t *)&header->magicNumber, sizeof(unsigned
long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->length, sizeof(unsigned long)
) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->channelID, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->sequence, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->messageVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->checksum, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&header->messageType, sizeof(long) )
!= sizeof(long) )
+        return AVERROR(EIO);
+
+    /* Now adjust the endianess */
+    NToHMessageHeader( header );
+
+    return 0;
+}
+
+static void NToHMessageHeader( MessageHeader *header )
+{
+    if( header ) {
+        header->magicNumber = av_be2ne32(header->magicNumber);
+        header->length = av_be2ne32(header->length);
+        header->channelID = av_be2ne32(header->channelID);
+        header->sequence = av_be2ne32(header->sequence);
+        header->messageVersion = av_be2ne32(header->messageVersion);
+        header->checksum = av_be2ne32(header->checksum);
+        header->messageType = av_be2ne32(header->messageType);
+    }
+}
+
+static int ReadNetworkMessageBody( URLContext * h, NetworkMessage *message
)
+{
+    int         retVal = 0;
+
+    if( message != NULL && message->body == NULL ) {
+        /* Read based on the type of message we have */
+        switch( message->header.messageType ) {
+            case TCP_SRV_CONNECT_REJECT: {
+                retVal = ReadConnectRejectMessage( h, message );
+            }
+            break;
+
+            case TCP_SRV_FEATURE_CONNECT_REPLY: {
+                retVal = ReadFeatureConnectReplyMessage( h, message );
+            }
+            break;
+
+            case TCP_SRV_CONNECT_REPLY: {
+                retVal = ReadConnectReplyMessage( h, message );
+            }
+            break;
+
+            default:
+                /* We shouldn't get into this state so we'd better return
an error here... */
+                retVal = AVERROR(EIO);
+                break;
+        }
+
+    }
+
+    return retVal;
+}
+
+static int ReadConnectRejectMessage( URLContext * h, NetworkMessage
*message )
+{
+    SrvConnectRejectMsg *       bodyPtr = NULL;
+
+    /* Allocate the message body */
+    if( (message->body = av_malloc( sizeof(SrvConnectRejectMsg) )) == NULL
)
+        return AVERROR(ENOMEM);
+
+    /* Now read from the stream into the message */
+    bodyPtr = (SrvConnectRejectMsg *)message->body;
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->reason, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->timestamp, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->macAddr, MAC_ADDR_LENGTH ) !=
MAC_ADDR_LENGTH )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->appVersion, sizeof(unsigned
long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->minViewerVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    /* Correct the byte ordering */
+    bodyPtr->reason = av_be2ne32(bodyPtr->reason);
+    bodyPtr->timestamp = av_be2ne32(bodyPtr->timestamp);
+    bodyPtr->appVersion = av_be2ne32(bodyPtr->appVersion);
+    bodyPtr->minViewerVersion = av_be2ne32(bodyPtr->minViewerVersion);
+
+    return 0;
+}
+
+static int ReadConnectReplyMessage( URLContext * h, NetworkMessage
*message )
+{
+    SrvConnectReplyMsg *        bodyPtr = NULL;
+
+    /* Allocate memory in which to store the message body */
+    if( (message->body = av_malloc( sizeof(SrvConnectReplyMsg) )) == NULL )
+        return AVERROR(ENOMEM);
+
+    bodyPtr = (SrvConnectReplyMsg *)message->body;
+
+    /* Now read the message body, a field at a time */
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->numCameras, sizeof(long) )
!= sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->viewableCamMask,
sizeof(long) ) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->telemetryCamMask,
sizeof(long) ) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->failedCamMask, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->maxMsgInterval, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->timestamp, sizeof(int64_t) )
!= sizeof(int64_t) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->cameraTitles, (16 * 28) ) !=
(16 * 28) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitType, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->applicationVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->videoStandard, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->macAddr, MAC_ADDR_LENGTH ) !=
MAC_ADDR_LENGTH )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->unitName, UNIT_NAME_LENGTH )
!= UNIT_NAME_LENGTH )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->numFixedRealms, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    bodyPtr->numFixedRealms = av_be2ne32(bodyPtr->numFixedRealms);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->realmFlags, (sizeof(unsigned
long) * bodyPtr->numFixedRealms) ) != (sizeof(unsigned long) *
bodyPtr->numFixedRealms) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->minimumViewerVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    /* Correct the byte ordering */
+    bodyPtr->numCameras = av_be2ne32(bodyPtr->numCameras);
+    bodyPtr->viewableCamMask = av_be2ne32(bodyPtr->viewableCamMask);
+    bodyPtr->telemetryCamMask = av_be2ne32(bodyPtr->telemetryCamMask);
+    bodyPtr->failedCamMask = av_be2ne32(bodyPtr->failedCamMask);
+    bodyPtr->maxMsgInterval = av_be2ne32(bodyPtr->maxMsgInterval);
+    bodyPtr->unitType = av_be2ne32(bodyPtr->unitType);
+    bodyPtr->applicationVersion = av_be2ne32(bodyPtr->applicationVersion);
+    bodyPtr->videoStandard = av_be2ne32(bodyPtr->videoStandard);
+    bodyPtr->numFixedRealms = av_be2ne32(bodyPtr->numFixedRealms);
+    bodyPtr->minimumViewerVersion =
av_be2ne32(bodyPtr->minimumViewerVersion);
+
+    return 0;
+}
+
+static int ReadFeatureConnectReplyMessage( URLContext * h, NetworkMessage
*message )
+{
+    SrvFeatureConnectReplyMsg *        bodyPtr = NULL;
+
+    if( (message->body = av_malloc( sizeof(SrvFeatureConnectReplyMsg) ))
== NULL )
+        return AVERROR(ENOMEM);
+
+    bodyPtr = (SrvFeatureConnectReplyMsg *)message->body;
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->numCameras, sizeof(long) )
!= sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->viewableCamMask,
sizeof(long) ) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->telemetryCamMask,
sizeof(long) ) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->failedCamMask, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->maxMsgInterval, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->timestamp, sizeof(int64_t) )
!= sizeof(int64_t) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->cameraTitles, (16 * 28) ) !=
(16 * 28) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitType, sizeof(long) ) !=
sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->applicationVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->videoStandard, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->macAddr, MAC_ADDR_LENGTH ) !=
MAC_ADDR_LENGTH )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->unitName, UNIT_NAME_LENGTH )
!= UNIT_NAME_LENGTH )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->minimumViewerVersion,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitFeature01,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitFeature02,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitFeature03,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->unitFeature04,
sizeof(unsigned long) ) != sizeof(unsigned long) )
+        return AVERROR(EIO);
+
+    if( DSReadBuffer( h, (uint8_t *)&bodyPtr->numFixedRealms, sizeof(long)
) != sizeof(long) )
+        return AVERROR(EIO);
+
+    bodyPtr->numFixedRealms = av_be2ne32(bodyPtr->numFixedRealms);
+
+    if( DSReadBuffer( h, (uint8_t *)bodyPtr->realmFlags, (sizeof(unsigned
long) * bodyPtr->numFixedRealms) ) != (sizeof(unsigned long) *
bodyPtr->numFixedRealms) )
+        return AVERROR(EIO);
+
+    /* Correct the byte ordering */
+    bodyPtr->numCameras = av_be2ne32(bodyPtr->numCameras);
+    bodyPtr->viewableCamMask = av_be2ne32(bodyPtr->viewableCamMask);
+    bodyPtr->telemetryCamMask = av_be2ne32(bodyPtr->telemetryCamMask);
+    bodyPtr->failedCamMask = av_be2ne32(bodyPtr->failedCamMask);
+    bodyPtr->maxMsgInterval = av_be2ne32(bodyPtr->maxMsgInterval);
+    bodyPtr->unitType = av_be2ne32(bodyPtr->unitType);
+    bodyPtr->applicationVersion = av_be2ne32(bodyPtr->applicationVersion);
+    bodyPtr->videoStandard = av_be2ne32(bodyPtr->videoStandard);
+    bodyPtr->minimumViewerVersion =
av_be2ne32(bodyPtr->minimumViewerVersion);
+    bodyPtr->unitFeature01 = av_be2ne32(bodyPtr->unitFeature01);
+    bodyPtr->unitFeature02 = av_be2ne32(bodyPtr->unitFeature02);
+    bodyPtr->unitFeature03 = av_be2ne32(bodyPtr->unitFeature03);
+    bodyPtr->unitFeature04 = av_be2ne32(bodyPtr->unitFeature04);
+
+    return 0;
+}
+
+static void HToNMessageHeader( MessageHeader *header, unsigned char *buf )
+{
+    MessageHeader           tempHeader;
+    int                     bufIdx = 0;
+
+    if( header != NULL && buf != NULL ) {
+        /* Set whatever header values we can in here */
+        tempHeader.magicNumber = av_be2ne32(header->magicNumber);
+        memcpy( &buf[bufIdx], &tempHeader.magicNumber, sizeof(unsigned
long) );
+        bufIdx += sizeof(unsigned long);
+
+        tempHeader.length = av_be2ne32(header->length);
+        memcpy( &buf[bufIdx], &tempHeader.length, sizeof(unsigned long) );
+        bufIdx += sizeof(unsigned long);
+
+        tempHeader.channelID = av_be2ne32(header->channelID);
+        memcpy( &buf[bufIdx], &tempHeader.channelID, sizeof(long) );
+        bufIdx += sizeof(long);
+
+        tempHeader.sequence = av_be2ne32(header->sequence); /* Currently
unsupported at server */
+        memcpy( &buf[bufIdx], &tempHeader.sequence, sizeof(long) );
+        bufIdx += sizeof(long);
+
+        tempHeader.messageVersion = av_be2ne32(header->messageVersion);
+        memcpy( &buf[bufIdx], &tempHeader.messageVersion, sizeof(unsigned
long) );
+        bufIdx += sizeof(unsigned long);
+
+        tempHeader.checksum = av_be2ne32(header->checksum); /* As
suggested in protocol documentation */
+        memcpy( &buf[bufIdx], &tempHeader.checksum, sizeof(long) );
+        bufIdx += sizeof(long);
+
+        tempHeader.messageType = av_be2ne32(header->messageType);
+        memcpy( &buf[bufIdx], &tempHeader.messageType, sizeof(long) );
+        bufIdx += sizeof(long);
+    }
+}
+
+/****************************************************************************************************************
+ * Function: SendNetworkMessage
+ * Desc: Sends the given message over the connection.
+ * Params:
+ *  h - Pointer to URLContext struct used to store all connection info
associated with this connection
+ *  message - Pointer to the message to be sent
+ * Return:
+ *   0 on success, non 0 on failure
+
****************************************************************************************************************/
+static int SendNetworkMessage( URLContext *h, NetworkMessage *message )
+{
+    unsigned char       messageBuffer[DS_WRITE_BUFFER_SIZE];
+    int                 bufIdx = SIZEOF_MESSAGE_HEADER_IO;
+
+    /* 0 the buffer */
+    memset( messageBuffer, 0, DS_WRITE_BUFFER_SIZE );
+
+    /* Write the header into the buffer */
+    HToNMessageHeader( &message->header, messageBuffer );
+
+    /* Now write the rest of the message to the buffer based on its type */
+    switch( message->header.messageType ) {
+        case TCP_CLI_CONNECT: {
+            ClientConnectMsg        tempMsg;
+
+            tempMsg.udpPort = HTON32(((ClientConnectMsg
*)message->body)->udpPort);
+            memcpy( &messageBuffer[bufIdx], &tempMsg.udpPort,
sizeof(unsigned long) );
+            bufIdx += sizeof(unsigned long);
+
+            tempMsg.connectType = HTON32(((ClientConnectMsg
*)message->body)->connectType);
+            memcpy( &messageBuffer[bufIdx], &tempMsg.connectType,
sizeof(long) );
+            bufIdx += sizeof(long);
+
+            memcpy( &messageBuffer[bufIdx], ((ClientConnectMsg
*)message->body)->userID, MAX_USER_ID_LENGTH );
+            bufIdx += MAX_USER_ID_LENGTH;
+
+            memcpy( &messageBuffer[bufIdx], ((ClientConnectMsg
*)message->body)->accessKey, ACCESS_KEY_LENGTH );
+            bufIdx += ACCESS_KEY_LENGTH;
+        }
+        break;
+
+        case TCP_CLI_IMG_LIVE_REQUEST: {
+            long            temp;
+
+            temp = HTON32(
((CliImgLiveRequestMsg*)message->body)->cameraMask );
+            memcpy( &messageBuffer[bufIdx], &temp, sizeof(long) );
+            bufIdx += sizeof(long);
+
+            temp = HTON32(
((CliImgLiveRequestMsg*)message->body)->resolution );
+            memcpy( &messageBuffer[bufIdx], &temp, sizeof(long) );
+            bufIdx += sizeof(long);
+        }
+        break;
+
+        case TCP_CLI_IMG_PLAY_REQUEST: {
+            long            temp;
+            int64_t          tempBig;
+
+            temp = HTON32(
((CliImgPlayRequestMsg*)message->body)->cameraMask );
+            memcpy( &messageBuffer[bufIdx], &temp, sizeof(long) );
+            bufIdx += sizeof(long);
+
+            temp = HTON32( ((CliImgPlayRequestMsg*)message->body)->mode );
+            memcpy( &messageBuffer[bufIdx], &temp, sizeof(long) );
+            bufIdx += sizeof(long);
+
+            temp = HTON32( ((CliImgPlayRequestMsg*)message->body)->pace );
+            memcpy( &messageBuffer[bufIdx], &temp, sizeof(long) );
+            bufIdx += sizeof(long);
+
+            tempBig = HTON64(
((CliImgPlayRequestMsg*)message->body)->fromTime );
+            memcpy( &messageBuffer[bufIdx], &tempBig, sizeof(int64_t) );
+            bufIdx += sizeof(int64_t);
+
+            tempBig = HTON64(
((CliImgPlayRequestMsg*)message->body)->toTime );
+            memcpy( &messageBuffer[bufIdx], &tempBig, sizeof(int64_t) );
+            bufIdx += sizeof(int64_t);
+        }
+        break;
+    }
+
+    /* Write to output stream - remember to add on the 4 bytes for the
magic number which precedes the length */
+    return DSWrite( h, messageBuffer, message->header.length +
sizeof(unsigned long) );
+}
+
+static int64_t TimeTolong64( time_t time )
+{
+    int64_t          timeOut = 0;
+    unsigned short  flags = 0;
+    unsigned short  ms = 0;
+    uint8_t *       bufPtr = NULL;
+
+    /* For now, we're saying we don't know the time zone */
+    SET_FLAG_ZONE_UNKNOWN(flags);
+
+    bufPtr = (uint8_t*)&timeOut;
+
+    memcpy( bufPtr, &flags, sizeof(unsigned short) );
+    bufPtr += sizeof(unsigned short);
+
+    memcpy( bufPtr, &ms, sizeof(unsigned short) );
+    bufPtr += sizeof(unsigned short);
+
+    memcpy( bufPtr, &time, sizeof(time_t) );
+
+    return timeOut;
+}
+
+
+URLProtocol ff_dm_protocol = {
+    .name                = "dm",
+    .url_open            = DSOpen,
+    .url_read            = DSRead,
+    .url_write           = DSWrite,
+    .url_close           = DSClose,
+};
diff --git a/libavformat/ds.h b/libavformat/ds.h
new file mode 100644
index 0000000000..c152d1a061
--- /dev/null
+++ b/libavformat/ds.h
@@ -0,0 +1,135 @@ 
+/*
+ * Protocol for AD-Holdings Digital Sprite stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef __DS_H__
+#define __DS_H__
+
+#include "ds_exports.h"
+
+/* -------------------------------------- Constants
-------------------------------------- */
+#define DS_HEADER_MAGIC_NUMBER                      0xFACED0FF
+
+
+/* -------------------------------------- Structures/types
-------------------------------------- */
+typedef enum {
+    INVALID_MESSAGE_TYPE,            /* 0 */
+    TCP_CLI_CONNECT,                        /* 1 */    /* Initial
connection */
+    TCP_SRV_CONNECT_REJECT,                /* 2 */    /* Connection
rejected */
+    TCP_SRV_CONNECT_REPLY,                /* 3 */    /* Connect accepted -
returns DSL configuration */
+    TCP_CLI_IMG_LIVE_REQUEST,            /* 4 */    /* Request an image
stream */
+    TCP_CLI_IMG_PLAY_REQUEST,            /* 5 */      /* Request a
playback stream */
+    TCP_SRV_IMG_DATA,                        /* 6 */    /* Image stream
data */
+    TCP_SRV_NUDGE,                            /* 7 */    /* Nudge */
+    TCP_SRV_DISCONNECT,                    /* 8 */    /* Server dropping
connection  - ie: unit shutting down etc. */
+    UDP_CLI_IMG_CONTROL_REQUEST,        /* 9 */    /* Request to change
image stream ie: camera change */
+    UDP_SRV_IMG_CONTROL_REPLY,            /* A */    /* Acknowledgement of
request */
+    UDP_CLI_SYSTEM_STATUS_REQUEST,    /* B */    /* Request for update of
DSL configuration */
+    UDP_SRV_SYSTEM_STATUS,                /* C */    /* New DSL
configuration - may be sent by server if menus change */
+    UDP_CLI_TELEMETRY_REQUEST,            /* D */    /* Telemetry star
request */
+    UDP_CLI_TELEMETRY_COMMAND,            /* E */    /* Telemetry command
request */
+    UDP_CLI_TELEMETRY_STATUS_REQUEST,/* F */    /* Request for telemetry
status update */
+    UDP_SRV_TELEMETRY_STATUS,            /* 10*/    /* Ack with new
telemetry status */
+    UDP_CLI_DISCONNECT,                    /* 11*/    /* Polite message
notifying of client disconnect */
+    UDP_CLI_DISCOVER,                        /* 12*/    /* Broadcast to
identify units on subnet */
+    UDP_SRV_HELLO,                            /* 13*/    /* Reply
containing basic unit information */
+    UDP_CLI_MCI_COMMAND,                    /* 14*/    /* Send MCI command
*/
+    UDP_SRV_MCI_REPLY,                    /* 15*/    /* Reply from MCI
command */
+    UDP_SRV_MSG_FAIL,                        /* 16*/    /* UDP message
recognition failure - returns status code */
+    UDP_CLI_EVENT_FILTER_REQUEST,        /* 17*/    /* Request to
initialise an event filter. */
+    UDP_SRV_EVENT_FILTER_REPLY,        /* 18*/    /* Filter request either
accepted, or not accepted. */
+    UDP_CLI_EVENTS_REQUEST,                /* 19*/    /* Request to apply
filter and retrieve events */
+    UDP_SRV_EVENTS_REPLY,                /* 1A*/    /* Reply containing
filtered event */
+    UDP_CLI_ADMIN_REQUEST,                     /* 1B*/    /* Request from
user for admin */
+    UDP_SRV_ADMIN_REPLY,                         /* 1C*/    /* Reply -
contains success or fail reason */
+    UDP_CLI_USER_INFO_REQUEST,                  /* 1D*/    /* Request for
information on a specific user */
+    UDP_SRV_USER_INFO_REPLY,                  /* 1E*/    /* Information on
the requested user */
+    UDP_CLI_ADD_USER_REQUEST,                  /* 1F*/    /* Request to
add a user to the system */
+    UDP_SRV_ADD_USER_REPLY,                      /* 20*/    /* Reply to
indicate success or failure */
+    UDP_CLI_DELETE_USER_REQUEST,                  /* 21*/    /* Request to
delete a user from the system */
+    UDP_SRV_DELETE_USER_REPLY,                      /* 22*/    /* Reply to
delete user request */
+    UDP_CLI_CHANGE_PASSWORD_REQUEST,          /* 23*/    /* Change
password */
+    UDP_SRV_CHANGE_PASSWORD_REPLY,           /* 24*/    /* Reply to
request */
+    UDP_CLI_UPDATE_ACCESS_RIGHTS_REQUEST,  /* 25*/    /* Request to change
users realms */
+    UDP_SRV_UPDATE_ACCESS_RIGHTS_REPLY,       /* 26*/    /* Reply to
request */
+    TCP_CLI_CHANGE_PASSWORD,               /* 27*/    /* send password
change*/
+    UDP_CLI_CHANGE_OWN_PASSWORD_REQUEST,    /* 28*/    /* Change own
password */
+    UDP_SRV_CHANGE_OWN_PASSWORD_REPLY,        /* 29*/    /* Reply to
request */
+    UDP_CLI_EMAIL_REQUEST,                      /* 2A*/    /* Request for
email data */
+    UDP_SRV_EMAIL_REPLY,                         /* 2B*/    /* Reply with
email data */
+    UDP_CLI_CHANGE_EMAIL_REQUEST,              /* 2C*/    /* Request to
set new email data */
+    UDP_SRV_CHANGE_EMAIL_REPLY,               /* 2D*/    /* Reply to
setting new email data */
+    UDP_CLI_CHANGE_SESSION_REQUEST,           /* 2E*/    /* Request to
logon to different unit*/
+    TCP_CLI_IMG_DATA_REQUEST,                    /* 2F*/    /* request
from remote to grab image data */
+    TCP_SRV_DATA_DATA,                            /* 30*/    /* us sending
requested images as data */
+    TCP_SRV_NO_DATA,                                /* 31*/    /* Sent
when finished sending data */
+    UDP_CLI_ABORT_DATA_REQUEST,                /* 32*/    /* Cancel data
transfer */
+    UDP_CLI_EVENT_DATA_REQUEST,                /* 33*/    /* Request to
obtain end time of an event */
+    UDP_SRV_EVENT_DATA_REPLY,                    /* 34*/    /* reply */
+    TCP_CLI_CONTROL_CONNECT,            /* 35*/    /* Initial connection
for TCP control link */
+    TCP_SRV_CONTROL_CONNECT_REJECT,
+    TCP_SRV_CONTROL_CONNECT_REPLY,
+    UDP_CLI_SERVICE_CHECK,                        /* 38*/    /* Sent to
check if UDP service working */
+    UDP_SRV_SERVICE_CHECK_REPLY,
+    UDP_CLI_DRIVE_DETAIL_REQUEST,                /* 3A*/    /* Request for
hard drive details */
+    UDP_SRV_DRIVE_DETAIL_REPLY,                /* 3B*/    /* Reply with
data */
+    UDP_CLI_DRIVE_SMART_REQUEST,                /* 3C*/    /* Request for
hard drive S.M.A.R.T. details */
+    UDP_SRV_DRIVE_SMART_REPLY,                    /* 3D*/    /* Reply with
S.M.A.R.T. data */
+    UDP_CLI_DRIVE_LOG_REQUEST,                    /* 3E*/    /* Request
for hard drive log details */
+    UDP_SRV_DRIVE_LOG_REPLY,                    /* 3F*/    /* Reply with
log data */
+    UDP_CLI_DRIVE_TEST_REQUEST,                /* 40*/    /* Request for
hard drive offline test */
+    UDP_SRV_DRIVE_TEST_REPLY,                    /* 41*/    /* Reply with
confirmation of test */
+    TCP_SRV_FEATURE_CONNECT_REPLY,            /* 42*/    /* Connect
accepted - returns DSL configuration */
+    TCP_CLI_ALM_CONNECT,                   /* 43*/    /* Initial alarm
connection to PC */
+    TCP_SRV_ALM_REJECT,                            /* 44*/    /* reject
message with reasons not registered, auth required, invalid password, busy
etc*/
+    TCP_SRV_ALM_ACCEPT,                            /* 45*/    /* Client
connection accepted - send and alarm msg */
+    TCP_CLI_ALM_MSG,                                /* 46*/    /* Alarm
details */
+    TCP_SRV_ALM_ACK,                                /* 47*/    /* Server
ack of an alarm message */
+    UDP_CLI_CONFIG_REQUEST,                        /* 48*/    /* Request
name/value pairs from unit Get/Send*/
+    UDP_SRV_CONFIG_REJECT,                        /* 49*/    /* Server
denied access to config */
+    UDP_CLI_CONFIG_ITEM,                   /* 4A*/    /* has item x of y
to determine missing and last item */
+    UDP_SRV_CONFIG_ITEM,
+    UDP_CLI_RELAY_REQUEST,
+    UDP_SRV_RELAY_REPLY,
+    UDP_CLI_CHANGE_ACTIVE_FEATURES,
+    UDP_SRV_ACTIVE_FEATURES_REPLY,
+    UDP_CLI_POS_FILTER_REQUEST,            /* 50*/    /* POS
keyworldsearch filter */
+    UDP_SRV_POS_TICK,                      /* 51*/    /* sends during
search to say still searching */
+    UDP_SRV_POS_FILTER_REPLY,              /* 52*/    /* reply to filter */
+    UDP_CLI_POS_LINES_REQUEST,             /* 53*/    /* Request for POS
lines */
+    UDP_SRV_POS_LINES_REPLY,               /* 54*/    /* Reply with POS
matches */
+    UDP_CLI_POS_DATA_REQUEST,              /* 55*/    /* Request for info
on a specific POS event */
+    UDP_SRV_POS_DATA_REPLY,                /* 56*/    /* Replies with
start & end time etc */
+    NET_NUMBER_OF_MESSAGE_TYPES            /* 57*/    /* ALWAYS KEEP AS
LAST ITEMS */
+} ControlMessageTypes;
+
+typedef struct _messageHeader {
+    unsigned long       magicNumber;
+    unsigned long       length;
+    long                channelID;
+    long                sequence;
+    unsigned long       messageVersion;
+    long                checksum;
+    long                messageType;
+} MessageHeader;
+#define SIZEOF_MESSAGE_HEADER_IO                28      /* Size in bytes
of the MessageHeader structure. Can't use sizeof to read/write one of these
to network as structure packing may differ based on platform */
+
+#endif /* __DS_H__ */
diff --git a/libavformat/ds_exports.h b/libavformat/ds_exports.h
new file mode 100644
index 0000000000..7cb845898b
--- /dev/null
+++ b/libavformat/ds_exports.h
@@ -0,0 +1,173 @@ 
+/*
+ * Data exportable to clients for AD-Holdings data types
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef AVFORMAT_DS_EXPORTS_H
+#define AVFORMAT_DS_EXPORTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+
+struct NetVuPicture {
+    uint16_t src_pixels;        ///< Input image size (horizontal)
+    uint16_t src_lines;         ///< Input image size (vertical)
+    uint16_t target_pixels;     ///< Output image size (horizontal)
+    uint16_t target_lines;      ///< Output image size (vertical)
+    uint16_t pixel_offset;      ///< Image start offset (horizontal)
+    uint16_t line_offset;       ///< Image start offset (vertical)
+};
+
+struct NetVuImageData {
+    uint32_t version;                ///<  structure version number */
+
+    /** mode: in PIC_REVISION 0 this was the DFT style FULL_HI etc
+     *        in PIC_REVISION 1 this is used to specify AD or JFIF format
image
+     */
+    int32_t mode;
+    int32_t cam;                ///< camera number
+    int32_t vid_format;         ///< 422 or 411
+    uint32_t start_offset;      ///< start of picture
+    int32_t size;               ///< size of image
+    int32_t max_size;           ///< maximum size allowed
+    int32_t target_size;        ///< size wanted for compression
+    int32_t factor;             ///< Q factor
+    uint32_t alm_bitmask_hi;    ///< High 32 bits of the alarm bitmask
+    int32_t status;             ///< status of last action performed on
picture
+    uint32_t session_time;      ///< playback time of image
+    uint32_t milliseconds;      ///< sub-second count for playback speed
control
+    char res[4];                ///< picture size
+    char title[31];             ///< camera title
+    char alarm[31];             ///< alarm text - title, comment etc
+    struct NetVuPicture format; ///< NOTE: Do not assign to a pointer due
to CW-alignment
+    char locale[30];            ///< Timezone name
+    int32_t utc_offset;         ///< Timezone difference in minutes
+    uint32_t alm_bitmask;
+};
+#define NetVuImageDataHeaderSize 168
+
+struct NetVuAudioData {
+    uint32_t            version;
+    int32_t             mode;
+    int32_t             channel;
+    int32_t             sizeOfAdditionalData;
+    int32_t             sizeOfAudioData;
+    uint32_t            seconds;
+    uint32_t            msecs;
+    unsigned char *     additionalData;
+};
+#define NetVuAudioDataHeaderSize (28 + sizeof(unsigned char *))
+
+#define ID_LENGTH                       8
+#define NUM_ACTIVITIES                  8
+#define CAM_TITLE_LENGTH                24
+#define ALARM_TEXT_LENGTH               24
+
+struct DMImageData {
+    char            identifier[ID_LENGTH];
+    unsigned long   jpegLength;
+    int64_t         imgSeq;
+    int64_t         imgTime;
+    unsigned char   camera;
+    unsigned char   status;
+    unsigned short  activity[NUM_ACTIVITIES];
+    unsigned short  QFactor;
+    unsigned short  height;
+    unsigned short  width;
+    unsigned short  resolution;
+    unsigned short  interlace;
+    unsigned short  subHeaderMask;
+    char            camTitle[CAM_TITLE_LENGTH];
+    char            alarmText[ALARM_TEXT_LENGTH];
+};
+
+
+enum ADFrameType {
+    FrameTypeUnknown = 0,
+    NetVuVideo,
+    NetVuAudio,
+    DMVideo,
+    DMNudge,
+    NetVuDataInfo,
+    NetVuDataLayout,
+    RTPAudio
+};
+
+#define VSD_M0      0
+#define VSD_M1      1
+#define VSD_M2      2
+#define VSD_M3      3
+#define VSD_M4      4
+#define VSD_M5      5
+#define VSD_M6      6
+#define VSD_FM0     7
+#define VSD_F       8
+#define VSD_EM0     9
+#define VSD_COUNT  10
+
+#define ACTMASKLEN  16
+#define VSDARRAYLEN 16
+
+/** This is the data structure that the ffmpeg parser fills in as part of
the
+ * parsing routines. It will be shared between adpic and dspic so that our
+ * clients can be compatible with either stream more easily
+ */
+struct ADFrameData {
+    /// Type of frame we have. See ADFrameType enum for supported types
+    enum ADFrameType    frameType;
+    /// Pointer to structure holding the information for the frame.
+    void *              frameData;
+    /// Pointer to text block
+    void *              additionalData;
+    /// Data parsed out of text (if exists)
+    int                 activeZones;
+    /// Data parsed out of text (if exists)
+    uint32_t            frameNum;
+    /// Data parsed out of text (if exists)
+    uint16_t            activityMask[ACTMASKLEN];
+    /// Data parsed out of text (if exists)
+    int                 vsd[VSD_COUNT][VSDARRAYLEN];
+};
+
+#define RTP_PAYLOAD_TYPE_8000HZ_ADPCM                       5
+#define RTP_PAYLOAD_TYPE_11025HZ_ADPCM                      16
+#define RTP_PAYLOAD_TYPE_16000HZ_ADPCM                      6
+#define RTP_PAYLOAD_TYPE_22050HZ_ADPCM                      17
+#define RTP_PAYLOAD_TYPE_32000HZ_ADPCM                      96
+#define RTP_PAYLOAD_TYPE_44100HZ_ADPCM                      97
+#define RTP_PAYLOAD_TYPE_48000HZ_ADPCM                      98
+#define RTP_PAYLOAD_TYPE_8000HZ_PCM                         100
+#define RTP_PAYLOAD_TYPE_11025HZ_PCM                        101
+#define RTP_PAYLOAD_TYPE_16000HZ_PCM                        102
+#define RTP_PAYLOAD_TYPE_22050HZ_PCM                        103
+#define RTP_PAYLOAD_TYPE_32000HZ_PCM                        104
+#define RTP_PAYLOAD_TYPE_44100HZ_PCM                        11
+#define RTP_PAYLOAD_TYPE_48000HZ_PCM                        105
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/libavformat/dsenc.c b/libavformat/dsenc.c
new file mode 100644
index 0000000000..250d107c9f
--- /dev/null
+++ b/libavformat/dsenc.c
@@ -0,0 +1,488 @@ 
+/*
+ * Protocol for AD-Holdings Digital Sprite stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "avformat.h"
+#include "libavutil/avstring.h"
+#include "dsenc.h"
+
+/* Constants for MD5Transform routine.
+ */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+
+/* POINTER defines a generic pointer type */
+typedef unsigned char *       POINTER;
+typedef const unsigned char * CPOINTER;
+
+/* UINT2 defines a two byte word */
+typedef unsigned short int UINT2;
+
+/* UINT4 defines a four byte word */
+typedef unsigned long int UINT4;
+
+
+/* MD5 context. */
+typedef struct {
+    UINT4 state[4];                   /* state (ABCD) */
+    UINT4 count[2];                    /* number of bits, modulo 2^64 (lsb
first) */
+    unsigned char buffer[64];         /* input buffer */
+} MD5_CTX;
+
+
+/* Length of test block, number of test blocks.
+ */
+#define TEST_BLOCK_LEN 1000
+#define TEST_BLOCK_COUNT 1000
+
+
+// Macro Definitions
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+        (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+        (a) = ROTATE_LEFT ((a), (s)); \
+        (a) += (b); \
+    }
+#define GG(a, b, c, d, x, s, ac) { \
+        (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+        (a) = ROTATE_LEFT ((a), (s)); \
+        (a) += (b); \
+    }
+#define HH(a, b, c, d, x, s, ac) { \
+        (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+        (a) = ROTATE_LEFT ((a), (s)); \
+        (a) += (b); \
+    }
+#define II(a, b, c, d, x, s, ac) { \
+        (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+        (a) = ROTATE_LEFT ((a), (s)); \
+        (a) += (b); \
+    }
+
+#define VIEWER_VERSION_104_001                      0x00104001
+
+
+static void MD5Init(MD5_CTX * context, const unsigned char *pub_key);
+static void MD5Update(MD5_CTX* context, const unsigned char* input,
unsigned int inputLen);
+static void MD5Final(unsigned char * digest, MD5_CTX * context);
+static void MD5Transform(UINT4 * state, const unsigned char * block);
+static void Encode(unsigned char * output, const UINT4 * input, unsigned
int len);
+static void Decode(UINT4 * output, const unsigned char * input, unsigned
int len);
+static unsigned char hex_val (const char *char2);
+static void MD5Print(char* FingerPrint, int size, const unsigned char *
digest);
+static void GetFingerPrint(char* FingerPrint, const char* Source, unsigned
int Length, const char *cpublic_key);
+
+#if !defined(_WIN32)
+static char* strupr(char *theString)
+{
+    int ii;
+
+    for(ii = 0; ii < strlen(theString); ii++)  {
+        theString[ii] = toupper(theString[ii]);
+    }
+    return theString;
+}
+#endif
+
+//---------------------------------------------------------------------------
+
+#pragma pack(1)
+//#pragma package(smart_init)
+
+static unsigned char PADDING[64] = {
+    0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context. */
+static void MD5Init(MD5_CTX * context, const unsigned char *pub_key)
+{
+    context->count[0] = context->count[1] = 0;
+    /* Load magic initialization constants.*/
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xefcdab89;
+    context->state[2] = 0x98badcfe;
+    context->state[3] = 0x10325476;
+    if (pub_key != NULL) {
+        context->state[0] &= 0xFF0000FF ;
+        context->state[0] |= ((pub_key[4] << 8) | (pub_key[1] << 16)) ;
+        context->state[1] &= 0xFFFFFF00 ;
+        context->state[1] |= (pub_key[5]) ;
+        context->state[2] &= 0x00FF00FF ;
+        context->state[2] |= ((pub_key[0] << 8) | (pub_key[2] << 24)) ;
+        context->state[3] &= 0xFF00FFFF ;
+        context->state[3] |= (pub_key[3] << 16) ;
+    }
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+  operation, processing another message block, and updating the
+  context.
+ */
+static void MD5Update(MD5_CTX* context, const unsigned char* input,
unsigned int inputLen)
+{
+    unsigned int i, index, partLen;
+
+    /* Compute number of bytes mod 64 */
+    index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+    /* Update number of bits */
+    if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen
<< 3))
+        context->count[1]++;
+
+    context->count[1] += ((UINT4)inputLen >> 29);
+
+    partLen = 64 - index;
+
+    /* Transform as many times as possible */
+    if (inputLen >= partLen) {
+        memcpy((POINTER)&context->buffer[index], (CPOINTER)input, partLen);
+        MD5Transform (context->state, context->buffer);
+
+        for (i = partLen; i + 63 < inputLen; i += 64)
+            MD5Transform (context->state, &input[i]);
+
+        index = 0;
+    }
+    else {
+        i = 0;
+    }
+
+    /* Buffer remaining input */
+    if (inputLen > i)
+        memcpy((POINTER)&context->buffer[index], (CPOINTER)&input[i],
inputLen - i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+  the message digest and zeroizing the context.
+ */
+static void MD5Final(unsigned char * digest, MD5_CTX * context)
+{
+    unsigned char bits[8];
+    unsigned int index, padLen;
+
+    /* Save number of bits */
+    Encode (bits, context->count, 8);
+
+    /* Pad out to 56 mod 64 */
+    index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+    padLen = (index < 56) ? (56 - index) : (120 - index);
+    MD5Update (context, PADDING, padLen);
+
+    /* Append length (before padding) */
+    MD5Update (context, bits, 8);
+    /* Store state in digest */
+    Encode (digest, context->state, 16);
+
+    /* Zeroize sensitive information */
+    memset ((POINTER)context, 0, sizeof (*context));
+}
+
+
+
+/* MD5 basic transformation. Transforms state based on block */
+static void MD5Transform(UINT4 * state, const unsigned char * block)
+{
+    UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+    Decode (x, block, 64);
+
+    /* Round 1 */
+    FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+    FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+    FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+    FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+    FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+    FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+    FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+    FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+    FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+    FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+    FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+    FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+    FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+    FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+    FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+    FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+    /* Round 2 */
+    GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+    GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+    GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+    GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+    GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+    GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
+    GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+    GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+    GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+    GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+    GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+    GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+    GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+    GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+    GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+    GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+    /* Round 3 */
+    HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+    HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+    HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+    HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+    HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+    HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+    HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+    HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+    HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+    HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+    HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+    HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
+    HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+    HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+    HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+    HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+    /* Round 4 */
+    II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+    II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+    II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+    II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+    II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+    II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+    II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+    II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+    II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+    II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+    II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+    II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+    II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+    II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+    II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+    II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+    /* Zeroize sensitive information */
+    memset ((POINTER)x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is a
multiple of 4 */
+static void Encode(unsigned char * output, const UINT4 *input, unsigned
int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+        output[j] = (unsigned char)(input[i] & 0xff);
+        output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
+        output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
+        output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
+    }
+}
+
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+  a multiple of 4.
+ */
+static void Decode(UINT4 * output, const unsigned char *input, unsigned
int len)
+{
+    unsigned int i, j;
+
+    for (i = 0, j = 0; j < len; i++, j += 4) {
+        output[i] = ((UINT4)input[j]) |
+                    (((UINT4)input[j+1]) << 8) |
+                    (((UINT4)input[j+2]) << 16) |
+                    (((UINT4)input[j+3]) << 24);
+    }
+}
+
+static unsigned char hex_val (const char *char2)
+{
+    unsigned long    ret_val ;
+    if (char2[0] > '9')
+        ret_val = (char2[0] - 'A' + 10) * 0x10 ;
+    else
+        ret_val = (char2[0] - '0') * 0x10 ;
+
+    if (char2[1] > '9')
+        ret_val += (char2[1] - 'A' + 10) ;
+    else
+        ret_val += (char2[1] - '0') ;
+
+    return (unsigned char)(ret_val & 0x000000FF) ;
+}
+
+// Main Function to get a finger print
+static void GetFingerPrint(char* FingerPrint, const char* Source, unsigned
int Length, const char *cpublic_key)
+{
+    MD5_CTX context;
+    unsigned char digest[16];
+
+    short j ;
+    unsigned char public_key[6] ;
+
+    char    local_fp[32+1] ;
+    //unsigned int len = strlen(Source);
+
+    if (cpublic_key != NULL) {
+        for (j = 0; j < 6; j++)
+            public_key[j] = hex_val (&(cpublic_key[j*2])) ;
+        MD5Init (&context, public_key);
+    }
+    else
+        MD5Init (&context, NULL);
+
+    MD5Update (&context, (const unsigned char*)Source, Length);
+    MD5Final (digest, &context);
+
+
+    MD5Print (local_fp, 32 + 1, digest);
+
+    av_strlcpy (FingerPrint, local_fp, 32) ;
+
+}
+
+
+/* Prints a message digest in hexadecimal */
+static void MD5Print(char* FingerPrint, int size, const unsigned char *
digest)
+{
+    unsigned int i;
+
+    char    temp[20];
+
+    strcpy(FingerPrint, "");
+
+    for (i = 0; i < 16; i++) {
+        snprintf (temp, 20, "%02x", digest[i]);
+
+        av_strlcat(FingerPrint, temp, size);
+    }
+
+}
+
+static char *CreateUserPassword( const char *Username, const char
*Password )
+{
+    char *      userPassword = NULL;
+
+    if( Username != NULL && Password != NULL ) {
+        /* Allocate space for both strings */
+        if( (userPassword = (char*) av_malloc( strlen( Username ) +
strlen( Password ) + 1 )) != NULL ) {
+            /* Copy the username into the output string */
+            strcpy( userPassword, Username );
+
+            userPassword[strlen(Username)] = '\0';
+
+            /* Now add the password */
+            av_strlcat( userPassword, Password, strlen( Username ) +
strlen( Password ) + 1 );
+
+            /* NULL terminate */
+            userPassword[strlen(Username) + strlen(Password)] = '\0';
+
+            /* Now convert it to uppercase */
+            strupr( userPassword );
+        }
+    }
+
+    return userPassword;
+}
+
+char * EncryptPasswordString(const char * Username,
+                             const char * Password,
+                             long Timestamp,
+                             const char * MacAddress,
+                             long RemoteApplicationVersion )
+{
+    // Encrypt Password
+    char        EncPassword[33];
+    char        TransmittedData[33];
+    char        Source[128];
+    char *      UserPassword = NULL;
+    char *      EncryptedPassword = NULL;
+    int         canContinue = 1;
+
+    // If connected to a new unit send concat username too
+    if( RemoteApplicationVersion >= VIEWER_VERSION_104_001 ) {     //
version 1.4(001) First version with password handling
+        if( (UserPassword = CreateUserPassword( Username, Password )) !=
NULL ) {
+
+            GetFingerPrint( EncPassword, UserPassword, (unsigned
int)strlen(UserPassword), MacAddress );
+            EncPassword[32] = '\0';
+
+            av_free( UserPassword );
+        }
+        else
+            canContinue = 0;
+    }
+    else {
+        GetFingerPrint( EncPassword, Password, (unsigned
int)(strlen(Password)), MacAddress );
+        EncPassword[32] = '\0';
+    }
+
+    if( canContinue ) {
+        snprintf(Source, 128, "%08X", (int)Timestamp);
+        av_strlcat(Source, strupr(EncPassword), 128);
+
+        GetFingerPrint( TransmittedData, Source, (unsigned
int)strlen(Source), MacAddress );
+        TransmittedData[32] = '\0';
+
+        /* Take a copy of this and return it */
+        if( (EncryptedPassword = (char *) av_malloc(
strlen(TransmittedData) + 1 )) != NULL ) {
+            strcpy( EncryptedPassword, TransmittedData );
+            EncryptedPassword[strlen(TransmittedData)] = '\0';
+        }
+    }
+
+    return EncryptedPassword;
+}
diff --git a/libavformat/dsenc.h b/libavformat/dsenc.h
new file mode 100644
index 0000000000..fe6372efbe
--- /dev/null
+++ b/libavformat/dsenc.h
@@ -0,0 +1,35 @@ 
+/*
+ * Protocol for AD-Holdings Digital Sprite stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+//---------------------------------------------------------------------------
+
+#ifndef __DM_ENCRYPTION_ROUTINES_H__
+#define __DM_ENCRYPTION_ROUTINES_H__
+//---------------------------------------------------------------------------
+
+char * EncryptPasswordString(const char * Username,
+                             const char * Password,
+                             long Timestamp,
+                             const char * MacAddress,
+                             long RemoteApplicationVersion );
+
+#endif /* __DM_ENCRYPTION_ROUTINES_H__ */
diff --git a/libavformat/dspic.c b/libavformat/dspic.c
new file mode 100644
index 0000000000..51c216da30
--- /dev/null
+++ b/libavformat/dspic.c
@@ -0,0 +1,317 @@ 
+/*
+ * Demuxer for AD-Holdings Digital Sprite stream format
+ * Copyright (c) 2006-2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#include "avformat.h"
+#include "libavcodec/avcodec.h"
+#include "libavutil/bswap.h"
+#include "ds.h"
+#include "adpic.h"
+
+static int dspicProbe( AVProbeData *p );
+static int dspicReadHeader( AVFormatContext *s );
+static int dspicReadPacket( AVFormatContext *s, AVPacket *pkt );
+static int dspicReadClose( AVFormatContext *s );
+static int ReadNetworkMessageHeader( AVIOContext *context, MessageHeader
*header );
+static struct DMImageData * parseDSJFIFHeader( uint8_t *data, int dataSize
);
+static int ExtractDSFrameData( uint8_t * buffer, struct DMImageData
*frameData );
+
+
+static const long       DSPacketHeaderMagicNumber = DS_HEADER_MAGIC_NUMBER;
+static const char *     DSApp0Identifier = "DigiSpr";
+
+
+#define TRUE    1
+#define FALSE   0
+
+
+static int dspicProbe( AVProbeData *p )
+{
+    long        magicNumber = 0;
+
+    if( p->buf_size <= sizeof(long) )
+        return 0;
+
+    /* Get what should be the magic number field of the first header */
+    memcpy( &magicNumber, p->buf, sizeof(long) );
+    /* Adjust the byte ordering */
+    magicNumber = av_be2ne32(magicNumber);
+
+    if( magicNumber == DSPacketHeaderMagicNumber )
+        return AVPROBE_SCORE_MAX;
+
+    return 0;
+}
+
+static int dspicReadHeader( AVFormatContext *s )
+{
+    return 0;
+}
+
+static int dspicReadPacket( AVFormatContext *s, AVPacket *pkt )
+{
+    AVIOContext *         ioContext = s->pb;
+    int                     retVal = 0;
+    MessageHeader           header;
+    int                     dataSize = 0;
+    struct DMImageData *           videoFrameData = NULL;
+    AVStream *              stream = NULL;
+    struct ADFrameData *           frameData = NULL;
+    enum ADFrameType             frameType = FrameTypeUnknown;
+
+    /* Attempt to read in a network message header */
+    if( (retVal = ReadNetworkMessageHeader( ioContext, &header )) != 0 )
+        return retVal;
+
+    /* Validate the header */
+    if( header.magicNumber == DSPacketHeaderMagicNumber ) {
+        if( header.messageType == TCP_SRV_NUDGE ) {
+            frameType = DMNudge;
+
+            /* Read any extra bytes then try again */
+            dataSize = header.length - (SIZEOF_MESSAGE_HEADER_IO -
sizeof(unsigned long));
+
+            if( (retVal = ad_new_packet( pkt, dataSize )) < 0 )
+                return retVal;
+
+            if( avio_read( ioContext, pkt->data, dataSize ) != dataSize )
+                return AVERROR(EIO);
+        }
+        else if( header.messageType == TCP_SRV_IMG_DATA ) {
+            frameType = DMVideo;
+
+            /* This should be followed by a jfif image */
+            dataSize = header.length - (SIZEOF_MESSAGE_HEADER_IO -
sizeof(unsigned long));
+
+            /* Allocate packet data large enough for what we have */
+            if( (retVal = ad_new_packet( pkt, dataSize )) < 0 )
+                return retVal;
+
+            /* Read the jfif data out of the buffer */
+            if( avio_read( ioContext, pkt->data, dataSize ) != dataSize )
+                return AVERROR(EIO);
+
+            /* Now extract the frame info that's in there */
+            if( (videoFrameData = parseDSJFIFHeader( pkt->data, dataSize
)) == NULL )
+                return AVERROR(EIO);
+
+            /* if( audioFrameData != NULL ) frameType |=
DS1_PACKET_TYPE_AUDIO; */
+
+            if ( (stream = ad_get_vstream(s, 0, 0, 1, PIC_MODE_JPEG_422,
NULL)) == NULL )
+                return AVERROR(EIO);
+        }
+    }
+    else
+        return AVERROR(EIO);
+
+    /* Now create a wrapper to hold this frame's data which we'll store in
the packet's private member field */
+    if( (frameData = av_malloc( sizeof(*frameData) )) != NULL ) {
+        frameData->frameType = frameType;
+        frameData->frameData = videoFrameData;
+        frameData->additionalData = NULL;
+
+        pkt->data = frameData;
+    }
+    else
+        goto fail_mem;
+
+    pkt->stream_index = ( stream != NULL ) ? stream->index : 0;
+    pkt->duration =  ((int)(AV_TIME_BASE * 1.0));
+
+    return retVal;
+
+fail_mem:
+    /* Make sure everything that might have been allocated is released
before we return... */
+    av_free( frameData );
+    av_free( videoFrameData );
+    return AVERROR(ENOMEM);
+}
+
+static struct DMImageData * parseDSJFIFHeader( uint8_t *data, int dataSize
)
+{
+    struct DMImageData *            frameData = NULL;
+    int                         i;
+    unsigned short              length, marker;
+    int                         sos = FALSE;
+
+    i = 0;
+    while( ((unsigned char)data[i] != 0xff) && (i < dataSize) )
+        i++;
+
+    if ( (unsigned char) data[++i] != 0xd8)
+        return NULL;  /* Bad SOI */
+
+    i++;
+
+    while( !sos && (i < dataSize) ) {
+        memcpy(&marker, &data[i], 2 );
+        i += 2;
+        memcpy(&length, &data[i], 2 );
+        i += 2;
+        marker = av_be2ne16(marker);
+        length = av_be2ne16(length);
+
+        switch (marker) {
+            case 0xffe0 : {    // APP0
+                /* Have a little look at the data in this block, see if
it's what we're looking for */
+                if( memcmp( &data[i], DSApp0Identifier,
strlen(DSApp0Identifier) ) == 0 ) {
+                    int         offset = i;
+
+                    if( (frameData = av_mallocz( sizeof(struct
DMImageData) )) != NULL ) {
+                        /* Extract the values into a data structure */
+                        if( ExtractDSFrameData( &data[offset], frameData )
< 0 ) {
+                            av_free( frameData );
+                            return NULL;
+                        }
+                    }
+                }
+
+                i += length - 2;
+            }
+            break;
+
+            case 0xffdb :    // Q table
+                i += length - 2;
+                break;
+
+            case 0xffc0 :    // SOF
+                i += length - 2;
+                break;
+
+            case 0xffc4 :    // Huffman table
+                i += length - 2;
+                break;
+
+            case 0xffda :    // SOS
+                i += length - 2;
+                sos = TRUE;
+                break;
+
+            case 0xffdd :    // DRI
+                i += length - 2;
+                break;
+
+            case 0xfffe :    // Comment
+                i += length - 2;
+                break;
+
+            default :
+                /* Unknown marker encountered, better just skip past it */
+                i += length - 2;    // JCB 026 skip past the unknown field
+                break;
+        }
+    }
+
+    return frameData;
+}
+
+static int ExtractDSFrameData( uint8_t * buffer, struct DMImageData
*frameData )
+{
+    int         retVal = AVERROR(EIO);
+    int         bufIdx = 0;
+
+    if( buffer != NULL ) {
+        memcpy( frameData->identifier, &buffer[bufIdx], ID_LENGTH );
+        bufIdx += ID_LENGTH;
+
+        memcpy( &frameData->jpegLength, &buffer[bufIdx], sizeof(unsigned
long) );
+        bufIdx += sizeof(unsigned long);
+        frameData->jpegLength = av_be2ne32(frameData->jpegLength);
+
+        memcpy( &frameData->imgSeq, &buffer[bufIdx], sizeof(int64_t) );
+        bufIdx += sizeof(int64_t);
+        frameData->imgSeq = av_be2ne64(frameData->imgSeq);
+
+        memcpy( &frameData->imgTime, &buffer[bufIdx], sizeof(int64_t) );
+        bufIdx += sizeof(int64_t);
+
+        memcpy( &frameData->camera, &buffer[bufIdx], sizeof(unsigned char)
);
+        bufIdx += sizeof(unsigned char);
+
+        memcpy( &frameData->status, &buffer[bufIdx], sizeof(unsigned char)
);
+        bufIdx += sizeof(unsigned char);
+
+        memcpy( &frameData->activity, &buffer[bufIdx], sizeof(unsigned
short) * NUM_ACTIVITIES );
+        bufIdx += sizeof(unsigned short) * NUM_ACTIVITIES;
+
+        memcpy( &frameData->QFactor, &buffer[bufIdx], sizeof(unsigned
short) );
+        bufIdx += sizeof(unsigned short);
+        frameData->QFactor = av_be2ne16(frameData->QFactor);
+
+        memcpy( &frameData->height, &buffer[bufIdx], sizeof(unsigned
short) );
+        bufIdx += sizeof(unsigned short);
+        frameData->height = av_be2ne16(frameData->height);
+
+        memcpy( &frameData->width, &buffer[bufIdx], sizeof(unsigned short)
);
+        bufIdx += sizeof(unsigned short);
+        frameData->width = av_be2ne16(frameData->width);
+
+        memcpy( &frameData->resolution, &buffer[bufIdx], sizeof(unsigned
short) );
+        bufIdx += sizeof(unsigned short);
+        frameData->resolution = av_be2ne16(frameData->resolution);
+
+        memcpy( &frameData->interlace, &buffer[bufIdx], sizeof(unsigned
short) );
+        bufIdx += sizeof(unsigned short);
+        frameData->interlace = av_be2ne16(frameData->interlace);
+
+        memcpy( &frameData->subHeaderMask, &buffer[bufIdx],
sizeof(unsigned short) );
+        bufIdx += sizeof(unsigned short);
+        frameData->subHeaderMask = av_be2ne16(frameData->subHeaderMask);
+
+        memcpy( frameData->camTitle, &buffer[bufIdx], sizeof(char) *
CAM_TITLE_LENGTH );
+        bufIdx += sizeof(char) * CAM_TITLE_LENGTH;
+
+        memcpy( frameData->alarmText, &buffer[bufIdx], sizeof(char) *
ALARM_TEXT_LENGTH );
+        bufIdx += sizeof(char) * ALARM_TEXT_LENGTH;
+
+        retVal = 0;
+    }
+
+    return retVal;
+}
+
+static int ReadNetworkMessageHeader( AVIOContext *context, MessageHeader
*header )
+{
+    // Read the header in a piece at a time...
+    header->magicNumber    = avio_rb32(context);
+    header->length         = avio_rb32(context);
+    header->channelID      = avio_rb32(context);
+    header->sequence       = avio_rb32(context);
+    header->messageVersion = avio_rb32(context);
+    header->checksum       = avio_rb32(context);
+    header->messageType    = avio_rb32(context);
+    return 0;
+}
+
+static int dspicReadClose( AVFormatContext *s )
+{
+    return 0;
+}
+
+
+AVInputFormat ff_dspic_demuxer = {
+    .name           = "dspic",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings Digital-Sprite
format"),
+    .read_probe     = dspicProbe,
+    .read_header    = dspicReadHeader,
+    .read_packet    = dspicReadPacket,
+    .read_close     = dspicReadClose,
+};
diff --git a/libavformat/libpar.c b/libavformat/libpar.c
new file mode 100644
index 0000000000..6393b3e011
--- /dev/null
+++ b/libavformat/libpar.c
@@ -0,0 +1,1030 @@ 
+/*
+ * AD-Holdings PAR file demuxer
+ * Copyright (c) 2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+/**
+ * @file
+ * AD-Holdings PAR file demuxer
+ */
+
+#include <strings.h>
+#include <parreader.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "libavutil/avstring.h"
+#include "libavutil/intreadwrite.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/time.h"
+#include "libpar.h"
+#include "adpic.h"
+
+
+typedef struct {
+    ParDisplaySettings dispSet;
+    ParFrameInfo frameInfo;
+    int fileChanged;
+    int frameCached;
+    unsigned long seqStartAdded;
+} PARDecContext;
+
+struct PAREncStreamContext {
+    int index;
+    char name[64];
+    int camera;
+    int64_t startTime;
+    int utc_offset;
+    //struct PAREncStreamContext *next;
+};
+
+typedef struct {
+    ParFrameInfo frameInfo;
+    struct PAREncStreamContext master;
+    struct PAREncStreamContext stream[20];
+    //struct PAREncStreamContext *firstStream;
+    int picHeaderSize;
+} PAREncContext;
+
+#ifdef AD_SIDEDATA_IN_PRIV
+void libpar_packet_destroy(struct AVPacket *packet);
+#endif
+
+
+const unsigned int MAX_FRAMEBUFFER_SIZE = 512 * 1024;
+
+
+static void importMetadata(const AVDictionaryEntry *tag, struct
PAREncStreamContext *ps)
+{
+    if (av_strcasecmp(tag->key, "title") == 0)
+        av_strlcpy(ps->name, tag->value, sizeof(ps->name));
+    else if (av_strcasecmp(tag->key, "date") == 0)  {
+        av_parse_time(&ps->startTime, tag->value, 0);
+        ps->startTime *= 1000;
+    }
+    else if (av_strcasecmp(tag->key, "track") == 0)
+        sscanf(tag->value, "%d", &(ps->camera));
+    else if (av_strcasecmp(tag->key, "timezone") == 0)
+        sscanf(tag->value, "%d", &(ps->utc_offset));
+}
+
+static void parreaderLogger(int level, const char *format, va_list args)
+{
+    int av_log_level = -1;
+    switch(level)  {
+        case (PARREADER_LOG_CRITICAL):
+            av_log_level = AV_LOG_FATAL;
+            break;
+        case(PARREADER_LOG_ERROR):
+            av_log_level = AV_LOG_ERROR;
+            break;
+        case(PARREADER_LOG_WARNING):
+            av_log_level = AV_LOG_WARNING;
+            break;
+        case(PARREADER_LOG_INFO):
+            av_log_level = AV_LOG_INFO;
+            break;
+        case(PARREADER_LOG_DEBUG):
+            av_log_level = AV_LOG_DEBUG;
+            break;
+    }
+    av_vlog(NULL, av_log_level, format, args);
+}
+
+
+static int par_write_header(AVFormatContext *avf)
+{
+    PAREncContext *p = avf->priv_data;
+    AVDictionaryEntry *tag = NULL;
+    int ii, result;
+    char *fnameNoExt;
+
+    p->picHeaderSize = parReader_getPicStructSize();
+    do  {
+        tag = av_dict_get(avf->metadata, "", tag, AV_DICT_IGNORE_SUFFIX);
+        if (tag)
+            importMetadata(tag, &(p->master));
+    }
+    while (tag);
+
+    for (ii = 0; ii < avf->nb_streams; ii++)  {
+        AVStream *st = avf->streams[ii];
+
+        // Set timebase to 1 millisecond, and min frame rate to 1 /
timebase
+        avpriv_set_pts_info(st, 32, 1, 1000);
+        st->r_frame_rate = (AVRational) { 1, 1 };
+    }
+
+    // parReader_initWritePartition will automatically append .par to
filename
+    // so strip it off name passed in to prevent file being called
file.par.par
+    ii = strlen(avf->filename);
+    fnameNoExt = av_malloc(ii+1);
+    strcpy(fnameNoExt, avf->filename);
+    if (av_strcasecmp(avf->filename + ii - 4, ".par") == 0)
+        fnameNoExt[ii - 4] = '\0';
+    result = parReader_initWritePartition(&p->frameInfo, fnameNoExt, -1);
+    av_free(fnameNoExt);
+
+    if (result == 1)
+        return 0;
+    else
+        return AVERROR(EIO);
+}
+
+static int par_write_packet(AVFormatContext *avf, AVPacket * pkt)
+{
+    PAREncContext *p = avf->priv_data;
+    //struct PAREncStreamContext *ps = p->firstStream;
+    struct PAREncStreamContext *ps = &(p->stream[pkt->stream_index]);
+    AVDictionaryEntry *tag = NULL;
+    int64_t parTime;
+    AVStream *stream = avf->streams[pkt->stream_index];
+    void *hdr;
+    uint8_t *ptr;
+    int parFrameFormat;
+    int64_t srcTime = pkt->pts;
+    //uint32_t pktTypeCheck;
+    int written = 0;
+    int isADformat = 0;
+
+    // Metadata
+    if (ps->camera < 1)  {
+        // Copy over the values from the file data first
+        *ps = p->master;
+
+        // Now check if there are stream-specific values
+        do  {
+            tag = av_dict_get(stream->metadata, "", tag,
AV_DICT_IGNORE_SUFFIX);
+            if (tag)
+                importMetadata(tag, ps);
+        }
+        while (tag);
+
+        if (ps->camera < 1)
+            ps->camera = pkt->stream_index + 1;
+        if (strlen(ps->name) == 0)
+            snprintf(ps->name, sizeof(ps->name), "Camera %d", ps->camera);
+    }
+
+    isADformat = 0;
+#ifdef AD_SIDEDATA
+    av_packet_split_side_data(pkt);
+    for (int ii = 0; ii < pkt->side_data_elems; ii++)  {
+        if (pkt->side_data[ii].type == AV_PKT_DATA_AD_FRAME)
+            isADformat = 1;
+    }
+#endif
+
+    if (isADformat)  {
+        uint8_t *combBuf = NULL, *combPtr = NULL;
+        int combSize = 0;
+        for (int ii = 0; ii < pkt->side_data_elems; ii++)  {
+#ifdef AD_SIDEDATA
+            if ( (pkt->side_data[ii].type == AV_PKT_DATA_AD_FRAME) ||
(pkt->side_data[ii].type == AV_PKT_DATA_AD_TEXT) )  {
+                combBuf = av_realloc(combBuf, combSize +
pkt->side_data[ii].size);
+                combPtr = combBuf + combSize;
+                memcpy(combPtr, pkt->side_data[ii].data,
pkt->side_data[ii].size);
+                combSize += pkt->side_data[ii].size;
+            }
+#endif
+        }
+
+        combBuf = av_realloc(combBuf, combSize + pkt->size);
+        combPtr = combBuf + combSize;
+        memcpy(combPtr, pkt->data, pkt->size);
+        combSize += pkt->size;
+
+        p->frameInfo.frameBuffer = combBuf;
+        p->frameInfo.frameBufferSize = combSize;
+        written = parReader_writePartition(&p->frameInfo);
+        return written;
+    }
+    else  {
+        // PAR files have timestamps that are in UTC, not elapsed time
+        // So if the pts we have been given is the latter we need to
+        // add an offset to convert it to the former
+
+        if (srcTime < 0)
+            srcTime = pkt->dts;
+        if ( (srcTime == 0) && (ps->startTime == 0) )  {
+            AVDictionaryEntry *ffstarttime = av_dict_get(avf->metadata,
"creation_time", NULL, 0);
+            int64_t ffstarttimeint = 0;
+            if (ffstarttime)
+                sscanf(ffstarttime->value, "%"PRId64"", &ffstarttimeint);
+
+            if (avf->start_time_realtime > 0)
+                ps->startTime = avf->start_time_realtime / 1000;
+            else if (&ffstarttimeint > 0)
+                ps->startTime = ffstarttimeint * 1000;
+            else
+                ps->startTime = av_gettime() / 1000;
+        }
+        parTime = srcTime;
+        if (parTime < ps->startTime)
+            parTime = parTime + ps->startTime;
+
+        if (stream->codec->codec_id == AV_CODEC_ID_MJPEG)  {
+            //p->frameInfo.frameBuffer = parReader_jpegToIMAGE(pkt->data,
parTime, pkt->stream_index);
+            if ((stream->codec->pix_fmt == AV_PIX_FMT_YUV422P) ||
(stream->codec->pix_fmt == AV_PIX_FMT_YUVJ422P) )
+                parFrameFormat = FRAME_FORMAT_JPEG_422;
+            else
+                parFrameFormat = FRAME_FORMAT_JPEG_411;
+        }
+        else if (stream->codec->codec_id == AV_CODEC_ID_MPEG4) {
+            if (pkt->flags & AV_PKT_FLAG_KEY)
+                parFrameFormat = FRAME_FORMAT_MPEG4_411_GOV_I;
+            else
+                parFrameFormat = FRAME_FORMAT_MPEG4_411_GOV_P;
+        }
+        else  {
+            if (pkt->flags & AV_PKT_FLAG_KEY)
+                parFrameFormat = FRAME_FORMAT_H264_I;
+            else
+                parFrameFormat = FRAME_FORMAT_H264_P;
+        }
+
+        hdr = parReader_generatePicHeader(ps->camera,
+                                          parFrameFormat,
+                                          pkt->size,
+                                          parTime,
+                                          ps->name,
+                                          stream->codec->width,
+                                          stream->codec->height,
+                                          ps->utc_offset
+                                         );
+        p->frameInfo.frameBufferSize = pkt->size + p->picHeaderSize;
+        ptr = av_malloc(p->frameInfo.frameBufferSize);
+        if (ptr == NULL)
+            return AVERROR(ENOMEM);
+        p->frameInfo.frameBuffer = ptr;
+        memcpy(ptr, hdr, p->picHeaderSize);
+        memcpy(ptr + p->picHeaderSize, pkt->data, pkt->size);
+        written = parReader_writePartition(&p->frameInfo);
+        av_free(ptr);
+        parReader_freePicHeader(hdr);
+
+        return written;
+    }
+}
+
+static int par_write_trailer(AVFormatContext *avf)
+{
+    PAREncContext *p = avf->priv_data;
+    parReader_closeWritePartition(&p->frameInfo);
+    return 0;
+}
+
+#ifdef AD_SIDEDATA_IN_PRIV
+void libpar_packet_destroy(struct AVPacket *packet)
+{
+    LibparFrameExtra *fed = (LibparFrameExtra*)packet->priv;
+
+    if (packet->data == NULL) {
+        return;
+    }
+
+    if (fed)  {
+        if (fed->frameInfo->frameBuffer)
+            av_free(fed->frameInfo->frameBuffer);
+        if (fed->frameInfo)
+            av_free(fed->frameInfo);
+
+        if (fed->indexInfo)
+            parReader_freeIndexInfo(fed->indexInfo);
+
+        av_free(fed);
+    }
+
+    av_destruct_packet(packet);
+}
+#endif
+
+static void endianSwapAudioData(uint8_t *data, int size)
+{
+    const uint8_t *dataEnd = data + size;
+    uint8_t upper, lower;
+    uint16_t predictor = AV_RB16(data);
+
+    AV_WL16(data, predictor);
+    data += 4;
+
+    for (;data < dataEnd; data++)  {
+        upper = ((*data) & 0xF0) >> 4;
+        lower = ((*data) & 0x0F) << 4;
+        *data = upper | lower;
+    }
+}
+
+static int64_t getLastFrameTime(int fc, ParFrameInfo *fi,
ParDisplaySettings *disp)
+{
+    int64_t lastFrame = 0;
+    int isReliable;
+
+
+    lastFrame = parReader_getEndTime(fi, &isReliable) * 1000LL;
+
+    if (isReliable == 0)  {
+        long startFrame = fi->frameNumber;
+        int streamId = fi->channel;
+        int ii = 1;
+
+        disp->cameraNum = fi->channel & 0xFFFF;
+        disp->fileSeqNo = -1;
+        disp->fileLock = 1;    // Don't seek beyond the file
+        do  {
+            disp->frameNumber = fc - ii++;
+            if (disp->frameNumber <= startFrame)
+                break;
+            parReader_loadFrame(fi, disp, NULL);
+        } while (streamId != fi->channel);
+
+        lastFrame = fi->imageTime * 1000LL + fi->imageMS;
+
+        // Now go back to where we were and reset the state
+        disp->playMode = RWND;
+        disp->frameNumber = startFrame;
+        parReader_loadFrame(fi, disp, NULL);
+        disp->playMode = PLAY;
+        disp->fileLock = 0;
+    }
+
+    return lastFrame;
+}
+
+static AVStream* createStream(AVFormatContext * avf)
+{
+    PARDecContext *p = avf->priv_data;
+    ParFrameInfo *fi = (ParFrameInfo *)&p->frameInfo;
+    char textbuf[128];
+    int w, h;
+    int fc = 0;
+
+    unsigned long startT, endT;
+    AVStream * st = NULL;
+
+    if ((NULL==avf) || (NULL==fi) || (NULL==fi->frameBuffer))
+        return NULL;
+
+    st = avformat_new_stream(avf, NULL);
+    st->id = fi->channel;
+
+    parReader_getIndexData(fi, NULL, &fc, &startT, &endT);
+
+    if (parReader_frameIsVideo(fi))  {
+        st->codec->codec_type = AVMEDIA_TYPE_VIDEO;
+
+        switch(parReader_getFrameSubType(fi))  {
+            case(FRAME_FORMAT_JPEG_422):
+            case(FRAME_FORMAT_JPEG_411):
+                st->codec->codec_id = AV_CODEC_ID_MJPEG;
+                st->codec->has_b_frames = 0;
+                break;
+            case(FRAME_FORMAT_MPEG4_411):
+            case(FRAME_FORMAT_MPEG4_411_I):
+            case(FRAME_FORMAT_MPEG4_411_GOV_P):
+            case(FRAME_FORMAT_MPEG4_411_GOV_I):
+                st->codec->codec_id = AV_CODEC_ID_MPEG4;
+                break;
+            case(FRAME_FORMAT_RAW_422I):
+            case(FRAME_FORMAT_H264_I):
+            case(FRAME_FORMAT_H264_P):
+                st->codec->codec_id = AV_CODEC_ID_H264;
+                break;
+            case(FRAME_FORMAT_PBM):
+                st->codec->codec_id = AV_CODEC_ID_PBM;
+                break;
+            default:
+                // Set unknown types to data so we don't try and play them
+                st->codec->codec_type = AVMEDIA_TYPE_DATA;
+                st->codec->codec_id = AV_CODEC_ID_NONE;
+                break;
+        }
+
+        if (AVMEDIA_TYPE_VIDEO == st->codec->codec_type)  {
+            if (AV_CODEC_ID_PBM != st->codec->codec_id)  {
+                parReader_getFrameSize(fi, &w, &h);
+                st->codec->width = w;
+                st->codec->height = h;
+
+                // Set pixel aspect ratio, display aspect is (sar * width
/ height)
+                /// \todo Could set better values here by checking
resolutions and
+                /// assuming PAL/NTSC aspect
+                if( (w > 360) && (h < 480) )
+                    st->sample_aspect_ratio = (AVRational) { 1, 2 };
+                else
+                    st->sample_aspect_ratio = (AVRational) { 1, 1 };
+
+                parReader_getStreamName(fi->frameBuffer,
+                                        fi->frameBufferSize,
+                                        textbuf,
+                                        sizeof(textbuf));
+                av_dict_set(&st->metadata, "title", textbuf, 0);
+
+                parReader_getStreamDate(fi, textbuf, sizeof(textbuf));
+                av_dict_set(&st->metadata, "date", textbuf, 0);
+
+                snprintf(textbuf, sizeof(textbuf), "%d", fi->channel &
0xFFFF);
+                av_dict_set(&st->metadata, "track", textbuf, 0);
+
+                av_dict_set(&st->metadata, "type", "camera", 0);
+            }
+            else  {
+                av_dict_set(&st->metadata, "type", "mask", 0);
+            }
+        }
+
+        // Set timebase to 1 millisecond, and min frame rate to 1 /
timebase
+        avpriv_set_pts_info(st, 32, 1, 1000);
+        st->r_frame_rate = (AVRational) { 1, 1 };
+        st->start_time   = fi->imageTime * 1000LL + fi->imageMS;
+        st->duration     = getLastFrameTime(fc, fi, &p->dispSet) -
st->start_time;
+    }
+    else if (parReader_frameIsAudio(fi))  {
+        st->codec->codec_type = AVMEDIA_TYPE_AUDIO;
+        st->codec->channels = 1;
+        st->codec->block_align = 0;
+        st->start_time = fi->imageTime * 1000LL + fi->imageMS;
+        st->duration = getLastFrameTime(fc, fi, &p->dispSet) -
st->start_time;
+
+        switch(parReader_getFrameSubType(fi))  {
+            case(FRAME_FORMAT_AUD_ADPCM_8000):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 8000;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_16000):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 16000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_44100):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 441000;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_11025):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 11025;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_22050):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 22050;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_32000):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 32000;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_44100):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 44100;
+                break;
+            case(FRAME_FORMAT_AUD_ADPCM_48000):
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->bits_per_coded_sample = 4;
+                st->codec->sample_rate = 48000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_8000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 8000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_11025):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 11025;
+                break;
+            case(FRAME_FORMAT_AUD_L16_16000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 16000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_22050):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 22050;
+                break;
+            case(FRAME_FORMAT_AUD_L16_32000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 32000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_48000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 48000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_12000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 12000;
+                break;
+            case(FRAME_FORMAT_AUD_L16_24000):
+                st->codec->codec_id = AV_CODEC_ID_PCM_S16LE;
+                st->codec->bits_per_coded_sample = 16;
+                st->codec->sample_rate = 24000;
+                break;
+            default:
+                st->codec->codec_id = AV_CODEC_ID_ADPCM_IMA_WAV;
+                st->codec->sample_rate = 8000;
+                break;
+        }
+        avpriv_set_pts_info(st, 64, 1, st->codec->sample_rate);
+
+        if (fi->channel & 0x8000)  {
+            // Camera associated audio
+            snprintf(textbuf, sizeof(textbuf), "%d", fi->channel &
~0x8000);
+            av_dict_set(&st->metadata, "track", textbuf, 0);
+            av_dict_set(&st->metadata, "associated", "1", 0);
+        }
+        else  {
+            snprintf(textbuf, sizeof(textbuf), "%d", fi->channel);
+            av_dict_set(&st->metadata, "track", textbuf, 0);
+            av_dict_set(&st->metadata, "associated", "0", 0);
+        }
+    }
+    else  {
+        st->codec->codec_type = AVMEDIA_TYPE_DATA;
+
+        // Set timebase to 1 millisecond, and min frame rate to 1 /
timebase
+        avpriv_set_pts_info(st, 32, 1, 1000);
+        st->r_frame_rate = (AVRational) { 1, 1 };
+
+        st->start_time = startT * 1000LL;
+        st->duration   = getLastFrameTime(fc, fi, &p->dispSet) -
st->start_time;
+    }
+
+    return st;
+}
+
+static int createPacket(AVFormatContext *avf, AVPacket *pkt, int siz)
+{
+    PARDecContext *ctxt = avf->priv_data;
+    ParFrameInfo *fi = &ctxt->frameInfo;
+    int id = fi->channel;
+    int ii;
+    AVStream *st = NULL;
+
+#if defined(AD_SIDEDATA_IN_PRIV)
+    LibparFrameExtra *pktExt = NULL;
+    ParFrameInfo *pktFI = NULL;
+#elif defined(AD_SIDEDATA)
+    int adDataSize = 0;
+    uint8_t *sideData = NULL;
+    int textBufSize = 0;
+#endif
+
+    for(ii = 0; ii < avf->nb_streams; ii++)  {
+        if ( (NULL != avf->streams[ii]) && (avf->streams[ii]->id == id) )
 {
+            st = avf->streams[ii];
+            break;
+        }
+    }
+    if (NULL == st)  {
+        st = createStream(avf);
+        if (st == NULL)
+            return -1;
+    }
+
+
+    // If frame is MPEG4 video and is the first sent then add a VOL header
to it
+    if (st->codec->codec_id == AV_CODEC_ID_MPEG4)  {
+        if ((ctxt->seqStartAdded & (1<<st->index)) == 0)  {
+            if (parReader_isIFrame(fi)) {
+                if (parReader_add_start_of_sequence(fi->frameBuffer))
+                    ctxt->seqStartAdded |= 1 << st->index;
+            }
+        }
+    }
+
+    if (st->codec->codec_id == AV_CODEC_ID_PBM)  {
+        char *comment = NULL;
+        int w, h;
+        uint8_t *pbm = av_malloc(fi->size);
+        memcpy(pbm, fi->frameData, fi->size);
+        pkt->size = ad_pbmDecompress(&comment, &pbm, fi->size, pkt, &w,
&h);
+        if (pkt->size > 0)  {
+            st->codec->width = w;
+            st->codec->height = h;
+        }
+        if (comment)  {
+            int camera = parReader_getCamera(fi, NULL, 0);
+            char name[128];
+            snprintf(name, sizeof(name), "Camera %u: %s", camera, comment);
+            av_dict_set(&st->metadata, "title", name, 0);
+            av_free(comment);
+        }
+    }
+    else  {
+        if ( ((uint8_t*)fi->frameData + siz) < ((uint8_t*)fi->frameBuffer
+ MAX_FRAMEBUFFER_SIZE) )  {
+            av_new_packet(pkt, siz);
+
+            if (NULL == pkt->data)  {
+                pkt->size = 0;
+                return AVERROR(ENOMEM);
+            }
+            memcpy(pkt->data, fi->frameData, siz);
+        }
+        else  {
+            av_log(avf, AV_LOG_ERROR, "Copying %d bytes would read beyond
framebuffer end", siz);
+            return AVERROR(ENOMEM);
+        }
+    }
+    pkt->stream_index = st->index;
+
+    if (parReader_frameIsAudio(fi))  {
+        if (st->codec->codec_id == AV_CODEC_ID_ADPCM_IMA_WAV)
+            endianSwapAudioData(pkt->data, siz);
+    }
+    else if (parReader_frameIsVideo(fi))  {
+        if (parReader_isIFrame(fi))
+            pkt->flags |= AV_PKT_FLAG_KEY;
+    }
+
+    if (fi->imageTime > 0)  {
+        pkt->pts = fi->imageTime;
+        pkt->pts *= 1000ULL;
+        pkt->pts += fi->imageMS;
+    }
+    else if (fi->indexTime > 0)  {
+        pkt->pts = fi->indexTime;
+        pkt->pts *= 1000ULL;
+        pkt->pts += fi->indexMS;
+    }
+    else  {
+        pkt->pts = AV_NOPTS_VALUE;
+    }
+    pkt->dts = pkt->pts;
+    pkt->duration = 1;
+
+#if defined(AD_SIDEDATA_IN_PRIV)
+    pkt->destruct = libpar_packet_destroy;
+    pktExt = av_malloc(sizeof(LibparFrameExtra));
+    if (NULL == pktExt)  {
+        pkt->size = 0;
+        return AVERROR(ENOMEM);
+    }
+    pktExt->fileChanged = ctxt->fileChanged;
+    pktExt->indexInfoCount = parReader_getIndexInfo(fi,
&pktExt->indexInfo);
+
+    pktExt->frameInfo = av_mallocz(sizeof(ParFrameInfo));
+    if (NULL == pktExt->frameInfo)  {
+        pkt->size = 0;
+        return AVERROR(ENOMEM);
+    }
+    pktFI = pktExt->frameInfo;
+    if (parReader_frameIsVideo(fi))
+        pktFI->frameBufferSize = parReader_getPicStructSize();
+    else if (parReader_frameIsAudio(fi))
+        pktFI->frameBufferSize = parReader_getAudStructSize();
+
+    if (pktFI->frameBufferSize > 0)  {
+        // Make a copy of the ParFrameInfo struct and the frame header
+        // for use by client code that knows this is here
+
+        // Save frameBufferSize as it's about to be overwritten by memcpy
+        int fbs = pktFI->frameBufferSize;
+        memcpy(pktFI, fi, sizeof(ParFrameInfo));
+        pktFI->frameBufferSize = fbs;
+        pktFI->frameBuffer = av_malloc(fbs);
+        if (NULL == pktFI->frameBuffer)  {
+            pkt->size = 0;
+            return AVERROR(ENOMEM);
+        }
+        memcpy(pktFI->frameBuffer, fi->frameBuffer, fbs);
+        pktFI->frameData = NULL;
+    }
+
+    pkt->priv = pktExt;
+#elif defined(AD_SIDEDATA)
+    if (parReader_frameIsVideo(fi))
+        adDataSize = parReader_getPicStructSize();
+    else if (parReader_frameIsAudio(fi))
+        adDataSize = parReader_getAudStructSize();
+    if (adDataSize > 0)  {
+        sideData = av_packet_new_side_data(pkt, AV_PKT_DATA_AD_FRAME,
adDataSize);
+        if (sideData)
+            memcpy(sideData, fi->frameBuffer, adDataSize);
+    }
+
+    if (fi->frameText)  {
+        textBufSize = strlen(fi->frameText) + 1;
+        sideData = av_packet_new_side_data(pkt, AV_PKT_DATA_AD_TEXT,
textBufSize);
+        if (sideData)
+            memcpy(sideData, fi->frameText, textBufSize);
+    }
+
+    if (ctxt->fileChanged)  {
+        int fc = 0;
+        unsigned long startT, endT, lastFT;
+
+        parReader_getIndexData(fi, NULL, &fc, &startT, &endT);
+        lastFT = getLastFrameTime(fc, fi, &ctxt->dispSet);
+        for (ii = 0; ii < avf->nb_streams; ii++)  {
+            st->start_time = fi->imageTime * 1000LL + fi->imageMS;
+            st->duration = lastFT - st->start_time;
+        }
+
+        sideData = av_packet_new_side_data(pkt, AV_PKT_DATA_AD_PARINF,
sizeof(ctxt->frameInfo));
+        if (sideData)
+            memcpy(sideData, &(ctxt->frameInfo), sizeof(ctxt->frameInfo));
+        ctxt->fileChanged = 0;
+    }
+#endif
+
+    ctxt->frameCached = 0;
+
+    return 0;
+}
+
+
+static int par_probe(AVProbeData *p)
+{
+    unsigned long first4;
+    if (p->buf_size < 4)
+        return 0;
+
+    first4 = *((unsigned long *)p->buf);
+    first4 = av_le2ne32(first4);
+    if (first4 == 0x00524150)
+        return AVPROBE_SCORE_MAX;
+    else
+        return 0;
+}
+
+static int par_read_header(AVFormatContext * avf)
+{
+    int res, siz;
+    PARDecContext *p = avf->priv_data;
+    char **filelist;
+    int seqLen;
+    int64_t seconds = 0;
+    AVStream *strm = NULL;
+    char textbuf[128];
+
+
+    if (parReader_version(textbuf, sizeof(textbuf)) > 0)  {
+        av_log(avf, AV_LOG_INFO, "ParReader library version: %s\n",
textbuf);
+        av_dict_set(&avf->metadata, "ParReader", textbuf, 0);
+    }
+
+    parReader_initFrameInfo(&p->frameInfo, MAX_FRAMEBUFFER_SIZE,
av_malloc(MAX_FRAMEBUFFER_SIZE));
+    if (p->frameInfo.frameBuffer == NULL)
+        return AVERROR(ENOMEM);
+
+    switch(av_log_get_level())  {
+        case(AV_LOG_QUIET):
+            parReader_setLogLevel(-1);
+            break;
+        case(AV_LOG_PANIC):
+            parReader_setLogLevel(PARREADER_LOG_CRITICAL);
+            break;
+        case(AV_LOG_FATAL):
+            parReader_setLogLevel(PARREADER_LOG_CRITICAL);
+            break;
+        case(AV_LOG_ERROR):
+            parReader_setLogLevel(PARREADER_LOG_ERROR);
+            break;
+        case(AV_LOG_WARNING):
+            parReader_setLogLevel(PARREADER_LOG_WARNING);
+            break;
+        case(AV_LOG_INFO):
+            parReader_setLogLevel(PARREADER_LOG_INFO);
+            break;
+        case(AV_LOG_VERBOSE):
+            parReader_setLogLevel(PARREADER_LOG_INFO);
+            break;
+        case(AV_LOG_DEBUG):
+            parReader_setLogLevel(PARREADER_LOG_DEBUG);
+            break;
+    }
+    parReader_setLogCallback(parreaderLogger);
+
+    parReader_setDisplaySettingsDefaults(&p->dispSet);
+
+    res = parReader_loadParFile(NULL, avf->filename, -1, &p->frameInfo, 0);
+    if (0 == res)
+        return AVERROR(EIO);
+
+    seqLen = parReader_getFilelist(&p->frameInfo, &filelist);
+    if (1 == seqLen)  {
+        int frameNumber, frameCount;
+        unsigned long start, end;
+        if (parReader_getIndexData(&p->frameInfo, &frameNumber,
&frameCount, &start, &end))
+            seconds = end - start;
+    }
+    else  {
+        int res, frameNumber, frameCount;
+        unsigned long start, end, realStart, realEnd;
+        if (parReader_getIndexData(&p->frameInfo, &frameNumber,
&frameCount, &start, &end))  {
+            realStart = start;
+            av_log(avf, AV_LOG_DEBUG, "par_read_header:  %s (%d)\n",
filelist[0], seqLen - 1);
+            res = parReader_loadParFile(NULL, filelist[0], seqLen - 1,
&p->frameInfo, 0);
+            if (res && parReader_getIndexData(&p->frameInfo, &frameNumber,
&frameCount, &start, &end))  {
+                realEnd = end;
+                seconds = realEnd - realStart;
+            }
+            av_log(avf, AV_LOG_DEBUG, "par_read_header:  %s (%d)\n",
filelist[0], res);
+            res = parReader_loadParFile(NULL, filelist[0], -1,
&p->frameInfo, 0);
+        }
+    }
+
+    siz = parReader_loadFrame(&p->frameInfo, &p->dispSet, &p->fileChanged);
+
+    p->frameCached = siz;
+    p->fileChanged = 1;
+    p->seqStartAdded = 0;
+
+    snprintf(textbuf, sizeof(textbuf), "%d",
parReader_getUTCOffset(&p->frameInfo));
+    av_dict_set(&avf->metadata, "timezone", textbuf, 0);
+
+    strm = createStream(avf);
+//    if (strm)  {
+//        // Note: Do not set avf->start_time, ffmpeg computes it from
AVStream values
+//        avf->duration = av_rescale_q(seconds, secondsTB,
strm->time_base);
+//    }
+
+    avf->ctx_flags |= AVFMTCTX_NOHEADER;
+
+    return 0;
+}
+
+static int par_read_packet(AVFormatContext * avf, AVPacket * pkt)
+{
+    PARDecContext *p = avf->priv_data;
+    int siz = 0;
+
+    if (p->frameCached)  {
+        siz = p->frameCached;
+    }
+    else
+        siz = parReader_loadFrame(&p->frameInfo, &p->dispSet,
&p->fileChanged);
+
+    if (siz < 0)  {
+        p->frameCached = 0;
+        p->fileChanged = 0;
+        pkt->size = 0;
+        return AVERROR_EOF;
+    }
+
+    if (p->fileChanged)
+        parReader_getFilename(&p->frameInfo, avf->filename,
sizeof(avf->filename));
+
+    if ( (siz == 0) || (NULL == p->frameInfo.frameData) )  {
+        p->frameCached = 0;
+        return AVERROR(EAGAIN);
+    }
+
+    return createPacket(avf, pkt, siz);
+}
+
+static int par_read_seek(AVFormatContext *avf, int stream,
+                         int64_t target, int flags)
+{
+    PARDecContext *p = avf->priv_data;
+    int siz = 0;
+    int streamId = 0;
+    int isKeyFrame = 0;
+    int step;
+    int anyStreamWillDo = 0;
+    int prevPlayMode, prevLock;
+
+    av_log(avf, AV_LOG_DEBUG, "par_read_seek target    = %"PRId64"\n",
target);
+
+    if ((stream < 0) || (stream >= avf->nb_streams))  {
+        anyStreamWillDo = 1;
+        streamId = avf->streams[0]->id;
+    }
+    else
+        streamId = avf->streams[stream]->id;
+
+    prevPlayMode = p->dispSet.playMode;
+    prevLock = p->dispSet.fileLock;
+
+    p->seqStartAdded = 0;
+    p->dispSet.cameraNum = streamId;
+    if (flags & AVSEEK_FLAG_BACKWARD)
+        p->dispSet.playMode = RWND;
+
+    if ( (flags & AVSEEK_FLAG_FRAME) && (target < 0) )   {
+        p->dispSet.fileSeqNo = (-target) - 1;
+        p->dispSet.frameNumber = 0;
+    }
+    else  {
+        p->dispSet.fileSeqNo = -1;
+
+        if (flags & AVSEEK_FLAG_FRAME)  {
+            // Don't seek beyond the file
+            p->dispSet.fileLock = 1;
+            p->dispSet.frameNumber = target;
+        }
+        else  {
+            p->dispSet.timestamp = target / 1000LL;
+            p->dispSet.millisecs = target % 1000;
+        }
+    }
+
+    do  {
+        siz = parReader_loadFrame(&p->frameInfo, &p->dispSet,
&p->fileChanged);
+
+        // If this frame is not acceptable we want to just iterate through
+        // until we find one that is, not seek again, so reset targets
+        p->dispSet.frameNumber = -1;
+        p->dispSet.timestamp = 0;
+        p->dispSet.millisecs = 0;
+
+        if (siz < 0)
+            break;
+
+        if (parReader_frameIsVideo(&p->frameInfo))  {
+            if (flags & AVSEEK_FLAG_ANY)
+                isKeyFrame = 1;
+            else
+                isKeyFrame = parReader_isIFrame(&p->frameInfo);
+        }
+        else  {
+            // Always seek to a video frame
+            isKeyFrame = 0;
+        }
+
+        // If we don't care which stream then force the streamId to match
+        if (anyStreamWillDo)
+            streamId = p->frameInfo.channel;
+    }
+    while ( (streamId != p->frameInfo.channel) || (0 == isKeyFrame) );
+
+    p->dispSet.fileLock = prevLock;
+    p->dispSet.playMode = prevPlayMode;
+
+    if (siz > 0)  {
+        p->frameCached = siz;
+        for(step = 0; step < avf->nb_streams; step++)  {
+            if ( (NULL != avf->streams[step]) && (avf->streams[step]->id
== streamId) )  {
+                avf->streams[step]->codec->frame_number =
p->frameInfo.frameNumber;
+                break;
+            }
+        }
+        av_log(avf, AV_LOG_DEBUG, "par_read_seek seek done = %lu\n",
p->frameInfo.imageTime);
+        return p->frameInfo.imageTime;
+    }
+    else  {
+        av_log(avf, AV_LOG_DEBUG, "par_read_seek seek failed\n");
+        return -1;
+    }
+}
+
+static int par_read_close(AVFormatContext * avf)
+{
+    PARDecContext *p = avf->priv_data;
+    av_log(avf, AV_LOG_DEBUG, "par_read_close");
+    av_free(p->frameInfo.frameBuffer);
+    parReader_closeParFile(&p->frameInfo);
+    return 0;
+}
+
+
+AVOutputFormat ff_libparreader_muxer = {
+    .name           = "libpar",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings PAR format"),
+    .mime_type      = "video/adhbinary",
+    .extensions     = "par",
+    .priv_data_size = sizeof(PAREncContext),
+    .audio_codec    = AV_CODEC_ID_ADPCM_IMA_WAV,
+    .video_codec    = AV_CODEC_ID_MJPEG,
+    .write_header   = par_write_header,
+    .write_packet   = par_write_packet,
+    .write_trailer  = par_write_trailer,
+    .flags          = AVFMT_GLOBALHEADER,
+};
+
+AVInputFormat ff_libparreader_demuxer = {
+    .name           = "libpar",
+    .long_name      = NULL_IF_CONFIG_SMALL("AD-Holdings PAR format"),
+    .priv_data_size = sizeof(PARDecContext),
+    .read_probe     = par_probe,
+    .read_header    = par_read_header,
+    .read_packet    = par_read_packet,
+    .read_close     = par_read_close,
+    .read_seek      = par_read_seek,
+    .flags          = AVFMT_TS_DISCONT | AVFMT_VARIABLE_FPS |
AVFMT_NO_BYTE_SEEK,
+};
diff --git a/libavformat/libpar.h b/libavformat/libpar.h
new file mode 100644
index 0000000000..2d958727ba
--- /dev/null
+++ b/libavformat/libpar.h
@@ -0,0 +1,40 @@ 
+/*
+ * copyright (c) 2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#ifndef AVFORMAT_LIBPAR_H
+#define AVFORMAT_LIBPAR_H
+
+#include "avformat.h"
+
+
+#ifdef AD_SIDEDATA_IN_PRIV
+#include <parreader_types.h>
+
+typedef struct {
+    int indexInfoCount;
+    ParFrameInfo *frameInfo;
+    ParKeyValuePair *indexInfo;
+    int fileChanged;
+} LibparFrameExtra;
+
+#endif // AD_SIDEDATA_IN_PRIV
+
+#endif /* AVFORMAT_LIBPAR_H */
diff --git a/libavformat/netvu.c b/libavformat/netvu.c
new file mode 100644
index 0000000000..fdac338f21
--- /dev/null
+++ b/libavformat/netvu.c
@@ -0,0 +1,214 @@ 
+/*
+ * Netvu protocol for ffmpeg client
+ * Copyright (c) 2010 AD-Holdings plc
+ * Modified for FFmpeg by Tom Needham <06needhamt@gmail.com> (2018)
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <strings.h>
+
+#include "avformat.h"
+#include "internal.h"
+#include "http.h"
+#include "netvu.h"
+#include "libavutil/avstring.h"
+
+
+static void copy_value_to_field(const char *value, char **dest)
+{
+    int len = strlen(value) + 1;
+    if (*dest != NULL)
+        av_free(*dest);
+
+    *dest = av_malloc(len);
+    if (*dest)  {
+        av_strlcpy(*dest, value, len);
+    }
+}
+
+static void netvu_parse_content_type_header(char * p, NetvuContext *nv)
+{
+    int finishedContentHeader = 0;
+    char *  name  = NULL;
+    char *  value = NULL;
+
+    //strip the content-type from the headder
+    value = p;
+    while((*p != ';') && (*p != '\0'))
+        p++;
+
+    if(*p == '\0')
+        finishedContentHeader = 1;
+
+    *p = '\0';
+    p++;
+    copy_value_to_field( value, &nv->hdrs[NETVU_CONTENT] );
+
+    while( *p != '\0' && finishedContentHeader != 1)  {
+        while(isspace(*p))
+            p++; // Skip whitespace
+        name = p;
+
+        // Now we get attributes in <name>=<value> pairs
+        while (*p != '\0' && *p != '=')
+            p++;
+
+        if (*p != '=')
+            return;
+
+        *p = '\0';
+        p++;
+
+        value = p;
+
+        while (*p != '\0' && *p != ';')
+            p++;
+
+        if (*p == ';')  {
+            *p = '\0';
+            p++;
+        }
+
+        // Strip any "s off
+        if( strlen(value) > 0 )  {
+            int ii;
+
+            if( *value == '"' && *(value + strlen(value) - 1) == '"' )  {
+                *(value + strlen(value) - 1) = '\0';
+                value += 1;
+            }
+
+            // Copy the attribute into the relevant field
+            for(ii = 0; ii < NETVU_MAX_HEADERS; ii++)  {
+                if( av_strcasecmp(name, nv->hdrNames[ii] ) == 0 )
+                    copy_value_to_field(value, &nv->hdrs[ii]);
+            }
+            if(av_strcasecmp(name, "utc_offset") == 0)
+                nv->utc_offset = atoi(value);
+        }
+    }
+}
+
+static void processLine(char *line, NetvuContext *nv)
+{
+    char *p = line;
+    char *tag;
+
+    while (*p != '\0' && *p != ':')
+        p++;
+    if (*p != ':')
+        return;
+
+    *p = '\0';
+    tag = line;
+    p++;
+    while (isspace(*p))
+        p++;
+
+    if (!av_strcasecmp (tag, nv->hdrNames[NETVU_CONTENT]))
+        netvu_parse_content_type_header(p, nv);
+    else if(!av_strcasecmp(tag, nv->hdrNames[NETVU_SERVER]))
+        copy_value_to_field( p, &nv->hdrs[NETVU_SERVER]);
+}
+
+static int netvu_open(URLContext *h, const char *uri, int flags)
+{
+    char hostname[1024], auth[1024], path[1024], http[1024];
+    int port, err;
+    NetvuContext *nv = h->priv_data;
+
+    nv->hdrNames[NETVU_SERVER]      = "Server";
+    nv->hdrNames[NETVU_CONTENT]     = "Content-type";
+    nv->hdrNames[NETVU_RESOLUTION]  = "resolution";
+    nv->hdrNames[NETVU_COMPRESSION] = "compression";
+    nv->hdrNames[NETVU_RATE]        = "rate";
+    nv->hdrNames[NETVU_PPS]         = "pps";
+    nv->hdrNames[NETVU_SITE_ID]     = "site_id";
+    nv->hdrNames[NETVU_BOUNDARY]    = "boundary";
+    // Set utc_offset an invalid value so if server doesn't set it we can
ignore it
+    nv->utc_offset                  = 1441;
+
+    h->is_streamed = 1;
+
+    av_url_split(NULL, 0, auth, sizeof(auth), hostname, sizeof(hostname),
&port,
+                 path, sizeof(path), uri);
+    if (port < 0)
+        port = 80;
+    ff_url_join(http, sizeof(http), "http", auth, hostname, port, "%s",
path);
+
+    err = ffurl_open(&nv->hd, http, AVIO_FLAG_READ,
&h->interrupt_callback, NULL);
+    if (err >= 0)  {
+        char headers[1024];
+        char *startOfLine = &headers[0];
+        int ii;
+
+        size_t hdrSize = ff_http_get_headers(nv->hd, headers,
sizeof(headers));
+        if (hdrSize > 0)  {
+            for (ii = 0; ii < hdrSize; ii++)  {
+                if (headers[ii] == '\n')  {
+                    headers[ii] = '\0';
+                    processLine(startOfLine, nv);
+                    startOfLine = &headers[ii+1];
+                }
+            }
+        }
+        return 0;
+    }
+    else  {
+        if (nv->hd)
+            ffurl_close(nv->hd);
+        nv->hd = NULL;
+        return AVERROR(EIO);
+    }
+}
+
+static int netvu_read(URLContext *h, uint8_t *buf, int size)
+{
+    NetvuContext *nv = h->priv_data;
+    if (nv->hd)
+        return ffurl_read(nv->hd, buf, size);
+    return AVERROR_PROTOCOL_NOT_FOUND;
+}
+
+static int netvu_close(URLContext *h)
+{
+    NetvuContext *nv = h->priv_data;
+    int i, ret = 0;
+
+    if (nv->hd)
+        ret = ffurl_close(nv->hd);
+
+    for (i = 0; i < NETVU_MAX_HEADERS; i++)  {
+        if (nv->hdrs[i])
+            av_free(nv->hdrs[i]);
+    }
+
+    return ret;
+}
+
+
+URLProtocol ff_netvu_protocol = {
+    .name               = "netvu",
+    .url_open            = netvu_open,
+    .url_read            = netvu_read,
+    .url_close           = netvu_close,
+    .priv_data_size      = sizeof(NetvuContext),
+    .flags               = URL_PROTOCOL_FLAG_NETWORK,
+};
diff --git a/libavformat/netvu.h b/libavformat/netvu.h
new file mode 100644
index 0000000000..4dd72a6e19
--- /dev/null
+++ b/libavformat/netvu.h
@@ -0,0 +1,21 @@ 
+#include "avformat.h"
+
+enum NetvuHeaders { NETVU_SERVER = 0,
+                    NETVU_CONTENT,
+                    NETVU_RESOLUTION,
+                    NETVU_COMPRESSION,
+                    NETVU_RATE,
+                    NETVU_PPS,
+                    NETVU_SITE_ID,
+                    NETVU_BOUNDARY,
+                    NETVU_MAX_HEADERS
+                    };
+
+typedef struct {
+    const AVClass *class;
+    URLContext *hd;
+
+    char* hdrs[NETVU_MAX_HEADERS];
+    const char* hdrNames[NETVU_MAX_HEADERS];
+    int utc_offset;
+} NetvuContext;
diff --git a/libavformat/version.h b/libavformat/version.h
index 39b00f62ab..22ed534bfb 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,8 +32,8 @@ 
 // Major bumping may affect Ticket5467, 5421, 5451(compatibility with
Chromium)
 // Also please add any ticket numbers that you believe might be affected
here
 #define LIBAVFORMAT_VERSION_MAJOR  58
-#define LIBAVFORMAT_VERSION_MINOR  28
-#define LIBAVFORMAT_VERSION_MICRO 101
+#define LIBAVFORMAT_VERSION_MINOR  29
+#define LIBAVFORMAT_VERSION_MICRO 100