yading@11: /* yading@11: * RTP VP8 Depacketizer yading@11: * Copyright (c) 2010 Josh Allmann yading@11: * Copyright (c) 2012 Martin Storsjo yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: /** yading@11: * @file yading@11: * @brief RTP support for the VP8 payload yading@11: * @author Josh Allmann yading@11: * @see http://tools.ietf.org/html/draft-ietf-payload-vp8-05 yading@11: */ yading@11: yading@11: #include "libavcodec/bytestream.h" yading@11: yading@11: #include "rtpdec_formats.h" yading@11: yading@11: struct PayloadContext { yading@11: AVIOContext *data; yading@11: uint32_t timestamp; yading@11: int is_keyframe; yading@11: /* If sequence_ok is set, we keep returning data (even if we might have yading@11: * lost some data, but we haven't lost any too critical data that would yading@11: * cause the decoder to desynchronize and output random garbage). yading@11: */ yading@11: int sequence_ok; yading@11: int first_part_size; yading@11: uint16_t prev_seq; yading@11: int prev_pictureid; yading@11: int broken_frame; yading@11: /* If sequence_dirty is set, we have lost some data (critical or yading@11: * non-critical) and decoding will have some sort of artefacts, and yading@11: * we thus should request a new keyframe. yading@11: */ yading@11: int sequence_dirty; yading@11: int got_keyframe; yading@11: }; yading@11: yading@11: static void vp8_free_buffer(PayloadContext *vp8) yading@11: { yading@11: uint8_t *tmp; yading@11: if (!vp8->data) yading@11: return; yading@11: avio_close_dyn_buf(vp8->data, &tmp); yading@11: av_free(tmp); yading@11: vp8->data = NULL; yading@11: } yading@11: yading@11: static int vp8_broken_sequence(AVFormatContext *ctx, PayloadContext *vp8, yading@11: const char *msg) yading@11: { yading@11: vp8->sequence_ok = 0; yading@11: av_log(ctx, AV_LOG_WARNING, "%s", msg); yading@11: vp8_free_buffer(vp8); yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: static int vp8_handle_packet(AVFormatContext *ctx, PayloadContext *vp8, yading@11: AVStream *st, AVPacket *pkt, uint32_t *timestamp, yading@11: const uint8_t *buf, int len, uint16_t seq, yading@11: int flags) yading@11: { yading@11: int start_partition, end_packet; yading@11: int extended_bits, part_id; yading@11: int pictureid_present = 0, tl0picidx_present = 0, tid_present = 0, yading@11: keyidx_present = 0; yading@11: int pictureid = -1, pictureid_mask = 0; yading@11: int returned_old_frame = 0; yading@11: uint32_t old_timestamp = 0; yading@11: yading@11: if (!buf) { yading@11: if (vp8->data) { yading@11: int ret = ff_rtp_finalize_packet(pkt, &vp8->data, st->index); yading@11: if (ret < 0) yading@11: return ret; yading@11: *timestamp = vp8->timestamp; yading@11: if (vp8->sequence_dirty) yading@11: pkt->flags |= AV_PKT_FLAG_CORRUPT; yading@11: return 0; yading@11: } yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: if (len < 1) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: extended_bits = buf[0] & 0x80; yading@11: start_partition = buf[0] & 0x10; yading@11: part_id = buf[0] & 0x0f; yading@11: end_packet = flags & RTP_FLAG_MARKER; yading@11: buf++; yading@11: len--; yading@11: if (extended_bits) { yading@11: if (len < 1) yading@11: return AVERROR_INVALIDDATA; yading@11: pictureid_present = buf[0] & 0x80; yading@11: tl0picidx_present = buf[0] & 0x40; yading@11: tid_present = buf[0] & 0x20; yading@11: keyidx_present = buf[0] & 0x10; yading@11: buf++; yading@11: len--; yading@11: } yading@11: if (pictureid_present) { yading@11: if (len < 1) yading@11: return AVERROR_INVALIDDATA; yading@11: if (buf[0] & 0x80) { yading@11: if (len < 2) yading@11: return AVERROR_INVALIDDATA; yading@11: pictureid = AV_RB16(buf) & 0x7fff; yading@11: pictureid_mask = 0x7fff; yading@11: buf += 2; yading@11: len -= 2; yading@11: } else { yading@11: pictureid = buf[0] & 0x7f; yading@11: pictureid_mask = 0x7f; yading@11: buf++; yading@11: len--; yading@11: } yading@11: } yading@11: if (tl0picidx_present) { yading@11: // Ignoring temporal level zero index yading@11: buf++; yading@11: len--; yading@11: } yading@11: if (tid_present || keyidx_present) { yading@11: // Ignoring temporal layer index, layer sync bit and keyframe index yading@11: buf++; yading@11: len--; yading@11: } yading@11: if (len < 1) yading@11: return AVERROR_INVALIDDATA; yading@11: yading@11: if (start_partition && part_id == 0 && len >= 3) { yading@11: int res; yading@11: int non_key = buf[0] & 0x01; yading@11: if (!non_key) { yading@11: vp8_free_buffer(vp8); yading@11: // Keyframe, decoding ok again yading@11: vp8->sequence_ok = 1; yading@11: vp8->sequence_dirty = 0; yading@11: vp8->got_keyframe = 1; yading@11: } else { yading@11: int can_continue = vp8->data && !vp8->is_keyframe && yading@11: avio_tell(vp8->data) >= vp8->first_part_size; yading@11: if (!vp8->sequence_ok) yading@11: return AVERROR(EAGAIN); yading@11: if (!vp8->got_keyframe) yading@11: return vp8_broken_sequence(ctx, vp8, "Keyframe missing\n"); yading@11: if (pictureid >= 0) { yading@11: if (pictureid != ((vp8->prev_pictureid + 1) & pictureid_mask)) { yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed a picture, sequence broken\n"); yading@11: } else { yading@11: if (vp8->data && !can_continue) yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed a picture, sequence broken\n"); yading@11: } yading@11: } else { yading@11: uint16_t expected_seq = vp8->prev_seq + 1; yading@11: int16_t diff = seq - expected_seq; yading@11: if (vp8->data) { yading@11: // No picture id, so we can't know if missed packets yading@11: // contained any new frames. If diff == 0, we did get yading@11: // later packets from the same frame (matching timestamp), yading@11: // so we know we didn't miss any frame. If diff == 1 and yading@11: // we still have data (not flushed by the end of frame yading@11: // marker), the single missed packet must have been part yading@11: // of the same frame. yading@11: if ((diff == 0 || diff == 1) && can_continue) { yading@11: // Proceed with what we have yading@11: } else { yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed too much, sequence broken\n"); yading@11: } yading@11: } else { yading@11: if (diff != 0) yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed unknown data, sequence broken\n"); yading@11: } yading@11: } yading@11: if (vp8->data) { yading@11: vp8->sequence_dirty = 1; yading@11: if (avio_tell(vp8->data) >= vp8->first_part_size) { yading@11: int ret = ff_rtp_finalize_packet(pkt, &vp8->data, st->index); yading@11: if (ret < 0) yading@11: return ret; yading@11: pkt->flags |= AV_PKT_FLAG_CORRUPT; yading@11: returned_old_frame = 1; yading@11: old_timestamp = vp8->timestamp; yading@11: } else { yading@11: // Shouldn't happen yading@11: vp8_free_buffer(vp8); yading@11: } yading@11: } yading@11: } yading@11: vp8->first_part_size = (AV_RL16(&buf[1]) << 3 | buf[0] >> 5) + 3; yading@11: if ((res = avio_open_dyn_buf(&vp8->data)) < 0) yading@11: return res; yading@11: vp8->timestamp = *timestamp; yading@11: vp8->broken_frame = 0; yading@11: vp8->prev_pictureid = pictureid; yading@11: vp8->is_keyframe = !non_key; yading@11: } else { yading@11: uint16_t expected_seq = vp8->prev_seq + 1; yading@11: yading@11: if (!vp8->sequence_ok) yading@11: return AVERROR(EAGAIN); yading@11: yading@11: if (vp8->timestamp != *timestamp) { yading@11: // Missed the start of the new frame, sequence broken yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Received no start marker; dropping frame\n"); yading@11: } yading@11: yading@11: if (seq != expected_seq) { yading@11: if (vp8->is_keyframe) { yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed part of a keyframe, sequence broken\n"); yading@11: } else if (vp8->data && avio_tell(vp8->data) >= vp8->first_part_size) { yading@11: vp8->broken_frame = 1; yading@11: vp8->sequence_dirty = 1; yading@11: } else { yading@11: return vp8_broken_sequence(ctx, vp8, yading@11: "Missed part of the first partition, sequence broken\n"); yading@11: } yading@11: } yading@11: } yading@11: yading@11: if (!vp8->data) yading@11: return vp8_broken_sequence(ctx, vp8, "Received no start marker\n"); yading@11: yading@11: vp8->prev_seq = seq; yading@11: if (!vp8->broken_frame) yading@11: avio_write(vp8->data, buf, len); yading@11: yading@11: if (returned_old_frame) { yading@11: *timestamp = old_timestamp; yading@11: return end_packet ? 1 : 0; yading@11: } yading@11: yading@11: if (end_packet) { yading@11: int ret; yading@11: ret = ff_rtp_finalize_packet(pkt, &vp8->data, st->index); yading@11: if (ret < 0) yading@11: return ret; yading@11: if (vp8->sequence_dirty) yading@11: pkt->flags |= AV_PKT_FLAG_CORRUPT; yading@11: return 0; yading@11: } yading@11: yading@11: return AVERROR(EAGAIN); yading@11: } yading@11: yading@11: static PayloadContext *vp8_new_context(void) yading@11: { yading@11: PayloadContext *vp8 = av_mallocz(sizeof(PayloadContext)); yading@11: if (!vp8) yading@11: return NULL; yading@11: vp8->sequence_ok = 1; yading@11: return vp8; yading@11: } yading@11: yading@11: static void vp8_free_context(PayloadContext *vp8) yading@11: { yading@11: vp8_free_buffer(vp8); yading@11: av_free(vp8); yading@11: } yading@11: yading@11: static int vp8_need_keyframe(PayloadContext *vp8) yading@11: { yading@11: return vp8->sequence_dirty || !vp8->sequence_ok; yading@11: } yading@11: yading@11: RTPDynamicProtocolHandler ff_vp8_dynamic_handler = { yading@11: .enc_name = "VP8", yading@11: .codec_type = AVMEDIA_TYPE_VIDEO, yading@11: .codec_id = AV_CODEC_ID_VP8, yading@11: .alloc = vp8_new_context, yading@11: .free = vp8_free_context, yading@11: .parse_packet = vp8_handle_packet, yading@11: .need_keyframe = vp8_need_keyframe, yading@11: };