'keyframes' metatag is not part of the standard, it is just
convention to use such kind of metatag information for indexing.
Structure is following, it allows to have it inconsistent:
keyframes:
times (array):
time0 (num)
time1 (num)
time2 (num)
filepositions (array)
position0 (num)
position1 (num)
Signed-off-by: Anton Khirnov <anton@khirnov.net>
| ... | ... |
@@ -31,6 +31,10 @@ |
| 31 | 31 |
#include "avio_internal.h" |
| 32 | 32 |
#include "flv.h" |
| 33 | 33 |
|
| 34 |
+#define KEYFRAMES_TAG "keyframes" |
|
| 35 |
+#define KEYFRAMES_TIMESTAMP_TAG "times" |
|
| 36 |
+#define KEYFRAMES_BYTEOFFSET_TAG "filepositions" |
|
| 37 |
+ |
|
| 34 | 38 |
typedef struct {
|
| 35 | 39 |
int wrong_dts; ///< wrong dts due to negative cts |
| 36 | 40 |
} FLVContext; |
| ... | ... |
@@ -125,6 +129,64 @@ static int amf_get_string(AVIOContext *ioc, char *buffer, int buffsize) {
|
| 125 | 125 |
return length; |
| 126 | 126 |
} |
| 127 | 127 |
|
| 128 |
+static int parse_keyframes_index(AVFormatContext *s, AVIOContext *ioc, AVStream *vstream, int64_t max_pos) {
|
|
| 129 |
+ unsigned int arraylen = 0, timeslen = 0, fileposlen = 0, i; |
|
| 130 |
+ double num_val; |
|
| 131 |
+ char str_val[256]; |
|
| 132 |
+ int64_t *times = NULL; |
|
| 133 |
+ int64_t *filepositions = NULL; |
|
| 134 |
+ int ret = 0; |
|
| 135 |
+ |
|
| 136 |
+ while (avio_tell(ioc) < max_pos - 2 && amf_get_string(ioc, str_val, sizeof(str_val)) > 0) {
|
|
| 137 |
+ int64_t* current_array; |
|
| 138 |
+ |
|
| 139 |
+ // Expect array object in context |
|
| 140 |
+ if (avio_r8(ioc) != AMF_DATA_TYPE_ARRAY) |
|
| 141 |
+ break; |
|
| 142 |
+ |
|
| 143 |
+ arraylen = avio_rb32(ioc); |
|
| 144 |
+ /* |
|
| 145 |
+ * Expect only 'times' or 'filepositions' sub-arrays in other case refuse to use such metadata |
|
| 146 |
+ * for indexing |
|
| 147 |
+ */ |
|
| 148 |
+ if (!strcmp(KEYFRAMES_TIMESTAMP_TAG, str_val) && !times) {
|
|
| 149 |
+ if (!(times = av_mallocz(sizeof(*times) * arraylen))) {
|
|
| 150 |
+ ret = AVERROR(ENOMEM); |
|
| 151 |
+ goto finish; |
|
| 152 |
+ } |
|
| 153 |
+ timeslen = arraylen; |
|
| 154 |
+ current_array = times; |
|
| 155 |
+ } else if (!strcmp(KEYFRAMES_BYTEOFFSET_TAG, str_val) && !filepositions) {
|
|
| 156 |
+ if (!(filepositions = av_mallocz(sizeof(*filepositions) * arraylen))) {
|
|
| 157 |
+ ret = AVERROR(ENOMEM); |
|
| 158 |
+ goto finish; |
|
| 159 |
+ } |
|
| 160 |
+ fileposlen = arraylen; |
|
| 161 |
+ current_array = filepositions; |
|
| 162 |
+ } else // unexpected metatag inside keyframes, will not use such metadata for indexing |
|
| 163 |
+ break; |
|
| 164 |
+ |
|
| 165 |
+ for (i = 0; i < arraylen && avio_tell(ioc) < max_pos - 1; i++) {
|
|
| 166 |
+ if (avio_r8(ioc) != AMF_DATA_TYPE_NUMBER) |
|
| 167 |
+ goto finish; |
|
| 168 |
+ num_val = av_int2dbl(avio_rb64(ioc)); |
|
| 169 |
+ current_array[i] = num_val; |
|
| 170 |
+ } |
|
| 171 |
+ } |
|
| 172 |
+ |
|
| 173 |
+ if (timeslen == fileposlen) |
|
| 174 |
+ for(i = 0; i < arraylen; i++) |
|
| 175 |
+ av_add_index_entry(vstream, filepositions[i], times[i]*1000, 0, 0, AVINDEX_KEYFRAME); |
|
| 176 |
+ else |
|
| 177 |
+ av_log(s, AV_LOG_WARNING, "Invalid keyframes object, skipping.\n"); |
|
| 178 |
+ |
|
| 179 |
+finish: |
|
| 180 |
+ av_freep(×); |
|
| 181 |
+ av_freep(&filepositions); |
|
| 182 |
+ avio_seek(ioc, max_pos, SEEK_SET); |
|
| 183 |
+ return ret; |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 128 | 186 |
static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vstream, const char *key, int64_t max_pos, int depth) {
|
| 129 | 187 |
AVCodecContext *acodec, *vcodec; |
| 130 | 188 |
AVIOContext *ioc; |
| ... | ... |
@@ -149,6 +211,10 @@ static int amf_parse_object(AVFormatContext *s, AVStream *astream, AVStream *vst |
| 149 | 149 |
case AMF_DATA_TYPE_OBJECT: {
|
| 150 | 150 |
unsigned int keylen; |
| 151 | 151 |
|
| 152 |
+ if (!strcmp(KEYFRAMES_TAG, key) && depth == 1) |
|
| 153 |
+ if (parse_keyframes_index(s, ioc, vstream, max_pos) < 0) |
|
| 154 |
+ return -1; |
|
| 155 |
+ |
|
| 152 | 156 |
while(avio_tell(ioc) < max_pos - 2 && (keylen = avio_rb16(ioc))) {
|
| 153 | 157 |
avio_skip(ioc, keylen); //skip key string |
| 154 | 158 |
if(amf_parse_object(s, NULL, NULL, NULL, max_pos, depth + 1) < 0) |