|
|
|
@@ -31,6 +31,10 @@ |
|
|
|
#include "avio_internal.h" |
|
|
|
#include "flv.h" |
|
|
|
|
|
|
|
#define KEYFRAMES_TAG "keyframes" |
|
|
|
#define KEYFRAMES_TIMESTAMP_TAG "times" |
|
|
|
#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" |
|
|
|
|
|
|
|
typedef struct { |
|
|
|
int wrong_dts; ///< wrong dts due to negative cts |
|
|
|
} FLVContext; |
|
|
|
@@ -125,6 +129,64 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) { |
|
|
|
return length; |
|
|
|
} |
|
|
|
|
|
|
|
static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) { |
|
|
|
unsigned int arraylen = 0, timeslen = 0, fileposlen = 0, i; |
|
|
|
double num_val; |
|
|
|
char str_val[256]; |
|
|
|
int64_t *times = NULL; |
|
|
|
int64_t *filepositions = NULL; |
|
|
|
int ret = 0; |
|
|
|
|
|
|
|
while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) { |
|
|
|
int64_t* current_array; |
|
|
|
|
|
|
|
// Expect array object in context |
|
|
|
if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY) |
|
|
|
break; |
|
|
|
|
|
|
|
arraylen = avio_rb32(ioc); |
|
|
|
/* |
|
|
|
* Expect only 'times' or 'filepositions' sub-arrays in other case refuse to use such metadata |
|
|
|
* for indexing |
|
|
|
*/ |
|
|
|
if (!strcmp(KEYFRAMES_TIMESTAMP_TAG, str_val) && !times) { |
|
|
|
if (!(times = av_mallocz(sizeof(*times) * arraylen))) { |
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
goto finish; |
|
|
|
} |
|
|
|
timeslen = arraylen; |
|
|
|
current_array = times; |
|
|
|
} else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions) { |
|
|
|
if (!(filepositions = av_mallocz(sizeof(*filepositions) * arraylen))) { |
|
|
|
ret = AVERROR(ENOMEM); |
|
|
|
goto finish; |
|
|
|
} |
|
|
|
fileposlen = arraylen; |
|
|
|
current_array = filepositions; |
|
|
|
} else // unexpected metatag inside keyframes, will not use such metadata for indexing |
|
|
|
break; |
|
|
|
|
|
|
|
for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) { |
|
|
|
if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER) |
|
|
|
goto finish; |
|
|
|
num_val = av_int2dbl(avio_rb64(ioc)); |
|
|
|
current_array[i] = num_val; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (timeslen == fileposlen) |
|
|
|
for(i = 0; i < arraylen; i++) |
|
|
|
av_add_index_entry(vstream, filepositions[i], times[i]*1000, 0, 0, AVINDEX_KEYFRAME); |
|
|
|
else |
|
|
|
av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n"); |
|
|
|
|
|
|
|
finish: |
|
|
|
av_freep(×); |
|
|
|
av_freep(&filepositions); |
|
|
|
avio_seek(ioc, max_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) { |
|
|
|
AVCodecContext *acodec, *vcodec; |
|
|
|
AVIOContext *ioc; |
|
|
|
@@ -149,6 +211,10 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst |
|
|
|
case AMF_DATA_TYPE_OBJECT: { |
|
|
|
unsigned int keylen; |
|
|
|
|
|
|
|
if (!strcmp(KEYFRAMES_TAG, key) && depth == 1) |
|
|
|
if (parse_keyframes_index(s, ioc, vstream, max_pos) < 0) |
|
|
|
return -1; |
|
|
|
|
|
|
|
while(avio_tell(ioc) < max_pos - 2 && (keylen = avio_rb16(ioc))) { |
|
|
|
avio_skip(ioc, keylen); //skip key string |
|
|
|
if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0) |
|
|
|
|