Skip to content

Commit

Permalink
Support different chroma formats and bit depths with libde265 1.0.0, …
Browse files Browse the repository at this point in the history
…added support for VLC 3.0.
  • Loading branch information
fancycode committed Jul 2, 2015
1 parent facfa3b commit 5e6ddc2
Show file tree
Hide file tree
Showing 3 changed files with 258 additions and 20 deletions.
5 changes: 5 additions & 0 deletions Makefile.am
Expand Up @@ -12,6 +12,11 @@ EXTRA_DIST = \

lib_LTLIBRARIES = libde265_plugin.la

CFLAGS = -DVLC_MODULE_COPYRIGHT="\"Copyright \(c) struktur AG\""
CFLAGS += -DVLC_MODULE_LICENSE=\"LGPL\"



libde265_plugin_la_CFLAGS = -DMODULE_STRING=\"de265\" $(libde265_CFLAGS) $(vlc_CFLAGS)
libde265_plugin_la_CPPFLAGS = -DMODULE_STRING=\"de265\" $(libde265_CFLAGS) $(vlc_CFLAGS)
libde265_plugin_la_LIBADD = $(libde265_LIBS) $(vlc_LIBS)
Expand Down
4 changes: 4 additions & 0 deletions configure.ac
Expand Up @@ -48,6 +48,10 @@ PKG_CHECK_MODULES([vlc220], [vlc-plugin >= 2.2],
[AC_DEFINE([HAVE_VLC_HEVC_TS], [1], [Whether VLC with HEVC in MPEG-TS support was found.])
have_vlc_hevc_ts="yes" have_vlc_hevc_mp4="yes"],
[have_vlc_hevc_ts="no" have_vlc_hevc_mp4="no"])
PKG_CHECK_MODULES([vlc300], [vlc-plugin >= 3.0],
[AC_DEFINE([HAVE_VLC_REFCOUNT_PICTURE], [1], [Whether VLC refcounts pictures directly.])
have_vlc_refcount_picture="yes"],
[have_vlc_refcount_picture="no"])

AM_CONDITIONAL([HAVE_VLC_HEVC], [test "x$have_vlc_hevc" != "xno"])
AM_CONDITIONAL([HAVE_VLC_HEVC_TS], [test "x$have_vlc_hevc_ts" != "xno"])
Expand Down
269 changes: 249 additions & 20 deletions src/codec/libde265dec.c
Expand Up @@ -27,6 +27,8 @@
# include "config.h"
#endif

#include <assert.h>

#include <vlc_common.h>
#include <vlc_plugin.h>
#include <vlc_codec.h>
Expand Down Expand Up @@ -70,6 +72,19 @@
static int Open(vlc_object_t *);
static void Close(vlc_object_t *);

#define CODEC_UNKNOWN VLC_FOURCC(0xff, 0xff, 0xff, 0xff)

#if LIBDE265_NUMERIC_VERSION < 0x01000000
// libde265 < 1.0 only supported 8 bits per pixel
#define de265_get_bits_per_pixel(image, plane) 8
#endif

#ifdef HAVE_VLC_REFCOUNT_PICTURE
#define decoder_DeletePicture(decoder, picture) picture_Release(picture)
#define decoder_LinkPicture(decoder, picture) picture_Hold(picture)
#define decoder_UnlinkPicture(decoder, picture) picture_Release(picture)
#endif

/*****************************************************************************
* Module descriptor
*****************************************************************************/
Expand Down Expand Up @@ -114,6 +129,130 @@ struct picture_ref_t
picture_t *picture;
};

static inline enum de265_chroma ImageFormatToChroma(enum de265_image_format format) {
switch (format) {
case de265_image_format_mono8:
return de265_chroma_mono;
case de265_image_format_YUV420P8:
return de265_chroma_420;
case de265_image_format_YUV422P8:
return de265_chroma_422;
case de265_image_format_YUV444P8:
return de265_chroma_444;
default:
assert(false);
return 0;
}
}

static vlc_fourcc_t GetVlcCodec(decoder_t *dec, enum de265_chroma chroma, int bits_per_pixel) {
vlc_fourcc_t result = CODEC_UNKNOWN;
switch (chroma) {
case de265_chroma_mono:
result = VLC_CODEC_GREY;
break;
case de265_chroma_420:
switch (bits_per_pixel) {
case 8:
result = VLC_CODEC_I420;
break;
case 9:
result = VLC_CODEC_I420_9L;
break;
case 10:
result = VLC_CODEC_I420_10L;
break;
default:
if (bits_per_pixel > 10 && bits_per_pixel <= 16) {
#ifdef VLC_CODEC_I420_16L
result = VLC_CODEC_I420_16L;
#else
result = VLC_CODEC_I420_10L;
#endif
} else {
msg_Err(dec, "Unsupported output colorspace %d with %d bits per pixel",
chroma, bits_per_pixel);
}
break;
}
break;
case de265_chroma_422:
switch (bits_per_pixel) {
case 8:
result = VLC_CODEC_I422;
break;
case 9:
result = VLC_CODEC_I422_9L;
break;
case 10:
result = VLC_CODEC_I422_10L;
break;
default:
if (bits_per_pixel > 10 && bits_per_pixel <= 16) {
#ifdef VLC_CODEC_I422_16L
result = VLC_CODEC_I422_16L;
#else
result = VLC_CODEC_I422_10L;
#endif
} else {
msg_Err(dec, "Unsupported output colorspace %d with %d bits per pixel",
chroma, bits_per_pixel);
}
break;
}
break;
case de265_chroma_444:
switch (bits_per_pixel) {
case 8:
result = VLC_CODEC_I444;
break;
case 9:
result = VLC_CODEC_I444_9L;
break;
case 10:
result = VLC_CODEC_I444_10L;
break;
default:
if (bits_per_pixel > 10 && bits_per_pixel <= 16) {
#ifdef VLC_CODEC_I444_16L
result = VLC_CODEC_I444_16L;
#else
result = VLC_CODEC_I444_10L;
#endif
} else {
msg_Err(dec, "Unsupported output colorspace %d with %d bits per pixel",
chroma, bits_per_pixel);
}
break;
}
break;
default:
msg_Err(dec, "Unsupported output colorspace %d",
chroma);
break;
}
return result;
}

static bool IsCodec16Bit(vlc_fourcc_t codec) {
switch (codec) {
#ifdef VLC_CODEC_I420_16L
case VLC_CODEC_I420_16L:
return true;
#endif
#ifdef VLC_CODEC_I422_16L
case VLC_CODEC_I422_16L:
return true;
#endif
#ifdef VLC_CODEC_I444_16L
case VLC_CODEC_I444_16L:
return true;
#endif
default:
return false;
}
}

/*****************************************************************************
* SetDecodeRation: tell the decoder to decode only a percentage of the framerate
*****************************************************************************/
Expand Down Expand Up @@ -357,11 +496,6 @@ static picture_t *Decode(decoder_t *dec, block_t **pp_block)
return NULL;
}

if (de265_get_chroma_format(image) != de265_chroma_420) {
msg_Err(dec, "Unsupported output colorspace %d", de265_get_chroma_format(image));
return NULL;
}

if (use_decoder_pts) {
pts = de265_get_image_PTS(image);
}
Expand All @@ -382,7 +516,22 @@ static picture_t *Decode(decoder_t *dec, block_t **pp_block)
}
} while (!drawpicture);

int bits_per_pixel = __MAX(__MAX(de265_get_bits_per_pixel(image, 0),
de265_get_bits_per_pixel(image, 1)),
de265_get_bits_per_pixel(image, 2));

vlc_fourcc_t chroma = GetVlcCodec(dec,
de265_get_chroma_format(image),
bits_per_pixel);
if (chroma == CODEC_UNKNOWN) {
return NULL;
}

dec->fmt_out.i_codec = chroma;

video_format_t *v = &dec->fmt_out.video;
v->i_chroma = chroma;

int width = de265_get_image_width(image, 0);
int height = de265_get_image_height(image, 0);

Expand All @@ -406,17 +555,68 @@ static picture_t *Decode(decoder_t *dec, block_t **pp_block)
if (!pic)
return NULL;

const vlc_chroma_description_t *vlc_chroma = vlc_fourcc_GetChromaDescription(chroma);
assert(vlc_chroma != NULL);

int max_bits_per_pixel = vlc_chroma->pixel_bits;
for (int plane = 0; plane < pic->i_planes; plane++ ) {
int src_stride;
const uint8_t *src = de265_get_image_plane(image, plane, &src_stride);
int plane_bits_per_pixel = de265_get_bits_per_pixel(image, plane);
int dst_stride = pic->p[plane].i_pitch;
uint8_t *dst = pic->p[plane].p_pixels;

int size = __MIN( src_stride, dst_stride );
for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
memcpy( dst, src, size );
src += src_stride;
dst += dst_stride;
if (plane_bits_per_pixel > max_bits_per_pixel) {
// More bits per pixel in this plane than supported by the VLC output format
int shift = (plane_bits_per_pixel - max_bits_per_pixel);
for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
uint16_t *s = (uint16_t *) src;
uint16_t *d = (uint16_t *) dst;
for (int pos=0; pos<size/2; pos++) {
*d = *s >> shift;
d++;
s++;
}
src += src_stride;
dst += dst_stride;
}
} else if (plane_bits_per_pixel < max_bits_per_pixel && plane_bits_per_pixel > 8) {
// Less bits per pixel in this plane than the rest of the picture
// but more than 8bpp.
int shift = (max_bits_per_pixel - plane_bits_per_pixel);
for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
uint16_t *s = (uint16_t *) src;
uint16_t *d = (uint16_t *) dst;
for (int pos=0; pos<size/2; pos++) {
*d = *s << shift;
d++;
s++;
}
src += src_stride;
dst += dst_stride;
}
} else if (plane_bits_per_pixel < max_bits_per_pixel && plane_bits_per_pixel == 8) {
// 8 bits per pixel in this plane, which is less than the rest of the picture.
int shift = (max_bits_per_pixel - plane_bits_per_pixel);
for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
uint8_t *s = (uint8_t *) src;
uint16_t *d = (uint16_t *) dst;
for (int pos=0; pos<size; pos++) {
*d = *s << shift;
d++;
s++;
}
src += src_stride;
dst += dst_stride;
}
} else {
// Bits per pixel of image match output format.
for( int line = 0; line < pic->p[plane].i_visible_lines; line++ ) {
memcpy( dst, src, size );
src += src_stride;
dst += dst_stride;
}
}
}
}
Expand Down Expand Up @@ -444,33 +644,62 @@ static void ReleasePictureRef(struct picture_ref_t *ref)
/*****************************************************************************
* GetPicture: create a vlc picture that can be used for direct rendering
*****************************************************************************/
static picture_t *GetPicture(decoder_t *dec, struct de265_image_spec* spec)
static picture_t *GetPicture(decoder_t *dec, struct de265_image_spec* spec, struct de265_image *image)
{
decoder_sys_t *sys = dec->p_sys;
int width = spec->width;
int width = (spec->width + spec->alignment - 1) / spec->alignment * spec->alignment;
int height = spec->height;

if (width % spec->alignment) {
width += spec->alignment - (width % spec->alignment);
}
if (width == 0 || height == 0 || width > 8192 || height > 8192) {
msg_Err(dec, "Invalid frame size %dx%d.", width, height);
return NULL;
}

const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(dec->fmt_out.video.i_chroma);
if (de265_get_bits_per_pixel(image, 0) != de265_get_bits_per_pixel(image, 1) ||
de265_get_bits_per_pixel(image, 0) != de265_get_bits_per_pixel(image, 2) ||
de265_get_bits_per_pixel(image, 1) != de265_get_bits_per_pixel(image, 2)) {
if (sys->direct_rendering_used != 0) {
msg_Dbg(dec, "input format has multiple bits per pixel (%d/%d/d)",
de265_get_bits_per_pixel(image, 0),
de265_get_bits_per_pixel(image, 1),
de265_get_bits_per_pixel(image, 2));
}
return NULL;
}

int bits_per_pixel = de265_get_bits_per_pixel(image, 0);
vlc_fourcc_t chroma = GetVlcCodec(dec,
ImageFormatToChroma(spec->format),
bits_per_pixel);
if (chroma == CODEC_UNKNOWN) {
// Unsupported chroma format.
return NULL;
}

const vlc_chroma_description_t *dsc = vlc_fourcc_GetChromaDescription(chroma);
assert(dsc != NULL);
if (dsc->pixel_bits < bits_per_pixel) {
if (sys->direct_rendering_used != 0) {
msg_Dbg(dec, "output format doesn't provide enough bits per pixel (%d/%d)",
dsc->pixel_bits, bits_per_pixel);
}
return NULL;
}

for (unsigned int i=0; dsc && i<dsc->plane_count; i++) {
int alignment = spec->alignment * dsc->p[i].w.den;
int aligned_width = (width + alignment - 1) / alignment * alignment;
if (width != aligned_width) {
int plane_width = width * dsc->p[i].w.num / dsc->p[i].w.den;
int aligned_width = (plane_width + spec->alignment - 1) / spec->alignment * spec->alignment;;
if (plane_width != aligned_width) {
if (sys->direct_rendering_used != 0) {
msg_Dbg(dec, "plane %d: aligned width doesn't match (%d/%d)",
i, width, aligned_width);
i, plane_width, aligned_width);
}
return NULL;
}
}

dec->fmt_out.i_codec = chroma;
dec->fmt_out.video.i_chroma = chroma;
dec->fmt_out.video.i_width = width;
dec->fmt_out.video.i_height = height;

Expand Down Expand Up @@ -537,7 +766,7 @@ static int GetBuffer(de265_decoder_context* ctx, struct de265_image_spec* spec,
decoder_t *dec = (decoder_t *) userdata;
decoder_sys_t *sys = dec->p_sys;

picture_t *pic = GetPicture(dec, spec);
picture_t *pic = GetPicture(dec, spec, img);
if (pic == NULL) {
if (sys->direct_rendering_used != 0) {
msg_Warn(dec, "disabling direct rendering");
Expand Down

0 comments on commit 5e6ddc2

Please sign in to comment.