Originally committed as revision 23165 to svn://svn.ffmpeg.org/ffmpeg/trunk
Martin Storsjö authored on 2010/05/19 04:48:25... | ... |
@@ -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) { |