diff mbox

[FFmpeg-devel] Added support for XV video files

Message ID a729ce8e-435e-a816-414b-151c4e0d51e6@iitk.ac.in
State New
Headers show

Commit Message

Shivam Goyal March 24, 2019, 11:05 a.m. UTC
The attached patch is for ticket #3720

     https://trac.ffmpeg.org/ticket/3720

I have tested this demuxer on the files 1.xv and 5.xv attached with the 
ticket

http://samples.ffmpeg.org/ffmpeg-bugs/trac/ticket3720/


  It is working on both of these files.

As XV video files are flv files with some encrypted data. These can be 
converted to flv without loosing video quality (and it would be really 
fast). I have also tried this and it is working "ffmpeg -i 1.xv -c copy 
converted.flv".


Please suggest any improvements

Comments

Moritz Barsnick March 24, 2019, 12:39 p.m. UTC | #1
On Sun, Mar 24, 2019 at 16:35:40 +0530, Shivam Goyal wrote:
>  libavformat/xvdec.c      | 1395 ++++++++++++++++++++++++++++++++++++++
>  libavformat/xvtools.h    |   95 +++

This looks like a 90% copy of flvdec, with some modifications. I
believe it should be merged into flvdec as an additional supported
format, thereby sharing the code.

> +// /*
> +//  * XV demuxer
> +//  * Copyright (c) 2003 The FFmpeg Project

Crazy comment style. ;-)

Moritz
Moritz Barsnick March 24, 2019, 11:17 p.m. UTC | #2
On Sun, Mar 24, 2019 at 13:39:09 +0100, Moritz Barsnick wrote:

Another thing:

> On Sun, Mar 24, 2019 at 16:35:40 +0530, Shivam Goyal wrote:
> >  libavformat/xvdec.c      | 1395 ++++++++++++++++++++++++++++++++++++++
> >  libavformat/xvtools.h    |   95 +++

You shouldn't put actual code in header files, that's really bad style.
If you need complementary functions, they should be defined in e.g.
xvtools.c and declared in xvtools.h.

Moritz
Shivam Goyal March 25, 2019, 2:27 a.m. UTC | #3
On 3/24/19 6:09 PM, Moritz Barsnick wrote:
> On Sun, Mar 24, 2019 at 16:35:40 +0530, Shivam Goyal wrote:
>>   libavformat/xvdec.c      | 1395 ++++++++++++++++++++++++++++++++++++++
>>   libavformat/xvtools.h    |   95 +++
> This looks like a 90% copy of flvdec, with some modifications. I
> believe it should be merged into flvdec as an additional supported
> format, thereby sharing the code.


Yeah as XV file is an flv file i used the same demuxer with improvements 
specific for xv video. It can be merged with flvdec.c, but than i 
thought that the problem is with input functions which are present in 
the entire flvdec.c file. So, if i to merge it, i have to check which 
file is it xv or flv (or to store it in a variable in the FLVContext, 
which would be intialised at the time of reading header) whenever we 
need to read from the input file, Which would recquire more processing 
and would make the program slow for both xv  and flv files, and the 
total no. of occurences of the functions avio_r8, avio_rb16, avio_rb24, 
avio_rb32, avio_rb64 is around 39 . So, should i merge it with flvdec.c.

>> +// /*
>> +//  * XV demuxer
>> +//  * Copyright (c) 2003 The FFmpeg Project
> Crazy comment style. ;-)

Sorry, i commented it twice and forgot to remove it. I would improve it.

Thanks

>
> Moritz
> _______________________________________________
> 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".
Shivam Goyal March 25, 2019, 2:35 a.m. UTC | #4
On 3/25/19 4:47 AM, Moritz Barsnick wrote:
> On Sun, Mar 24, 2019 at 13:39:09 +0100, Moritz Barsnick wrote:
>
> Another thing:
>
>> On Sun, Mar 24, 2019 at 16:35:40 +0530, Shivam Goyal wrote:
>>>   libavformat/xvdec.c      | 1395 ++++++++++++++++++++++++++++++++++++++
>>>   libavformat/xvtools.h    |   95 +++
> You shouldn't put actual code in header files, that's really bad style.
> If you need complementary functions, they should be defined in e.g.
> xvtools.c and declared in xvtools.h.

I would saperate the two file, would send another patch with all the 
change.

Thanks

>
> Moritz
> _______________________________________________
> 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".
Moritz Barsnick March 25, 2019, 10:14 a.m. UTC | #5
On Mon, Mar 25, 2019 at 07:57:04 +0530, Shivam Goyal wrote:

> which would be intialised at the time of reading header) whenever we
> need to read from the input file, Which would recquire more processing
> and would make the program slow for both xv  and flv files

Good point. I agree that deciding whether to do rot with each and
every frame and other function calls. Can this not be optimized?

> total no. of occurences of the functions avio_r8, avio_rb16, avio_rb24,
> avio_rb32, avio_rb64 is around 39 . So, should i merge it with flvdec.c.

I cannot decide, I am just commenting. I was just pointing out that
copying a large amount of code for few modifications makes the whole
thing ineffective and a large maintenance burden.

Michael Niedermayer is the flvdec maintainer, and other core developers
will also know better what to do.

Moritz
Michael Niedermayer March 25, 2019, 10:29 a.m. UTC | #6
On Sun, Mar 24, 2019 at 04:35:40PM +0530, Shivam Goyal wrote:
> The attached patch is for ticket #3720
> 
>     https://trac.ffmpeg.org/ticket/3720
> 
> I have tested this demuxer on the files 1.xv and 5.xv attached with the
> ticket
> 
> http://samples.ffmpeg.org/ffmpeg-bugs/trac/ticket3720/
> 
> 
>  It is working on both of these files.
> 
> As XV video files are flv files with some encrypted data. These can be
> converted to flv without loosing video quality (and it would be really
> fast). I have also tried this and it is working "ffmpeg -i 1.xv -c copy
> converted.flv".
> 
> 
> Please suggest any improvements
> 
> 
[...]

> diff --git a/libavformat/xvtools.h b/libavformat/xvtools.h
> new file mode 100644
> index 0000000000..42c2c65c46
> --- /dev/null
> +++ b/libavformat/xvtools.h
> @@ -0,0 +1,95 @@

> +int flvid = 0x46;
> +
> +
> +int byte_shift(int byte,int shift);
> +int find_rot(AVIOContext *pb);
> +int xvio_r8(AVIOContext *pb, int rot);
> +unsigned int xvio_rb16(AVIOContext *s, int rot);
> +unsigned int xvio_rb24(AVIOContext *s, int rot);
> +unsigned int xvio_rb32(AVIOContext *s, int rot);
> +uint64_t xvio_rb64(AVIOContext *s, int rot);
> +int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot);
> +
> +
> +
> +int byte_shift(int byte,int shift){
> +    return ((byte + shift)&0xff);
> +}
> +
> +
> +int find_rot(AVIOContext *pb){
> +    int64_t curr_pos = avio_tell(pb);
> +    avio_seek(pb,0x200000,SEEK_SET);
> +    int rot = flvid - avio_r8(pb);
> +    avio_seek(pb,curr_pos, SEEK_SET);
> +    return rot;
> +}
> +
> +
> +
> +int xvio_r8(AVIOContext *pb, int rot){
> +    int64_t curr_pos = avio_tell(pb);
> +    if(curr_pos < 0x200400){
> +        int ret=avio_r8(pb);
> +        if(ret == 0)return 0;
> +        ret = byte_shift(ret,rot);
> +        return ret;
> +    }
> +    int ret=avio_r8(pb);
> +    return ret;
> +}

There is so much wrong with this code
global variables are not ok in a demuxer that can have multiple instances
global variables do not belong in headers either
also non static inline functions do not belong in headers

this xvio stuff all looks like a bad idea.
Do i guess correctly that this fileformat is flv but scrambled at the IO
layer ?
if this is true it may be possible to implement a xv demuxer as a
descrambler which than passes the data on to a unchanged flv demuxer.
If that how this xv fundamentally works then that would be much
cleaner.
Also theflv demuxer duplication is not ok



> +
> +
> +unsigned int xvio_rb16(AVIOContext *s, int rot)
> +{
> +    unsigned int val;
> +    val = xvio_r8(s, rot) << 8;
> +    val |= xvio_r8(s, rot);
> +    return val;
> +}
> +
> +
> +unsigned int xvio_rb24(AVIOContext *s, int rot)
> +{
> +    unsigned int val;
> +    val = xvio_rb16(s, rot) << 8;
> +    val |= xvio_r8(s, rot);
> +    return val;
> +}
> +
> +
> +unsigned int xvio_rb32(AVIOContext *s, int rot)
> +{
> +    unsigned int val;
> +    val = xvio_rb16(s, rot) << 16;
> +    val |= xvio_rb16(s, rot);
> +    return val;
> +}
> +
> +
> +uint64_t xvio_rb64(AVIOContext *s, int rot)
> +{
> +    uint64_t val;
> +    val = (uint64_t)xvio_rb32(s, rot) << 32;
> +    val |= (uint64_t)xvio_rb32(s, rot);
> +    return val;
> +}
> +
> +
> +
> +int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot){
> +    if(curr_pos>=0x200400){
> +        return 0;
> +    }
> +    for(int i=0;i<length;i++){
> +        if((curr_pos+i) < 0x200400){
> +            buffer[i] = byte_shift(buffer[i],rot);
> +        }
> +        else{
> +            break;
> +        }
> +    }
> +    return 0;
> +}
> +
> +
> -- 
> 2.21.0
> 

> _______________________________________________
> 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".
Shivam Goyal March 25, 2019, 10:49 a.m. UTC | #7
On 3/25/19 3:59 PM, Michael Niedermayer wrote:
> On Sun, Mar 24, 2019 at 04:35:40PM +0530, Shivam Goyal wrote:
>> The attached patch is for ticket #3720
>>
>>      https://trac.ffmpeg.org/ticket/3720
>>
>> I have tested this demuxer on the files 1.xv and 5.xv attached with the
>> ticket
>>
>> http://samples.ffmpeg.org/ffmpeg-bugs/trac/ticket3720/
>>
>>
>>   It is working on both of these files.
>>
>> As XV video files are flv files with some encrypted data. These can be
>> converted to flv without loosing video quality (and it would be really
>> fast). I have also tried this and it is working "ffmpeg -i 1.xv -c copy
>> converted.flv".
>>
>>
>> Please suggest any improvements
>>
>>
> [...]
>
>> diff --git a/libavformat/xvtools.h b/libavformat/xvtools.h
>> new file mode 100644
>> index 0000000000..42c2c65c46
>> --- /dev/null
>> +++ b/libavformat/xvtools.h
>> @@ -0,0 +1,95 @@
>> +int flvid = 0x46;
>> +
>> +
>> +int byte_shift(int byte,int shift);
>> +int find_rot(AVIOContext *pb);
>> +int xvio_r8(AVIOContext *pb, int rot);
>> +unsigned int xvio_rb16(AVIOContext *s, int rot);
>> +unsigned int xvio_rb24(AVIOContext *s, int rot);
>> +unsigned int xvio_rb32(AVIOContext *s, int rot);
>> +uint64_t xvio_rb64(AVIOContext *s, int rot);
>> +int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot);
>> +
>> +
>> +
>> +int byte_shift(int byte,int shift){
>> +    return ((byte + shift)&0xff);
>> +}
>> +
>> +
>> +int find_rot(AVIOContext *pb){
>> +    int64_t curr_pos = avio_tell(pb);
>> +    avio_seek(pb,0x200000,SEEK_SET);
>> +    int rot = flvid - avio_r8(pb);
>> +    avio_seek(pb,curr_pos, SEEK_SET);
>> +    return rot;
>> +}
>> +
>> +
>> +
>> +int xvio_r8(AVIOContext *pb, int rot){
>> +    int64_t curr_pos = avio_tell(pb);
>> +    if(curr_pos < 0x200400){
>> +        int ret=avio_r8(pb);
>> +        if(ret == 0)return 0;
>> +        ret = byte_shift(ret,rot);
>> +        return ret;
>> +    }
>> +    int ret=avio_r8(pb);
>> +    return ret;
>> +}
> There is so much wrong with this code
> global variables are not ok in a demuxer that can have multiple instances
> global variables do not belong in headers either
> also non static inline functions do not belong in headers
>
> this xvio stuff all looks like a bad idea.
> Do i guess correctly that this fileformat is flv but scrambled at the IO
> layer ?
> if this is true it may be possible to implement a xv demuxer as a
> descrambler which than passes the data on to a unchanged flv demuxer.
> If that how this xv fundamentally works then that would be much
> cleaner.
> Also theflv demuxer duplication is not ok

Yeah The XV file is a flv file which starts from 0x200000 byte and its 
_next__0x400_ bytes are encrypted by something like caesar cipher (the 
bytes after 0x200400 till the end of the file are not encrypted) . So, 
Please suggest what would be best solution for this situation and how 
can i change this.

     Thank you, Shivam Goyal


>
>
>> +
>> +
>> +unsigned int xvio_rb16(AVIOContext *s, int rot)
>> +{
>> +    unsigned int val;
>> +    val = xvio_r8(s, rot) << 8;
>> +    val |= xvio_r8(s, rot);
>> +    return val;
>> +}
>> +
>> +
>> +unsigned int xvio_rb24(AVIOContext *s, int rot)
>> +{
>> +    unsigned int val;
>> +    val = xvio_rb16(s, rot) << 8;
>> +    val |= xvio_r8(s, rot);
>> +    return val;
>> +}
>> +
>> +
>> +unsigned int xvio_rb32(AVIOContext *s, int rot)
>> +{
>> +    unsigned int val;
>> +    val = xvio_rb16(s, rot) << 16;
>> +    val |= xvio_rb16(s, rot);
>> +    return val;
>> +}
>> +
>> +
>> +uint64_t xvio_rb64(AVIOContext *s, int rot)
>> +{
>> +    uint64_t val;
>> +    val = (uint64_t)xvio_rb32(s, rot) << 32;
>> +    val |= (uint64_t)xvio_rb32(s, rot);
>> +    return val;
>> +}
>> +
>> +
>> +
>> +int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot){
>> +    if(curr_pos>=0x200400){
>> +        return 0;
>> +    }
>> +    for(int i=0;i<length;i++){
>> +        if((curr_pos+i) < 0x200400){
>> +            buffer[i] = byte_shift(buffer[i],rot);
>> +        }
>> +        else{
>> +            break;
>> +        }
>> +    }
>> +    return 0;
>> +}
>> +
>> +
>> -- 
>> 2.21.0
>>
>> _______________________________________________
>> 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".
>
>
> _______________________________________________
> 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".
Michael Niedermayer March 26, 2019, 12:50 p.m. UTC | #8
On Mon, Mar 25, 2019 at 04:19:37PM +0530, Shivam Goyal wrote:
> 
> On 3/25/19 3:59 PM, Michael Niedermayer wrote:
> >On Sun, Mar 24, 2019 at 04:35:40PM +0530, Shivam Goyal wrote:
> >>The attached patch is for ticket #3720
> >>
> >>     https://trac.ffmpeg.org/ticket/3720
> >>
> >>I have tested this demuxer on the files 1.xv and 5.xv attached with the
> >>ticket
> >>
> >>http://samples.ffmpeg.org/ffmpeg-bugs/trac/ticket3720/
> >>
> >>
> >>  It is working on both of these files.
> >>
> >>As XV video files are flv files with some encrypted data. These can be
> >>converted to flv without loosing video quality (and it would be really
> >>fast). I have also tried this and it is working "ffmpeg -i 1.xv -c copy
> >>converted.flv".
> >>
> >>
> >>Please suggest any improvements
> >>
> >>
> >[...]
> >
> >>diff --git a/libavformat/xvtools.h b/libavformat/xvtools.h
> >>new file mode 100644
> >>index 0000000000..42c2c65c46
> >>--- /dev/null
> >>+++ b/libavformat/xvtools.h
> >>@@ -0,0 +1,95 @@
> >>+int flvid = 0x46;
> >>+
> >>+
> >>+int byte_shift(int byte,int shift);
> >>+int find_rot(AVIOContext *pb);
> >>+int xvio_r8(AVIOContext *pb, int rot);
> >>+unsigned int xvio_rb16(AVIOContext *s, int rot);
> >>+unsigned int xvio_rb24(AVIOContext *s, int rot);
> >>+unsigned int xvio_rb32(AVIOContext *s, int rot);
> >>+uint64_t xvio_rb64(AVIOContext *s, int rot);
> >>+int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot);
> >>+
> >>+
> >>+
> >>+int byte_shift(int byte,int shift){
> >>+    return ((byte + shift)&0xff);
> >>+}
> >>+
> >>+
> >>+int find_rot(AVIOContext *pb){
> >>+    int64_t curr_pos = avio_tell(pb);
> >>+    avio_seek(pb,0x200000,SEEK_SET);
> >>+    int rot = flvid - avio_r8(pb);
> >>+    avio_seek(pb,curr_pos, SEEK_SET);
> >>+    return rot;
> >>+}
> >>+
> >>+
> >>+
> >>+int xvio_r8(AVIOContext *pb, int rot){
> >>+    int64_t curr_pos = avio_tell(pb);
> >>+    if(curr_pos < 0x200400){
> >>+        int ret=avio_r8(pb);
> >>+        if(ret == 0)return 0;
> >>+        ret = byte_shift(ret,rot);
> >>+        return ret;
> >>+    }
> >>+    int ret=avio_r8(pb);
> >>+    return ret;
> >>+}
> >There is so much wrong with this code
> >global variables are not ok in a demuxer that can have multiple instances
> >global variables do not belong in headers either
> >also non static inline functions do not belong in headers
> >
> >this xvio stuff all looks like a bad idea.
> >Do i guess correctly that this fileformat is flv but scrambled at the IO
> >layer ?
> >if this is true it may be possible to implement a xv demuxer as a
> >descrambler which than passes the data on to a unchanged flv demuxer.
> >If that how this xv fundamentally works then that would be much
> >cleaner.
> >Also theflv demuxer duplication is not ok
> 
> Yeah The XV file is a flv file which starts from 0x200000 byte and its
> _next__0x400_ bytes are encrypted by something like caesar cipher (the bytes
> after 0x200400 till the end of the file are not encrypted) . So, Please
> suggest what would be best solution for this situation and how can i change
> this.

look at the KUX format patch

for the IO stuff, there are 2 obvious ways
1. if this is limited to the only once read header, it should be possible
to just allocate an array, read the bytes for the header decrypt it create
a avio context on it and pass this to the header decode code. This also
assumes that the size is limited and never too large
2. if this is not limited to the once read header but can extend into the
flv frames, a avio context can be created that passes its read calls on to
the parent and decrypts as needed. so there are always 2 avio contexts and 
the flv demuxer code would see "virtual" data that has been decrypted

there are many other ways. You can probably as the one working on this
better decide as ou work on it what path makes sense. But please try to
keep the implementation cleaner than the initial patch was

Thanks

[...]
diff mbox

Patch

From 84418c7dd384e9b3e42a2c43f4823ff4816e634e Mon Sep 17 00:00:00 2001
From: Shivam Goyal <shivgo@iitk.ac.in>
Date: Sun, 24 Mar 2019 16:19:05 +0530
Subject: [PATCH] Added XV file support

---
 libavformat/Makefile     |    1 +
 libavformat/allformats.c |    1 +
 libavformat/xvdec.c      | 1395 ++++++++++++++++++++++++++++++++++++++
 libavformat/xvtools.h    |   95 +++
 4 files changed, 1492 insertions(+)
 create mode 100644 libavformat/xvdec.c
 create mode 100644 libavformat/xvtools.h

diff --git a/libavformat/Makefile b/libavformat/Makefile
index c010fc83f9..9fc1288ed3 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -187,6 +187,7 @@  OBJS-$(CONFIG_FLAC_MUXER)                += flacenc.o flacenc_header.o \
                                             vorbiscomment.o
 OBJS-$(CONFIG_FLIC_DEMUXER)              += flic.o
 OBJS-$(CONFIG_FLV_DEMUXER)               += flvdec.o
+OBJS-$(CONFIG_XV_DEMUXER)                += xvdec.o
 OBJS-$(CONFIG_LIVE_FLV_DEMUXER)          += flvdec.o
 OBJS-$(CONFIG_FLV_MUXER)                 += flvenc.o avc.o
 OBJS-$(CONFIG_FOURXM_DEMUXER)            += 4xm.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index 73d69cda99..7a6a26328f 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -144,6 +144,7 @@  extern AVInputFormat  ff_flac_demuxer;
 extern AVOutputFormat ff_flac_muxer;
 extern AVInputFormat  ff_flic_demuxer;
 extern AVInputFormat  ff_flv_demuxer;
+extern AVInputFormat  ff_xv_demuxer;
 extern AVOutputFormat ff_flv_muxer;
 extern AVInputFormat  ff_live_flv_demuxer;
 extern AVInputFormat  ff_fourxm_demuxer;
diff --git a/libavformat/xvdec.c b/libavformat/xvdec.c
new file mode 100644
index 0000000000..70683635b3
--- /dev/null
+++ b/libavformat/xvdec.c
@@ -0,0 +1,1395 @@ 
+// /*
+//  * XV demuxer
+//  * Copyright (c) 2003 The FFmpeg Project
+//  *
+//  * This demuxer will generate a 1 byte extradata for VP6F content.
+//  * It is composed of:
+//  *  - upper 4 bits: difference between encoded width and visible width
+//  *  - lower 4 bits: difference between encoded height and visible height
+//  *
+//  * 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 "libavutil/avstring.h"
+#include "libavutil/channel_layout.h"
+#include "libavutil/dict.h"
+#include "libavutil/opt.h"
+#include "libavutil/intfloat.h"
+#include "libavutil/mathematics.h"
+#include "libavcodec/bytestream.h"
+#include "libavcodec/mpeg4audio.h"
+#include "avformat.h"
+#include "internal.h"
+#include "avio_internal.h"
+#include "flv.h"
+#include "xvtools.h"
+#define VALIDATE_INDEX_TS_THRESH 2500
+
+#define RESYNC_BUFFER_SIZE (1<<20)
+
+typedef struct XVContext {
+    const AVClass *class; ///< Class for private options.
+    int trust_metadata;   ///< configure streams according onMetaData
+    int trust_datasize;   ///< trust data size of FLVTag
+    int dump_full_metadata;   ///< Dump full metadata of the onMetadata
+    int wrong_dts;        ///< wrong dts due to negative cts
+    uint8_t *new_extradata[FLV_STREAM_TYPE_NB];
+    int new_extradata_size[FLV_STREAM_TYPE_NB];
+    int last_sample_rate;
+    int last_channels;
+    struct {
+        int64_t dts;
+        int64_t pos;
+    } validate_index[2];
+    int validate_next;
+    int validate_count;
+    int searched_for_end;
+
+    uint8_t resync_buffer[2*RESYNC_BUFFER_SIZE];
+
+    int broken_sizes;
+    int sum_flv_tag_size;
+
+    int last_keyframe_stream_index;
+    int keyframe_count;
+    int64_t video_bit_rate;
+    int64_t audio_bit_rate;
+    int64_t *keyframe_times;
+    int64_t *keyframe_filepositions;
+    int missing_streams;
+    AVRational framerate;
+    int64_t last_ts;
+    int64_t time_offset;
+    int64_t time_pos;
+    int rot_value; ///< The value by which some bytes are encrypted
+} XVContext;
+
+
+static int xv_probe(AVProbeData *p)
+{
+    const uint8_t *d = p->buf;
+    
+    if(d[0] == 'X' && d[1] == 'L' && d[2] == 'V' && d[3] =='F'){
+        return AVPROBE_SCORE_MAX;
+    }
+    
+    return 0;
+}
+
+static void add_keyframes_index(AVFormatContext *s)
+{
+    XVContext *xv   = s->priv_data;
+    AVStream *stream  = NULL;
+    unsigned int i    = 0;
+
+    if (xv->last_keyframe_stream_index < 0) {
+        av_log(s, AV_LOG_DEBUG, "keyframe stream hasn't been created\n");
+        return;
+    }
+
+    av_assert0(xv->last_keyframe_stream_index <= s->nb_streams);
+    stream = s->streams[xv->last_keyframe_stream_index];
+
+    if (stream->nb_index_entries == 0) {
+        for (i = 0; i < xv->keyframe_count; i++) {
+            av_log(s, AV_LOG_TRACE, "keyframe filepositions = %"PRId64" times = %"PRId64"\n",
+                   xv->keyframe_filepositions[i], xv->keyframe_times[i] * 1000);
+            av_add_index_entry(stream, xv->keyframe_filepositions[i],
+                xv->keyframe_times[i] * 1000, 0, 0, AVINDEX_KEYFRAME);
+        }
+    } else
+        av_log(s, AV_LOG_WARNING, "Skipping duplicate index\n");
+
+    if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+        av_freep(&xv->keyframe_times);
+        av_freep(&xv->keyframe_filepositions);
+        xv->keyframe_count = 0;
+    }
+}
+
+static AVStream *create_stream(AVFormatContext *s, int codec_type)
+{
+    XVContext *xv   = s->priv_data;
+    AVStream *st = avformat_new_stream(s, NULL);
+    if (!st)
+        return NULL;
+    st->codecpar->codec_type = codec_type;
+    if (s->nb_streams>=3 ||(   s->nb_streams==2
+                           && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
+                           && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE
+                           && s->streams[0]->codecpar->codec_type != AVMEDIA_TYPE_DATA
+                           && s->streams[1]->codecpar->codec_type != AVMEDIA_TYPE_DATA))
+        s->ctx_flags &= ~AVFMTCTX_NOHEADER;
+    if (codec_type == AVMEDIA_TYPE_AUDIO) {
+        st->codecpar->bit_rate = xv->audio_bit_rate;
+        xv->missing_streams &= ~FLV_HEADER_FLAG_HASAUDIO;
+    }
+    if (codec_type == AVMEDIA_TYPE_VIDEO) {
+        st->codecpar->bit_rate = xv->video_bit_rate;
+        xv->missing_streams &= ~FLV_HEADER_FLAG_HASVIDEO;
+        st->avg_frame_rate = xv->framerate;
+    }
+
+
+    avpriv_set_pts_info(st, 32, 1, 1000); /* 32 bit pts in ms */
+    xv->last_keyframe_stream_index = s->nb_streams - 1;
+    add_keyframes_index(s);
+    return st;
+}
+
+static int flv_same_audio_codec(AVCodecParameters *apar, int flags)
+{
+    int bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
+    int flv_codecid           = flags & FLV_AUDIO_CODECID_MASK;
+    int codec_id;
+
+    if (!apar->codec_id && !apar->codec_tag)
+        return 1;
+
+    if (apar->bits_per_coded_sample != bits_per_coded_sample)
+        return 0;
+
+    switch (flv_codecid) {
+    // no distinction between S16 and S8 PCM codec flags
+    case FLV_CODECID_PCM:
+        codec_id = bits_per_coded_sample == 8
+                   ? AV_CODEC_ID_PCM_U8
+#if HAVE_BIGENDIAN
+                   : AV_CODEC_ID_PCM_S16BE;
+#else
+                   : AV_CODEC_ID_PCM_S16LE;
+#endif
+        return codec_id == apar->codec_id;
+    case FLV_CODECID_PCM_LE:
+        codec_id = bits_per_coded_sample == 8
+                   ? AV_CODEC_ID_PCM_U8
+                   : AV_CODEC_ID_PCM_S16LE;
+        return codec_id == apar->codec_id;
+    case FLV_CODECID_AAC:
+        return apar->codec_id == AV_CODEC_ID_AAC;
+    case FLV_CODECID_ADPCM:
+        return apar->codec_id == AV_CODEC_ID_ADPCM_SWF;
+    case FLV_CODECID_SPEEX:
+        return apar->codec_id == AV_CODEC_ID_SPEEX;
+    case FLV_CODECID_MP3:
+        return apar->codec_id == AV_CODEC_ID_MP3;
+    case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
+    case FLV_CODECID_NELLYMOSER_16KHZ_MONO:
+    case FLV_CODECID_NELLYMOSER:
+        return apar->codec_id == AV_CODEC_ID_NELLYMOSER;
+    case FLV_CODECID_PCM_MULAW:
+        return apar->sample_rate == 8000 &&
+               apar->codec_id    == AV_CODEC_ID_PCM_MULAW;
+    case FLV_CODECID_PCM_ALAW:
+        return apar->sample_rate == 8000 &&
+               apar->codec_id    == AV_CODEC_ID_PCM_ALAW;
+    default:
+        return apar->codec_tag == (flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
+    }
+}
+
+static void xv_set_audio_codec(AVFormatContext *s, AVStream *astream,
+                                AVCodecParameters *apar, int flv_codecid)
+{
+    switch (flv_codecid) {
+    // no distinction between S16 and S8 PCM codec flags
+    case FLV_CODECID_PCM:
+        apar->codec_id = apar->bits_per_coded_sample == 8
+                           ? AV_CODEC_ID_PCM_U8
+#if HAVE_BIGENDIAN
+                           : AV_CODEC_ID_PCM_S16BE;
+#else
+                           : AV_CODEC_ID_PCM_S16LE;
+#endif
+        break;
+    case FLV_CODECID_PCM_LE:
+        apar->codec_id = apar->bits_per_coded_sample == 8
+                           ? AV_CODEC_ID_PCM_U8
+                           : AV_CODEC_ID_PCM_S16LE;
+        break;
+    case FLV_CODECID_AAC:
+        apar->codec_id = AV_CODEC_ID_AAC;
+        break;
+    case FLV_CODECID_ADPCM:
+        apar->codec_id = AV_CODEC_ID_ADPCM_SWF;
+        break;
+    case FLV_CODECID_SPEEX:
+        apar->codec_id    = AV_CODEC_ID_SPEEX;
+        apar->sample_rate = 16000;
+        break;
+    case FLV_CODECID_MP3:
+        apar->codec_id      = AV_CODEC_ID_MP3;
+        astream->need_parsing = AVSTREAM_PARSE_FULL;
+        break;
+    case FLV_CODECID_NELLYMOSER_8KHZ_MONO:
+        // in case metadata does not otherwise declare samplerate
+        apar->sample_rate = 8000;
+        apar->codec_id    = AV_CODEC_ID_NELLYMOSER;
+        break;
+    case FLV_CODECID_NELLYMOSER_16KHZ_MONO:
+        apar->sample_rate = 16000;
+        apar->codec_id    = AV_CODEC_ID_NELLYMOSER;
+        break;
+    case FLV_CODECID_NELLYMOSER:
+        apar->codec_id = AV_CODEC_ID_NELLYMOSER;
+        break;
+    case FLV_CODECID_PCM_MULAW:
+        apar->sample_rate = 8000;
+        apar->codec_id    = AV_CODEC_ID_PCM_MULAW;
+        break;
+    case FLV_CODECID_PCM_ALAW:
+        apar->sample_rate = 8000;
+        apar->codec_id    = AV_CODEC_ID_PCM_ALAW;
+        break;
+    default:
+        avpriv_request_sample(s, "Audio codec (%x)",
+               flv_codecid >> FLV_AUDIO_CODECID_OFFSET);
+        apar->codec_tag = flv_codecid >> FLV_AUDIO_CODECID_OFFSET;
+    }
+}
+
+static int flv_same_video_codec(AVCodecParameters *vpar, int flags)
+{
+    int flv_codecid = flags & FLV_VIDEO_CODECID_MASK;
+
+    if (!vpar->codec_id && !vpar->codec_tag)
+        return 1;
+
+    switch (flv_codecid) {
+    case FLV_CODECID_H263:
+        return vpar->codec_id == AV_CODEC_ID_FLV1;
+    case FLV_CODECID_SCREEN:
+        return vpar->codec_id == AV_CODEC_ID_FLASHSV;
+    case FLV_CODECID_SCREEN2:
+        return vpar->codec_id == AV_CODEC_ID_FLASHSV2;
+    case FLV_CODECID_VP6:
+        return vpar->codec_id == AV_CODEC_ID_VP6F;
+    case FLV_CODECID_VP6A:
+        return vpar->codec_id == AV_CODEC_ID_VP6A;
+    case FLV_CODECID_H264:
+        return vpar->codec_id == AV_CODEC_ID_H264;
+    default:
+        return vpar->codec_tag == flv_codecid;
+    }
+}
+
+static int xv_set_video_codec(AVFormatContext *s, AVStream *vstream,
+                               int flv_codecid, int read)
+{
+    int ret = 0;
+    AVCodecParameters *par = vstream->codecpar;
+    enum AVCodecID old_codec_id = vstream->codecpar->codec_id;
+    switch (flv_codecid) {
+    case FLV_CODECID_H263:
+        par->codec_id = AV_CODEC_ID_FLV1;
+        break;
+    case FLV_CODECID_REALH263:
+        par->codec_id = AV_CODEC_ID_H263;
+        break; // Really mean it this time
+    case FLV_CODECID_SCREEN:
+        par->codec_id = AV_CODEC_ID_FLASHSV;
+        break;
+    case FLV_CODECID_SCREEN2:
+        par->codec_id = AV_CODEC_ID_FLASHSV2;
+        break;
+    case FLV_CODECID_VP6:
+        par->codec_id = AV_CODEC_ID_VP6F;
+    case FLV_CODECID_VP6A:
+        if (flv_codecid == FLV_CODECID_VP6A)
+            par->codec_id = AV_CODEC_ID_VP6A;
+        if (read) {
+            if (par->extradata_size != 1) {
+                ff_alloc_extradata(par, 1);
+            }
+            if (par->extradata)
+                par->extradata[0] = avio_r8(s->pb);
+            else
+                avio_skip(s->pb, 1);
+        }
+        ret = 1;     // 1 byte body size adjustment for xv_read_packet()
+        break;
+    case FLV_CODECID_H264:
+        par->codec_id = AV_CODEC_ID_H264;
+        vstream->need_parsing = AVSTREAM_PARSE_HEADERS;
+        ret = 3;     // not 4, reading packet type will consume one byte
+        break;
+    case FLV_CODECID_MPEG4:
+        par->codec_id = AV_CODEC_ID_MPEG4;
+        ret = 3;
+        break;
+    default:
+        avpriv_request_sample(s, "Video codec (%x)", flv_codecid);
+        par->codec_tag = flv_codecid;
+    }
+
+    if (!vstream->internal->need_context_update && par->codec_id != old_codec_id) {
+        avpriv_request_sample(s, "Changing the codec id midstream");
+        return AVERROR_PATCHWELCOME;
+    }
+
+    return ret;
+}
+
+static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize, int rot)
+{
+
+    int length = xvio_rb16(ioc, rot);
+    if (length >= buffsize) {
+        avio_skip(ioc, length);
+        return -1;
+    }
+    int64_t curr_pos = avio_tell(ioc);
+    avio_read(ioc, buffer, length);
+
+    buffer[length] = '\0';
+    int ret = xv_string_decrypt(curr_pos, buffer, length, rot);
+    if(ret != 0){
+        return -1;
+    }
+    return length;
+}
+
+static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, int64_t max_pos)
+{
+    XVContext *xv       = s->priv_data;
+    unsigned int timeslen = 0, fileposlen = 0, i;
+    char str_val[256];
+    int64_t *times         = NULL;
+    int64_t *filepositions = NULL;
+    int ret                = AVERROR(ENOSYS);
+    int64_t initial_pos    = avio_tell(ioc);
+    int rot;
+    if(xv->rot_value){
+        rot=xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+    if (xv->keyframe_count > 0) {
+        av_log(s, AV_LOG_DEBUG, "keyframes have been paresed\n");
+        return 0;
+    }
+    av_assert0(!xv->keyframe_times);
+    av_assert0(!xv->keyframe_filepositions);
+
+    if (s->flags & AVFMT_FLAG_IGNIDX)
+        return 0;
+
+    while (avio_tell(ioc) < max_pos - 2 &&
+           amf_get_string(ioc, str_val, sizeof(str_val), rot) > 0) {
+        int64_t **current_array;
+        unsigned int arraylen;
+
+        // Expect array object in context
+        if (xvio_r8(ioc, rot) != AMF_DATA_TYPE_ARRAY)
+            break;
+
+        arraylen = xvio_rb32(ioc, rot);
+        if (arraylen>>28)
+            break;
+
+        if       (!strcmp(KEYFRAMES_TIMESTAMP_TAG , str_val) && !times) {
+            current_array = &times;
+            timeslen      = arraylen;
+        } else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) &&
+                   !filepositions) {
+            current_array = &filepositions;
+            fileposlen    = arraylen;
+        } else
+            // unexpected metatag inside keyframes, will not use such
+            // metadata for indexing
+            break;
+
+        if (!(*current_array = av_mallocz(sizeof(**current_array) * arraylen))) {
+            ret = AVERROR(ENOMEM);
+            goto finish;
+        }
+
+        for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
+            if (xvio_r8(ioc, rot) != AMF_DATA_TYPE_NUMBER)
+                goto invalid;
+            current_array[0][i] = av_int2double(xvio_rb64(ioc, rot));
+        }
+        if (times && filepositions) {
+            // All done, exiting at a position allowing amf_parse_object
+            // to finish parsing the object
+            ret = 0;
+            break;
+        }
+    }
+
+    if (timeslen == fileposlen && fileposlen>1 && max_pos <= filepositions[0]) {
+        for (i = 0; i < FFMIN(2,fileposlen); i++) {
+            xv->validate_index[i].pos = filepositions[i];
+            xv->validate_index[i].dts = times[i] * 1000;
+            xv->validate_count        = i + 1;
+        }
+        xv->keyframe_times = times;
+        xv->keyframe_filepositions = filepositions;
+        xv->keyframe_count = timeslen;
+        times = NULL;
+        filepositions = NULL;
+    } else {
+invalid:
+        av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n");
+    }
+
+finish:
+    av_freep(&times);
+    av_freep(&filepositions);
+    avio_seek(ioc, initial_pos, SEEK_SET);
+    return ret;
+}
+
+static int amf_parse_object(AVFormatContext *s, AVStream *astream,
+                            AVStream *vstream, const char *key,
+                            int64_t max_pos, int depth)
+{
+    AVCodecParameters *apar, *vpar;
+    XVContext *xv = s->priv_data;
+    AVIOContext *ioc;
+    AMFDataType amf_type;
+    char str_val[1024];
+    double num_val;
+    int rot;
+    if(xv->rot_value){
+        rot = xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+    num_val  = 0;
+    ioc      = s->pb;
+    amf_type = xvio_r8(ioc, rot);
+
+    switch (amf_type) {
+    case AMF_DATA_TYPE_NUMBER:
+        num_val = av_int2double(xvio_rb64(ioc, rot));
+        break;
+    case AMF_DATA_TYPE_BOOL:
+        num_val = xvio_r8(ioc, rot);
+        break;
+    case AMF_DATA_TYPE_STRING:
+        if (amf_get_string(ioc, str_val, sizeof(str_val), rot) < 0) {
+            av_log(s, AV_LOG_ERROR, "AMF_DATA_TYPE_STRING parsing failed\n");
+            return -1;
+        }
+        break;
+    case AMF_DATA_TYPE_OBJECT:
+        if (key &&
+            (ioc->seekable & AVIO_SEEKABLE_NORMAL) &&
+            !strcmp(KEYFRAMES_TAG, key) && depth == 1)
+            if (parse_keyframes_index(s, ioc,
+                                      max_pos) < 0)
+                av_log(s, AV_LOG_ERROR, "Keyframe index parsing failed\n");
+            else
+                add_keyframes_index(s);
+        while (avio_tell(ioc) < max_pos - 2 &&
+               amf_get_string(ioc, str_val, sizeof(str_val), rot) > 0)
+            if (amf_parse_object(s, astream, vstream, str_val, max_pos,
+                                 depth + 1) < 0)
+                return -1;     // if we couldn't skip, bomb out.
+        if (xvio_r8(ioc, rot) != AMF_END_OF_OBJECT) {
+            av_log(s, AV_LOG_ERROR, "Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_OBJECT\n");
+            return -1;
+        }
+        break;
+    case AMF_DATA_TYPE_NULL:
+    case AMF_DATA_TYPE_UNDEFINED:
+    case AMF_DATA_TYPE_UNSUPPORTED:
+        break;     // these take up no additional space
+    case AMF_DATA_TYPE_MIXEDARRAY:
+    {
+        unsigned v;
+        avio_skip(ioc, 4);     // skip 32-bit max array index
+        while (avio_tell(ioc) < max_pos - 2 &&
+               amf_get_string(ioc, str_val, sizeof(str_val), rot) > 0)
+            // this is the only case in which we would want a nested
+            // parse to not skip over the object
+            if (amf_parse_object(s, astream, vstream, str_val, max_pos,
+                                 depth + 1) < 0)
+                return -1;
+        v = xvio_r8(ioc, rot);
+        if (v != AMF_END_OF_OBJECT) {
+            av_log(s, AV_LOG_ERROR, "Missing AMF_END_OF_OBJECT in AMF_DATA_TYPE_MIXEDARRAY, found %d\n", v);
+            return -1;
+        }
+        break;
+    }
+    case AMF_DATA_TYPE_ARRAY:
+    {
+        unsigned int arraylen, i;
+
+        arraylen = xvio_rb32(ioc, rot);
+        for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++)
+            if (amf_parse_object(s, NULL, NULL, NULL, max_pos,
+                                 depth + 1) < 0)
+                return -1;      // if we couldn't skip, bomb out.
+    }
+    break;
+    case AMF_DATA_TYPE_DATE:
+        avio_skip(ioc, 8 + 2);  // timestamp (double) and UTC offset (int16)
+        break;
+    default:                    // unsupported type, we couldn't skip
+        av_log(s, AV_LOG_ERROR, "unsupported amf type %d\n", amf_type);
+        return -1;
+    }
+
+    if (key) {
+        apar = astream ? astream->codecpar : NULL;
+        vpar = vstream ? vstream->codecpar : NULL;
+
+        // stream info doesn't live any deeper than the first object
+        if (depth == 1) {
+            if (amf_type == AMF_DATA_TYPE_NUMBER ||
+                amf_type == AMF_DATA_TYPE_BOOL) {
+                if (!strcmp(key, "duration"))
+                    s->duration = num_val * AV_TIME_BASE;
+                else if (!strcmp(key, "videodatarate") &&
+                         0 <= (int)(num_val * 1024.0))
+                    xv->video_bit_rate = num_val * 1024.0;
+                else if (!strcmp(key, "audiodatarate") &&
+                         0 <= (int)(num_val * 1024.0))
+                    xv->audio_bit_rate = num_val * 1024.0;
+                else if (!strcmp(key, "datastream")) {
+                    AVStream *st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
+                    if (!st)
+                        return AVERROR(ENOMEM);
+                    st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+                } else if (!strcmp(key, "framerate")) {
+                    xv->framerate = av_d2q(num_val, 1000);
+                    if (vstream)
+                        vstream->avg_frame_rate = xv->framerate;
+                } else if (xv->trust_metadata) {
+                    if (!strcmp(key, "videocodecid") && vpar) {
+                        int ret = xv_set_video_codec(s, vstream, num_val, 0);
+                        if (ret < 0)
+                            return ret;
+                    } else if (!strcmp(key, "audiocodecid") && apar) {
+                        int id = ((int)num_val) << FLV_AUDIO_CODECID_OFFSET;
+                        xv_set_audio_codec(s, astream, apar, id);
+                    } else if (!strcmp(key, "audiosamplerate") && apar) {
+                        apar->sample_rate = num_val;
+                    } else if (!strcmp(key, "audiosamplesize") && apar) {
+                        apar->bits_per_coded_sample = num_val;
+                    } else if (!strcmp(key, "stereo") && apar) {
+                        apar->channels       = num_val + 1;
+                        apar->channel_layout = apar->channels == 2 ?
+                                               AV_CH_LAYOUT_STEREO :
+                                               AV_CH_LAYOUT_MONO;
+                    } else if (!strcmp(key, "width") && vpar) {
+                        vpar->width = num_val;
+                    } else if (!strcmp(key, "height") && vpar) {
+                        vpar->height = num_val;
+                    }
+                }
+            }
+            if (amf_type == AMF_DATA_TYPE_STRING) {
+                if (!strcmp(key, "encoder")) {
+                    int version = -1;
+                    if (1 == sscanf(str_val, "Open Broadcaster Software v0.%d", &version)) {
+                        if (version > 0 && version <= 655)
+                            xv->broken_sizes = 1;
+                    }
+                } else if (!strcmp(key, "metadatacreator")) {
+                    if (   !strcmp (str_val, "MEGA")
+                        || !strncmp(str_val, "FlixEngine", 10))
+                        xv->broken_sizes = 1;
+                }
+            }
+        }
+
+        if (amf_type == AMF_DATA_TYPE_OBJECT && s->nb_streams == 1 &&
+           ((!apar && !strcmp(key, "audiocodecid")) ||
+            (!vpar && !strcmp(key, "videocodecid"))))
+                s->ctx_flags &= ~AVFMTCTX_NOHEADER; //If there is either audio/video missing, codecid will be an empty object
+
+        if ((!strcmp(key, "duration")        ||
+            !strcmp(key, "filesize")        ||
+            !strcmp(key, "width")           ||
+            !strcmp(key, "height")          ||
+            !strcmp(key, "videodatarate")   ||
+            !strcmp(key, "framerate")       ||
+            !strcmp(key, "videocodecid")    ||
+            !strcmp(key, "audiodatarate")   ||
+            !strcmp(key, "audiosamplerate") ||
+            !strcmp(key, "audiosamplesize") ||
+            !strcmp(key, "stereo")          ||
+            !strcmp(key, "audiocodecid")    ||
+            !strcmp(key, "datastream")) && !xv->dump_full_metadata)
+            return 0;
+
+        s->event_flags |= AVFMT_EVENT_FLAG_METADATA_UPDATED;
+        if (amf_type == AMF_DATA_TYPE_BOOL) {
+            av_strlcpy(str_val, num_val > 0 ? "true" : "false",
+                       sizeof(str_val));
+            av_dict_set(&s->metadata, key, str_val, 0);
+        } else if (amf_type == AMF_DATA_TYPE_NUMBER) {
+            snprintf(str_val, sizeof(str_val), "%.f", num_val);
+            av_dict_set(&s->metadata, key, str_val, 0);
+        } else if (amf_type == AMF_DATA_TYPE_STRING)
+            av_dict_set(&s->metadata, key, str_val, 0);
+    }
+
+    return 0;
+}
+
+#define TYPE_ONTEXTDATA 1
+#define TYPE_ONCAPTION 2
+#define TYPE_ONCAPTIONINFO 3
+#define TYPE_UNKNOWN 9
+
+static int xv_read_metabody(AVFormatContext *s, int64_t next_pos)
+{
+    XVContext *xv = s->priv_data;
+    AMFDataType type;
+    AVStream *stream, *astream, *vstream;
+    AVStream av_unused *dstream;
+    AVIOContext *ioc;
+    int i;
+    char buffer[32];
+    int rot;
+    if(xv->rot_value){
+        rot = xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+    astream = NULL;
+    vstream = NULL;
+    dstream = NULL;
+    ioc     = s->pb;
+
+    // first object needs to be "onMetaData" string
+    type = xvio_r8(ioc, rot);
+    if (type != AMF_DATA_TYPE_STRING ||
+        amf_get_string(ioc, buffer, sizeof(buffer), rot) < 0)
+        return TYPE_UNKNOWN;
+
+    if (!strcmp(buffer, "onTextData"))
+        return TYPE_ONTEXTDATA;
+
+    if (!strcmp(buffer, "onCaption"))
+        return TYPE_ONCAPTION;
+
+    if (!strcmp(buffer, "onCaptionInfo"))
+        return TYPE_ONCAPTIONINFO;
+
+    if (strcmp(buffer, "onMetaData") && strcmp(buffer, "onCuePoint")) {
+        av_log(s, AV_LOG_DEBUG, "Unknown type %s\n", buffer);
+        return TYPE_UNKNOWN;
+    }
+
+    // find the streams now so that amf_parse_object doesn't need to do
+    // the lookup every time it is called.
+    for (i = 0; i < s->nb_streams; i++) {
+        stream = s->streams[i];
+        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
+            vstream = stream;
+            xv->last_keyframe_stream_index = i;
+        } else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+            astream = stream;
+            if (xv->last_keyframe_stream_index == -1)
+                xv->last_keyframe_stream_index = i;
+        }
+        else if (stream->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+            dstream = stream;
+    }
+
+    // parse the second object (we want a mixed array)
+    if (amf_parse_object(s, astream, vstream, buffer, next_pos, 0) < 0)
+        return -1;
+
+    return 0;
+}
+
+
+
+static int xv_read_header(AVFormatContext *s)
+{
+    int flags;
+    XVContext *xv = s->priv_data;
+    int offset;
+    int pre_tag_size = 0;
+    int rot;
+    if(!xv->rot_value){
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+    else{
+        rot = xv->rot_value;
+    }
+    avio_seek(s->pb, 0x200004, SEEK_SET);
+    flags = xvio_r8(s->pb, rot);
+
+    xv->missing_streams = flags & (FLV_HEADER_FLAG_HASVIDEO | FLV_HEADER_FLAG_HASAUDIO);
+
+    s->ctx_flags |= AVFMTCTX_NOHEADER;
+
+    offset = xvio_rb32(s->pb, rot) + 0x200000;
+
+    avio_seek(s->pb, offset, SEEK_SET);
+
+    /* Annex E. The FLV File Format
+     * E.3 TheFLVFileBody
+     *     Field               Type    Comment
+     *     PreviousTagSize0    UI32    Always 0
+     * */
+    pre_tag_size = xvio_rb32(s->pb, rot);
+    if (pre_tag_size) {
+        av_log(s, AV_LOG_WARNING, "Read FLV header error, input file is not a standard xv format, first PreviousTagSize0 always is 0\n");
+    }
+
+    s->start_time = 0;
+    xv->sum_flv_tag_size = 0;
+    xv->last_keyframe_stream_index = -1;
+
+    return 0;
+}
+
+static int xv_read_close(AVFormatContext *s)
+{
+    int i;
+    XVContext *xv = s->priv_data;
+    for (i=0; i<FLV_STREAM_TYPE_NB; i++)
+        av_freep(&xv->new_extradata[i]);
+    av_freep(&xv->keyframe_times);
+    av_freep(&xv->keyframe_filepositions);
+    return 0;
+}
+
+static int xv_get_extradata(AVFormatContext *s, AVStream *st, int size)
+{
+    if (!size)
+        return 0;
+
+    av_freep(&st->codecpar->extradata);
+    if (ff_get_extradata(s, st->codecpar, s->pb, size) < 0)
+        return AVERROR(ENOMEM);
+    st->internal->need_context_update = 1;
+    return 0;
+}
+
+static int xv_queue_extradata(XVContext *xv, AVIOContext *pb, int stream,
+                               int size)
+{
+    if (!size)
+        return 0;
+
+    av_free(xv->new_extradata[stream]);
+    xv->new_extradata[stream] = av_mallocz(size +
+                                            AV_INPUT_BUFFER_PADDING_SIZE);
+    if (!xv->new_extradata[stream])
+        return AVERROR(ENOMEM);
+    xv->new_extradata_size[stream] = size;
+    avio_read(pb, xv->new_extradata[stream], size);
+    return 0;
+}
+
+static void clear_index_entries(AVFormatContext *s, int64_t pos)
+{
+    int i, j, out;
+    av_log(s, AV_LOG_WARNING,
+           "Found invalid index entries, clearing the index.\n");
+    for (i = 0; i < s->nb_streams; i++) {
+        AVStream *st = s->streams[i];
+        /* Remove all index entries that point to >= pos */
+        out = 0;
+        for (j = 0; j < st->nb_index_entries; j++)
+            if (st->index_entries[j].pos < pos)
+                st->index_entries[out++] = st->index_entries[j];
+        st->nb_index_entries = out;
+    }
+}
+
+static int amf_skip_tag(AVIOContext *pb, AMFDataType type)
+{
+    int nb = -1, ret, parse_name = 1;
+
+    switch (type) {
+    case AMF_DATA_TYPE_NUMBER:
+        avio_skip(pb, 8);
+        break;
+    case AMF_DATA_TYPE_BOOL:
+        avio_skip(pb, 1);
+        break;
+    case AMF_DATA_TYPE_STRING:
+        avio_skip(pb, avio_rb16(pb));
+        break;
+    case AMF_DATA_TYPE_ARRAY:
+        parse_name = 0;
+    case AMF_DATA_TYPE_MIXEDARRAY:
+        nb = avio_rb32(pb);
+    case AMF_DATA_TYPE_OBJECT:
+        while(!pb->eof_reached && (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY)) {
+            if (parse_name) {
+                int size = avio_rb16(pb);
+                if (!size) {
+                    avio_skip(pb, 1);
+                    break;
+                }
+                avio_skip(pb, size);
+            }
+            if ((ret = amf_skip_tag(pb, avio_r8(pb))) < 0)
+                return ret;
+        }
+        break;
+    case AMF_DATA_TYPE_NULL:
+    case AMF_DATA_TYPE_OBJECT_END:
+        break;
+    default:
+        return AVERROR_INVALIDDATA;
+    }
+    return 0;
+}
+
+static int xv_data_packet(AVFormatContext *s, AVPacket *pkt,
+                           int64_t dts, int64_t next)
+{
+    AVIOContext *pb = s->pb;
+    XVContext *xv = s->priv_data;
+    AVStream *st    = NULL;
+    char buf[20];
+    int ret = AVERROR_INVALIDDATA;
+    int i, length = -1;
+    int array = 0;
+    int rot;
+    if(xv->rot_value){
+        rot = xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+    switch (xvio_r8(pb,rot)) {
+    case AMF_DATA_TYPE_ARRAY:
+        array = 1;
+    case AMF_DATA_TYPE_MIXEDARRAY:
+        avio_seek(pb, 4, SEEK_CUR);
+    case AMF_DATA_TYPE_OBJECT:
+        break;
+    default:
+        goto skip;
+    }
+
+    while (array || (ret = amf_get_string(pb, buf, sizeof(buf), rot)) > 0) {
+        AMFDataType type = xvio_r8(pb,rot);
+        if (type == AMF_DATA_TYPE_STRING && (array || !strcmp(buf, "text"))) {
+            length = xvio_rb16(pb, rot);
+            ret    = av_get_packet(pb, pkt, length);
+            if (ret < 0)
+                goto skip;
+            else
+                break;
+        } else {
+            if ((ret = amf_skip_tag(pb, type)) < 0)
+                goto skip;
+        }
+    }
+
+    if (length < 0) {
+        ret = AVERROR_INVALIDDATA;
+        goto skip;
+    }
+
+    for (i = 0; i < s->nb_streams; i++) {
+        st = s->streams[i];
+        if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+            break;
+    }
+
+    if (i == s->nb_streams) {
+        st = create_stream(s, AVMEDIA_TYPE_SUBTITLE);
+        if (!st)
+            return AVERROR(ENOMEM);
+        st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+    }
+
+    pkt->dts  = dts;
+    pkt->pts  = dts;
+    pkt->size = ret;
+
+    pkt->stream_index = st->index;
+    pkt->flags       |= AV_PKT_FLAG_KEY;
+
+skip:
+    avio_seek(s->pb, next + 4, SEEK_SET);
+
+    return ret;
+}
+
+static int resync(AVFormatContext *s)
+{
+    XVContext *xv = s->priv_data;
+    int64_t i;
+    int64_t pos = avio_tell(s->pb);
+    int rot;
+    if(xv->rot_value){
+        rot = xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+    for (i=0; !avio_feof(s->pb); i++) {
+        int j  = i & (RESYNC_BUFFER_SIZE-1);
+        int j1 = j + RESYNC_BUFFER_SIZE;
+        xv->resync_buffer[j ] =
+        xv->resync_buffer[j1] = xvio_r8(s->pb,rot);
+
+        if (i >= 8 && pos) {
+            uint8_t *d = xv->resync_buffer + j1 - 8;
+            if (d[0] == 'F' &&
+                d[1] == 'L' &&
+                d[2] == 'V' &&
+                d[3] < 5 && d[5] == 0) {
+                av_log(s, AV_LOG_WARNING, "Concatenated FLV detected, might fail to demux, decode and seek %"PRId64"\n", xv->last_ts);
+                xv->time_offset = xv->last_ts + 1;
+                xv->time_pos    = avio_tell(s->pb);
+            }
+        }
+
+        if (i > 22) {
+            unsigned lsize2 = AV_RB32(xv->resync_buffer + j1 - 4);
+            if (lsize2 >= 11 && lsize2 + 8LL < FFMIN(i, RESYNC_BUFFER_SIZE)) {
+                unsigned  size2 = AV_RB24(xv->resync_buffer + j1 - lsize2 + 1 - 4);
+                unsigned lsize1 = AV_RB32(xv->resync_buffer + j1 - lsize2 - 8);
+                if (lsize1 >= 11 && lsize1 + 8LL + lsize2 < FFMIN(i, RESYNC_BUFFER_SIZE)) {
+                    unsigned  size1 = AV_RB24(xv->resync_buffer + j1 - lsize1 + 1 - lsize2 - 8);
+                    if (size1 == lsize1 - 11 && size2  == lsize2 - 11) {
+                        avio_seek(s->pb, pos + i - lsize1 - lsize2 - 8, SEEK_SET);
+                        return 1;
+                    }
+                }
+            }
+        }
+    }
+    return AVERROR_EOF;
+}
+
+static int xv_read_packet(AVFormatContext *s, AVPacket *pkt)
+{
+    XVContext *xv = s->priv_data;
+    int ret, i, size, flags;
+    enum FlvTagType type;
+    int stream_type=-1;
+    int64_t next, pos, meta_pos;
+    int64_t dts, pts = AV_NOPTS_VALUE;
+    int av_uninit(channels);
+    int av_uninit(sample_rate);
+    AVStream *st    = NULL;
+    int last = -1;
+    int orig_size;
+    int rot;
+    if(xv->rot_value){
+        rot=xv->rot_value;
+    }
+    else{
+        rot = find_rot(s->pb);
+        xv->rot_value = rot;
+    }
+
+retry:
+    /* pkt size is repeated at end. skip it */
+        pos  = avio_tell(s->pb);
+        type = (xvio_r8(s->pb, rot) & 0x1F);
+        orig_size =
+        size = xvio_rb24(s->pb, rot);
+        xv->sum_flv_tag_size += size + 11;
+        dts  = xvio_rb24(s->pb, rot);
+        dts |= (unsigned)xvio_r8(s->pb, rot) << 24;
+        av_log(s, AV_LOG_TRACE, "type:%d, size:%d, last:%d, dts:%"PRId64" pos:%"PRId64"\n", type, size, last, dts, avio_tell(s->pb));
+        if (avio_feof(s->pb))
+            return AVERROR_EOF;
+        avio_skip(s->pb, 3); /* stream id, always 0 */
+        flags = 0;
+
+        if (xv->validate_next < xv->validate_count) {
+            int64_t validate_pos = xv->validate_index[xv->validate_next].pos;
+            if (pos == validate_pos) {
+                if (FFABS(dts - xv->validate_index[xv->validate_next].dts) <=
+                    VALIDATE_INDEX_TS_THRESH) {
+                    xv->validate_next++;
+                } else {
+                    clear_index_entries(s, validate_pos);
+                    xv->validate_count = 0;
+                }
+            } else if (pos > validate_pos) {
+                clear_index_entries(s, validate_pos);
+                xv->validate_count = 0;
+            }
+        }
+
+        if (size == 0) {
+            ret = FFERROR_REDO;
+            goto leave;
+        }
+
+        next = size + avio_tell(s->pb);
+
+        if (type == FLV_TAG_TYPE_AUDIO) {
+            stream_type = FLV_STREAM_TYPE_AUDIO;
+            flags    = xvio_r8(s->pb, rot);
+            size--;
+        } else if (type == FLV_TAG_TYPE_VIDEO) {
+            stream_type = FLV_STREAM_TYPE_VIDEO;
+            flags    = xvio_r8(s->pb, rot);
+            size--;
+            if ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_VIDEO_INFO_CMD)
+                goto skip;
+        } else if (type == FLV_TAG_TYPE_META) {
+            stream_type=FLV_STREAM_TYPE_SUBTITLE;
+            if (size > 13 + 1 + 4) { // Header-type metadata stuff
+                int type;
+                meta_pos = avio_tell(s->pb);
+                type = xv_read_metabody(s, next);
+                if (type == 0 && dts == 0 || type < 0) {
+                    if (type < 0 && xv->validate_count &&
+                        xv->validate_index[0].pos     > next &&
+                        xv->validate_index[0].pos - 4 < next
+                    ) {
+                        av_log(s, AV_LOG_WARNING, "Adjusting next position due to index mismatch\n");
+                        next = xv->validate_index[0].pos - 4;
+                    }
+                    goto skip;
+                } else if (type == TYPE_ONTEXTDATA) {
+                    avpriv_request_sample(s, "OnTextData packet");
+                    return xv_data_packet(s, pkt, dts, next);
+                } else if (type == TYPE_ONCAPTION) {
+                    return xv_data_packet(s, pkt, dts, next);
+                } else if (type == TYPE_UNKNOWN) {
+                    stream_type = FLV_STREAM_TYPE_DATA;
+                }
+                avio_seek(s->pb, meta_pos, SEEK_SET);
+            }
+        } else {
+            av_log(s, AV_LOG_DEBUG,
+                   "Skipping xv packet: type %d, size %d, flags %d.\n",
+                   type, size, flags);
+skip:
+            if (avio_seek(s->pb, next, SEEK_SET) != next) {
+                 // This can happen if xv_read_metabody above read past
+                 // next, on a non-seekable input, and the preceding data has
+                 // been flushed out from the IO buffer.
+                 av_log(s, AV_LOG_ERROR, "Unable to seek to the next packet\n");
+                 return AVERROR_INVALIDDATA;
+            }
+            ret = FFERROR_REDO;
+            goto leave;
+        }
+
+        /* skip empty data packets */
+        if (!size) {
+            ret = FFERROR_REDO;
+            goto leave;
+        }
+
+        /* now find stream */
+        for (i = 0; i < s->nb_streams; i++) {
+            st = s->streams[i];
+            if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
+                    (s->audio_codec_id || flv_same_audio_codec(st->codecpar, flags)))
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
+                    (s->video_codec_id || flv_same_video_codec(st->codecpar, flags)))
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE)
+                    break;
+            } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+                if (st->codecpar->codec_type == AVMEDIA_TYPE_DATA)
+                    break;
+            }
+        }
+        if (i == s->nb_streams) {
+            static const enum AVMediaType stream_types[] = {AVMEDIA_TYPE_VIDEO, AVMEDIA_TYPE_AUDIO, AVMEDIA_TYPE_SUBTITLE, AVMEDIA_TYPE_DATA};
+            st = create_stream(s, stream_types[stream_type]);
+            if (!st)
+                return AVERROR(ENOMEM);
+
+        }
+        av_log(s, AV_LOG_TRACE, "%d %X %d \n", stream_type, flags, st->discard);
+
+        if (xv->time_pos <= pos) {
+            dts += xv->time_offset;
+        }
+
+        if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+            ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY ||
+              stream_type == FLV_STREAM_TYPE_AUDIO))
+            av_add_index_entry(st, pos, dts, size, 0, AVINDEX_KEYFRAME);
+
+        if (  (st->discard >= AVDISCARD_NONKEY && !((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY || (stream_type == FLV_STREAM_TYPE_AUDIO)))
+            ||(st->discard >= AVDISCARD_BIDIR  &&  ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_DISP_INTER && (stream_type == FLV_STREAM_TYPE_VIDEO)))
+            || st->discard >= AVDISCARD_ALL
+        ) {
+            avio_seek(s->pb, next, SEEK_SET);
+            ret = FFERROR_REDO;
+            goto leave;
+        }
+
+    // if not streamed and no duration from metadata then seek to end to find
+    // the duration from the timestamps
+    if ((s->pb->seekable & AVIO_SEEKABLE_NORMAL) &&
+        (!s->duration || s->duration == AV_NOPTS_VALUE) &&
+        !xv->searched_for_end) {
+        int size;
+        const int64_t pos   = avio_tell(s->pb);
+        // Read the last 4 bytes of the file, this should be the size of the
+        // previous FLV tag. Use the timestamp of its payload as duration.
+        int64_t fsize       = avio_size(s->pb);
+retry_duration:
+        avio_seek(s->pb, fsize - 4, SEEK_SET);
+        size = xvio_rb32(s->pb, rot);
+        if (size > 0 && size < fsize) {
+            // Seek to the start of the last FLV tag at position (fsize - 4 - size)
+            // but skip the byte indicating the type.
+            avio_seek(s->pb, fsize - 3 - size, SEEK_SET);
+            if (size == xvio_rb24(s->pb, rot) + 11) {
+                uint32_t ts = xvio_rb24(s->pb, rot);
+                ts         |= xvio_r8(s->pb, rot) << 24;
+                if (ts)
+                    s->duration = ts * (int64_t)AV_TIME_BASE / 1000;
+                else if (fsize >= 8 && fsize - 8 >= size) {
+                    fsize -= size+4;
+                    goto retry_duration;
+                }
+            }
+        }
+
+        avio_seek(s->pb, pos, SEEK_SET);
+        xv->searched_for_end = 1;
+    }
+
+    if (stream_type == FLV_STREAM_TYPE_AUDIO) {
+        int bits_per_coded_sample;
+        channels = (flags & FLV_AUDIO_CHANNEL_MASK) == FLV_STEREO ? 2 : 1;
+        sample_rate = 44100 << ((flags & FLV_AUDIO_SAMPLERATE_MASK) >>
+                                FLV_AUDIO_SAMPLERATE_OFFSET) >> 3;
+        bits_per_coded_sample = (flags & FLV_AUDIO_SAMPLESIZE_MASK) ? 16 : 8;
+        if (!st->codecpar->channels || !st->codecpar->sample_rate ||
+            !st->codecpar->bits_per_coded_sample) {
+            st->codecpar->channels              = channels;
+            st->codecpar->channel_layout        = channels == 1
+                                               ? AV_CH_LAYOUT_MONO
+                                               : AV_CH_LAYOUT_STEREO;
+            st->codecpar->sample_rate           = sample_rate;
+            st->codecpar->bits_per_coded_sample = bits_per_coded_sample;
+        }
+        if (!st->codecpar->codec_id) {
+            xv_set_audio_codec(s, st, st->codecpar,
+                                flags & FLV_AUDIO_CODECID_MASK);
+            xv->last_sample_rate =
+            sample_rate           = st->codecpar->sample_rate;
+            xv->last_channels    =
+            channels              = st->codecpar->channels;
+        } else {
+            AVCodecParameters *par = avcodec_parameters_alloc();
+            if (!par) {
+                ret = AVERROR(ENOMEM);
+                goto leave;
+            }
+            par->sample_rate = sample_rate;
+            par->bits_per_coded_sample = bits_per_coded_sample;
+            xv_set_audio_codec(s, st, par, flags & FLV_AUDIO_CODECID_MASK);
+            sample_rate = par->sample_rate;
+            avcodec_parameters_free(&par);
+        }
+    } else if (stream_type == FLV_STREAM_TYPE_VIDEO) {
+        int ret = xv_set_video_codec(s, st, flags & FLV_VIDEO_CODECID_MASK, 1);
+        if (ret < 0)
+            return ret;
+        size -= ret;
+    } else if (stream_type == FLV_STREAM_TYPE_SUBTITLE) {
+        st->codecpar->codec_id = AV_CODEC_ID_TEXT;
+    } else if (stream_type == FLV_STREAM_TYPE_DATA) {
+        st->codecpar->codec_id = AV_CODEC_ID_NONE; // Opaque AMF data
+    }
+
+    if (st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+        st->codecpar->codec_id == AV_CODEC_ID_H264 ||
+        st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
+        int type = xvio_r8(s->pb, rot);
+        size--;
+
+        if (size < 0) {
+            ret = AVERROR_INVALIDDATA;
+            goto leave;
+        }
+
+        if (st->codecpar->codec_id == AV_CODEC_ID_H264 || st->codecpar->codec_id == AV_CODEC_ID_MPEG4) {
+            // sign extension
+            int32_t cts = (xvio_rb24(s->pb, rot) + 0xff800000) ^ 0xff800000;
+            pts = dts + cts;
+            if (cts < 0) { // dts might be wrong
+                if (!xv->wrong_dts)
+                    av_log(s, AV_LOG_WARNING,
+                        "Negative cts, previous timestamps might be wrong.\n");
+                xv->wrong_dts = 1;
+            } else if (FFABS(dts - pts) > 1000*60*15) {
+                av_log(s, AV_LOG_WARNING,
+                       "invalid timestamps %"PRId64" %"PRId64"\n", dts, pts);
+                dts = pts = AV_NOPTS_VALUE;
+            }
+        }
+        if (type == 0 && (!st->codecpar->extradata || st->codecpar->codec_id == AV_CODEC_ID_AAC ||
+            st->codecpar->codec_id == AV_CODEC_ID_H264)) {
+            AVDictionaryEntry *t;
+
+            if (st->codecpar->extradata) {
+                if ((ret = xv_queue_extradata(xv, s->pb, stream_type, size)) < 0)
+                    return ret;
+                ret = FFERROR_REDO;
+                goto leave;
+            }
+            if ((ret = xv_get_extradata(s, st, size)) < 0)
+                return ret;
+
+            /* Workaround for buggy Omnia A/XE encoder */
+            t = av_dict_get(s->metadata, "Encoder", NULL, 0);
+            if (st->codecpar->codec_id == AV_CODEC_ID_AAC && t && !strcmp(t->value, "Omnia A/XE"))
+                st->codecpar->extradata_size = 2;
+
+            if (st->codecpar->codec_id == AV_CODEC_ID_AAC && 0) {
+                MPEG4AudioConfig cfg;
+
+                if (avpriv_mpeg4audio_get_config(&cfg, st->codecpar->extradata,
+                                                 st->codecpar->extradata_size * 8, 1) >= 0) {
+                st->codecpar->channels       = cfg.channels;
+                st->codecpar->channel_layout = 0;
+                if (cfg.ext_sample_rate)
+                    st->codecpar->sample_rate = cfg.ext_sample_rate;
+                else
+                    st->codecpar->sample_rate = cfg.sample_rate;
+                av_log(s, AV_LOG_TRACE, "mp4a config channels %d sample rate %d\n",
+                        st->codecpar->channels, st->codecpar->sample_rate);
+                }
+            }
+
+            ret = FFERROR_REDO;
+            goto leave;
+        }
+    }
+
+    /* skip empty data packets */
+    if (!size) {
+        ret = FFERROR_REDO;
+        goto leave;
+    }
+
+    ret = av_get_packet(s->pb, pkt, size);
+    if (ret < 0)
+        return ret;
+    pkt->dts          = dts;
+    pkt->pts          = pts == AV_NOPTS_VALUE ? dts : pts;
+    pkt->stream_index = st->index;
+    pkt->pos          = pos;
+    if (xv->new_extradata[stream_type]) {
+        uint8_t *side = av_packet_new_side_data(pkt, AV_PKT_DATA_NEW_EXTRADATA,
+                                                xv->new_extradata_size[stream_type]);
+        if (side) {
+            memcpy(side, xv->new_extradata[stream_type],
+                   xv->new_extradata_size[stream_type]);
+            av_freep(&xv->new_extradata[stream_type]);
+            xv->new_extradata_size[stream_type] = 0;
+        }
+    }
+    if (stream_type == FLV_STREAM_TYPE_AUDIO &&
+                    (sample_rate != xv->last_sample_rate ||
+                     channels    != xv->last_channels)) {
+        xv->last_sample_rate = sample_rate;
+        xv->last_channels    = channels;
+        ff_add_param_change(pkt, channels, 0, sample_rate, 0, 0);
+    }
+
+    if (    stream_type == FLV_STREAM_TYPE_AUDIO ||
+            ((flags & FLV_VIDEO_FRAMETYPE_MASK) == FLV_FRAME_KEY) ||
+            stream_type == FLV_STREAM_TYPE_SUBTITLE ||
+            stream_type == FLV_STREAM_TYPE_DATA)
+        pkt->flags |= AV_PKT_FLAG_KEY;
+
+leave:
+    last = xvio_rb32(s->pb, rot);
+    if (!xv->trust_datasize) {
+        if (last != orig_size + 11 && last != orig_size + 10 &&
+            !avio_feof(s->pb) &&
+            (last != orig_size || !last) && last != xv->sum_flv_tag_size &&
+            !xv->broken_sizes) {
+            av_log(s, AV_LOG_ERROR, "Packet mismatch %d %d %d\n", last, orig_size + 11, xv->sum_flv_tag_size);
+            avio_seek(s->pb, pos + 1, SEEK_SET);
+            ret = resync(s);
+            av_packet_unref(pkt);
+            if (ret >= 0) {
+                goto retry;
+            }
+        }
+    }
+
+    if (ret >= 0)
+        xv->last_ts = pkt->dts;
+
+    return ret;
+}
+
+static int xv_read_seek(AVFormatContext *s, int stream_index,
+                         int64_t ts, int flags)
+{
+    XVContext *xv = s->priv_data;
+    xv->validate_count = 0;
+    return avio_seek_time(s->pb, stream_index, ts, flags);
+}
+
+#define OFFSET(x) offsetof(XVContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+    { "flv_metadata", "Allocate streams according to the onMetaData array", OFFSET(trust_metadata), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+    { "flv_full_metadata", "Dump full metadata of the onMetadata", OFFSET(dump_full_metadata), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+    { "flv_ignore_prevtag", "Ignore the Size of previous tag", OFFSET(trust_datasize), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VD },
+    { "missing_streams", "", OFFSET(missing_streams), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 0xFF, VD | AV_OPT_FLAG_EXPORT | AV_OPT_FLAG_READONLY },
+    { NULL }
+};
+
+static const AVClass xv_class = {
+    .class_name = "xvdec",
+    .item_name  = av_default_item_name,
+    .option     = options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+AVInputFormat ff_xv_demuxer = {
+    .name           = "xv",
+    .long_name      = NULL_IF_CONFIG_SMALL("Xunlei Video File"),
+    .priv_data_size = sizeof(XVContext),
+    .read_probe     = xv_probe,
+    .read_header    = xv_read_header,
+    .read_packet    = xv_read_packet,
+    .read_seek      = xv_read_seek,
+    .read_close     = xv_read_close,
+    .extensions     = "xv",
+    .priv_class     = &xv_class,
+};
+
diff --git a/libavformat/xvtools.h b/libavformat/xvtools.h
new file mode 100644
index 0000000000..42c2c65c46
--- /dev/null
+++ b/libavformat/xvtools.h
@@ -0,0 +1,95 @@ 
+int flvid = 0x46;
+
+
+int byte_shift(int byte,int shift);
+int find_rot(AVIOContext *pb);
+int xvio_r8(AVIOContext *pb, int rot);
+unsigned int xvio_rb16(AVIOContext *s, int rot);
+unsigned int xvio_rb24(AVIOContext *s, int rot);
+unsigned int xvio_rb32(AVIOContext *s, int rot);
+uint64_t xvio_rb64(AVIOContext *s, int rot);
+int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot);
+
+
+
+int byte_shift(int byte,int shift){
+    return ((byte + shift)&0xff);
+}
+
+
+int find_rot(AVIOContext *pb){
+    int64_t curr_pos = avio_tell(pb);
+    avio_seek(pb,0x200000,SEEK_SET);
+    int rot = flvid - avio_r8(pb);
+    avio_seek(pb,curr_pos, SEEK_SET);
+    return rot;
+}
+
+
+
+int xvio_r8(AVIOContext *pb, int rot){
+    int64_t curr_pos = avio_tell(pb);
+    if(curr_pos < 0x200400){
+        int ret=avio_r8(pb);
+        if(ret == 0)return 0;
+        ret = byte_shift(ret,rot);
+        return ret;
+    }
+    int ret=avio_r8(pb);
+    return ret;
+}
+
+
+unsigned int xvio_rb16(AVIOContext *s, int rot)
+{
+    unsigned int val;
+    val = xvio_r8(s, rot) << 8;
+    val |= xvio_r8(s, rot);
+    return val;
+}
+
+
+unsigned int xvio_rb24(AVIOContext *s, int rot)
+{
+    unsigned int val;
+    val = xvio_rb16(s, rot) << 8;
+    val |= xvio_r8(s, rot);
+    return val;
+}
+
+
+unsigned int xvio_rb32(AVIOContext *s, int rot)
+{
+    unsigned int val;
+    val = xvio_rb16(s, rot) << 16;
+    val |= xvio_rb16(s, rot);
+    return val;
+}
+
+
+uint64_t xvio_rb64(AVIOContext *s, int rot)
+{
+    uint64_t val;
+    val = (uint64_t)xvio_rb32(s, rot) << 32;
+    val |= (uint64_t)xvio_rb32(s, rot);
+    return val;
+}
+
+
+
+int xv_string_decrypt(int64_t curr_pos, char *buffer, int length, int rot){
+    if(curr_pos>=0x200400){
+        return 0;
+    }
+    for(int i=0;i<length;i++){
+        if((curr_pos+i) < 0x200400){
+            buffer[i] = byte_shift(buffer[i],rot);
+        }
+        else{
+            break;
+        }
+    }
+    return 0;
+}
+
+
-- 
2.21.0