[FFmpeg-devel] [PATCH v3 2/2] rtp: rfc4175: add handler for YCbCr-4:2:2

Rostislav Pehlivanov atomnuker at gmail.com
Wed Mar 1 00:40:38 EET 2017


On 28 February 2017 at 18:39, Damien Riegel <
damien.riegel at savoirfairelinux.com> wrote:

> 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 at savoirfairelinux.com>
> ---
> Changes in v3:
>  - rename rawvideo to rfc4175
>  - set pixel format in codec parameters
>  - add additional check to prevent buffer overflow
>
>  libavformat/Makefile         |   1 +
>  libavformat/rtpdec.c         |   1 +
>  libavformat/rtpdec_formats.h |   1 +
>  libavformat/rtpdec_rfc4175.c | 236 ++++++++++++++++++++++++++++++
> +++++++++++++
>  4 files changed, 239 insertions(+)
>  create mode 100644 libavformat/rtpdec_rfc4175.c
>
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index fc2d76067b..5a1795ccd7 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -55,6 +55,7 @@ OBJS-$(CONFIG_RTPDEC)                    += rdt.o
>                \
>                                              rtpdec_qcelp.o              \
>                                              rtpdec_qdm2.o               \
>                                              rtpdec_qt.o                 \
> +                                            rtpdec_rfc4175.o            \
>                                              rtpdec_svq3.o               \
>                                              rtpdec_vc2hq.o              \
>                                              rtpdec_vp8.o                \
> diff --git a/libavformat/rtpdec.c b/libavformat/rtpdec.c
> index 53cdad7396..4acb1ca629 100644
> --- a/libavformat/rtpdec.c
> +++ b/libavformat/rtpdec.c
> @@ -114,6 +114,7 @@ void ff_register_rtp_dynamic_payload_handlers(void)
>      ff_register_dynamic_payload_handler(&ff_qt_rtp_vid_handler);
>      ff_register_dynamic_payload_handler(&ff_quicktime_rtp_aud_handler);
>      ff_register_dynamic_payload_handler(&ff_quicktime_rtp_vid_handler);
> +    ff_register_dynamic_payload_handler(&ff_rfc4175_rtp_handler);
>      ff_register_dynamic_payload_handler(&ff_svq3_dynamic_handler);
>      ff_register_dynamic_payload_handler(&ff_theora_dynamic_handler);
>      ff_register_dynamic_payload_handler(&ff_vc2hq_dynamic_handler);
> diff --git a/libavformat/rtpdec_formats.h b/libavformat/rtpdec_formats.h
> index 3292a3d265..a436c9d62c 100644
> --- a/libavformat/rtpdec_formats.h
> +++ b/libavformat/rtpdec_formats.h
> @@ -82,6 +82,7 @@ extern RTPDynamicProtocolHandler ff_qt_rtp_aud_handler;
>  extern RTPDynamicProtocolHandler ff_qt_rtp_vid_handler;
>  extern RTPDynamicProtocolHandler ff_quicktime_rtp_aud_handler;
>  extern RTPDynamicProtocolHandler ff_quicktime_rtp_vid_handler;
> +extern RTPDynamicProtocolHandler ff_rfc4175_rtp_handler;
>  extern RTPDynamicProtocolHandler ff_svq3_dynamic_handler;
>  extern RTPDynamicProtocolHandler ff_theora_dynamic_handler;
>  extern RTPDynamicProtocolHandler ff_vc2hq_dynamic_handler;
> diff --git a/libavformat/rtpdec_rfc4175.c b/libavformat/rtpdec_rfc4175.c
> new file mode 100644
> index 0000000000..9e3d520ee3
> --- /dev/null
> +++ b/libavformat/rtpdec_rfc4175.c
> @@ -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 (!strcmp(data->sampling, "YCbCr-4:2:2")) {
>

Use strncmp()


> +        tag = MKTAG('U', 'Y', 'V', 'Y');
>

I don't like to use tag but okay.


> +        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 -1;
> +        }
> +    } else {
> +        return -1;
> +    }
> +
> +    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;
>

Do you need to set the frame size?


> +
> +    return 0;
> +}
> +
> +static int rfc4175_parse_fmtp(AVFormatContext *s, AVStream *stream,
> +                              PayloadContext *data, const char *attr,
> +                              const char *value)
> +{
> +    if (!strcmp(attr, "width"))
> +        data->width = atoi(value);
> +    else if (!strcmp(attr, "height"))
> +        data->height = atoi(value);
> +    else if (!strcmp(attr, "sampling"))
> +        data->sampling = av_strdup(value);
> +    else if (!strcmp(attr, "depth"))
> +        data->depth = atoi(value);
> +
>

strncmp() again, we don't want to take chances


> +    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,
> +};
> --
> 2.11.1
>
> _______________________________________________
> ffmpeg-devel mailing list
> ffmpeg-devel at ffmpeg.org
> http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
>


Any chance the demuxer itself could spit out raw 8 bit frames rather than
BITPACKED packets?
Otherwise fix those and the rest of the code looks okay to me, though
someone else should look at it as well.


More information about the ffmpeg-devel mailing list