[vlc-devel] [PATCH v2 2/3] Add CineForm decoding/encoding plugin
Emeric Grange
emeric.grange at gmail.com
Thu Aug 30 15:40:46 CEST 2018
From: Emeric <emeric.grange at gmail.com>
Merge all files together, fix problems pointed by JB, cleanup settings
handling (add translation macros, remove useless settings, stop using
strcmp method to parse values).
---
modules/codec/Makefile.am | 9 +
modules/codec/cineform.c | 932 ++++++++++++++++++++++++++++++++++++++
2 files changed, 941 insertions(+)
create mode 100644 modules/codec/cineform.c
diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am
index 9f0b9bf4bb..880269b432 100644
--- a/modules/codec/Makefile.am
+++ b/modules/codec/Makefile.am
@@ -130,6 +130,15 @@ EXTRA_LTLIBRARIES += libschroedinger_plugin.la
codec_LTLIBRARIES += $(LTLIBschroedinger)
+libcineform_plugin_la_SOURCES = dummy.cpp codec/cineform.c
+libcineform_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_cineform)
+libcineform_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(CFLAGS_cineform)
+libcineform_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(codecdir)'
+libcineform_plugin_la_LIBADD = $(LIBS_cineform)
+EXTRA_LTLIBRARIES += libcineform_plugin.la
+codec_LTLIBRARIES += $(LTLIBcineform)
+
+
### Image codecs ###
libpng_plugin_la_SOURCES = codec/png.c
diff --git a/modules/codec/cineform.c b/modules/codec/cineform.c
new file mode 100644
index 0000000000..fbc9075889
--- /dev/null
+++ b/modules/codec/cineform.c
@@ -0,0 +1,932 @@
+/*****************************************************************************
+ * cineform.c : CineForm decoding and encoding plugin
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ * $Id$
+ *
+ * Authors: Emeric Grange <emeric.grange at gmail.com>
+ *
+ * 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.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_codec.h>
+
+#include <libcineform/CFHDDecoder.h>
+#ifdef ENABLE_SOUT
+#include <libcineform/CFHDEncoder.h>
+#endif
+#include <libcineform/CFHDTypes.h>
+#include <libcineform/CFHDError.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+#include <math.h>
+
+/*****************************************************************************
+ * decoder_sys_t: cineform decoder descriptor
+ *****************************************************************************/
+typedef struct
+{
+ CFHD_DecoderRef decoderRef;
+ CFHD_MetadataRef metadataRef;
+
+ int SDKversion;
+ int SAMPLEversion;
+
+ // Flow control, used for stats
+ uint64_t sampleCount;
+ uint64_t sampleDecoded;
+ uint64_t sampleFailed;
+
+ // Source video parameters, decoded and returned by the SDK
+ int32_t sourceWidth;
+ int32_t sourceHeight;
+ int32_t sourceVisibleWidth;
+ int32_t sourceVisibleHeight;
+ int32_t sourceChannels;
+ uint32_t sourcePixelSize;
+ CFHD_EncodedFormat sourcePixelFormat; ///< Pixel format of the compressed video, informative only
+
+ // Internal decoding buffer
+ int32_t internalBufferWidth;
+ int32_t internalBufferHeight;
+ int32_t internalBufferPitch;
+ uint32_t internalBufferSizeComputed;
+ uint32_t internalBufferSize;
+ CFHD_PixelFormat internalBufferPixelFormat; ///< The output buffer format set by the CineForm SDK, using 'decFormat'
+
+ // User defined parameters
+ bool decFormatAuto;
+ CFHD_PixelFormat decFormat; ///< The output buffer format we'd like (may be superseeded if 'decSizeAuto' is set, or by the SDK)
+ CFHD_DecodedResolution decSize;
+ CFHD_DecodingFlags decFlags;
+
+ CFHD_Stereo3DType stereoType;
+ CFHD_StereoFlags stereoFlags;
+ CFHD_VideoSelect stereoChannelSelection;
+} decoder_sys_t;
+
+typedef struct cineform_packet_t {
+ unsigned char *sampleData;
+ size_t sampleSize;
+ int samplePTS;
+} cineform_packet_t;
+
+/*****************************************************************************
+ * encoder_sys_t: cineform encoder descriptor
+ *****************************************************************************/
+
+#ifdef ENABLE_SOUT
+typedef struct
+{
+ CFHD_EncoderRef encoderRef;
+ CFHD_MetadataRef metadataRef;
+ CFHD_ALLOCATOR allocator;
+
+ int SDKversion;
+ int SAMPLEversion;
+
+ // Flow control, used for stats
+ int sampleCount;
+
+ // User defined parameters
+ CFHD_EncodingQuality encQuality;
+ CFHD_EncodingFlags encFlags;
+ CFHD_EncodedFormat encFormat;
+
+ // Source video
+ int32_t videoWidth;
+ int32_t videoHeight;
+ int32_t videoChannels;
+
+ bool directMapping; ///< Indicate if we can encode directly the VLC picture, or if we need an intermediate internalBuffer
+
+ // Internal encoding buffer
+ char *internalBuffer;
+ CFHD_PixelFormat internalBufferPixelFormat;
+ uint32_t internalBufferPixelSize;
+ int32_t internalBufferWidth;
+ int32_t internalBufferHeight;
+ int32_t internalBufferPitch;
+ uint32_t internalBufferSizeComputed;
+ uint32_t internalBufferSize;
+} encoder_sys_t;
+#endif
+
+/****************************************************************************
+ * Local prototypes
+ ****************************************************************************/
+
+static int OpenDecoder(vlc_object_t *);
+static void CloseDecoder(vlc_object_t *);
+static int DecodeVideo(decoder_t *, block_t *);
+static picture_t *DecodeBlock(decoder_t *, block_t **);
+static CFHD_Error prepareDecoding(decoder_t *, decoder_sys_t *, cineform_packet_t *);
+
+#ifdef ENABLE_SOUT
+int OpenEncoder(vlc_object_t *);
+void CloseEncoder(vlc_object_t *);
+block_t *EncodeBlock(encoder_t *, picture_t *);
+#endif
+
+/*****************************************************************************
+ * Settings
+ *****************************************************************************/
+
+// Decoder
+
+#define DECODING_FORMAT_TEXT N_("Prefered decoding format")
+#define DECODING_FORMAT_LONGTEXT N_("Choose the prefered decoding format (can be overwritten at run time if prefered format 'color resolution' is not high enough to store decoded format).")
+static const char *const decoding_formats_text[] =
+ { N_("AUTO"), "YUYV", "BGRa" };
+static const CFHD_PixelFormat decoding_formats_values[] =
+ { CFHD_PIXEL_FORMAT_UNKNOWN, CFHD_PIXEL_FORMAT_YUYV, CFHD_PIXEL_FORMAT_BGRa };
+
+#define DECODING_SIZE_TEXT N_("Decoding size")
+#define DECODING_SIZE_LONGTEXT N_("Tweak the decoding size to substantially accelerated decoding time at the expense of quality.")
+static const char *const decoding_sizes_text[] =
+ { N_("FULL RESOLUTION"), N_("HALF RESOLUTION"), N_("QUARTER RESOLUTION") };
+static const CFHD_DecodedResolution decoding_sizes_values_list[] =
+ { CFHD_DECODED_RESOLUTION_FULL, CFHD_DECODED_RESOLUTION_HALF, CFHD_DECODED_RESOLUTION_QUARTER };
+
+#define STEREO_SELECT_TEXT N_("Eye selection")
+#define STEREO_SELECT_LONGTEXT N_("Eye selection (only for 3D videos).")
+static const char *const stereo_select_text[] =
+ { N_("DEFAULT"), N_("LEFT EYE"), N_("RIGHT EYE"), N_("BOTH EYES") };
+static const CFHD_VideoSelect stereo_select_values_list[] =
+ { VIDEO_SELECT_DEFAULT, VIDEO_SELECT_LEFT_EYE, VIDEO_SELECT_RIGHT_EYE, VIDEO_SELECT_BOTH_EYES };
+
+#define STEREO_TYPE_TEXT N_("Stereo 3D type")
+#define STEREO_TYPE_LONGTEXT N_("Stereo 3D type (only for 3D videos).")
+static const char *const stereo_type_text[] =
+ { N_("DEFAULT"), N_("STACKED"), N_("SIDE BY SIDE") };
+static const CFHD_Stereo3DType stereo_type_values_list[] =
+ { STEREO3D_TYPE_DEFAULT, STEREO3D_TYPE_STACKED, STEREO3D_TYPE_SIDEBYSIDE };
+
+#define STEREO_FLAGS_TEXT N_("Stereo flags")
+#define STEREO_FLAGS_LONGTEXT N_("Apply stereo flags (only for 3D videos).")
+static const char *const stereo_flags_text[] =
+ { N_("DEFAULT"), N_("SWAP EYES"), N_("SPEED 3D") };
+static const CFHD_StereoFlags stereo_flags_values_list[] =
+ { STEREO_FLAGS_DEFAULT, STEREO_FLAGS_SWAP_EYES, STEREO_FLAGS_SPEED_3D };
+
+// Encoder
+
+#define ENC_CFG_PREFIX "cineform-encoding"
+
+static const char *const enc_cfg_options[] =
+ { "encoding-format", "encoding-quality", NULL };
+
+#define ENCODING_FORMAT_TEXT N_("Encoding format")
+#define ENCODING_FORMAT_LONGTEXT N_("Choose the internal encoding format, depending on how much visual quality you want.")
+static const char *const encoding_formats_text[] =
+ { "YUV 4:2:2 (10 bits)", "RGB 4:4:4 (12 bits)", "RGBA 4:4:4:4 (12 bits)" };
+static const CFHD_EncodedFormat encoding_formats_values[] =
+ { CFHD_ENCODED_FORMAT_YUV_422, CFHD_ENCODED_FORMAT_RGB_444, CFHD_ENCODED_FORMAT_RGBA_4444 };
+
+#define ENCODING_QUALITY_TEXT N_("Encoding quality")
+#define ENCODING_QUALITY_LONGTEXT N_("Choose an encoding quality preset.")
+static const char *const encoding_quality_text[] =
+ { N_("LOW"), N_("MEDIUM"), N_("HIGH"), N_("FILMSCAN"), N_("UNCOMPRESSED") };
+static const CFHD_EncodingQuality encoding_quality_values[] =
+ { CFHD_ENCODING_QUALITY_LOW, CFHD_ENCODING_QUALITY_MEDIUM, CFHD_ENCODING_QUALITY_HIGH, CFHD_ENCODING_QUALITY_FILMSCAN1, CFHD_ENCODING_QUALITY_UNCOMPRESSED };
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+
+vlc_module_begin ()
+
+ set_category(CAT_INPUT)
+ set_subcategory(SUBCAT_INPUT_VCODEC)
+
+ // Description
+ set_shortname("CineForm")
+ set_help(N_("GoPro CineForm SDK decoding/encoding plugin"))
+
+ set_capability("video decoder", 100)
+ set_description(N_("CineForm decoding plugin"))
+ set_callbacks(OpenDecoder, CloseDecoder)
+ add_shortcut("cineform")
+
+ // User defined settings
+ set_section(N_("Decoding"), NULL)
+ add_integer("cineform-decoding-format", 0, DECODING_FORMAT_TEXT, DECODING_FORMAT_LONGTEXT, true)
+ change_integer_list(decoding_formats_values, decoding_formats_text)
+ add_integer("cineform-decoding-size", 0, DECODING_SIZE_TEXT, DECODING_SIZE_LONGTEXT, true)
+ change_integer_list(decoding_sizes_values_list, decoding_sizes_text)
+ add_integer("cineform-stereo-select", 0, STEREO_SELECT_TEXT, STEREO_SELECT_LONGTEXT, true)
+ change_integer_list(stereo_select_values_list, stereo_select_text)
+ add_integer("cineform-stereo-type", 0, STEREO_TYPE_TEXT, STEREO_TYPE_LONGTEXT, true)
+ change_integer_list(stereo_type_values_list, stereo_type_text)
+ add_integer("cineform-stereo-flags", 0, STEREO_FLAGS_TEXT, STEREO_FLAGS_LONGTEXT, true)
+ change_integer_list(stereo_flags_values_list, stereo_flags_text)
+
+#ifdef ENABLE_SOUT
+ add_submodule()
+
+ set_capability("encoder", 150)
+ set_description(N_("CineForm encoding plugin"))
+ set_callbacks(OpenEncoder, CloseEncoder)
+ add_shortcut("cineform")
+
+ // User defined settings
+ set_section(N_("Encoding"), NULL)
+ add_integer("cineform-encoding-format", 0, ENCODING_FORMAT_TEXT, ENCODING_FORMAT_LONGTEXT, true)
+ change_integer_list(encoding_formats_values, encoding_formats_text)
+ add_integer("cineform-encoding-quality", 0, ENCODING_QUALITY_LONGTEXT, ENCODING_QUALITY_LONGTEXT, true)
+ change_integer_list(encoding_quality_values, encoding_quality_text)
+#endif
+
+vlc_module_end ()
+
+/*****************************************************************************
+ * Common
+ *****************************************************************************/
+
+static const char *getErrorString(const CFHD_Error error)
+{
+ switch (error)
+ {
+ case CFHD_ERROR_OKAY:
+ return "OKAY";
+
+ case CFHD_ERROR_INVALID_ARGUMENT:
+ return "INVALID ARGUMENT";
+ case CFHD_ERROR_OUTOFMEMORY:
+ return "OUT OF MEMORY";
+ case CFHD_ERROR_BADFORMAT:
+ return "BAD FORMAT";
+ case CFHD_ERROR_BADSCALING:
+ return "BAD SCALING";
+ case CFHD_ERROR_BADSAMPLE:
+ return "BAD SAMPLE";
+ case CFHD_ERROR_INTERNAL:
+ return "INTERNAL ERROR";
+ case CFHD_ERROR_METADATA_CLASS:
+ return "METADATA CLASS";
+ case CFHD_ERROR_METADATA_UNDEFINED:
+ return "METADATA UNDEFINED";
+ case CFHD_ERROR_METADATA_END:
+ return "METADATA END";
+ case CFHD_ERROR_UNEXPECTED:
+ return "UNEXPECTED ERROR";
+ case CFHD_ERROR_BAD_RESOLUTION:
+ return "BAD RESOLUTION";
+ case CFHD_ERROR_BAD_PIXEL_SIZE:
+ return "BAD PIXEL SIZE";
+ case CFHD_ERROR_NOT_FINISHED:
+ return "NOT FINISHED";
+ case CFHD_ERROR_ENCODING_NOT_STARTED:
+ return "NOT STARTED";
+ case CFHD_ERROR_METADATA_ATTACHED:
+ return "METADATA ATTACHED";
+ case CFHD_ERROR_BAD_METADATA:
+ return "BAD METADATA";
+ case CFHD_ERROR_THREAD_CREATE_FAILED:
+ return "THREAD CREATE FAILED";
+ case CFHD_ERROR_THREAD_WAIT_FAILED:
+ return "THREAD WAIT FAILED";
+ case CFHD_ERROR_UNKNOWN_TAG:
+ return "UNKNOWN TAG";
+ case CFHD_ERROR_LICENSING:
+ return "LICENSING ERROR";
+
+ // Error codes returned by the codec library
+ case CFHD_ERROR_CODEC_ERROR:
+ return "CODEC ERROR";
+ case CFHD_ERROR_DECODE_BUFFER_SIZE:
+ return "DECODE BUFFER SIZE ERROR";
+
+ // Error codes used by the sample code distributed with the codec SDK
+ case CFHD_ERROR_SAMPLE_CODE:
+ return "SAMPLE CODE ERROR";
+ case CFHD_ERROR_FILE_CREATE:
+ return "FILE CREATE ERROR";
+ case CFHD_ERROR_FILE_OPEN:
+ return "FILE OPEN ERROR";
+ case CFHD_ERROR_BADFILE:
+ return "BAD FILE";
+ case CFHD_ERROR_READ_FAILURE:
+ return "READ FAILURE";
+ case CFHD_ERROR_WRITE_FAILURE:
+ return "WRITE FAILURE";
+ case CFHD_ERROR_FILE_SIZE:
+ return "FILE SIZE ERROR";
+ case CFHD_ERROR_END_OF_FILE:
+ return "END OF FILE";
+ case CFHD_ERROR_END_OF_DATABASE:
+ return "END OF DATABASE";
+ case CFHD_ERROR_THREAD:
+ return "THREAD ERROR";
+
+ default:
+ return "UNKNOWN ERROR";
+ }
+}
+
+/*****************************************************************************
+ * Decoder
+ *****************************************************************************/
+int OpenDecoder(vlc_object_t *obj)
+{
+ decoder_t *p_dec = (decoder_t*)obj;
+ decoder_sys_t *p_sys = NULL;
+
+ if (p_dec->fmt_in.i_codec != VLC_CODEC_CINEFORM)
+ return VLC_EGENERIC;
+
+ // Init decoder_sys_t // Allocate the memory needed to store the decoder's private context
+ if ((p_dec->p_sys = p_sys = (decoder_sys_t *)calloc(1, sizeof(*p_sys))) == NULL)
+ return VLC_ENOMEM;
+
+ // Init decoder_sys_t content
+ p_sys->decFormat = var_InheritInteger(p_dec, "cineform-decoding-format");
+ if (p_sys->decFormat == CFHD_PIXEL_FORMAT_YUYV)
+ {
+ p_dec->fmt_out.i_codec = VLC_CODEC_YUYV;
+ p_sys->decFormatAuto = false;
+ }
+ else if (p_sys->decFormat == CFHD_PIXEL_FORMAT_BGRa)
+ {
+ p_dec->fmt_out.i_codec = VLC_CODEC_BGRA;
+ p_sys->decFormatAuto = false;
+ }
+ else
+ {
+ p_sys->decFormat = CFHD_PIXEL_FORMAT_UNKNOWN;
+ p_sys->decFormatAuto = true;
+ }
+
+ p_sys->decSize = var_InheritInteger(p_dec, "cineform-decoding-size");
+ p_sys->stereoType = var_InheritInteger(p_dec, "cineform-stereo-type");
+ p_sys->stereoFlags = var_InheritInteger(p_dec, "cineform-stereo-flags");
+ p_sys->stereoChannelSelection = var_InheritInteger(p_dec, "cineform-stereo-select");
+
+ // Register VLC decoding callback
+ p_dec->pf_decode = DecodeVideo;
+
+ // Init CineForm decoder
+ CFHD_Error errorCode = CFHD_OpenDecoder(&(p_sys->decoderRef), NULL);
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_OpenDecoder(error %i / %s)", errorCode, getErrorString(errorCode));
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ // Open an interface to the CineForm metadata
+ errorCode = CFHD_OpenMetadata(&(p_sys->metadataRef));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_OpenMetadata(error %i / %s)", errorCode, getErrorString(errorCode));
+ CFHD_CloseDecoder(p_sys->decoderRef);
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ return VLC_SUCCESS;
+}
+
+void CloseDecoder(vlc_object_t *obj)
+{
+ decoder_t *p_dec = (decoder_t*)obj;
+ decoder_sys_t *p_sys = p_dec->p_sys;
+
+ CFHD_CloseDecoder(p_sys->decoderRef);
+ CFHD_CloseMetadata(p_sys->metadataRef);
+
+ free(p_sys);
+}
+
+int DecodeVideo(decoder_t *p_dec, block_t *p_block)
+{
+ if (p_block == NULL) /* No Drain */
+ return VLCDEC_SUCCESS;
+
+ picture_t *p_pic = DecodeBlock(p_dec, &p_block);
+ if (p_pic != NULL)
+ decoder_QueueVideo(p_dec, p_pic);
+
+ return VLCDEC_SUCCESS;
+}
+
+picture_t *DecodeBlock(decoder_t *p_dec, block_t **pp_block)
+{
+ decoder_sys_t *p_sys = p_dec->p_sys;
+ block_t *p_block;
+ CFHD_Error errorCode = CFHD_ERROR_OKAY;
+
+ if (!pp_block || !*pp_block)
+ return NULL;
+
+ p_block = *pp_block;
+ if (p_block->i_flags & (BLOCK_FLAG_CORRUPTED))
+ {
+ block_Release(p_block);
+ return NULL;
+ }
+
+ // We have a block
+ p_sys->sampleCount++;
+
+ // Packet handling
+ cineform_packet_t packet;
+ packet.sampleData = p_block->p_buffer;
+ packet.sampleSize = p_block->i_buffer;
+ packet.samplePTS = p_block->i_dts;
+
+ // Prepare decoding process
+ if (p_sys->sampleDecoded == 0)
+ {
+ int SDKversion = 0;
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet.sampleData, packet.sampleSize,
+ CFHD_SAMPLE_SDK_VERSION, &SDKversion, sizeof(SDKversion));
+ if (errorCode != CFHD_ERROR_OKAY)
+ {
+ // not critical...
+ msg_Warn(p_dec, "CFHD_GetSampleInfo(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ }
+ if (SDKversion)
+ {
+ msg_Dbg(p_dec, "CineForm SDK version used = %d.%d.%d", (SDKversion >> 16) & 0xFF, (SDKversion >> 8) & 0xFF, (SDKversion) & 0xFF);
+ p_sys->SDKversion = SDKversion;
+ }
+
+ int SAMPLEversion = 0;
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet.sampleData, packet.sampleSize,
+ CFHD_SAMPLE_ENCODE_VERSION, &SAMPLEversion, sizeof(SAMPLEversion));
+ if (errorCode != CFHD_ERROR_OKAY)
+ {
+ // not critical...
+ msg_Warn(p_dec, "CFHD_GetSampleInfo(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ }
+ if (SAMPLEversion)
+ {
+ msg_Dbg(p_dec, "CineForm SDK version used = %d.%d.%d", (SAMPLEversion >> 16) & 0xFF, (SAMPLEversion >> 8) & 0xFF, (SAMPLEversion) & 0xFF);
+ p_sys->SAMPLEversion = SAMPLEversion;
+ }
+
+ // Get various infos from the current sample, choose buffer formats, and then initialize the decoder
+ errorCode = prepareDecoding(p_dec, p_sys, &packet);
+ if (errorCode)
+ {
+ msg_Err(p_dec, "getSampleInfos(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return NULL;
+ }
+ }
+
+ // Ask VLC for a picture buffer
+ if (decoder_UpdateVideoFormat(p_dec))
+ return NULL;
+
+ picture_t *p_pic = decoder_NewPicture(p_dec);
+ if (p_pic == NULL)
+ return NULL;
+
+ if (!p_pic)
+ {
+ msg_Err(p_dec, "CineForm DECODER plugin (unable to get an %i * %i picture_t output buffer)",
+ p_dec->fmt_out.video.i_width, p_dec->fmt_out.video.i_height);
+ goto error;
+ }
+ else
+ {
+ p_pic->date = packet.samplePTS;
+ }
+
+ // Use the CineForm SDK to do the decoding into the VLC picture_t
+ errorCode = CFHD_DecodeSample(p_sys->decoderRef,
+ packet.sampleData, packet.sampleSize,
+ p_pic->p[0].p_pixels, p_pic->p[0].i_pitch);
+ if (errorCode != CFHD_ERROR_OKAY)
+ {
+ msg_Err(p_dec, "CFHD_DecodeSample(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ goto error;
+ }
+
+ block_Release(p_block);
+ *pp_block = NULL;
+
+ p_sys->sampleDecoded++;
+ return p_pic;
+
+error:
+ p_sys->sampleFailed++;
+ if (p_block)
+ {
+ block_Release(p_block);
+ *pp_block = NULL;
+ }
+ return NULL;
+}
+
+CFHD_Error prepareDecoding(decoder_t *p_dec, decoder_sys_t *p_sys,
+ cineform_packet_t *packet)
+{
+ CFHD_Error errorCode = CFHD_ERROR_OKAY;
+
+ // Get channel(s) count
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet->sampleData, packet->sampleSize,
+ CFHD_SAMPLE_INFO_CHANNELS, &(p_sys->sourceChannels), sizeof(p_sys->sourceChannels));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_GetSampleInfo(CFHD_SAMPLE_INFO_CHANNELS)(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return errorCode;
+ }
+ else
+ {
+ if (p_sys->sourceChannels < 1)
+ {
+ p_sys->sourceChannels = 1;
+ }
+ else if (p_sys->sourceChannels > 1)
+ {
+ msg_Warn(p_dec, "This video has more than one video channel (3D video?) and it's not supported.");
+ }
+ }
+
+ // Get display width and height
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet->sampleData, packet->sampleSize,
+ CFHD_SAMPLE_DISPLAY_WIDTH, &(p_sys->sourceVisibleWidth), sizeof(p_sys->sourceVisibleWidth));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_GetSampleInfo(CFHD_SAMPLE_DISPLAY_WIDTH)(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return errorCode;
+ }
+
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet->sampleData, packet->sampleSize,
+ CFHD_SAMPLE_DISPLAY_HEIGHT, &(p_sys->sourceVisibleHeight), sizeof(p_sys->sourceVisibleHeight));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_GetSampleInfo(CFHD_SAMPLE_DISPLAY_HEIGHT)(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return errorCode;
+ }
+
+ // Get internal encoding format
+ errorCode = CFHD_GetSampleInfo(p_sys->decoderRef, packet->sampleData, packet->sampleSize,
+ CFHD_SAMPLE_ENCODED_FORMAT, &(p_sys->sourcePixelFormat), sizeof(p_sys->sourcePixelFormat));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_GetSampleInfo(CFHD_SAMPLE_ENCODED_FORMAT)(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return errorCode;
+ }
+ else
+ {
+ // Old CineForm SDKs return CFHD_EncodedFormat differently, let's fix that
+ if (p_sys->SDKversion == 0 && p_sys->sourcePixelFormat)
+ {
+ switch ((int)p_sys->sourcePixelFormat)
+ {
+ case 1: p_sys->sourcePixelFormat = CFHD_ENCODED_FORMAT_YUV_422; break;
+ case 2: p_sys->sourcePixelFormat = CFHD_ENCODED_FORMAT_BAYER; break;
+ case 3: p_sys->sourcePixelFormat = CFHD_ENCODED_FORMAT_RGB_444; break;
+ case 4: p_sys->sourcePixelFormat = CFHD_ENCODED_FORMAT_RGBA_4444; break;
+ default:
+ msg_Err(p_dec, "CFHD_GetSampleInfo() error: Unable to get encoded format");
+ return CFHD_ERROR_BADFORMAT;
+ break;
+ }
+ }
+ }
+
+ // Autodetect best buffer format combination (now that we got the internal encoding format)
+ if (p_sys->decFormatAuto == true ||
+ p_sys->decFormat == CFHD_PIXEL_FORMAT_UNKNOWN)
+ {
+ // Adjust VLC output format depending on video characteristics AND CineForm plugin settings
+ if (p_sys->sourcePixelFormat == CFHD_ENCODED_FORMAT_YUV_422)
+ {
+ p_sys->decFormat = CFHD_PIXEL_FORMAT_YUYV;
+ p_dec->fmt_out.i_codec = VLC_CODEC_YUYV;
+ }
+ else if (p_sys->sourcePixelFormat == CFHD_ENCODED_FORMAT_RGB_444 ||
+ p_sys->sourcePixelFormat == CFHD_ENCODED_FORMAT_RGBA_4444)
+ {
+ p_sys->decFormat = CFHD_PIXEL_FORMAT_BGRa;
+ p_dec->fmt_out.i_codec = VLC_CODEC_BGRA;
+ }
+ else if (p_sys->sourcePixelFormat == CFHD_ENCODED_FORMAT_BAYER ||
+ p_sys->sourcePixelFormat == CFHD_ENCODED_FORMAT_YUVA_4444)
+ {
+ p_sys->decFormat = CFHD_PIXEL_FORMAT_BGRa;
+ p_dec->fmt_out.i_codec = VLC_CODEC_BGRA;
+ }
+ }
+
+ // Get sample size/format infos
+ errorCode = CFHD_PrepareToDecode(p_sys->decoderRef,
+ 0, 0, // do not use CineForm SDK internal scaler
+ p_sys->decFormat,
+ p_sys->decSize,
+ p_sys->decFlags,
+ packet->sampleData,
+ packet->sampleSize,
+ &(p_sys->sourceWidth),
+ &(p_sys->sourceHeight),
+ &(p_sys->internalBufferPixelFormat));
+ if (errorCode)
+ {
+ if (errorCode == CFHD_ERROR_BADFORMAT) // Fallback
+ {
+ p_dec->fmt_out.i_codec = VLC_CODEC_BGRA;
+ p_sys->decFormat = CFHD_PIXEL_FORMAT_BGRa;
+ }
+
+ msg_Err(p_dec, "CFHD_PrepareToDecode(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return errorCode;
+ }
+
+ // Get the size of ONE pixel
+ errorCode = CFHD_GetPixelSize(p_sys->internalBufferPixelFormat, &(p_sys->sourcePixelSize));
+ if (errorCode)
+ {
+ msg_Err(p_dec, "CFHD_GetPixelSize(error: %i)", errorCode);
+ return errorCode;
+ }
+
+ // To decode CineForm footage, the frame width must be evenly divisible by 16 and the frame height by 8.
+ // Dimensions that do not meet these requirements are rounded to a valid value.
+
+ if (p_sys->sourceWidth % 16 != 0 || p_sys->sourceHeight % 8 != 0)
+ {
+ msg_Warn(p_dec, "CineForm video width is not a multiple of 16");
+ p_sys->internalBufferWidth = ceilf((float)(p_sys->sourceWidth) / 16.f) * 16;
+ p_sys->internalBufferPitch = (p_sys->internalBufferWidth) * p_sys->sourcePixelSize;
+ }
+ else
+ {
+ // Get buffer pitch and round the pitch to a multiple of 16 bytes
+ p_sys->internalBufferWidth = p_sys->sourceWidth;
+ p_sys->internalBufferPitch = p_sys->sourceWidth * p_sys->sourcePixelSize;
+ p_sys->internalBufferPitch = ((p_sys->internalBufferPitch + 0x0F) & ~0x0F);
+ }
+
+ if (p_sys->sourceHeight % 8 != 0)
+ {
+ msg_Warn(p_dec, "CineForm video height is not a multiple of 8");
+ p_sys->internalBufferHeight = ceilf((float)(p_sys->sourceHeight) / 8.f) * 8;
+ }
+ else
+ {
+ p_sys->internalBufferHeight = p_sys->sourceHeight;
+ }
+
+ // Get buffer size
+ p_sys->internalBufferSizeComputed = p_sys->internalBufferPitch * p_sys->internalBufferHeight;
+
+ // Set VLC output properties
+ p_dec->fmt_out.i_cat = VIDEO_ES;
+ p_dec->fmt_out.video.i_width = p_sys->internalBufferWidth;
+ p_dec->fmt_out.video.i_height = p_sys->internalBufferHeight;
+ p_dec->fmt_out.video.i_visible_width = p_sys->sourceWidth;
+ p_dec->fmt_out.video.i_visible_height = p_sys->sourceHeight;
+
+ return errorCode;
+}
+
+#ifdef ENABLE_SOUT
+
+/*****************************************************************************
+ * Encoder
+ *****************************************************************************/
+int OpenEncoder(vlc_object_t *obj)
+{
+ encoder_t *p_enc = (encoder_t *)obj;
+ encoder_sys_t *p_sys = NULL;
+
+ if (p_enc->fmt_out.i_codec != VLC_CODEC_CINEFORM && !p_enc->obj.force)
+ return VLC_EGENERIC;
+
+ // Init encoder_sys_t // Allocate the memory needed to store the encoder's private context
+ if ((p_enc->p_sys = p_sys = (encoder_sys_t *)calloc(1, sizeof(*p_sys))) == NULL)
+ return VLC_ENOMEM;
+
+ p_enc->fmt_out.i_cat = VIDEO_ES;
+ p_enc->fmt_out.i_codec = VLC_CODEC_CINEFORM;
+ p_enc->fmt_in.i_codec = VLC_CODEC_YUYV;
+
+ // Init encoder_sys_t content
+ p_sys->encFormat = var_InheritInteger(p_enc, "cineform-encoding-format");
+ p_sys->encQuality = var_InheritInteger(p_enc, "cineform-encoding-quality");
+ p_sys->videoWidth = p_enc->fmt_in.video.i_visible_width;
+ p_sys->videoHeight = p_enc->fmt_in.video.i_visible_height;
+ p_sys->videoChannels = 1; // 3D not supported by this plugin
+ p_sys->internalBufferPixelFormat = CFHD_PIXEL_FORMAT_YUY2;
+
+ // Register settings
+ config_ChainParse(p_enc, ENC_CFG_PREFIX, enc_cfg_options, p_enc->p_cfg);
+
+ // Register encoding callback
+ p_enc->pf_encode_video = EncodeBlock;
+
+ // Ask a different vlc picture type than default input (YUYV) depending on choosen encoding format
+ if (p_sys->encFormat != CFHD_ENCODED_FORMAT_YUV_422)
+ {
+ msg_Info(p_enc, "Switching internal format to BGRa");
+ p_enc->fmt_in.i_codec = VLC_CODEC_BGRA;
+ p_sys->internalBufferPixelFormat = CFHD_PIXEL_FORMAT_BGRa;
+ }
+
+ // Init CineForm encoder
+ CFHD_Error errorCode = CFHD_OpenEncoder(&(p_sys->encoderRef), NULL);
+ if (errorCode)
+ {
+ msg_Err(p_enc, "CFHD_OpenEncoder(error %i / %s)", errorCode, getErrorString(errorCode));
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ // Open an interface to the CineForm metadata
+ errorCode = CFHD_OpenMetadata(&(p_sys->metadataRef));
+ if (errorCode)
+ {
+ msg_Err(p_enc, "CFHD_OpenMetadata(error %i / %s)", errorCode, getErrorString(errorCode));
+ CFHD_CloseEncoder(p_sys->encoderRef);
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ return VLC_SUCCESS;
+}
+
+void CloseEncoder(vlc_object_t *obj)
+{
+ encoder_t *p_enc = (encoder_t*)obj;
+ encoder_sys_t *p_sys = p_enc->p_sys;
+
+ CFHD_CloseEncoder(p_sys->encoderRef);
+ CFHD_CloseMetadata(p_sys->metadataRef);
+
+ if (p_sys->internalBuffer && !p_sys->directMapping)
+ free(p_sys->internalBuffer);
+
+ free(p_sys);
+}
+
+block_t *EncodeBlock(encoder_t *p_enc, picture_t *p_pic)
+{
+ CFHD_Error errorCode = CFHD_ERROR_OKAY;
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ block_t *p_block = NULL;
+
+ // Check picture datas
+ if (!p_pic)
+ return NULL;
+
+ // Prepare encoding process
+ if (p_sys->sampleCount == 0)
+ {
+ // Initialize the encoder with the height of each channel (note: disabled, assume no multi video track)
+ //int videochannel_gap = 0;
+ //int encodedHeight = (p_sys->videoHeight - videochannel_gap) / p_sys->videoChannels;
+
+ // Get pixel size in byte
+ errorCode = CFHD_GetPixelSize(p_sys->internalBufferPixelFormat, &(p_sys->internalBufferPixelSize));
+ if (errorCode)
+ {
+ msg_Warn(p_enc, "CFHD_GetPixelSize(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return NULL;
+ }
+
+ // To encode CineForm footage, the frame width must be evenly divisible by 16 and the frame height by 8.
+ // Dimensions that do not meet these requirements are rounded to a valid value.
+
+ if (p_sys->videoWidth % 16 != 0)
+ {
+ msg_Warn(p_enc, "CineForm video width is not a multiple of 16");
+ p_sys->internalBufferWidth = ceil((double)(p_sys->videoWidth) / 16.0) * 16;
+ }
+ else
+ p_sys->internalBufferWidth = p_sys->videoWidth;
+
+ if (p_sys->videoHeight % 8 != 0)
+ {
+ msg_Warn(p_enc, "CineForm video height is not a multiple of 8");
+ p_sys->internalBufferHeight = ceil((double)(p_sys->videoHeight) / 8.0) * 8;
+ }
+ else
+ p_sys->internalBufferHeight = p_sys->videoHeight;
+
+ // Compute
+ p_sys->internalBufferPitch = p_sys->internalBufferWidth * p_sys->internalBufferPixelSize;
+ p_sys->internalBufferSizeComputed = p_sys->internalBufferHeight * p_sys->internalBufferPitch;
+
+ // Prepare for encoding frames
+ errorCode = CFHD_PrepareToEncode(p_sys->encoderRef,
+ p_sys->internalBufferWidth,
+ p_sys->internalBufferHeight,
+ p_sys->internalBufferPixelFormat,
+ p_sys->encFormat,
+ p_sys->encFlags,
+ p_sys->encQuality);
+ if (errorCode != CFHD_ERROR_OKAY)
+ {
+ msg_Err(p_enc, "CFHD_PrepareToEncode(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return NULL;
+ }
+
+ // Allocate a buffer for the decoded frame
+ if (p_sys->internalBufferSizeComputed > p_sys->internalBufferSize)
+ {
+ if (p_sys->internalBuffer && !p_sys->directMapping)
+ {
+ free(p_sys->internalBuffer);
+ p_sys->internalBuffer = NULL;
+ }
+
+ // Allocate a new one
+ p_sys->internalBuffer = (char *)malloc(p_sys->internalBufferSizeComputed);
+ if (p_sys->internalBuffer == NULL)
+ {
+ msg_Err(p_enc, "Allocate internal output buffer FAILED");
+ return NULL;
+ }
+ else
+ {
+ p_sys->internalBufferSize = p_sys->internalBufferSizeComputed;
+ }
+ }
+
+ // Output params
+ p_enc->fmt_out.video.i_width = p_sys->internalBufferWidth;
+ p_enc->fmt_out.video.i_height = p_sys->internalBufferHeight;
+ p_enc->fmt_out.video.i_visible_width = p_sys->videoWidth;
+ p_enc->fmt_out.video.i_visible_height = p_sys->videoHeight;
+ }
+
+ if (p_enc->fmt_in.i_codec == VLC_CODEC_YUYV &&
+ p_sys->internalBufferPixelFormat == CFHD_PIXEL_FORMAT_YUY2)
+ {
+ p_sys->internalBuffer = (char *)(p_pic->p[0].p_pixels);
+ p_sys->directMapping = true;
+ }
+ else if (p_enc->fmt_in.i_codec == VLC_CODEC_BGRA &&
+ p_sys->internalBufferPixelFormat == CFHD_PIXEL_FORMAT_BGRa)
+ {
+ p_sys->internalBuffer = (char *)(p_pic->p[0].p_pixels);
+ p_sys->directMapping = true;
+ }
+ else
+ {
+ msg_Err(p_enc, "Unhandled picture input format");
+ return NULL;
+ }
+
+ // Encode the video frame
+ errorCode = CFHD_EncodeSample(p_sys->encoderRef, p_sys->internalBuffer, p_sys->internalBufferPitch);
+ if (errorCode)
+ {
+ msg_Err(p_enc, "CFHD_EncodeSample(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return NULL;
+ }
+
+ // Get the encoded frame from the encoder
+ unsigned char *sampleBuffer;
+ size_t sampleSize;
+
+ errorCode = CFHD_GetSampleData(p_sys->encoderRef, (void **)&sampleBuffer, &sampleSize);
+ if (errorCode)
+ {
+ msg_Err(p_enc, "CFHD_GetSampleData(error: '%i' / '%s')", errorCode, getErrorString(errorCode));
+ return NULL;
+ }
+
+ p_block = block_Alloc(sampleSize);
+ if (!p_block)
+ {
+ return NULL;
+ }
+
+ memcpy(p_block->p_buffer, sampleBuffer, sampleSize);
+ p_block->i_pts = p_block->i_dts = p_pic->date;
+
+ p_sys->sampleCount++;
+ return p_block;
+}
+
+#endif // ENABLE_SOUT
--
2.18.0
More information about the vlc-devel
mailing list