avio: rename ByteIOContext to AVIOContext.
[ffmpeg.git] / libavformat / rtpdec_vp8.c
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     AVIOContext *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_ERROR, "RTP VP8 payload implementation is incompatible "
133                                "with the latest spec drafts.\n");
134     return av_mallocz(sizeof(PayloadContext));
135 }
136
137 static void vp8_free_context(PayloadContext *vp8)
138 {
139     if (vp8->data) {
140         uint8_t *tmp;
141         url_close_dyn_buf(vp8->data, &tmp);
142         av_free(tmp);
143     }
144     av_free(vp8);
145 }
146
147 RTPDynamicProtocolHandler ff_vp8_dynamic_handler = {
148     .enc_name       = "VP8",
149     .codec_type     = AVMEDIA_TYPE_VIDEO,
150     .codec_id       = CODEC_ID_VP8,
151     .open           = vp8_new_context,
152     .close          = vp8_free_context,
153     .parse_packet   = vp8_handle_packet,
154 };