Browse code

Use a heuristic for describing the RTP packets using sample data

Originally committed as revision 23165 to svn://svn.ffmpeg.org/ffmpeg/trunk

Martin Storsjö authored on 2010/05/19 04:48:25
Showing 2 changed files
... ...
@@ -51,6 +51,20 @@ typedef struct MOVIentry {
51 51
     uint32_t     flags;
52 52
 } MOVIentry;
53 53
 
54
+typedef struct HintSample {
55
+    uint8_t *data;
56
+    int size;
57
+    int sample_number;
58
+    int offset;
59
+    int own_data;
60
+} HintSample;
61
+
62
+typedef struct {
63
+    int size;
64
+    int len;
65
+    HintSample *samples;
66
+} HintSampleQueue;
67
+
54 68
 typedef struct MOVIndex {
55 69
     int         mode;
56 70
     int         entry;
... ...
@@ -82,6 +96,8 @@ typedef struct MOVIndex {
82 82
     uint32_t    prev_rtp_ts;
83 83
     int64_t     cur_rtp_ts_unwrapped;
84 84
     uint32_t    max_packet_size;
85
+
86
+    HintSampleQueue sample_queue;
85 87
 } MOVTrack;
86 88
 
87 89
 typedef struct MOVMuxContext {
... ...
@@ -98,6 +98,188 @@ fail:
98 98
     return ret;
99 99
 }
100 100
 
101
+/**
102
+ * Remove the first sample from the sample queue.
103
+ */
104
+static void sample_queue_pop(HintSampleQueue *queue)
105
+{
106
+    if (queue->len <= 0)
107
+        return;
108
+    if (queue->samples[0].own_data)
109
+        av_free(queue->samples[0].data);
110
+    queue->len--;
111
+    memmove(queue->samples, queue->samples + 1, sizeof(HintSample)*queue->len);
112
+}
113
+
114
+/**
115
+ * Empty the sample queue, releasing all memory.
116
+ */
117
+static void sample_queue_free(HintSampleQueue *queue)
118
+{
119
+    int i;
120
+    for (i = 0; i < queue->len; i++)
121
+        if (queue->samples[i].own_data)
122
+            av_free(queue->samples[i].data);
123
+    av_freep(&queue->samples);
124
+    queue->len = 0;
125
+    queue->size = 0;
126
+}
127
+
128
+/**
129
+ * Add a reference to the sample data to the sample queue. The data is
130
+ * not copied. sample_queue_retain should be called before pkt->data
131
+ * is reused/freed.
132
+ */
133
+static void sample_queue_push(HintSampleQueue *queue, AVPacket *pkt, int sample)
134
+{
135
+    /* No need to keep track of smaller samples, since describing them
136
+     * with immediates is more efficient. */
137
+    if (pkt->size <= 14)
138
+        return;
139
+    if (!queue->samples || queue->len >= queue->size) {
140
+        HintSample* samples;
141
+        queue->size += 10;
142
+        samples = av_realloc(queue->samples, sizeof(HintSample)*queue->size);
143
+        if (!samples)
144
+            return;
145
+        queue->samples = samples;
146
+    }
147
+    queue->samples[queue->len].data = pkt->data;
148
+    queue->samples[queue->len].size = pkt->size;
149
+    queue->samples[queue->len].sample_number = sample;
150
+    queue->samples[queue->len].offset = 0;
151
+    queue->samples[queue->len].own_data = 0;
152
+    queue->len++;
153
+}
154
+
155
+/**
156
+ * Make local copies of all referenced sample data in the queue.
157
+ */
158
+static void sample_queue_retain(HintSampleQueue *queue)
159
+{
160
+    int i;
161
+    for (i = 0; i < queue->len; ) {
162
+        HintSample *sample = &queue->samples[i];
163
+        if (!sample->own_data) {
164
+            uint8_t* ptr = av_malloc(sample->size);
165
+            if (!ptr) {
166
+                /* Unable to allocate memory for this one, remove it */
167
+                memmove(queue->samples + i, queue->samples + i + 1,
168
+                        sizeof(HintSample)*(queue->len - i - 1));
169
+                queue->len--;
170
+                continue;
171
+            }
172
+            memcpy(ptr, sample->data, sample->size);
173
+            sample->data = ptr;
174
+            sample->own_data = 1;
175
+        }
176
+        i++;
177
+    }
178
+}
179
+
180
+/**
181
+ * Find matches of needle[n_pos ->] within haystack. If a sufficiently
182
+ * large match is found, matching bytes before n_pos are included
183
+ * in the match, too (within the limits of the arrays).
184
+ *
185
+ * @param haystack buffer that may contain parts of needle
186
+ * @param h_len length of the haystack buffer
187
+ * @param needle buffer containing source data that have been used to
188
+ *               construct haystack
189
+ * @param n_pos start position in needle used for looking for matches
190
+ * @param n_len length of the needle buffer
191
+ * @param match_h_offset_ptr offset of the first matching byte within haystack
192
+ * @param match_n_offset_ptr offset of the first matching byte within needle
193
+ * @param match_len_ptr length of the matched segment
194
+ * @return 0 if a match was found, < 0 if no match was found
195
+ */
196
+static int match_segments(const uint8_t *haystack, int h_len,
197
+                          const uint8_t *needle, int n_pos, int n_len,
198
+                          int *match_h_offset_ptr, int *match_n_offset_ptr,
199
+                          int *match_len_ptr)
200
+{
201
+    int h_pos;
202
+    for (h_pos = 0; h_pos < h_len; h_pos++) {
203
+        int match_len = 0;
204
+        int match_h_pos, match_n_pos;
205
+
206
+        /* Check how many bytes match at needle[n_pos] and haystack[h_pos] */
207
+        while (h_pos + match_len < h_len && n_pos + match_len < n_len &&
208
+               needle[n_pos + match_len] == haystack[h_pos + match_len])
209
+            match_len++;
210
+        if (match_len <= 8)
211
+            continue;
212
+
213
+        /* If a sufficiently large match was found, try to expand
214
+         * the matched segment backwards. */
215
+        match_h_pos = h_pos;
216
+        match_n_pos = n_pos;
217
+        while (match_n_pos > 0 && match_h_pos > 0 &&
218
+               needle[match_n_pos - 1] == haystack[match_h_pos - 1]) {
219
+            match_n_pos--;
220
+            match_h_pos--;
221
+            match_len++;
222
+        }
223
+        if (match_len <= 14)
224
+            continue;
225
+        *match_h_offset_ptr = match_h_pos;
226
+        *match_n_offset_ptr = match_n_pos;
227
+        *match_len_ptr = match_len;
228
+        return 0;
229
+    }
230
+    return -1;
231
+}
232
+
233
+/**
234
+ * Look for segments in samples in the sample queue matching the data
235
+ * in ptr. Samples not matching are removed from the queue. If a match
236
+ * is found, the next time it will look for matches starting from the
237
+ * end of the previous matched segment.
238
+ *
239
+ * @param data data to find matches for in the sample queue
240
+ * @param len length of the data buffer
241
+ * @param queue samples used for looking for matching segments
242
+ * @param pos the offset in data of the matched segment
243
+ * @param match_sample the number of the sample that contained the match
244
+ * @param match_offset the offset of the matched segment within the sample
245
+ * @param match_len the length of the matched segment
246
+ * @return 0 if a match was found, < 0 if no match was found
247
+ */
248
+static int find_sample_match(const uint8_t *data, int len,
249
+                             HintSampleQueue *queue, int *pos,
250
+                             int *match_sample, int *match_offset,
251
+                             int *match_len)
252
+{
253
+    while (queue->len > 0) {
254
+        HintSample *sample = &queue->samples[0];
255
+        /* If looking for matches in a new sample, skip the first 5 bytes,
256
+         * since they often may be modified/removed in the output packet. */
257
+        if (sample->offset == 0 && sample->size > 5)
258
+            sample->offset = 5;
259
+
260
+        if (match_segments(data, len, sample->data, sample->offset,
261
+                           sample->size, pos, match_offset, match_len) == 0) {
262
+            *match_sample = sample->sample_number;
263
+            /* Next time, look for matches at this offset, with a little
264
+             * margin to this match. */
265
+            sample->offset = *match_offset + *match_len + 5;
266
+            if (sample->offset + 10 >= sample->size)
267
+                sample_queue_pop(queue); /* Not enough useful data left */
268
+            return 0;
269
+        }
270
+
271
+        if (sample->offset < 10 && sample->size > 20) {
272
+            /* No match found from the start of the sample,
273
+             * try from the middle of the sample instead. */
274
+            sample->offset = sample->size/2;
275
+        } else {
276
+            /* No match for this sample, remove it */
277
+            sample_queue_pop(queue);
278
+        }
279
+    }
280
+    return -1;
281
+}
282
+
101 283
 static void output_immediate(const uint8_t *data, int size,
102 284
                              ByteIOContext *out, int *entries)
103 285
 {
... ...
@@ -118,10 +300,36 @@ static void output_immediate(const uint8_t *data, int size,
118 118
     }
119 119
 }
120 120
 
121
+static void output_match(ByteIOContext *out, int match_sample,
122
+                         int match_offset, int match_len, int *entries)
123
+{
124
+    put_byte(out, 2); /* sample constructor */
125
+    put_byte(out, 0); /* track reference */
126
+    put_be16(out, match_len);
127
+    put_be32(out, match_sample);
128
+    put_be32(out, match_offset);
129
+    put_be16(out, 1); /* bytes per block */
130
+    put_be16(out, 1); /* samples per block */
131
+    (*entries)++;
132
+}
133
+
121 134
 static void describe_payload(const uint8_t *data, int size,
122
-                             ByteIOContext *out, int *entries)
135
+                             ByteIOContext *out, int *entries,
136
+                             HintSampleQueue *queue)
123 137
 {
124 138
     /* Describe the payload using different constructors */
139
+    while (size > 0) {
140
+        int match_sample, match_offset, match_len, pos;
141
+        if (find_sample_match(data, size, queue, &pos, &match_sample,
142
+                              &match_offset, &match_len) < 0)
143
+            break;
144
+        output_immediate(data, pos, out, entries);
145
+        data += pos;
146
+        size -= pos;
147
+        output_match(out, match_sample, match_offset, match_len, entries);
148
+        data += match_len;
149
+        size -= match_len;
150
+    }
125 151
     output_immediate(data, size, out, entries);
126 152
 }
127 153
 
... ...
@@ -195,7 +403,7 @@ static int write_hint_packets(ByteIOContext *out, const uint8_t *data,
195 195
 
196 196
         entries = 0;
197 197
         /* Write one or more constructors describing the payload data */
198
-        describe_payload(data, packet_len, out, &entries);
198
+        describe_payload(data, packet_len, out, &entries, &trk->sample_queue);
199 199
         data += packet_len;
200 200
         size -= packet_len;
201 201
 
... ...
@@ -230,6 +438,8 @@ int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt,
230 230
     if (!rtp_ctx->pb)
231 231
         return AVERROR(ENOMEM);
232 232
 
233
+    sample_queue_push(&trk->sample_queue, pkt, sample);
234
+
233 235
     /* Feed the packet to the RTP muxer */
234 236
     local_pkt = *pkt;
235 237
     local_pkt.stream_index = 0;
... ...
@@ -269,6 +479,7 @@ int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt,
269 269
         ff_mov_write_packet(s, &hint_pkt);
270 270
 done:
271 271
     av_free(buf);
272
+    sample_queue_retain(&trk->sample_queue);
272 273
     return ret;
273 274
 }
274 275
 
... ...
@@ -277,6 +488,7 @@ void ff_mov_close_hinting(MOVTrack *track) {
277 277
     uint8_t *ptr;
278 278
 
279 279
     av_freep(&track->enc);
280
+    sample_queue_free(&track->sample_queue);
280 281
     if (!rtp_ctx)
281 282
         return;
282 283
     if (rtp_ctx->pb) {