From ada2d3874e353d8a16eecdd539e31a304f2b4a2b Mon Sep 17 00:00:00 2001 From: Martin Piatka <445597@mail.muni.cz> Date: Mon, 25 Feb 2019 14:02:28 +0100 Subject: [PATCH] Cineform: 12-bit decoding --- src/video_codec.c | 225 ++++++++++++++++++++++++++++++ src/video_codec.h | 1 + src/video_decompress/cineform.cpp | 134 ++++++++++++++++-- 3 files changed, 352 insertions(+), 8 deletions(-) diff --git a/src/video_codec.c b/src/video_codec.c index 3c8994078..19a0d8c68 100644 --- a/src/video_codec.c +++ b/src/video_codec.c @@ -1543,6 +1543,231 @@ void vc_copylineRGBtoR12L(unsigned char *dst, const unsigned char *src, int dst_ } } +/** + * @brief Converts R12L to RG48. + * Converts 12-bit packed RGB in full range (compatible with + * SMPTE 268M DPX version 1, Annex C, Method C4 packing) to 16-bit RGB + * @copydetails vc_copylinev210 + */ +void vc_copylineR12LtoRG48(unsigned char *dst, const unsigned char *src, int dst_len) +{ + while(dst_len >= 48){ + //0 + //R + *dst++ = src[BYTE_SWAP(0)] << 4; + *dst++ = (src[BYTE_SWAP(1)] << 4) | (src[BYTE_SWAP(0)] >> 4); + //G + *dst++ = src[BYTE_SWAP(1)] & 0xF0; + *dst++ = src[BYTE_SWAP(2)]; + //B + *dst++ = src[BYTE_SWAP(3)] << 4; + *dst++ = (src[4 + BYTE_SWAP(0)] << 4) | (src[BYTE_SWAP(3)] >> 4); + + //1 + *dst++ = src[4 + BYTE_SWAP(0)] & 0xF0; + *dst++ = src[4 + BYTE_SWAP(1)]; + + *dst++ = src[4 + BYTE_SWAP(2)] << 4; + *dst++ = (src[4 + BYTE_SWAP(3)] << 4) | (src[4 + BYTE_SWAP(2)] >> 4); + + *dst++ = src[4 + BYTE_SWAP(3)] & 0xF0; + *dst++ = src[8 + BYTE_SWAP(0)]; + + //2 + *dst++ = src[8 + BYTE_SWAP(1)] << 4; + *dst++ = (src[8 + BYTE_SWAP(2)] << 4) | (src[8 + BYTE_SWAP(1)] >> 4); + + *dst++ = src[8 + BYTE_SWAP(2)] & 0xF0; + *dst++ = src[8 + BYTE_SWAP(3)]; + + *dst++ = src[12 + BYTE_SWAP(0)] << 4; + *dst++ = (src[12 + BYTE_SWAP(1)] << 4) | (src[12 + BYTE_SWAP(0)] >> 4); + + //3 + *dst++ = src[12 + BYTE_SWAP(1)] & 0xF0; + *dst++ = src[12 + BYTE_SWAP(2)]; + + *dst++ = src[12 + BYTE_SWAP(3)] << 4; + *dst++ = (src[16 + BYTE_SWAP(0)] << 4) | (src[12 + BYTE_SWAP(3)] >> 4); + + *dst++ = src[16 + BYTE_SWAP(0)] & 0xF0; + *dst++ = src[16 + BYTE_SWAP(1)]; + + //4 + *dst++ = src[16 + BYTE_SWAP(2)] << 4; + *dst++ = (src[16 + BYTE_SWAP(3)] << 4) | (src[16 + BYTE_SWAP(2)] >> 4); + + *dst++ = src[16 + BYTE_SWAP(3)] & 0xF0; + *dst++ = src[20 + BYTE_SWAP(0)]; + + *dst++ = src[20 + BYTE_SWAP(1)] << 4; + *dst++ = (src[20 + BYTE_SWAP(2)] << 4) | (src[20 + BYTE_SWAP(1)] >> 4); + + //5 + *dst++ = src[20 + BYTE_SWAP(2)] & 0xF0; + *dst++ = src[20 + BYTE_SWAP(3)]; + + *dst++ = src[24 + BYTE_SWAP(0)] << 4; + *dst++ = (src[24 + BYTE_SWAP(1)] << 4) | (src[24 + BYTE_SWAP(0)] >> 4); + + *dst++ = src[24 + BYTE_SWAP(1)] & 0xF0; + *dst++ = src[24 + BYTE_SWAP(2)]; + + //6 + *dst++ = src[24 + BYTE_SWAP(3)] << 4; + *dst++ = (src[28 + BYTE_SWAP(0)] << 4) | (src[24 + BYTE_SWAP(3)] >> 4); + + *dst++ = src[28 + BYTE_SWAP(0)] & 0xF0; + *dst++ = src[28 + BYTE_SWAP(1)]; + + *dst++ = src[28 + BYTE_SWAP(2)] << 4; + *dst++ = (src[28 + BYTE_SWAP(3)] << 4) | (src[28 + BYTE_SWAP(2)] >> 4); + + //7 + *dst++ = src[28 + BYTE_SWAP(3)] & 0xF0; + *dst++ = src[32 + BYTE_SWAP(0)]; + + *dst++ = src[32 + BYTE_SWAP(1)] << 4; + *dst++ = (src[32 + BYTE_SWAP(2)] << 4) | (src[32 + BYTE_SWAP(1)] >> 4); + + *dst++ = src[32 + BYTE_SWAP(2)] & 0xF0; + *dst++ = src[32 + BYTE_SWAP(3)]; + + dst_len -= 48; + src += 36; + } +} + +/** + * @brief Converts RG48 to R12L. + * Converts 16-bit RGB to 12-bit packed RGB in full range (compatible with + * SMPTE 268M DPX version 1, Annex C, Method C4 packing) + * @copydetails vc_copylinev210 + */ +void vc_copylineRG48toR12L(unsigned char *dst, const unsigned char *src, int dst_len) +{ + while(dst_len >= 36){ + //0 + dst[BYTE_SWAP(0)] = src[0] >> 4; + dst[BYTE_SWAP(0)] |= src[1] << 4; + dst[BYTE_SWAP(1)] = src[1] >> 4; + src += 2; + + dst[BYTE_SWAP(1)] |= src[0] & 0xF0; + dst[BYTE_SWAP(2)] = src[1]; + src += 2; + + dst[BYTE_SWAP(3)] = src[0] >> 4; + dst[BYTE_SWAP(3)] |= src[1] << 4; + dst[4 + BYTE_SWAP(0)] = src[1] >> 4; + src += 2; + + //1 + dst[4 + BYTE_SWAP(0)] |= src[0] & 0xF0; + dst[4 + BYTE_SWAP(1)] = src[1]; + src += 2; + + dst[4 + BYTE_SWAP(2)] = src[0] >> 4; + dst[4 + BYTE_SWAP(2)] |= src[1] << 4; + dst[4 + BYTE_SWAP(3)] = src[1] >> 4; + src += 2; + + dst[4 + BYTE_SWAP(3)] |= src[0] & 0xF0; + dst[8 + BYTE_SWAP(0)] = src[1]; + src += 2; + + //2 + dst[8 + BYTE_SWAP(1)] = src[0] >> 4; + dst[8 + BYTE_SWAP(1)] |= src[1] << 4; + dst[8 + BYTE_SWAP(2)] = src[1] >> 4; + src += 2; + + dst[8 + BYTE_SWAP(2)] |= src[0] & 0xF0; + dst[8 + BYTE_SWAP(3)] = src[1]; + src += 2; + + dst[12 + BYTE_SWAP(0)] = src[0] >> 4; + dst[12 + BYTE_SWAP(0)] |= src[1] << 4; + dst[12 + BYTE_SWAP(1)] = src[1] >> 4; + src += 2; + + //3 + dst[12 + BYTE_SWAP(1)] |= src[0] & 0xF0; + dst[12 + BYTE_SWAP(2)] = src[1]; + src += 2; + + dst[12 + BYTE_SWAP(3)] = src[0] >> 4; + dst[12 + BYTE_SWAP(3)] |= src[1] << 4; + dst[16 + BYTE_SWAP(0)] = src[1] >> 4; + src += 2; + + dst[16 + BYTE_SWAP(0)] |= src[0] & 0xF0; + dst[16 + BYTE_SWAP(1)] = src[1]; + src += 2; + + //4 + dst[16 + BYTE_SWAP(2)] = src[0] >> 4; + dst[16 + BYTE_SWAP(2)] |= src[1] << 4; + dst[16 + BYTE_SWAP(3)] = src[1] >> 4; + src += 2; + + dst[16 + BYTE_SWAP(3)] |= src[0] & 0xF0; + dst[20 + BYTE_SWAP(0)] = src[1]; + src += 2; + + dst[20 + BYTE_SWAP(1)] = src[0] >> 4; + dst[20 + BYTE_SWAP(1)] |= src[1] << 4; + dst[20 + BYTE_SWAP(2)] = src[1] >> 4; + src += 2; + + //5 + dst[20 + BYTE_SWAP(2)] |= src[0] & 0xF0; + dst[20 + BYTE_SWAP(3)] = src[1]; + src += 2; + + dst[24 + BYTE_SWAP(0)] = src[0] >> 4; + dst[24 + BYTE_SWAP(0)] |= src[1] << 4; + dst[24 + BYTE_SWAP(1)] = src[1] >> 4; + src += 2; + + dst[24 + BYTE_SWAP(1)] |= src[0] & 0xF0; + dst[24 + BYTE_SWAP(2)] = src[1]; + src += 2; + + //6 + dst[24 + BYTE_SWAP(3)] = src[0] >> 4; + dst[24 + BYTE_SWAP(3)] |= src[1] << 4; + dst[28 + BYTE_SWAP(0)] = src[1] >> 4; + src += 2; + + dst[28 + BYTE_SWAP(0)] |= src[0] & 0xF0; + dst[28 + BYTE_SWAP(1)] = src[1]; + src += 2; + + dst[28 + BYTE_SWAP(2)] = src[0] >> 4; + dst[28 + BYTE_SWAP(2)] |= src[1] << 4; + dst[28 + BYTE_SWAP(3)] = src[1] >> 4; + src += 2; + + //7 + dst[28 + BYTE_SWAP(3)] |= src[0] & 0xF0; + dst[32 + BYTE_SWAP(0)] = src[1]; + src += 2; + + dst[32 + BYTE_SWAP(1)] = src[0] >> 4; + dst[32 + BYTE_SWAP(1)] |= src[1] << 4; + dst[32 + BYTE_SWAP(2)] = src[1] >> 4; + src += 2; + + dst[32 + BYTE_SWAP(2)] |= src[0] & 0xF0; + dst[32 + BYTE_SWAP(3)] = src[1]; + src += 2; + + dst += 36; + dst_len -= 36; + } +} + /** * @brief Converts RGB to UYVY. * Uses full scale Rec. 601 YUV (aka JPEG) diff --git a/src/video_codec.h b/src/video_codec.h index b5ad062ec..fc9752ec0 100644 --- a/src/video_codec.h +++ b/src/video_codec.h @@ -115,6 +115,7 @@ void vc_copylineRGBtoGrayscale_SSE(unsigned char *dst, const unsigned char *src, void vc_copylineRGBtoR12L(unsigned char *dst, const unsigned char *src, int len, int rshift, int gshift, int bshift); void vc_copylineR12LtoRG48(unsigned char *dst, const unsigned char *src, int len); +void vc_copylineRG48toR12L(unsigned char *dst, const unsigned char *src, int len); void vc_copylineUYVYtoRGB(unsigned char *dst, const unsigned char *src, int len); void vc_copylineUYVYtoRGB_SSE(unsigned char *dst, const unsigned char *src, int len); void vc_copylineUYVYtoGrayscale(unsigned char *dst, const unsigned char *src, int len); diff --git a/src/video_decompress/cineform.cpp b/src/video_decompress/cineform.cpp index 8fd9618f0..d53226ad9 100644 --- a/src/video_decompress/cineform.cpp +++ b/src/video_decompress/cineform.cpp @@ -1,3 +1,41 @@ +/** + * @file video_decompress/cineform.cpp + * @author Martin Piatka + */ +/* + * Copyright (c) 2019 CESNET, z. s. p. o. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, is permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of CESNET nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + #ifdef HAVE_CONFIG_H #include "config.h" #include "config_unix.h" @@ -15,6 +53,9 @@ #include "CFHDTypes.h" #include "CFHDDecoder.h" +#include +#include + struct state_cineform_decompress { int width, height; int pitch; @@ -22,6 +63,11 @@ struct state_cineform_decompress { int max_compressed_len; codec_t in_codec; codec_t out_codec; + CFHD_PixelFormat decode_codec; + void (*convert)(unsigned char *dst_buffer, + unsigned char *src_buffer, + int width, int height, int pitch); + std::vector conv_buf; unsigned last_frame_seq:22; // This gives last sucessfully decoded frame seq number. It is the buffer number from the packet format header, uses 22 bits. bool last_frame_seq_initialized; @@ -39,6 +85,7 @@ static void * cineform_decompress_init(void) s = new state_cineform_decompress(); s->width = s->height = s->pitch = 0; + s->convert = nullptr; s->prepared_to_decode = false; CFHD_Error status; @@ -59,6 +106,31 @@ static void cineform_decompress_done(void *state) delete s; } +static void rg48_to_r12l(unsigned char *dst_buffer, + unsigned char *src_buffer, + int width, int height, int pitch) +{ + int src_pitch = vc_get_linesize(width, RG48); + int dst_pitch = vc_get_linesize(width, R12L); + + for(unsigned i = 0; i < height; i++){ + vc_copylineRG48toR12L(dst_buffer, src_buffer, dst_pitch); + src_buffer += src_pitch; + dst_buffer += dst_pitch; + } +} + +static const struct { + codec_t ug_codec; + CFHD_PixelFormat cfhd_pixfmt; + void (*convert)(unsigned char *dst_buffer, + unsigned char *src_buffer, + int width, int height, int pitch); +} decode_codecs[] = { + {R12L, CFHD_PIXEL_FORMAT_RG48, rg48_to_r12l}, + {UYVY, CFHD_PIXEL_FORMAT_2VUY, nullptr}, +}; + static bool configure_with(struct state_cineform_decompress *s, struct video_desc desc) { @@ -66,7 +138,19 @@ static bool configure_with(struct state_cineform_decompress *s, s->prepared_to_decode = false; s->saved_desc = desc; - return true; + for(const auto& i : decode_codecs){ + if(i.ug_codec == s->out_codec){ + s->decode_codec = i.cfhd_pixfmt; + s->convert = i.convert; + CFHD_GetImagePitch(desc.width, i.cfhd_pixfmt, &s->pitch); + if(i.ug_codec == R12L){ + log_msg(LOG_LEVEL_NOTICE, "[cineform] Using decoding to 12-bit RGB.\n"); + } + return true; + } + } + + return false; } static int cineform_decompress_reconfigure(void *state, struct video_desc desc, @@ -75,10 +159,10 @@ static int cineform_decompress_reconfigure(void *state, struct video_desc desc, struct state_cineform_decompress *s = (struct state_cineform_decompress *) state; - s->pitch = pitch; assert(out_codec == UYVY || out_codec == RGB || - out_codec == v210); + out_codec == v210 || + out_codec == R12L); s->pitch = pitch; s->rshift = rshift; @@ -108,7 +192,7 @@ static bool prepare(struct state_cineform_decompress *s, status = CFHD_PrepareToDecode(s->decoderRef, s->saved_desc.width, s->saved_desc.height, - CFHD_PIXEL_FORMAT_2VUY, + s->decode_codec, CFHD_DECODED_RESOLUTION_FULL, CFHD_DECODING_FLAGS_NONE, src, @@ -122,7 +206,12 @@ static bool prepare(struct state_cineform_decompress *s, int actualPitch; CFHD_GetImagePitch(actualWidth, actualFormat, &actualPitch); assert(actualPitch == s->pitch); - assert(actualFormat == CFHD_PIXEL_FORMAT_2VUY); + assert(actualFormat == s->decode_codec); + if(s->convert){ + s->conv_buf.resize(s->height * s->pitch); + } else { + s->conv_buf.clear(); + } if(status != CFHD_ERROR_OKAY){ log_msg(LOG_LEVEL_ERROR, "[cineform] Failed to prepare for decoding\n"); return false; @@ -144,13 +233,18 @@ static decompress_status cineform_decompress(void *state, unsigned char *dst, un return res; } + unsigned char *decode_dst = s->convert ? s->conv_buf.data() : dst; + status = CFHD_DecodeSample(s->decoderRef, src, src_len, - dst, + decode_dst, s->pitch); if(status == CFHD_ERROR_OKAY){ + if(s->convert){ + s->convert(dst, decode_dst, s->width, s->height, s->pitch); + } res = DECODER_GOT_FRAME; } else { log_msg(LOG_LEVEL_ERROR, "[cineform] Failed to decode %i\n", status); @@ -185,12 +279,36 @@ static int cineform_decompress_get_property(void *state, int property, void *val return ret; } +ADD_TO_PARAM(lavd_use_10bit, "cfhd-use-12bit", + "* cfhd-use-12bit\n" + " Indicates that we are using decoding to R12L.\n" + " With this flag, R12L (12-bit RGB)\n" + " will be announced as a supported codec.\n"); + static const struct decode_from_to *cineform_decompress_get_decoders() { - static const struct decode_from_to dec_static[] = { + const struct decode_from_to dec_static[] = { { CFHD, UYVY, 500 }, }; - return dec_static; + static struct decode_from_to ret[sizeof dec_static / sizeof dec_static[0] + + 1 /* terminating zero */ + + 10 /* place for additional decoders, see below */]; + + static std::mutex mutex; + + std::lock_guard lock(mutex); + + if (ret[0].from == VIDEO_CODEC_NONE) { // not yet initialized + memcpy(ret, dec_static, sizeof dec_static); + if (get_commandline_param("cfhd-use-12bit")) { + log_msg(LOG_LEVEL_NOTICE, "[cineform] param 12-bit RGB.\n"); + //Report only 12-bit formats + ret[0] = (struct decode_from_to) {CFHD, R12L, 100}; + ret[1] = { }; + } + } + + return ret; } static const struct video_decompress_info cineform_info = {