You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
865 lines
22 KiB
865 lines
22 KiB
/*****************************************************************************
|
|
* rtp-rawvid.c: RTP raw video decoder
|
|
*****************************************************************************
|
|
* Copyright (C) 2022 Rémi Denis-Courmont
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
|
|
*****************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdbit.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <vlc_common.h>
|
|
#include <vlc_plugin.h>
|
|
#include <vlc_codec.h>
|
|
|
|
#define PLANES_3(bits) \
|
|
uint##bits##_t *restrict p0 = planes[0]; \
|
|
uint##bits##_t *restrict p1 = planes[1]; \
|
|
uint##bits##_t *restrict p2 = planes[2]; \
|
|
(void)0
|
|
|
|
#define PLANES_4(bits) \
|
|
uint##bits##_t *restrict p0 = planes[0]; \
|
|
uint##bits##_t *restrict p1 = planes[1]; \
|
|
uint##bits##_t *restrict p2 = planes[2]; \
|
|
uint##bits##_t *restrict p3 = planes[3]; \
|
|
(void)0
|
|
|
|
/* Read one 8-bit sample from one octet */
|
|
#define READ_8(a) \
|
|
uint_fast8_t a; \
|
|
\
|
|
do { \
|
|
a = *(in++); \
|
|
len--; \
|
|
} while (0)
|
|
|
|
/* Read four 10-bit samples from five octets. */
|
|
#define READ_10(a, b, c, d) \
|
|
uint_fast16_t a, b, c, d; \
|
|
\
|
|
do { \
|
|
uint_fast8_t b0_ = in[0]; \
|
|
uint_fast8_t b1_ = in[1]; \
|
|
uint_fast8_t b2_ = in[2]; \
|
|
uint_fast8_t b3_ = in[3]; \
|
|
uint_fast8_t b4_ = in[4]; \
|
|
\
|
|
a = (b0_ << 2) | (b1_ >> 6); \
|
|
b = ((b1_ << 4) | (b2_ >> 4)) & 0x3ff; \
|
|
c = ((b2_ << 6) | (b3_ >> 2)) & 0x3ff; \
|
|
d = ((b3_ << 8) | (b4_ >> 0)) & 0x3ff; \
|
|
in += 5; \
|
|
len -= 5; \
|
|
} while (0)
|
|
|
|
/* Read two 12-bit samples from three octets. */
|
|
#define READ_12(a, b) \
|
|
uint_fast16_t a, b; \
|
|
\
|
|
do { \
|
|
uint_fast8_t b0_ = in[0]; \
|
|
uint_fast8_t b1_ = in[1]; \
|
|
uint_fast8_t b2_ = in[2]; \
|
|
\
|
|
a = (b0_ << 4) | (b1_ >> 4); \
|
|
b = ((b1_ << 4) | (b2_ >> 0)) & 0xfff; \
|
|
in += 3; \
|
|
len -= 3; \
|
|
} while (0)
|
|
|
|
|
|
/* Read one 16-bit sample from two octets */
|
|
#define READ_16(a) \
|
|
uint_fast16_t a; \
|
|
\
|
|
do { \
|
|
a = GetWBE(in); \
|
|
in += 2; \
|
|
len -= 2; \
|
|
} while (0)
|
|
|
|
#define WRITE_RGB(n) \
|
|
do { \
|
|
*(p0++) = g##n; \
|
|
*(p1++) = b##n; \
|
|
*(p2++) = r##n; \
|
|
} while (0)
|
|
|
|
#define WRITE_RGBA(n) \
|
|
do { \
|
|
*(p0++) = g##n; \
|
|
*(p1++) = b##n; \
|
|
*(p2++) = r##n; \
|
|
*(p3++) = a##n; \
|
|
} while (0)
|
|
|
|
#define WRITE_YUV444(n) \
|
|
do { \
|
|
*(p0++) = y##n; \
|
|
*(p1++) = u##n; \
|
|
*(p2++) = v##n; \
|
|
} while (0)
|
|
|
|
#define WRITE_YUV422(n) \
|
|
do { \
|
|
*(p0++) = y0##n; \
|
|
*(p0++) = y1##n; \
|
|
*(p1++) = u##n; \
|
|
*(p2++) = v##n; \
|
|
} while (0)
|
|
|
|
#define WRITE_YUV420(n) \
|
|
do { \
|
|
*(p0++) = y00##n; \
|
|
*(p0++) = y01##n; \
|
|
*(p3++) = y10##n; \
|
|
*(p3++) = y11##n; \
|
|
*(p1++) = u##n; \
|
|
*(p2++) = v##n; \
|
|
} while (0)
|
|
|
|
#define WRITE_YUV411(n) \
|
|
do { \
|
|
*(p0++) = y0##n; \
|
|
*(p0++) = y1##n; \
|
|
*(p0++) = y2##n; \
|
|
*(p0++) = y3##n; \
|
|
*(p1++) = u##n; \
|
|
*(p2++) = v##n; \
|
|
} while (0)
|
|
|
|
static void decode_rgb_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(r0);
|
|
READ_8(g0);
|
|
READ_8(b0);
|
|
WRITE_RGB(0);
|
|
}
|
|
}
|
|
|
|
static void decode_rgb_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(r0, g0, b0, r1);
|
|
READ_10(g1, b1, r2, g2);
|
|
READ_10(b2, r3, b3, g3);
|
|
WRITE_RGB(0);
|
|
WRITE_RGB(1);
|
|
WRITE_RGB(2);
|
|
WRITE_RGB(3);
|
|
}
|
|
}
|
|
|
|
static void decode_rgb_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(r0, g0);
|
|
READ_12(b0, r1);
|
|
READ_12(g1, b1);
|
|
WRITE_RGB(0);
|
|
WRITE_RGB(1);
|
|
}
|
|
}
|
|
|
|
static void decode_rgb_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(r0);
|
|
READ_16(g0);
|
|
READ_16(b0);
|
|
WRITE_RGB(0);
|
|
}
|
|
}
|
|
|
|
static void decode_rgba_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(r0);
|
|
READ_8(g0);
|
|
READ_8(b0);
|
|
READ_8(a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_rgba_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(r0, g0, b0, a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_rgba_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(r0, g0);
|
|
READ_12(b0, a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_rgba_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(r0);
|
|
READ_16(g0);
|
|
READ_16(b0);
|
|
READ_16(a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgr_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(b0);
|
|
READ_8(g0);
|
|
READ_8(r0);
|
|
WRITE_RGB(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgr_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(b0, g0, r0, b1);
|
|
READ_10(g1, r1, b2, g2);
|
|
READ_10(r2, b3, g3, r3);
|
|
WRITE_RGB(0);
|
|
WRITE_RGB(1);
|
|
WRITE_RGB(2);
|
|
WRITE_RGB(3);
|
|
}
|
|
}
|
|
|
|
static void decode_bgr_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(b0, g0);
|
|
READ_12(r0, b1);
|
|
READ_12(g1, r1);
|
|
WRITE_RGB(0);
|
|
WRITE_RGB(1);
|
|
}
|
|
}
|
|
|
|
static void decode_bgr_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(b0);
|
|
READ_16(g0);
|
|
READ_16(r0);
|
|
WRITE_RGB(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgra_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(b0);
|
|
READ_8(g0);
|
|
READ_8(r0);
|
|
READ_8(a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgra_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(b0, g0, r0, a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgra_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(b0, g0);
|
|
READ_12(r0, a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_bgra_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(b0);
|
|
READ_16(g0);
|
|
READ_16(r0);
|
|
READ_16(a0);
|
|
WRITE_RGBA(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv444_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(u0);
|
|
READ_8(y0);
|
|
READ_8(v0);
|
|
WRITE_YUV444(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv444_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(u0, y0, v0, u1);
|
|
READ_10(y1, v1, u2, y2);
|
|
READ_10(v2, u3, y3, v3);
|
|
WRITE_YUV444(0);
|
|
WRITE_YUV444(1);
|
|
WRITE_YUV444(2);
|
|
WRITE_YUV444(3);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv444_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(u0, y0);
|
|
READ_12(v0, u1);
|
|
READ_12(y1, v1);
|
|
WRITE_YUV444(0);
|
|
WRITE_YUV444(1);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv444_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(u0);
|
|
READ_16(y0);
|
|
READ_16(v0);
|
|
WRITE_YUV444(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv422_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(u0);
|
|
READ_8(y00);
|
|
READ_8(v0);
|
|
READ_8(y10);
|
|
WRITE_YUV422(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv422_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(u0, y00, v0, y10);
|
|
WRITE_YUV422(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv422_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(u0, y00);
|
|
READ_12(v0, y10);
|
|
WRITE_YUV422(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv422_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(u0);
|
|
READ_16(y00);
|
|
READ_16(v0);
|
|
READ_16(y10);
|
|
WRITE_YUV422(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv420_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(y000);
|
|
READ_8(y010);
|
|
READ_8(y100);
|
|
READ_8(y110);
|
|
READ_8(u0);
|
|
READ_8(v0);
|
|
WRITE_YUV420(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv420_10(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_10(y000, y010, y100, y110);
|
|
READ_10(u0, v0, y001, y011);
|
|
READ_10(y101, y111, u1, v1);
|
|
WRITE_YUV420(0);
|
|
WRITE_YUV420(1);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv420_12(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_12(y000, y010);
|
|
READ_12(y100, y110);
|
|
READ_12(u0, v0);
|
|
WRITE_YUV420(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv420_16(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_4(16);
|
|
|
|
while (len > 0) {
|
|
READ_16(y000);
|
|
READ_16(y010);
|
|
READ_16(y100);
|
|
READ_16(y110);
|
|
READ_16(u0);
|
|
READ_16(v0);
|
|
WRITE_YUV420(0);
|
|
}
|
|
}
|
|
|
|
static void decode_yuv411_8(void *restrict *restrict planes,
|
|
const unsigned char *restrict in, size_t len)
|
|
{
|
|
PLANES_3(8);
|
|
|
|
while (len > 0) {
|
|
READ_8(u0);
|
|
READ_8(y00);
|
|
READ_8(y10);
|
|
READ_8(v0);
|
|
READ_8(y20);
|
|
READ_8(y30);
|
|
WRITE_YUV411(0);
|
|
}
|
|
}
|
|
|
|
typedef void (*vlc_rtp_video_raw_cb)(void *restrict * restrict,
|
|
const unsigned char * restrict, size_t);
|
|
|
|
struct vlc_rtp_video_raw_dec {
|
|
unsigned int pgroup;
|
|
bool half_height_uv;
|
|
vlc_rtp_video_raw_cb decode_line;
|
|
picture_t *pic;
|
|
};
|
|
|
|
static int Decode(decoder_t *dec, vlc_frame_t *block)
|
|
{
|
|
struct vlc_rtp_video_raw_dec *sys = dec->p_sys;
|
|
picture_t *pic = sys->pic;
|
|
bool continuation;
|
|
|
|
if (block == NULL) {
|
|
/* Draining */
|
|
if (pic != NULL) /* Send incomplete picture */
|
|
decoder_QueueVideo(dec, pic);
|
|
sys->pic = NULL;
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
if ((block->i_flags & VLC_FRAME_FLAG_DISCONTINUITY)
|
|
&& pic != NULL && pic->date != block->i_pts) {
|
|
/* Ideally, the EOS is set on the last block for a picture.
|
|
* This manual check is necessary to deal with packet loss. */
|
|
decoder_QueueVideo(dec, pic);
|
|
pic = sys->pic = NULL;
|
|
}
|
|
|
|
if (pic == NULL) {
|
|
pic = decoder_NewPicture(dec);
|
|
|
|
if (pic == NULL) {
|
|
block_Release(block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
pic->date = block->i_pts;
|
|
pic->b_progressive = true; /* TODO: interlacing */
|
|
sys->pic = pic;
|
|
}
|
|
|
|
const unsigned char *in = block->p_buffer;
|
|
size_t inlen = block->i_buffer;
|
|
const unsigned int width = dec->fmt_out.video.i_width;
|
|
const unsigned int height = dec->fmt_out.video.i_height;
|
|
|
|
do {
|
|
if (unlikely(inlen < 6)) {
|
|
corrupt: msg_Err(dec, "corrupt packet, %zu bytes remaining", inlen);
|
|
break;
|
|
}
|
|
|
|
uint_fast16_t length = GetWBE(in);
|
|
uint_fast16_t lineno = GetWBE(in + 2);
|
|
uint_fast16_t offset = GetWBE(in + 4);
|
|
|
|
lineno &= 0x7fff; /* TODO: interlacing */
|
|
continuation = (offset & 0x8000) != 0;
|
|
offset &= 0x7fff;
|
|
|
|
in += 6;
|
|
inlen -= 6;
|
|
|
|
div_t d = div(length, sys->pgroup);
|
|
|
|
if (inlen < length /* input buffer underflow */
|
|
|| d.rem != 0 /* length must be a multiple of pgroup size */
|
|
|| offset + d.quot >= width /* output scanline overflow */
|
|
|| lineno >= height /* output picture overflow */)
|
|
goto corrupt;
|
|
|
|
void *restrict planes[4];
|
|
|
|
if (sys->half_height_uv) {
|
|
/* For I420, treat the odd Y lines as the 4th plane */
|
|
if (unlikely(lineno & 1))
|
|
goto corrupt; /* line number must always be even */
|
|
|
|
assert(pic->i_planes <= 3);
|
|
planes[0] = pic->p[0].p_pixels + lineno * pic->p[0].i_pitch
|
|
+ offset * pic->p[0].i_pixel_pitch;
|
|
planes[3] = ((unsigned char *)planes[0]) + pic->p[0].i_pitch;
|
|
lineno /= 2;
|
|
|
|
for (int i = 1; i < pic->i_planes; i++) {
|
|
plane_t *p = &pic->p[i];
|
|
|
|
planes[i] = p->p_pixels + lineno * p->i_pitch
|
|
+ offset * p->i_pixel_pitch;
|
|
}
|
|
} else {
|
|
for (int i = 0; i < pic->i_planes; i++) {
|
|
plane_t *p = &pic->p[i];
|
|
|
|
planes[i] = p->p_pixels + lineno * p->i_pitch
|
|
+ offset * p->i_pixel_pitch;
|
|
}
|
|
}
|
|
|
|
sys->decode_line(planes, in, length);
|
|
} while (continuation);
|
|
|
|
if (block->i_flags & VLC_FRAME_FLAG_END_OF_SEQUENCE) {
|
|
decoder_QueueVideo(dec, pic);
|
|
sys->pic = NULL;
|
|
}
|
|
|
|
block_Release(block);
|
|
return VLCDEC_SUCCESS;
|
|
}
|
|
|
|
static void Close(vlc_object_t *obj)
|
|
{
|
|
decoder_t *dec = (decoder_t *)obj;
|
|
struct vlc_rtp_video_raw_dec *sys = dec->p_sys;
|
|
|
|
if (sys->pic != NULL)
|
|
picture_Release(sys->pic);
|
|
}
|
|
|
|
struct vlc_rtp_video_raw_format {
|
|
vlc_fourcc_t fourcc;
|
|
vlc_rtp_video_raw_cb line_cb;
|
|
};
|
|
|
|
/**
|
|
* RTP video/raw sampling
|
|
*
|
|
* This defines the list of all supported (per component) bit depths.
|
|
*/
|
|
struct vlc_rtp_video_raw_sampling {
|
|
struct vlc_rtp_video_raw_format depth8;
|
|
struct vlc_rtp_video_raw_format depth10;
|
|
struct vlc_rtp_video_raw_format depth12;
|
|
struct vlc_rtp_video_raw_format depth16;
|
|
};
|
|
|
|
/**
|
|
* RTP video/raw samplings
|
|
*
|
|
* This type defines the list of all support RTP video/raw samplings.
|
|
* \note This is purposedly a structure rather than an array so that CPU
|
|
* optimisations can readily cherry-pick which samplings to optimise.
|
|
*/
|
|
struct vlc_rtp_video_raw_samplings {
|
|
struct vlc_rtp_video_raw_sampling rgb;
|
|
struct vlc_rtp_video_raw_sampling rgba;
|
|
struct vlc_rtp_video_raw_sampling bgr;
|
|
struct vlc_rtp_video_raw_sampling bgra;
|
|
struct vlc_rtp_video_raw_sampling yuv444;
|
|
struct vlc_rtp_video_raw_sampling yuv422;
|
|
struct vlc_rtp_video_raw_sampling yuv420;
|
|
struct vlc_rtp_video_raw_sampling yuv411;
|
|
};
|
|
|
|
#ifdef WORDS_BIGENDIAN
|
|
#define NE(x) x##B
|
|
#else
|
|
#define NE(x) x##L
|
|
#endif
|
|
|
|
static const struct vlc_rtp_video_raw_samplings samplings = {
|
|
.rgb = {
|
|
{ VLC_CODEC_GBR_PLANAR, decode_rgb_8, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_10), decode_rgb_10, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_12), decode_rgb_12, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_16), decode_rgb_16, },
|
|
},
|
|
.rgba = {
|
|
{ VLC_CODEC_GBR_PLANAR, decode_rgba_8, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_10), decode_rgba_10, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_12), decode_rgba_12, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_16), decode_rgba_16, },
|
|
},
|
|
.bgr = {
|
|
{ VLC_CODEC_GBR_PLANAR, decode_bgr_8, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_10), decode_bgr_10, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_12), decode_bgr_12, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_16), decode_bgr_16, },
|
|
},
|
|
.bgra = {
|
|
{ VLC_CODEC_GBR_PLANAR, decode_bgra_8, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_10), decode_bgra_10, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_12), decode_bgra_12, },
|
|
{ NE(VLC_CODEC_GBR_PLANAR_16), decode_bgra_16, },
|
|
},
|
|
.yuv444 = {
|
|
{ VLC_CODEC_I444, decode_yuv444_8, },
|
|
{ NE(VLC_CODEC_I444_10), decode_yuv444_10, },
|
|
{ NE(VLC_CODEC_I444_12), decode_yuv444_12, },
|
|
{ NE(VLC_CODEC_I444_16), decode_yuv444_16, },
|
|
},
|
|
.yuv422 = {
|
|
{ VLC_CODEC_I422, decode_yuv422_8, },
|
|
{ NE(VLC_CODEC_I422_10), decode_yuv422_10, },
|
|
{ NE(VLC_CODEC_I422_12), decode_yuv422_12, },
|
|
{ NE(VLC_CODEC_I422_16), decode_yuv422_16, },
|
|
},
|
|
.yuv420 = {
|
|
{ VLC_CODEC_I420, decode_yuv420_8, },
|
|
{ NE(VLC_CODEC_I420_10), decode_yuv420_10, },
|
|
{ NE(VLC_CODEC_I420_12), decode_yuv420_12, },
|
|
{ NE(VLC_CODEC_I420_16), decode_yuv420_16, },
|
|
},
|
|
.yuv411 = {
|
|
{ VLC_CODEC_I411, decode_yuv411_8, },
|
|
/* High-definition 4:1:1 not supported */
|
|
{ 0, NULL }, { 0, NULL }, { 0, NULL }
|
|
},
|
|
};
|
|
|
|
static int Open(vlc_object_t *obj)
|
|
{
|
|
decoder_t *dec = (decoder_t *)obj;
|
|
const char *sname = dec->fmt_in->p_extra;
|
|
|
|
if (dec->fmt_in->i_codec != VLC_CODEC_RTP_VIDEO_RAW)
|
|
return VLC_ENOTSUP;
|
|
if (dec->fmt_in->i_extra <= 0 || sname[dec->fmt_in->i_extra - 1] != '\0')
|
|
return VLC_EINVAL;
|
|
|
|
/* Sampling is supplied as extra data, bit depth as level */
|
|
unsigned int depth = dec->fmt_in->i_level;
|
|
const struct vlc_rtp_video_raw_sampling *sampling;
|
|
unsigned int spmp; /* samples per macropixel */
|
|
bool half_height_uv = false;
|
|
|
|
if (strcmp(sname, "RGB") == 0) {
|
|
sampling = &samplings.rgb;
|
|
spmp = 3;
|
|
} else if (strcmp(sname, "RGBA") == 0) {
|
|
sampling = &samplings.rgba;
|
|
spmp = 4;
|
|
} else if (strcmp(sname, "BGR") == 0) {
|
|
sampling = &samplings.bgr;
|
|
spmp = 3;
|
|
} else if (strcmp(sname, "BGRA") == 0) {
|
|
sampling = &samplings.bgra;
|
|
spmp = 4;
|
|
} else if (strcmp(sname, "YCbCr-4:4:4") == 0) {
|
|
sampling = &samplings.yuv444;
|
|
spmp = 3;
|
|
} else if (strcmp(sname, "YCbCr-4:2:2") == 0) {
|
|
sampling = &samplings.yuv422;
|
|
spmp = 4;
|
|
} else if (strcmp(sname, "YCbCr-4:2:0") == 0) {
|
|
sampling = &samplings.yuv420;
|
|
spmp = 6;
|
|
half_height_uv = true;
|
|
} else if (strcmp(sname, "YCbCr-4:1:1") == 0) {
|
|
sampling = &samplings.yuv411;
|
|
spmp = 6;
|
|
} else {
|
|
msg_Err(obj, "unknown RTP video sampling %s", sname);
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
const struct vlc_rtp_video_raw_format *format;
|
|
|
|
switch (depth) {
|
|
case 8:
|
|
format = &sampling->depth8;
|
|
break;
|
|
case 10:
|
|
format = &sampling->depth10;
|
|
break;
|
|
case 12:
|
|
format = &sampling->depth12;
|
|
break;
|
|
case 16:
|
|
format = &sampling->depth16;
|
|
break;
|
|
default:
|
|
msg_Err(obj, "unsupported RTP video bit depth %u", depth);
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
if (format->fourcc == 0) {
|
|
msg_Err(obj, "unimplemented RTP video format %u-bit %s",
|
|
depth, sname);
|
|
return VLC_ENOTSUP;
|
|
}
|
|
|
|
struct vlc_rtp_video_raw_dec *sys = vlc_obj_malloc(obj, sizeof (*sys));
|
|
if (unlikely(sys == NULL))
|
|
return VLC_ENOMEM;
|
|
|
|
es_format_Copy(&dec->fmt_out, dec->fmt_in);
|
|
dec->fmt_out.i_codec = format->fourcc;
|
|
dec->fmt_out.video.i_chroma = format->fourcc;
|
|
|
|
int ret = decoder_UpdateVideoFormat(dec);
|
|
if (ret != VLC_SUCCESS)
|
|
return ret;
|
|
|
|
unsigned int bpmp = depth * spmp;
|
|
|
|
/* pixel group size equals LCM(depth * spmp, 8) bits */
|
|
sys->pgroup = bpmp >> ((bpmp % 8) ? stdc_trailing_zeros(bpmp) : 3);
|
|
sys->half_height_uv = half_height_uv;
|
|
sys->decode_line = format->line_cb;
|
|
sys->pic = NULL;
|
|
dec->p_sys = sys;
|
|
dec->pf_decode = Decode;
|
|
return VLC_SUCCESS;
|
|
}
|
|
|
|
vlc_module_begin()
|
|
set_description(N_("RTP raw video decoder"))
|
|
set_capability("video decoder", 50)
|
|
set_subcategory(SUBCAT_INPUT_VCODEC)
|
|
set_callbacks(Open, Close)
|
|
vlc_module_end()
|
|
|