-/*
+/**
+ *
+ * MLT SDI Consumer:
+ * request video and audio data from MLT and generate an SDI stream
+ *
+ * Copyright (C) Broadcasting Center Europe S.A. http://www.bce.lu
+ * an RTL Group Company http://www.rtlgroup.com
+ * All rights reserved.
+ *
+ * E-mail: support_plasec@bce.lu
*
- * MLT SDI Consumer: request video and audio data from MLT and generate an SDI stream
- * Copyright (C) 2008 Broadcasting Center Europe S.A. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*
- *
* DESCRIPTION:
* This software act as interface between the MLT Frameworkas as
- * MLT Consumer and the Linear Systems Ltd. SMPTE 259M-C boards.
+ * MLT Consumer and the Linear Systems Ltd. SMPTE 292M and SMPTE 259M-C boards.
*
* Linear Systems can be contacted at http://www.linsys.ca
*
- * ---------------------------------------------------------------
+ **********************************************************************************
* System : INTeL I686 64Bit
- * OS : Linux SuSE Kernel 2.6.27.23-0.1-default
- * Compiler: gcc (c++)
- * ---------------------------------------------------------------
- * Project : MLT SDI Consumer for SD
+ * OS : Linux SuSE Kernel 2.6.27.39-0.2-default
+ * Compiler: gcc 4.3.2 (c++)
+ **********************************************************************************
+ * Project : MLT SDI Consumer for SD and HD
* Started by : Thomas Kurpick, Dipl.Inf. (FH)
- * ---------------------------------------------------------------
- * This program writes an SDI stream to the SDI driver provided
- * device files. E.g. SDI device file:
- * /dev/sditx0 (SD)
- * /dev/sditx1 (SD)
- * /dev/sditx2 (SD)
- * /dev/sditx4 (SD)
+ **********************************************************************************
+ * Supported and tested boards for SD-SDI or HD-SDI Output:
+ * PCI SDI Master™ (model 107)
+ * PCIe SDI Master™ (model 159)
+ * PCIe LP SDI Master™ FD (model 145)
+ * PCIe LP SDI Master™ Quad/o (model 180)
+ * PCIe LP HD-SDI Master™ O (model 193)
*
- * Tested with:
- * SDI Master™ PCI
- * SDI Master™ PCIe
- * SDI Master™ FD PCIe LP
- * SDI Master™ Quad/o PCIe LP
+ * Note: PCIe LP HD-SDI Master™ O (model 193) is an VidPort model and supports an
+ * seperate video and audio interface. Device file:
+ * /dev/sdivideotx[] for active video data
+ * /dev/sdiaudiotx[] for pcm audio data
+ *
+ * This mlt consumer use the following device files:
+ * /dev/sditx[] (SD-PAL) up to 8 x AES (8 x stereo / 16 audio channels)
+ * /dev/sdivideotx[] (HD)
+ * /dev/sdiaudiotx[] (HD) up to 4 x AES (4 x stereo / 8 audio channels)
+ *
+ *
+ **********************************************************************************
+ * Last modified by:
+ * Thomas Kurpick 08.Jan.2010
+ * and
+ * Dan Dennedy 10.Feb.2010
+ * Ver. 2.0
+ * See also Git commit log.
+ *
+ **********************************************************************************
+ *
+ * Consumer properties:
+ * 'dev_video'
+ * 'dev_audio'
+ * 'blanking'
+ * Only to monitor the SDI output a beta version of jpeg-writer is implemented.
+ * 'jpeg_files' a number for output interval
+ * 'save_jpegs' path for image
+ *
+ * EXAMPLE:
+ *
+ * SDI boards with full frame stream (with blanking):
+ * melt video.dv -consumer sdi:/dev/sditx0
+ * melt video.dv -consumer sdi:/dev/sditx0 blanking=true
+ * melt video.dv -consumer sdi dev_video=/dev/sditx0 blanking=true
+ * melt video.dv audio_index=all -consumer sdi dev_video=/dev/sditx0 blanking=true
+ *
+ * SDI boards without full frame stream (without blanking):
+ * melt -profile atsc_1080i_50 video.mpeg audio_index=1 -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false
+ * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false
+ * melt -profile atsc_1080i_50 video.mpeg audio_index=all -consumer sdi dev_video=/dev/sdivideotx0 dev_audio=/dev/sdiaudiotx0 blanking=false jpeg_files=25 save_jpegs=channel_04.jpg
*
- */
-
-/*
- * consumer_SDIstream.c
- * Consumer for MLT Framework > 0.2.4 (release 0.2.4 does NOT work, profiles where not implemented)
*
- * Example:
- * inigo video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
- * melt video.dv -consumer linsys_sdi:/dev/sditx0 buffer=0;
+ * SDI output formats and MLT profiles:
+ * #####################################################################################################################################################
+ * ########## SMPTE 274M 1920 x 1080 Image Sample Structure ############################################################################################
+ * #####################################################################################################################################################
+ * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model)
+ * 4 1920 x 1080/60/I interlaced 30 HZ 4 x AES (8 channels) atsc_1080i_60 193
+ * 5 1920 x 1080/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080i_5994 193
+ * 6 1920 x 1080/50/I interlaced 25 HZ 4 x AES (8 channels) atsc_1080i_50 193
+ * 7 1920 x 1080/30/P progressive 30 HZ 4 x AES (8 channels) atsc_1080p_30 193
+ * 8 1920 x 1080/29.97/P progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_1080p_2997 193
+ * 9 1920 x 1080/25/P progressive 25 HZ 4 x AES (8 channels) atsc_1080p_25 193
+ * 10 1920 x 1080/24/P progressive 24 HZ 4 x AES (8 channels) atsc_1080p_24 193
+ * 11 1920 x 1080/23.98/P progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_1080p_2398 193
*
- */
+ * #####################################################################################################################################################
+ * ########## SMPTE 296M 1280 × 720 Progressive Image Sample Structure #################################################################################
+ * #####################################################################################################################################################
+ * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model)
+ * 1 1280 × 720/60 progressive 60 HZ 4 x AES (8 channels) atsc_720p_60 193
+ * 2 1280 × 720/59.94 progressive 60000/1001 ~ 59.97 HZ 4 x AES (8 channels) atsc_720p_5994 193
+ * 3 1280 × 720/50 progressive 50 HZ 4 x AES (8 channels) atsc_720p_50 193
+ * 4 1280 × 720/30 progressive 30 HZ 4 x AES (8 channels) atsc_720p_30 193
+ * 5 1280 × 720/29.97 progressive 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) atsc_720p_2997 193
+ * 6 1280 × 720/25 progressive 25 HZ 4 x AES (8 channels) atsc_720p_25 193
+ * 7 1280 × 720/24 progressive 24 HZ 4 x AES (8 channels) atsc_720p_24 193
+ * 8 1280 × 720/23.98 progressive 24000/1001 ~ 23.98 HZ 4 x AES (8 channels) atsc_720p_2398 193
+ *
+ * #####################################################################################################################################################
+ * ########## SMPTE 125M 486i 29.97Hz & BT.656 576i 25Hz ###############################################################################################
+ * #####################################################################################################################################################
+ * System No. System nomenclature Form of scanning Frame rate Embedded Audio MLT profile Linsys board support (model)
+ * SD PAL 720 × 576/50/I interlaced 25 HZ 8 x AES (16 channels) dv_pal 180,145,159,107
+ * SD PAL 720 × 576/50/I interlaced 25 HZ 4 x AES (8 channels) dv_pal 193
+ * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 8 x AES (16 channels) sdi_486i_5994 TODO:180,145,159,107
+ * SD NTSC 720 × 486/59.94/I interlaced 30000/1001 ~ 29.97 HZ 4 x AES (8 channels) sdi_486i_5994 193
+ *
+ **/
-#include <framework/mlt_frame.h>
#include <framework/mlt_consumer.h>
-#include <stdio.h>
+#include <framework/mlt_frame.h>
+#include <framework/mlt_profile.h>
+#include <framework/mlt_log.h>
+#include <framework/mlt_events.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <stdint.h>
#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
-#include "sdi_generator.c"
-
-// definitions
+#ifdef WITH_JPEG
+// for JPEG output
+#include <jpeglib.h>
+#endif
-#define MAX_AUDIO_SAMPLES (1920*2) // 1920 Samples per Channel and we have stereo (48000 Hz /25 fps = 1920 Samples per frame)
-#define PAL_HEIGHT 576 // only used for MAX_FRAMESIZE
-#define PAL_WIDTH 720 // only used for MAX_FRAMESIZE
-#define MAX_FRAMESIZE (PAL_HEIGHT*PAL_WIDTH*2) // SDI frame size, (2 Pixels are represented by 4 bytes, yuyv422)
+#include "sdi_generator.c"
// alias for "struct consumer_SDIstream_s *" , now we can write "consumer_SDIstream". Makes it more readable...
typedef struct consumer_SDIstream_s *consumer_SDIstream;
struct consumer_SDIstream_s {
-
+ // Most of these values are set to their defaults by the parent consumer
struct mlt_consumer_s parent; // This is the basic Consumer from which we fetch our data
mlt_image_format pix_fmt; // Must be mlt_image_yuv422 for SDI
- mlt_audio_format aformat; // default: mlt_audio_pcm
+
int width;
int height;
- int frequency;
- int samples;
- int channels;
- char *path_destination_sdi; // Path for SDI output
- uint8_t video_buffer[MAX_FRAMESIZE]; // our picture for this frame
- int16_t audio_buffer0[MAX_AUDIO_SAMPLES]; // our audio channelpair 1 for this frame
-};
-/** Forward references to static functions.
- */
+ struct audio_format audio_format;
+ /**
+ * device file:
+ * /dev/sditx0
+ * /dev/sdivideotx0
+ * /dev/sdiaudiotx0
+ **/
+ char *device_file_video; // Path for SDI output
+ char *device_file_audio; // Path for exlusive SDI audio output
+
+ /**
+ * write own HANC (ancillary data) is available for:
+ * SDI board ASSY 193 HD 'blanking=false'
+ * SDI board ASSY 180 SD quad 'blanking=true'
+ * SDI board ASSY 145 SD single 'blanking=true'
+ *
+ * 0=false, 1=true
+ *
+ **/
+ uint8_t blanking;
+
+ // our audio channel pair for this frame
+ int16_t audio_buffer[MAX_AUDIO_STREAMS][MAX_AUDIO_SAMPLES]; // The SDI audio channel pairs for this frame
+
+ char *video_fmt_name; // 1080i25, 1080p25, 576i50, 486i2997, ...
+
+};
+/**
+ * Forward references to static functions.
+ **/
static int consumer_start(mlt_consumer this);
static int consumer_stop(mlt_consumer this);
static int consumer_is_stopped(mlt_consumer this);
static void consumer_close(mlt_consumer parent);
static void *consumer_thread(void *);
-//*****************************************************************************************************
-//*****************************************************************************************************
-//*******************************************SDI Master Consumer***************************************
-//*****************************************************************************************************
-//*****************************************************************************************************
+static void consumer_write_JPEG(char * path, uint8_t **vBuffer, mlt_profile myProfile);
+int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb);
+/*****************************************************************************************************
+ ****************************************** SDI Master Consumer **************************************
+ *****************************************************************************************************/
-/**
- * This is what will be called by the factory
+/** This is what will be called by the factory
* @param profile: profile name for consumer
* @param type: unused
* @param *id: unused
mlt_consumer consumer_SDIstream_init(mlt_profile profile, mlt_service_type type, const char *id, char *arg) {
// Create the consumer object
- consumer_SDIstream this = calloc(sizeof(struct consumer_SDIstream_s), 1);
+ consumer_SDIstream this = calloc( 1, sizeof(struct consumer_SDIstream_s) );
// If malloc and consumer init ok
if (this != NULL && mlt_consumer_init(&this->parent, this, profile) == 0) {
+
// Get the parent consumer object
mlt_consumer parent = &this->parent;
// Set output path for SDI, default is "/dev/sditx0"
if (arg == NULL) {
- this->path_destination_sdi = strdup("/dev/sditx0");
+ this->device_file_video = strdup("/dev/sditx0");
} else {
- this->path_destination_sdi = strdup(arg);
+ this->device_file_video = strdup(arg);
}
// Set up start/stop/terminated callbacks
parent->stop = consumer_stop;
parent->is_stopped = consumer_is_stopped;
+ // Set explicit to zero or other value
+ int i, j;
+ for (i = 0; i < MAX_AUDIO_STREAMS; i++) {
+ for (j = 0; j < MAX_AUDIO_SAMPLES; j++) {
+ this->audio_buffer[i][j] = j;
+ }
+ }
+
+ mlt_events_register( MLT_CONSUMER_PROPERTIES(parent), "consumer-fatal-error", NULL );
+
// Return the consumer produced
return parent;
}
* Start the consumer.
**/
static int consumer_start(mlt_consumer parent) {
+
// Get the properties
mlt_properties properties = mlt_consumer_properties(parent);
}
/**
- * Stop the consumer.
+ * Stop the consumer
**/
static int consumer_stop(mlt_consumer parent) {
// Check that we're running
if (mlt_properties_get_int(properties, "running")) {
+
// Get the threads
pthread_t *consumer_pthread = mlt_properties_get_data(properties, "consumer_pthread", NULL);
// Wait for termination
pthread_join(*consumer_pthread, NULL);
+
}
return 0;
}
/**
- * Determine if the consumer is stopped.
+ * Determine if the consumer is stopped
**/
static int consumer_is_stopped(mlt_consumer this) {
// Get the properties
* Threaded wrapper for pipe.
**/
static void *consumer_thread(void *arg) {
+
// Identify the arg
consumer_SDIstream this = arg;
mlt_consumer consumer = &this->parent;
// Convenience functionality (this is to stop melt/inigo after the end of a playout)
- int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES(consumer), "terminate_on_pause");
- int terminated = 0;
+ int terminate_on_pause = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "terminate_on_pause");
+ int terminated = 0; // save ony status
- // Define a frame pointer
- mlt_frame frame;
+ int save_jpegs = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "save_jpegs");
+ char * jpeg_folder = mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "jpeg_file");
- if (!sdimaster_init(this->path_destination_sdi, 0)) {
- exit(0);
+ // If no folder is specified, skip jpeg export
+ if (jpeg_folder == NULL) {
+ save_jpegs = 0;
}
+ if (save_jpegs > 0)
+ mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Saving a JPEG every %i frame.\n", save_jpegs);
+
+ int counter = 0; // each second we save a Jpeg
+
+ // set properties (path) for device files
+ if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video") != NULL) {
+ this->device_file_video = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_video"));
+ }
+ if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio") != NULL) {
+ if (this->blanking == 0) {
+ this->device_file_audio = strdup(mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "dev_audio"));
+ } else {
+ // if we write HANC we do not write further audio data
+ mlt_log_info(MLT_CONSUMER_SERVICE(consumer), "Audio device file is set but will not be used.\n");
+ }
+ }
+
+ // Set additional device file defaults
+ struct stat st;
+ int fd = -1;
+ if (this->device_file_video)
+ fd = stat(this->device_file_video, &st);
+ if (fd == -1) {
+ if (this->device_file_video)
+ free(this->device_file_video);
+ this->device_file_video = strdup("/dev/sdivideotx0");
+ } else {
+ close(fd);
+ }
+ if (this->device_file_audio) {
+ fd = stat(this->device_file_audio, &st);
+ if (fd == -1) {
+ if (this->device_file_audio)
+ free(this->device_file_audio);
+ this->device_file_audio = strdup("/dev/sdiaudiotx0");
+ } else {
+ close(fd);
+ }
+ } else if (this->device_file_video &&
+ strstr(this->device_file_video, "sdivideotx")) {
+ if (this->device_file_audio)
+ free(this->device_file_audio);
+ this->device_file_audio = strdup("/dev/sdiaudiotx0");
+ }
+
+ // set blanking flag; is not nessary we write no own blanking(HANC) for HD board ASSY 193
+ if (mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking")) {
+ // set value
+ if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "false")) {
+ this->blanking = 0;
+ } else if (!strcmp( mlt_properties_get(MLT_CONSUMER_PROPERTIES( consumer ), "blanking"), "true")) {
+ this->blanking = 1;
+ } else {
+ this->blanking = mlt_properties_get_int(MLT_CONSUMER_PROPERTIES( consumer ), "blanking");
+ }
+ } else if (this->device_file_video && strstr(this->device_file_video, "sdivideotx")) {
+ this->blanking = 0;
+ } else {
+ // set default value without HD board, also with blanking
+ this->blanking = 1;
+ }
+
+ // Define a frame pointer
+ mlt_frame frame;
+
// set Datablock number for SDI encoding
int my_dbn = 1;
+ double fps = mlt_properties_get_double(MLT_CONSUMER_PROPERTIES(consumer), "fps");
+ unsigned int count = 0;
+
+ // Tell the framework how we want our audio and video
+ int frequency = this->audio_format.sample_rate;
+ int channels = mlt_properties_get_int( MLT_CONSUMER_PROPERTIES(consumer), "channels" );
+ int samples;
+
+ // set number of audio channels, linsys vidport model 193 is limited to 8 channels (4AES frames)
+ this->audio_format.channels = 8; /* 0,2,4,6,8 */
+ this->audio_format.aformat = mlt_audio_s16; /* 16, 24, 32 */
+ this->audio_format.sample_rate = 48000;
+ this->pix_fmt = mlt_image_yuv422;
+
+ if (this->device_file_video && this->device_file_audio &&
+ !sdi_init(this->device_file_video, this->device_file_audio, this->blanking, mlt_service_profile((mlt_service) consumer), &this->audio_format)) {
+ mlt_log_fatal( MLT_CONSUMER_SERVICE(consumer), "failed to initialize\n" );
+ mlt_events_fire( MLT_CONSUMER_PROPERTIES(consumer), "consumer-fatal-error", NULL );
+ }
+
+ uint8_t *video_buffer;
+ int16_t *audio_buffer_tmp; // the upstream audio buffer
+
// Loop until told not to
while (!consumer_is_stopped(consumer) && terminated == 0) { //
// Check for termination
if (terminate_on_pause && frame != NULL) {
- terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "_speed") == 0.0;
+ terminated = mlt_properties_get_double(MLT_FRAME_PROPERTIES( frame ), "_speed") == 0.0;
if (terminated == 1) {
- printf("\nEnd of playout reached, terminating\n");
- fflush(stdout);
+ mlt_log_verbose(MLT_CONSUMER_SERVICE(consumer), "\nEnd of playout reached, terminating\n");
consumer_stop(consumer);
}
}
if (mlt_properties_get_int(mlt_frame_properties(frame), "rendered") == 1) {
// Get the video from this frame and save it to our video_buffer
- mlt_frame_get_image(frame, &this->video_buffer, &this->pix_fmt, &this->width, &this->height, 1);
+ mlt_frame_get_image(frame, &video_buffer, &this->pix_fmt, &this->width, &this->height, 1);
// Get the audio from this frame and save it to our audio_buffer
- //mlt_frame_get_audio(frame, &this->audio_buffer0, &this->aformat, &this->frequency, &this->channels, &this->samples );
- mlt_frame_get_audio(frame, &this->audio_buffer0, &this->aformat, &this->frequency, &this->channels, &this->samples);
-
- //printf("\n &this->channels: %i\n", this->channels);
-
- // Tell the sdi_generator.c to playout our frame
- // 8 audio streams with 2 stereo channels are possible
- // copy first to all audio stream, because no second or more audio streams are currently available (we think)
- // TODO struct params with <n> audio buffer and properties (size_of)
- if (this->video_buffer) {
-
- my_dbn = sdimaster_playout(&this->video_buffer, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0,
- &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, &this->audio_buffer0, sizeof(this->video_buffer),
- sizeof(this->audio_buffer0), 1, my_dbn);
- } else
- printf("SDI-Consumer: Videobuffer was NULL, skipping playout!\n");
-
+ samples = mlt_sample_calculator(fps, frequency, count++);
+ mlt_frame_get_audio(frame, (void**) &audio_buffer_tmp, &this->audio_format.aformat, &frequency, &channels, &samples);
+
+ this->audio_format.sample_rate = frequency;
+ this->audio_format.samples = samples;
+
+ /* TODO: Audio is currently hard coded to 8 channels because write 8 channels to the sdi board.
+ The Linys SDI board has to be configured with the same number of channels!
+ this->audio_format.channels = channels; // take given number of channels
+ */
+
+ /* Tell the sdi_generator.c to playout our frame
+ * 8 AES (8 x stereo channels are possible, max. 16 channels) Linsys SD board model: 107, 159, 145, 180
+ * 4 AES (4 x stereo channels are possible, max. 8 channels) Linsys HD board model: 193
+ */
+ if (video_buffer) {
+
+ // provide mapping of audio channels
+ int i, j = 0;
+ int map_channels, map_start;
+
+ for (i = 0; i < MAX_AUDIO_STREAMS && j < channels; i++) {
+ char key[27];
+ int c;
+
+ sprintf(key, "meta.map.audio.%d.channels", i);
+ map_channels = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
+ sprintf(key, "meta.map.audio.%d.start", i);
+ map_start = mlt_properties_get_int(MLT_FRAME_PROPERTIES(frame), key);
+
+ if (!map_channels)
+ map_channels = channels - j;
+ for (c = 0; c < map_channels && j < channels; c++, j++) {
+ int16_t *src = audio_buffer_tmp + j;
+ int16_t *dest = this->audio_buffer[(map_start + c) / 2] + (map_start + c) % 2;
+ int s = samples + 1;
+
+ while (--s) {
+ *dest = *src;
+ dest += 2;
+ src += channels;
+ }
+ }
+ }
+
+ // generate SDI frame and playout
+ my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
+
+ // write a JPEG of every X-th frame
+ if (save_jpegs > 0 && counter >= save_jpegs) {
+ consumer_write_JPEG(jpeg_folder, &video_buffer, mlt_service_profile((mlt_service) consumer));
+ counter = 0;
+ } else if (save_jpegs > 0) {
+ counter++;
+ }
+ mlt_events_fire(MLT_CONSUMER_PROPERTIES( consumer ), "consumer-frame-show", frame, NULL );
+ } else {
+ mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
+ }
} else {
- printf("WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
+ mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "WARNING the requested frame is not yet rendered! This will cause image disturbance!\n");
+
+ if (video_buffer) {
+ my_dbn = sdi_playout(video_buffer, this->audio_buffer, &this->audio_format, (channels + 1) / 2, my_dbn);
+ } else {
+ mlt_log_warning(MLT_CONSUMER_SERVICE(consumer), "Videobuffer was NULL, skipping playout!\n");
+ }
}
if (frame != NULL)
mlt_frame_close(frame);
}
+
}
return NULL;
}
// Get the actual object
consumer_SDIstream this = parent->child;
- free(this->path_destination_sdi);
+ free(this->device_file_video);
+ free(this->device_file_audio);
// Now clean up the rest (the close = NULL is a bit nasty but needed for now)
parent->close = NULL;
// Finally clean up this
free(this);
}
+
+/**
+ * Write videobuffer as JPEG to path
+ * @param path
+ **/
+static void consumer_write_JPEG(char * filename, uint8_t **vBuffer, mlt_profile myProfile) {
+
+#ifdef WITH_JPEG
+
+ int bytes_per_pixel = 3; // or 1 for GRACYSCALE images
+ int color_space = JCS_RGB; // or JCS_GRAYSCALE for grayscale images
+
+ uint8_t * buffer_position = *vBuffer;
+ uint8_t image_rgb[myProfile->width * myProfile->height * bytes_per_pixel];
+
+ //convert vBuffer to RGB
+ int i;
+ for (i = 0; i < sizeof(image_rgb) / 6; i++) {
+ int y1 = *(buffer_position++);
+ int cb = *(buffer_position++);
+ int y2 = *(buffer_position++);
+ int cr = *(buffer_position++);
+ convertYCBCRtoRGB(y1, cb, cr, y2, &image_rgb[i * 6]);
+ }
+
+ struct jpeg_compress_struct cinfo;
+ struct jpeg_error_mgr jerr;
+
+ // this is a pointer to one row of image data
+ JSAMPROW row_pointer[1];
+
+ FILE *outfile = fopen(filename, "wb");
+
+ if (!outfile) {
+ mlt_log_error(NULL, "%s: Error opening output jpeg file %s\n!", __FILE__, filename);
+ return;
+ }
+ cinfo.err = jpeg_std_error(&jerr);
+ jpeg_create_compress(&cinfo);
+ jpeg_stdio_dest(&cinfo, outfile);
+
+ // Setting the parameters of the output file here
+ cinfo.image_width = myProfile->width;
+ cinfo.image_height = myProfile->height;
+ cinfo.input_components = bytes_per_pixel;
+ cinfo.in_color_space = (J_COLOR_SPACE) color_space;
+
+ // default compression parameters, we shouldn't be worried about these
+ jpeg_set_defaults(&cinfo);
+
+ // Now do the compression
+ jpeg_start_compress(&cinfo, TRUE);
+
+ // like reading a file, this time write one row at a time
+ while (cinfo.next_scanline < cinfo.image_height) {
+ row_pointer[0] = &image_rgb[cinfo.next_scanline * cinfo.image_width * cinfo.input_components];
+ jpeg_write_scanlines(&cinfo, row_pointer, 1);
+ }
+ // similar to read file, clean up after we're done compressing
+ jpeg_finish_compress(&cinfo);
+ jpeg_destroy_compress(&cinfo);
+ fclose(outfile);
+#endif
+}
+
+/**
+ * converts YCbCr samples to two 32-bit RGB values
+ * @param y1 cb cr and y2 values
+ * @param target pointer
+ * @return 0 upon success
+ **/
+int convertYCBCRtoRGB(int y1, int cb, int cr, int y2, uint8_t * target_rgb) {
+
+#ifdef WITH_JPEG
+
+ if(y1 > 235)
+ y1 = 235;
+ if(y1 < 16)
+ y1 = 16;
+
+ if(y2 > 235)
+ y2 = 235;
+ if(y2 < 16)
+ y2 = 16;
+
+ if(cr > 240)
+ cr = 240;
+ if(cr < 16)
+ cr = 16;
+
+ if(cb > 240)
+ cb = 240;
+ if(cb < 16)
+ cb = 16;
+
+ uint8_t r1, g1, b1, r2, g2, b2;
+
+ //pointer to current output buffer position
+ uint8_t * target_pointer = target_rgb;
+
+ r1 = y1 + 1.402 * (cr - 128);
+ g1 = y1 - 0.34414 * (cb - 128) - 0.71414 * (cr - 128);
+ b1 = y1 + 1.772 * (cb - 128);
+
+ r2 = y2 + 1.402 * (cr - 128);
+ g2 = y2 - 0.34414 * (cb - 128) - 0.71414 *(cr - 128);
+ b2 = y2 + 1.772 * (cb - 128);
+
+ *target_pointer++ = r1;
+ *target_pointer++ = g1;
+ *target_pointer++ = b1;
+
+ *target_pointer++ = r2;
+ *target_pointer++ = g2;
+ *target_pointer++ = b2;
+
+ return 0;
+#endif
+ return 1;
+}