mirror of https://github.com/FFmpeg/FFmpeg.git
This adds partial support for the RFC 4175 (raw video over RTP). The only supported formats are the YCbCr-4:2:2 8 bit because it's natively supported by FFmpeg with pixel format UYVY, and 10 bit which requires the vrawdepay codec to convert the payload in a format handled by FFmpeg. Signed-off-by: Damien Riegel <damien.riegel@savoirfairelinux.com> Reviewed-by: Rostislav Pehlivanov <atomnuker@gmail.com>pull/117/merge
parent
549acc9995
commit
01718dc0df
4 changed files with 239 additions and 0 deletions
@ -0,0 +1,236 @@ |
||||
/*
|
||||
* RTP Depacketization of RAW video (TR-03) |
||||
* Copyright (c) 2016 Savoir-faire Linux, Inc |
||||
* |
||||
* 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 |
||||
*/ |
||||
|
||||
/* Development sponsored by CBC/Radio-Canada */ |
||||
|
||||
#include "avio_internal.h" |
||||
#include "rtpdec_formats.h" |
||||
#include "libavutil/avstring.h" |
||||
#include "libavutil/pixdesc.h" |
||||
|
||||
struct PayloadContext { |
||||
char *sampling; |
||||
int depth; |
||||
int width; |
||||
int height; |
||||
|
||||
uint8_t *frame; |
||||
unsigned int frame_size; |
||||
unsigned int pgroup; /* size of the pixel group in bytes */ |
||||
unsigned int xinc; |
||||
|
||||
uint32_t timestamp; |
||||
}; |
||||
|
||||
static int rfc4175_parse_format(AVStream *stream, PayloadContext *data) |
||||
{ |
||||
enum AVPixelFormat pixfmt = AV_PIX_FMT_NONE; |
||||
int bits_per_sample = 0; |
||||
int tag = 0; |
||||
|
||||
if (!strncmp(data->sampling, "YCbCr-4:2:2", 11)) { |
||||
tag = MKTAG('U', 'Y', 'V', 'Y'); |
||||
data->xinc = 2; |
||||
|
||||
if (data->depth == 8) { |
||||
data->pgroup = 4; |
||||
bits_per_sample = 16; |
||||
pixfmt = AV_PIX_FMT_UYVY422; |
||||
} else if (data->depth == 10) { |
||||
data->pgroup = 5; |
||||
bits_per_sample = 20; |
||||
pixfmt = AV_PIX_FMT_YUV422P10; |
||||
} else { |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
} else { |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
|
||||
stream->codecpar->format = pixfmt; |
||||
stream->codecpar->codec_tag = tag; |
||||
stream->codecpar->bits_per_coded_sample = bits_per_sample; |
||||
data->frame_size = data->width * data->height * data->pgroup / data->xinc; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream, |
||||
PayloadContext *data, const char *attr, |
||||
const char *value) |
||||
{ |
||||
if (!strncmp(attr, "width", 5)) |
||||
data->width = atoi(value); |
||||
else if (!strncmp(attr, "height", 6)) |
||||
data->height = atoi(value); |
||||
else if (!strncmp(attr, "sampling", 8)) |
||||
data->sampling = av_strdup(value); |
||||
else if (!strncmp(attr, "depth", 5)) |
||||
data->depth = atoi(value); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int rfc4175_parse_sdp_line(AVFormatContext *s, int st_index, |
||||
PayloadContext *data, const char *line) |
||||
{ |
||||
const char *p; |
||||
|
||||
if (st_index < 0) |
||||
return 0; |
||||
|
||||
if (av_strstart(line, "fmtp:", &p)) { |
||||
AVStream *stream = s->streams[st_index]; |
||||
int ret = ff_parse_fmtp(s, stream, data, p, rfc4175_parse_fmtp); |
||||
|
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
|
||||
if (!data->sampling || !data->depth || !data->width || !data->height) |
||||
return -1; |
||||
|
||||
stream->codecpar->width = data->width; |
||||
stream->codecpar->height = data->height; |
||||
|
||||
ret = rfc4175_parse_format(stream, data); |
||||
av_freep(&data->sampling); |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int rfc4175_finalize_packet(PayloadContext *data, AVPacket *pkt, |
||||
int stream_index) |
||||
{ |
||||
int ret; |
||||
|
||||
pkt->stream_index = stream_index; |
||||
ret = av_packet_from_data(pkt, data->frame, data->frame_size); |
||||
if (ret < 0) { |
||||
av_freep(&data->frame); |
||||
} |
||||
|
||||
data->frame = NULL; |
||||
|
||||
return ret; |
||||
} |
||||
|
||||
static int rfc4175_handle_packet(AVFormatContext *ctx, PayloadContext *data, |
||||
AVStream *st, AVPacket *pkt, uint32_t *timestamp, |
||||
const uint8_t * buf, int len, |
||||
uint16_t seq, int flags) |
||||
{ |
||||
int length, line, offset, cont; |
||||
const uint8_t *headers = buf + 2; /* skip extended seqnum */ |
||||
const uint8_t *payload = buf + 2; |
||||
int payload_len = len - 2; |
||||
int missed_last_packet = 0; |
||||
|
||||
uint8_t *dest; |
||||
|
||||
if (*timestamp != data->timestamp) { |
||||
if (data->frame) { |
||||
/*
|
||||
* if we're here, it means that two RTP packets didn't have the |
||||
* same timestamp, which is a sign that they were packets from two |
||||
* different frames, but we didn't get the flag RTP_FLAG_MARKER on |
||||
* the first one of these frames (last packet of a frame). |
||||
* Finalize the previous frame anyway by filling the AVPacket. |
||||
*/ |
||||
av_log(ctx, AV_LOG_ERROR, "Missed previous RTP Marker\n"); |
||||
missed_last_packet = 1; |
||||
rfc4175_finalize_packet(data, pkt, st->index); |
||||
} |
||||
|
||||
data->frame = av_malloc(data->frame_size); |
||||
|
||||
data->timestamp = *timestamp; |
||||
|
||||
if (!data->frame) { |
||||
av_log(ctx, AV_LOG_ERROR, "Out of memory.\n"); |
||||
return AVERROR(ENOMEM); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* looks for the 'Continuation bit' in scan lines' headers |
||||
* to find where data start |
||||
*/ |
||||
do { |
||||
if (payload_len < 6) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
cont = payload[4] & 0x80; |
||||
payload += 6; |
||||
payload_len -= 6; |
||||
} while (cont); |
||||
|
||||
/* and now iterate over every scan lines */ |
||||
do { |
||||
int copy_offset; |
||||
|
||||
if (payload_len < data->pgroup) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
length = (headers[0] << 8) | headers[1]; |
||||
line = ((headers[2] & 0x7f) << 8) | headers[3]; |
||||
offset = ((headers[4] & 0x7f) << 8) | headers[5]; |
||||
cont = headers[4] & 0x80; |
||||
headers += 6; |
||||
|
||||
if (length % data->pgroup) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
if (length > payload_len) |
||||
length = payload_len; |
||||
|
||||
/* prevent ill-formed packets to write after buffer's end */ |
||||
copy_offset = (line * data->width + offset) * data->pgroup / data->xinc; |
||||
if (copy_offset + length > data->frame_size) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
dest = data->frame + copy_offset; |
||||
memcpy(dest, payload, length); |
||||
|
||||
payload += length; |
||||
payload_len -= length; |
||||
} while (cont); |
||||
|
||||
if ((flags & RTP_FLAG_MARKER)) { |
||||
return rfc4175_finalize_packet(data, pkt, st->index); |
||||
} else if (missed_last_packet) { |
||||
return 0; |
||||
} |
||||
|
||||
return AVERROR(EAGAIN); |
||||
} |
||||
|
||||
RTPDynamicProtocolHandler ff_rfc4175_rtp_handler = { |
||||
.enc_name = "raw", |
||||
.codec_type = AVMEDIA_TYPE_VIDEO, |
||||
.codec_id = AV_CODEC_ID_BITPACKED, |
||||
.priv_data_size = sizeof(PayloadContext), |
||||
.parse_sdp_a_line = rfc4175_parse_sdp_line, |
||||
.parse_packet = rfc4175_handle_packet, |
||||
}; |
Loading…
Reference in new issue