1/*
2 * RTP VP8 Depacketizer
3 * Copyright (c) 2010 Josh Allmann
4 *
5 * This file is part of Libav.
6 *
7 * Libav 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 * Libav 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 Libav; 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 * @see http://www.webmproject.org/code/specs/rtp/
27 */
28
29#include "libavcodec/bytestream.h"
30
31#include "rtpdec_formats.h"
32
33struct PayloadContext {
34    AVIOContext *data;
35    uint32_t       timestamp;
36    int is_keyframe;
37};
38
39static 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         = avio_close_dyn_buf(vp8->data, &pkt->data);
45    pkt->destruct     = av_destruct_packet;
46    vp8->data         = NULL;
47}
48
49static 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 = avio_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        avio_write(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
130static 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
137static void vp8_free_context(PayloadContext *vp8)
138{
139    if (vp8->data) {
140        uint8_t *tmp;
141        avio_close_dyn_buf(vp8->data, &tmp);
142        av_free(tmp);
143    }
144    av_free(vp8);
145}
146
147RTPDynamicProtocolHandler ff_vp8_dynamic_handler = {
148    .enc_name       = "VP8",
149    .codec_type     = AVMEDIA_TYPE_VIDEO,
150    .codec_id       = CODEC_ID_VP8,
151    .alloc          = vp8_new_context,
152    .free           = vp8_free_context,
153    .parse_packet   = vp8_handle_packet,
154};
155