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