Browse code

flvdec: read index stored in the 'keyframes' tag.

'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>

Kharkov Alexander authored on 2011/03/18 16:26:51
Showing 1 changed files
... ...
@@ -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(&times);
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)