2 * MOV, 3GP, MP4 muxer RTP hinting
3 * Copyright (c) 2010 Martin Storsjo
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 #include "libavutil/intreadwrite.h"
26 int ff_mov_init_hinting(AVFormatContext *s, int index, int src_index)
28 MOVMuxContext *mov = s->priv_data;
29 MOVTrack *track = &mov->tracks[index];
30 MOVTrack *src_track = &mov->tracks[src_index];
31 AVStream *src_st = s->streams[src_index];
32 int ret = AVERROR(ENOMEM);
33 AVOutputFormat *rtp_format = av_guess_format("rtp", NULL, NULL);
35 track->tag = MKTAG('r','t','p',' ');
36 track->src_track = src_index;
39 ret = AVERROR(ENOENT);
43 track->enc = avcodec_alloc_context();
46 track->enc->codec_type = AVMEDIA_TYPE_DATA;
47 track->enc->codec_tag = track->tag;
49 track->rtp_ctx = avformat_alloc_context();
52 track->rtp_ctx->oformat = rtp_format;
53 if (!av_new_stream(track->rtp_ctx, 0))
56 /* Copy stream parameters */
57 track->rtp_ctx->streams[0]->sample_aspect_ratio =
58 src_st->sample_aspect_ratio;
60 avcodec_copy_context(track->rtp_ctx->streams[0]->codec, src_st->codec);
62 if ((ret = url_open_dyn_packet_buf(&track->rtp_ctx->pb,
63 RTP_MAX_PACKET_SIZE)) < 0)
65 ret = av_write_header(track->rtp_ctx);
69 /* Copy the RTP AVStream timebase back to the hint AVStream */
70 track->timescale = track->rtp_ctx->streams[0]->time_base.den;
72 /* Mark the hinted track that packets written to it should be
73 * sent to this track for hinting. */
74 src_track->hint_track = index;
77 av_log(s, AV_LOG_WARNING,
78 "Unable to initialize hinting of stream %d\n", src_index);
79 if (track->rtp_ctx && track->rtp_ctx->pb) {
81 url_close_dyn_buf(track->rtp_ctx->pb, &buf);
85 avformat_free_context(track->rtp_ctx);
86 track->rtp_ctx = NULL;
88 av_freep(&track->enc);
89 /* Set a default timescale, to avoid crashes in dump_format */
90 track->timescale = 90000;
95 * Remove the first sample from the sample queue.
97 static void sample_queue_pop(HintSampleQueue *queue)
101 if (queue->samples[0].own_data)
102 av_free(queue->samples[0].data);
104 memmove(queue->samples, queue->samples + 1, sizeof(HintSample)*queue->len);
108 * Empty the sample queue, releasing all memory.
110 static void sample_queue_free(HintSampleQueue *queue)
113 for (i = 0; i < queue->len; i++)
114 if (queue->samples[i].own_data)
115 av_free(queue->samples[i].data);
116 av_freep(&queue->samples);
122 * Add a reference to the sample data to the sample queue. The data is
123 * not copied. sample_queue_retain should be called before pkt->data
126 static void sample_queue_push(HintSampleQueue *queue, AVPacket *pkt, int sample)
128 /* No need to keep track of smaller samples, since describing them
129 * with immediates is more efficient. */
132 if (!queue->samples || queue->len >= queue->size) {
135 samples = av_realloc(queue->samples, sizeof(HintSample)*queue->size);
138 queue->samples = samples;
140 queue->samples[queue->len].data = pkt->data;
141 queue->samples[queue->len].size = pkt->size;
142 queue->samples[queue->len].sample_number = sample;
143 queue->samples[queue->len].offset = 0;
144 queue->samples[queue->len].own_data = 0;
149 * Make local copies of all referenced sample data in the queue.
151 static void sample_queue_retain(HintSampleQueue *queue)
154 for (i = 0; i < queue->len; ) {
155 HintSample *sample = &queue->samples[i];
156 if (!sample->own_data) {
157 uint8_t* ptr = av_malloc(sample->size);
159 /* Unable to allocate memory for this one, remove it */
160 memmove(queue->samples + i, queue->samples + i + 1,
161 sizeof(HintSample)*(queue->len - i - 1));
165 memcpy(ptr, sample->data, sample->size);
167 sample->own_data = 1;
174 * Find matches of needle[n_pos ->] within haystack. If a sufficiently
175 * large match is found, matching bytes before n_pos are included
176 * in the match, too (within the limits of the arrays).
178 * @param haystack buffer that may contain parts of needle
179 * @param h_len length of the haystack buffer
180 * @param needle buffer containing source data that have been used to
182 * @param n_pos start position in needle used for looking for matches
183 * @param n_len length of the needle buffer
184 * @param match_h_offset_ptr offset of the first matching byte within haystack
185 * @param match_n_offset_ptr offset of the first matching byte within needle
186 * @param match_len_ptr length of the matched segment
187 * @return 0 if a match was found, < 0 if no match was found
189 static int match_segments(const uint8_t *haystack, int h_len,
190 const uint8_t *needle, int n_pos, int n_len,
191 int *match_h_offset_ptr, int *match_n_offset_ptr,
195 for (h_pos = 0; h_pos < h_len; h_pos++) {
197 int match_h_pos, match_n_pos;
199 /* Check how many bytes match at needle[n_pos] and haystack[h_pos] */
200 while (h_pos + match_len < h_len && n_pos + match_len < n_len &&
201 needle[n_pos + match_len] == haystack[h_pos + match_len])
206 /* If a sufficiently large match was found, try to expand
207 * the matched segment backwards. */
210 while (match_n_pos > 0 && match_h_pos > 0 &&
211 needle[match_n_pos - 1] == haystack[match_h_pos - 1]) {
218 *match_h_offset_ptr = match_h_pos;
219 *match_n_offset_ptr = match_n_pos;
220 *match_len_ptr = match_len;
227 * Look for segments in samples in the sample queue matching the data
228 * in ptr. Samples not matching are removed from the queue. If a match
229 * is found, the next time it will look for matches starting from the
230 * end of the previous matched segment.
232 * @param data data to find matches for in the sample queue
233 * @param len length of the data buffer
234 * @param queue samples used for looking for matching segments
235 * @param pos the offset in data of the matched segment
236 * @param match_sample the number of the sample that contained the match
237 * @param match_offset the offset of the matched segment within the sample
238 * @param match_len the length of the matched segment
239 * @return 0 if a match was found, < 0 if no match was found
241 static int find_sample_match(const uint8_t *data, int len,
242 HintSampleQueue *queue, int *pos,
243 int *match_sample, int *match_offset,
246 while (queue->len > 0) {
247 HintSample *sample = &queue->samples[0];
248 /* If looking for matches in a new sample, skip the first 5 bytes,
249 * since they often may be modified/removed in the output packet. */
250 if (sample->offset == 0 && sample->size > 5)
253 if (match_segments(data, len, sample->data, sample->offset,
254 sample->size, pos, match_offset, match_len) == 0) {
255 *match_sample = sample->sample_number;
256 /* Next time, look for matches at this offset, with a little
257 * margin to this match. */
258 sample->offset = *match_offset + *match_len + 5;
259 if (sample->offset + 10 >= sample->size)
260 sample_queue_pop(queue); /* Not enough useful data left */
264 if (sample->offset < 10 && sample->size > 20) {
265 /* No match found from the start of the sample,
266 * try from the middle of the sample instead. */
267 sample->offset = sample->size/2;
269 /* No match for this sample, remove it */
270 sample_queue_pop(queue);
276 static void output_immediate(const uint8_t *data, int size,
277 ByteIOContext *out, int *entries)
283 put_byte(out, 1); /* immediate constructor */
284 put_byte(out, len); /* amount of valid data */
285 put_buffer(out, data, len);
289 for (; len < 14; len++)
296 static void output_match(ByteIOContext *out, int match_sample,
297 int match_offset, int match_len, int *entries)
299 put_byte(out, 2); /* sample constructor */
300 put_byte(out, 0); /* track reference */
301 put_be16(out, match_len);
302 put_be32(out, match_sample);
303 put_be32(out, match_offset);
304 put_be16(out, 1); /* bytes per block */
305 put_be16(out, 1); /* samples per block */
309 static void describe_payload(const uint8_t *data, int size,
310 ByteIOContext *out, int *entries,
311 HintSampleQueue *queue)
313 /* Describe the payload using different constructors */
315 int match_sample, match_offset, match_len, pos;
316 if (find_sample_match(data, size, queue, &pos, &match_sample,
317 &match_offset, &match_len) < 0)
319 output_immediate(data, pos, out, entries);
322 output_match(out, match_sample, match_offset, match_len, entries);
326 output_immediate(data, size, out, entries);
330 * Write an RTP hint (that may contain one or more RTP packets)
331 * for the packets in data. data contains one or more packets with a
334 * @param out buffer where the hints are written
335 * @param data buffer containing RTP packets
336 * @param size the size of the data buffer
337 * @param trk the MOVTrack for the hint track
338 * @param pts pointer where the timestamp for the written RTP hint is stored
339 * @return the number of RTP packets in the written hint
341 static int write_hint_packets(ByteIOContext *out, const uint8_t *data,
342 int size, MOVTrack *trk, int64_t *pts)
345 int64_t count_pos, entries_pos;
346 int count = 0, entries;
348 count_pos = url_ftell(out);
349 /* RTPsample header */
350 put_be16(out, 0); /* packet count */
351 put_be16(out, 0); /* reserved */
354 uint32_t packet_len = AV_RB32(data);
360 if (packet_len > size || packet_len <= 12)
362 if (data[1] >= 200 && data[1] <= 204) {
363 /* RTCP packet, just skip */
369 if (packet_len > trk->max_packet_size)
370 trk->max_packet_size = packet_len;
372 seq = AV_RB16(&data[2]);
373 ts = AV_RB32(&data[4]);
375 if (trk->prev_rtp_ts == 0)
376 trk->prev_rtp_ts = ts;
377 /* Unwrap the 32-bit RTP timestamp that wraps around often
378 * into a not (as often) wrapping 64-bit timestamp. */
379 trk->cur_rtp_ts_unwrapped += (int32_t) (ts - trk->prev_rtp_ts);
380 trk->prev_rtp_ts = ts;
381 if (*pts == AV_NOPTS_VALUE)
382 *pts = trk->cur_rtp_ts_unwrapped;
385 /* RTPpacket header */
386 put_be32(out, 0); /* relative_time */
387 put_buffer(out, data, 2); /* RTP header */
388 put_be16(out, seq); /* RTPsequenceseed */
389 put_be16(out, 0); /* reserved + flags */
390 entries_pos = url_ftell(out);
391 put_be16(out, 0); /* entry count */
398 /* Write one or more constructors describing the payload data */
399 describe_payload(data, packet_len, out, &entries, &trk->sample_queue);
403 curpos = url_ftell(out);
404 url_fseek(out, entries_pos, SEEK_SET);
405 put_be16(out, entries);
406 url_fseek(out, curpos, SEEK_SET);
409 curpos = url_ftell(out);
410 url_fseek(out, count_pos, SEEK_SET);
411 put_be16(out, count);
412 url_fseek(out, curpos, SEEK_SET);
416 int ff_mov_add_hinted_packet(AVFormatContext *s, AVPacket *pkt,
417 int track_index, int sample)
419 MOVMuxContext *mov = s->priv_data;
420 MOVTrack *trk = &mov->tracks[track_index];
421 AVFormatContext *rtp_ctx = trk->rtp_ctx;
424 ByteIOContext *hintbuf = NULL;
429 return AVERROR(ENOENT);
431 return AVERROR(ENOMEM);
433 sample_queue_push(&trk->sample_queue, pkt, sample);
435 /* Feed the packet to the RTP muxer */
436 ff_write_chained(rtp_ctx, 0, pkt, s);
438 /* Fetch the output from the RTP muxer, open a new output buffer
440 size = url_close_dyn_buf(rtp_ctx->pb, &buf);
441 if ((ret = url_open_dyn_packet_buf(&rtp_ctx->pb,
442 RTP_MAX_PACKET_SIZE)) < 0)
448 /* Open a buffer for writing the hint */
449 if ((ret = url_open_dyn_buf(&hintbuf)) < 0)
451 av_init_packet(&hint_pkt);
452 count = write_hint_packets(hintbuf, buf, size, trk, &hint_pkt.dts);
455 /* Write the hint data into the hint track */
456 hint_pkt.size = size = url_close_dyn_buf(hintbuf, &buf);
458 hint_pkt.pts = hint_pkt.dts;
459 hint_pkt.stream_index = track_index;
460 if (pkt->flags & AV_PKT_FLAG_KEY)
461 hint_pkt.flags |= AV_PKT_FLAG_KEY;
463 ff_mov_write_packet(s, &hint_pkt);
466 sample_queue_retain(&trk->sample_queue);
470 void ff_mov_close_hinting(MOVTrack *track) {
471 AVFormatContext* rtp_ctx = track->rtp_ctx;
474 av_freep(&track->enc);
475 sample_queue_free(&track->sample_queue);
479 av_write_trailer(rtp_ctx);
480 url_close_dyn_buf(rtp_ctx->pb, &ptr);
483 avformat_free_context(rtp_ctx);