6380
|
1 /*
|
|
2 * RTP VP8 Depacketizer
|
|
3 * Copyright (c) 2010 Josh Allmann
|
|
4 *
|
|
5 * This file is part of FFmpeg.
|
|
6 *
|
|
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.
|
|
11 *
|
|
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.
|
|
16 *
|
|
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
|
|
20 */
|
|
21
|
|
22 /**
|
|
23 * @file
|
|
24 * @brief RTP support for the VP8 payload
|
|
25 * @author Josh Allmann <joshua.allmann@gmail.com>
|
|
26 * ( http://www.webmproject.org/code/specs/rtp/ )
|
|
27 */
|
|
28
|
|
29 #include "libavcodec/bytestream.h"
|
|
30
|
|
31 #include "rtpdec_formats.h"
|
|
32
|
|
33 struct PayloadContext {
|
|
34 ByteIOContext *data;
|
|
35 uint32_t timestamp;
|
|
36 int is_keyframe;
|
|
37 };
|
|
38
|
|
39 static void prepare_packet(AVPacket *pkt, PayloadContext *vp8, int stream)
|
|
40 {
|
|
41 av_init_packet(pkt);
|
|
42 pkt->stream_index = stream;
|
|
43 pkt->flags = vp8->is_keyframe ? AV_PKT_FLAG_KEY : 0;
|
|
44 pkt->size = url_close_dyn_buf(vp8->data, &pkt->data);
|
|
45 pkt->destruct = av_destruct_packet;
|
|
46 vp8->data = NULL;
|
|
47 }
|
|
48
|
|
49 static int vp8_handle_packet(AVFormatContext *ctx,
|
|
50 PayloadContext *vp8,
|
|
51 AVStream *st,
|
|
52 AVPacket *pkt,
|
|
53 uint32_t *timestamp,
|
|
54 const uint8_t *buf,
|
|
55 int len, int flags)
|
|
56 {
|
|
57 int start_packet, end_packet, has_au, ret = AVERROR(EAGAIN);
|
|
58
|
|
59 if (!buf) {
|
|
60 // only called when vp8_handle_packet returns 1
|
|
61 if (!vp8->data) {
|
|
62 av_log(ctx, AV_LOG_ERROR, "Invalid VP8 data passed\n");
|
|
63 return AVERROR_INVALIDDATA;
|
|
64 }
|
|
65 prepare_packet(pkt, vp8, st->index);
|
|
66 *timestamp = vp8->timestamp;
|
|
67 return 0;
|
|
68 }
|
|
69
|
|
70 start_packet = *buf & 1;
|
|
71 end_packet = flags & RTP_FLAG_MARKER;
|
|
72 has_au = *buf & 2;
|
|
73 buf++;
|
|
74 len--;
|
|
75
|
|
76 if (start_packet) {
|
|
77 int res;
|
|
78 uint32_t ts = *timestamp;
|
|
79 if (vp8->data) {
|
|
80 // missing end marker; return old frame anyway. untested
|
|
81 prepare_packet(pkt, vp8, st->index);
|
|
82 *timestamp = vp8->timestamp; // reset timestamp from old frame
|
|
83
|
|
84 // if current frame fits into one rtp packet, need to hold
|
|
85 // that for the next av_get_packet call
|
|
86 ret = end_packet ? 1 : 0;
|
|
87 }
|
|
88 if ((res = url_open_dyn_buf(&vp8->data)) < 0)
|
|
89 return res;
|
|
90 vp8->is_keyframe = *buf & 1;
|
|
91 vp8->timestamp = ts;
|
|
92 }
|
|
93
|
|
94 if (!vp8->data || vp8->timestamp != *timestamp && ret == AVERROR(EAGAIN)) {
|
|
95 av_log(ctx, AV_LOG_WARNING,
|
|
96 "Received no start marker; dropping frame\n");
|
|
97 return AVERROR(EAGAIN);
|
|
98 }
|
|
99
|
|
100 // cycle through VP8AU headers if needed
|
|
101 // not tested with actual VP8AUs
|
|
102 while (len) {
|
|
103 int au_len = len;
|
|
104 if (has_au && len > 2) {
|
|
105 au_len = AV_RB16(buf);
|
|
106 buf += 2;
|
|
107 len -= 2;
|
|
108 if (buf + au_len > buf + len) {
|
|
109 av_log(ctx, AV_LOG_ERROR, "Invalid VP8AU length\n");
|
|
110 return AVERROR_INVALIDDATA;
|
|
111 }
|
|
112 }
|
|
113
|
|
114 put_buffer(vp8->data, buf, au_len);
|
|
115 buf += au_len;
|
|
116 len -= au_len;
|
|
117 }
|
|
118
|
|
119 if (ret != AVERROR(EAGAIN)) // did we miss a end marker?
|
|
120 return ret;
|
|
121
|
|
122 if (end_packet) {
|
|
123 prepare_packet(pkt, vp8, st->index);
|
|
124 return 0;
|
|
125 }
|
|
126
|
|
127 return AVERROR(EAGAIN);
|
|
128 }
|
|
129
|
|
130 static PayloadContext *vp8_new_context(void)
|
|
131 {
|
|
132 av_log(NULL, AV_LOG_WARNING, "RTP VP8 payload is still experimental\n");
|
|
133 return av_mallocz(sizeof(PayloadContext));
|
|
134 }
|
|
135
|
|
136 static void vp8_free_context(PayloadContext *vp8)
|
|
137 {
|
|
138 if (vp8->data) {
|
|
139 uint8_t *tmp;
|
|
140 url_close_dyn_buf(vp8->data, &tmp);
|
|
141 av_free(tmp);
|
|
142 }
|
|
143 av_free(vp8);
|
|
144 }
|
|
145
|
|
146 RTPDynamicProtocolHandler ff_vp8_dynamic_handler = {
|
|
147 .enc_name = "VP8",
|
|
148 .codec_type = AVMEDIA_TYPE_VIDEO,
|
|
149 .codec_id = CODEC_ID_VP8,
|
|
150 .open = vp8_new_context,
|
|
151 .close = vp8_free_context,
|
|
152 .parse_packet = vp8_handle_packet,
|
|
153 };
|