gstreamer/ext/openjpeg/gstopenjpegenc.c
Stéphane Cerveau 0d0e89108b jpeg2000parse + openjpeg: Switch striped mode to its own caps
It's not compatible with any other element that use the non-striped
mode. In addition, in this mode, we require that every frame have the
same number of stripes or that the MARKER bit be present, which is
different from the other
formats too.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/979>
2021-09-20 15:11:03 +00:00

1452 lines
43 KiB
C

/*
* Copyright (C) 2012 Collabora Ltd.
* Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
* Copyright (C) 2013 Sebastian Dröge <slomo@circular-chaos.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
/**
* SECTION:element-openjpegenc
* @title: openjpegenc
* @see_also: openjpegdec
*
* openjpegenc encodes raw video stream.
*
* ## Example launch lines
* |[
* gst-launch-1.0 -v videotestsrc num-buffers=10 ! openjpegenc ! jpeg2000parse ! openjpegdec ! videoconvert ! autovideosink sync=false
* ]| Encode and decode whole frames.
* |[
* gst-launch-1.0 -v videotestsrc num-buffers=10 ! openjpegenc num-threads=8 num-stripes=8 ! jpeg2000parse ! openjpegdec max-threads=8 ! videoconvert ! autovideosink sync=fals
* ]| Encode and decode frame split with stripes.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstopenjpegenc.h"
#include <gst/codecparsers/gstjpeg2000sampling.h>
#include <string.h>
#include <math.h>
GST_DEBUG_CATEGORY_STATIC (gst_openjpeg_enc_debug);
#define GST_CAT_DEFAULT gst_openjpeg_enc_debug
#define GST_OPENJPEG_ENC_TYPE_PROGRESSION_ORDER (gst_openjpeg_enc_progression_order_get_type())
static GType
gst_openjpeg_enc_progression_order_get_type (void)
{
static const GEnumValue values[] = {
{OPJ_LRCP, "LRCP", "lrcp"},
{OPJ_RLCP, "RLCP", "rlcp"},
{OPJ_RPCL, "RPCL", "rpcl"},
{OPJ_PCRL, "PCRL", "pcrl"},
{OPJ_CPRL, "CPRL", "crpl"},
{0, NULL, NULL}
};
static GType id = 0;
if (g_once_init_enter ((gsize *) & id)) {
GType _id;
_id = g_enum_register_static ("GstOpenJPEGEncProgressionOrder", values);
g_once_init_leave ((gsize *) & id, _id);
}
return id;
}
enum
{
PROP_0,
PROP_NUM_LAYERS,
PROP_NUM_RESOLUTIONS,
PROP_PROGRESSION_ORDER,
PROP_TILE_OFFSET_X,
PROP_TILE_OFFSET_Y,
PROP_TILE_WIDTH,
PROP_TILE_HEIGHT,
PROP_NUM_STRIPES,
PROP_NUM_THREADS,
PROP_LAST
};
#define DEFAULT_NUM_LAYERS 1
#define DEFAULT_NUM_RESOLUTIONS 6
#define DEFAULT_PROGRESSION_ORDER OPJ_LRCP
#define DEFAULT_TILE_OFFSET_X 0
#define DEFAULT_TILE_OFFSET_Y 0
#define DEFAULT_TILE_WIDTH 0
#define DEFAULT_TILE_HEIGHT 0
#define GST_OPENJPEG_ENC_DEFAULT_NUM_STRIPES 1
#define GST_OPENJPEG_ENC_DEFAULT_NUM_THREADS 0
/* prototypes */
static void gst_openjpeg_enc_finalize (GObject * object);
static GstStateChangeReturn
gst_openjpeg_enc_change_state (GstElement * element, GstStateChange transition);
static void gst_openjpeg_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_openjpeg_enc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static gboolean gst_openjpeg_enc_start (GstVideoEncoder * encoder);
static gboolean gst_openjpeg_enc_stop (GstVideoEncoder * encoder);
static gboolean gst_openjpeg_enc_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state);
static GstFlowReturn gst_openjpeg_enc_handle_frame (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame);
static gboolean gst_openjpeg_enc_propose_allocation (GstVideoEncoder * encoder,
GstQuery * query);
static GstFlowReturn gst_openjpeg_enc_encode_frame_multiple (GstVideoEncoder *
encoder, GstVideoCodecFrame * frame);
static GstFlowReturn gst_openjpeg_enc_encode_frame_single (GstVideoEncoder *
encoder, GstVideoCodecFrame * frame);
static GstOpenJPEGCodecMessage
* gst_openjpeg_encode_message_free (GstOpenJPEGCodecMessage * message);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define GRAY16 "GRAY16_LE"
#define YUV10 "Y444_10LE, I422_10LE, I420_10LE"
#else
#define GRAY16 "GRAY16_BE"
#define YUV10 "Y444_10BE, I422_10BE, I420_10BE"
#endif
static GstStaticPadTemplate gst_openjpeg_enc_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ ARGB64, ARGB, xRGB, "
"AYUV64, " YUV10 ", "
"AYUV, Y444, Y42B, I420, Y41B, YUV9, " "GRAY8, " GRAY16 " }"))
);
static GstStaticPadTemplate gst_openjpeg_enc_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("image/x-j2c, "
"width = (int) [1, MAX], "
"height = (int) [1, MAX], "
"num-components = (int) [1, 4], "
GST_JPEG2000_SAMPLING_LIST ","
GST_JPEG2000_COLORSPACE_LIST "; "
"image/x-jpc, "
"width = (int) [1, MAX], "
"height = (int) [1, MAX], "
"num-components = (int) [1, 4], "
"num-stripes = (int) [1, MAX], "
"alignment = (string) { frame, stripe }, "
GST_JPEG2000_SAMPLING_LIST ","
GST_JPEG2000_COLORSPACE_LIST "; "
"image/jp2, " "width = (int) [1, MAX], "
"height = (int) [1, MAX] ;"
"image/x-jpc-striped, "
"width = (int) [1, MAX], "
"height = (int) [1, MAX], "
"num-components = (int) [1, 4], "
GST_JPEG2000_SAMPLING_LIST ", "
GST_JPEG2000_COLORSPACE_LIST ", "
"num-stripes = (int) [2, MAX], stripe-height = (int) [1 , MAX]")
);
#define parent_class gst_openjpeg_enc_parent_class
G_DEFINE_TYPE (GstOpenJPEGEnc, gst_openjpeg_enc, GST_TYPE_VIDEO_ENCODER);
GST_ELEMENT_REGISTER_DEFINE (openjpegenc, "openjpegenc",
GST_RANK_PRIMARY, GST_TYPE_OPENJPEG_ENC);
static void
gst_openjpeg_enc_class_init (GstOpenJPEGEncClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
GstVideoEncoderClass *video_encoder_class;
gobject_class = (GObjectClass *) klass;
element_class = (GstElementClass *) klass;
video_encoder_class = (GstVideoEncoderClass *) klass;
gobject_class->set_property = gst_openjpeg_enc_set_property;
gobject_class->get_property = gst_openjpeg_enc_get_property;
gobject_class->finalize = gst_openjpeg_enc_finalize;
element_class->change_state =
GST_DEBUG_FUNCPTR (gst_openjpeg_enc_change_state);
g_object_class_install_property (gobject_class, PROP_NUM_LAYERS,
g_param_spec_int ("num-layers", "Number of layers",
"Number of layers", 1, 10, DEFAULT_NUM_LAYERS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_NUM_RESOLUTIONS,
g_param_spec_int ("num-resolutions", "Number of resolutions",
"Number of resolutions", 1, 10, DEFAULT_NUM_RESOLUTIONS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_PROGRESSION_ORDER,
g_param_spec_enum ("progression-order", "Progression Order",
"Progression order", GST_OPENJPEG_ENC_TYPE_PROGRESSION_ORDER,
DEFAULT_PROGRESSION_ORDER,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TILE_OFFSET_X,
g_param_spec_int ("tile-offset-x", "Tile Offset X",
"Tile Offset X", G_MININT, G_MAXINT, DEFAULT_TILE_OFFSET_X,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TILE_OFFSET_Y,
g_param_spec_int ("tile-offset-y", "Tile Offset Y",
"Tile Offset Y", G_MININT, G_MAXINT, DEFAULT_TILE_OFFSET_Y,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TILE_WIDTH,
g_param_spec_int ("tile-width", "Tile Width",
"Tile Width", 0, G_MAXINT, DEFAULT_TILE_WIDTH,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_TILE_HEIGHT,
g_param_spec_int ("tile-height", "Tile Height",
"Tile Height", 0, G_MAXINT, DEFAULT_TILE_HEIGHT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstOpenJPEGEnc:num-stripes:
*
* Number of stripes to use for low latency encoding . (1 = low latency disabled)
*
* Since: 1.18
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NUM_STRIPES,
g_param_spec_int ("num-stripes", "Number of stripes",
"Number of stripes for low latency encoding. (1 = low latency disabled)",
1, G_MAXINT, GST_OPENJPEG_ENC_DEFAULT_NUM_STRIPES,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstOpenJPEGEnc:num-threads:
*
* Max number of simultaneous threads to encode stripes, default: encode with streaming thread
*
* Since: 1.20
*/
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_NUM_THREADS,
g_param_spec_uint ("num-threads", "Number of threads",
"Max number of simultaneous threads to encode stripe or frame, default: encode with streaming thread.",
0, G_MAXINT, GST_OPENJPEG_ENC_DEFAULT_NUM_THREADS,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (element_class,
&gst_openjpeg_enc_src_template);
gst_element_class_add_static_pad_template (element_class,
&gst_openjpeg_enc_sink_template);
gst_element_class_set_static_metadata (element_class,
"OpenJPEG JPEG2000 encoder",
"Codec/Encoder/Video",
"Encode JPEG2000 streams",
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
video_encoder_class->start = GST_DEBUG_FUNCPTR (gst_openjpeg_enc_start);
video_encoder_class->stop = GST_DEBUG_FUNCPTR (gst_openjpeg_enc_stop);
video_encoder_class->set_format =
GST_DEBUG_FUNCPTR (gst_openjpeg_enc_set_format);
video_encoder_class->handle_frame =
GST_DEBUG_FUNCPTR (gst_openjpeg_enc_handle_frame);
video_encoder_class->propose_allocation = gst_openjpeg_enc_propose_allocation;
GST_DEBUG_CATEGORY_INIT (gst_openjpeg_enc_debug, "openjpegenc", 0,
"OpenJPEG Encoder");
gst_type_mark_as_plugin_api (GST_OPENJPEG_ENC_TYPE_PROGRESSION_ORDER, 0);
}
static void
gst_openjpeg_enc_init (GstOpenJPEGEnc * self)
{
GST_PAD_SET_ACCEPT_TEMPLATE (GST_VIDEO_ENCODER_SINK_PAD (self));
opj_set_default_encoder_parameters (&self->params);
self->params.cp_fixed_quality = 1;
self->params.cp_disto_alloc = 0;
self->params.cp_fixed_alloc = 0;
/*
* TODO: Add properties / caps fields for these
*
* self->params.csty;
* self->params.tcp_rates;
* self->params.tcp_distoratio;
* self->params.mode;
* self->params.irreversible;
* self->params.cp_cinema;
* self->params.cp_rsiz;
*/
self->params.tcp_numlayers = DEFAULT_NUM_LAYERS;
self->params.numresolution = DEFAULT_NUM_RESOLUTIONS;
self->params.prog_order = DEFAULT_PROGRESSION_ORDER;
self->params.cp_tx0 = DEFAULT_TILE_OFFSET_X;
self->params.cp_ty0 = DEFAULT_TILE_OFFSET_Y;
self->params.cp_tdx = DEFAULT_TILE_WIDTH;
self->params.cp_tdy = DEFAULT_TILE_HEIGHT;
self->params.tile_size_on = (self->params.cp_tdx != 0
&& self->params.cp_tdy != 0);
self->num_stripes = GST_OPENJPEG_ENC_DEFAULT_NUM_STRIPES;
g_cond_init (&self->messages_cond);
g_queue_init (&self->messages);
self->available_threads = GST_OPENJPEG_ENC_DEFAULT_NUM_THREADS;
}
static void
gst_openjpeg_enc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (object);
switch (prop_id) {
case PROP_NUM_LAYERS:
self->params.tcp_numlayers = g_value_get_int (value);
break;
case PROP_NUM_RESOLUTIONS:
self->params.numresolution = g_value_get_int (value);
break;
case PROP_PROGRESSION_ORDER:
self->params.prog_order = g_value_get_enum (value);
break;
case PROP_TILE_OFFSET_X:
self->params.cp_tx0 = g_value_get_int (value);
break;
case PROP_TILE_OFFSET_Y:
self->params.cp_ty0 = g_value_get_int (value);
break;
case PROP_TILE_WIDTH:
self->params.cp_tdx = g_value_get_int (value);
self->params.tile_size_on = (self->params.cp_tdx != 0
&& self->params.cp_tdy != 0);
break;
case PROP_TILE_HEIGHT:
self->params.cp_tdy = g_value_get_int (value);
self->params.tile_size_on = (self->params.cp_tdx != 0
&& self->params.cp_tdy != 0);
break;
case PROP_NUM_STRIPES:
self->num_stripes = g_value_get_int (value);
break;
case PROP_NUM_THREADS:
self->available_threads = g_value_get_uint (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_openjpeg_enc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (object);
switch (prop_id) {
case PROP_NUM_LAYERS:
g_value_set_int (value, self->params.tcp_numlayers);
break;
case PROP_NUM_RESOLUTIONS:
g_value_set_int (value, self->params.numresolution);
break;
case PROP_PROGRESSION_ORDER:
g_value_set_enum (value, self->params.prog_order);
break;
case PROP_TILE_OFFSET_X:
g_value_set_int (value, self->params.cp_tx0);
break;
case PROP_TILE_OFFSET_Y:
g_value_set_int (value, self->params.cp_ty0);
break;
case PROP_TILE_WIDTH:
g_value_set_int (value, self->params.cp_tdx);
break;
case PROP_TILE_HEIGHT:
g_value_set_int (value, self->params.cp_tdy);
break;
case PROP_NUM_STRIPES:
g_value_set_int (value, self->num_stripes);
break;
case PROP_NUM_THREADS:
g_value_set_uint (value, self->available_threads);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static gboolean
gst_openjpeg_enc_start (GstVideoEncoder * encoder)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (encoder);
GST_DEBUG_OBJECT (self, "Starting");
if (self->available_threads)
self->encode_frame = gst_openjpeg_enc_encode_frame_multiple;
else
self->encode_frame = gst_openjpeg_enc_encode_frame_single;
return TRUE;
}
static gboolean
gst_openjpeg_enc_stop (GstVideoEncoder * video_encoder)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (video_encoder);
GST_DEBUG_OBJECT (self, "Stopping");
if (self->output_state) {
gst_video_codec_state_unref (self->output_state);
self->output_state = NULL;
}
if (self->input_state) {
gst_video_codec_state_unref (self->input_state);
self->input_state = NULL;
}
GST_DEBUG_OBJECT (self, "Stopped");
return TRUE;
}
static void
gst_openjpeg_enc_flush_messages (GstOpenJPEGEnc * self)
{
GstOpenJPEGCodecMessage *enc_params;
GST_OBJECT_LOCK (self);
while ((enc_params = g_queue_pop_head (&self->messages))) {
gst_openjpeg_encode_message_free (enc_params);
}
g_cond_broadcast (&self->messages_cond);
GST_OBJECT_UNLOCK (self);
}
static void
gst_openjpeg_enc_finalize (GObject * object)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (object);
gst_openjpeg_enc_flush_messages (self);
g_cond_clear (&self->messages_cond);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static GstStateChangeReturn
gst_openjpeg_enc_change_state (GstElement * element, GstStateChange transition)
{
GstOpenJPEGEnc *self;
g_return_val_if_fail (GST_IS_OPENJPEG_ENC (element),
GST_STATE_CHANGE_FAILURE);
self = GST_OPENJPEG_ENC (element);
switch (transition) {
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_openjpeg_enc_flush_messages (self);
break;
default:
break;
}
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
static guint
get_stripe_height (GstOpenJPEGEnc * self, guint slice_num, guint frame_height)
{
guint nominal_stripe_height = frame_height / self->num_stripes;
return (slice_num <
self->num_stripes -
1) ? nominal_stripe_height : frame_height -
(slice_num * nominal_stripe_height);
}
static void
fill_image_packed16_4 (opj_image_t * image, GstVideoFrame * frame)
{
gint x, y, w, h;
const guint16 *data_in, *tmp;
gint *data_out[4];
gint sstride;
w = GST_VIDEO_FRAME_WIDTH (frame);
h = image->y1 - image->y0;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) / 2;
data_in =
(guint16 *) GST_VIDEO_FRAME_PLANE_DATA (frame, 0) + image->y0 * sstride;
data_out[0] = image->comps[0].data;
data_out[1] = image->comps[1].data;
data_out[2] = image->comps[2].data;
data_out[3] = image->comps[3].data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out[3] = tmp[0];
*data_out[0] = tmp[1];
*data_out[1] = tmp[2];
*data_out[2] = tmp[3];
tmp += 4;
data_out[0]++;
data_out[1]++;
data_out[2]++;
data_out[3]++;
}
data_in += sstride;
}
}
static void
fill_image_packed8_4 (opj_image_t * image, GstVideoFrame * frame)
{
gint x, y, w, h;
const guint8 *data_in, *tmp;
gint *data_out[4];
gint sstride;
w = GST_VIDEO_FRAME_WIDTH (frame);
h = image->y1 - image->y0;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
data_in =
(guint8 *) GST_VIDEO_FRAME_PLANE_DATA (frame, 0) + image->y0 * sstride;
data_out[0] = image->comps[0].data;
data_out[1] = image->comps[1].data;
data_out[2] = image->comps[2].data;
data_out[3] = image->comps[3].data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out[3] = tmp[0];
*data_out[0] = tmp[1];
*data_out[1] = tmp[2];
*data_out[2] = tmp[3];
tmp += 4;
data_out[0]++;
data_out[1]++;
data_out[2]++;
data_out[3]++;
}
data_in += sstride;
}
}
static void
fill_image_packed8_3 (opj_image_t * image, GstVideoFrame * frame)
{
gint x, y, w, h;
const guint8 *data_in, *tmp;
gint *data_out[3];
gint sstride;
w = GST_VIDEO_FRAME_WIDTH (frame);
h = image->y1 - image->y0;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
data_in =
(guint8 *) GST_VIDEO_FRAME_PLANE_DATA (frame, 0) + image->y0 * sstride;
data_out[0] = image->comps[0].data;
data_out[1] = image->comps[1].data;
data_out[2] = image->comps[2].data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out[0] = tmp[1];
*data_out[1] = tmp[2];
*data_out[2] = tmp[3];
tmp += 4;
data_out[0]++;
data_out[1]++;
data_out[2]++;
}
data_in += sstride;
}
}
static void
fill_image_planar16_3 (opj_image_t * image, GstVideoFrame * frame)
{
gint c, x, y, w, h;
const guint16 *data_in, *tmp;
gint *data_out;
gint sstride;
for (c = 0; c < 3; c++) {
opj_image_comp_t *comp = image->comps + c;
w = GST_VIDEO_FRAME_COMP_WIDTH (frame, c);
h = comp->h;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, c) / 2;
data_in =
(guint16 *) GST_VIDEO_FRAME_COMP_DATA (frame,
c) + (image->y0 / comp->dy) * sstride;
data_out = comp->data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out = *tmp;
data_out++;
tmp++;
}
data_in += sstride;
}
}
}
static void
fill_image_planar8_3 (opj_image_t * image, GstVideoFrame * frame)
{
gint c, x, y, w, h;
const guint8 *data_in, *tmp;
gint *data_out;
gint sstride;
for (c = 0; c < 3; c++) {
opj_image_comp_t *comp = image->comps + c;
w = GST_VIDEO_FRAME_COMP_WIDTH (frame, c);
h = comp->h;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, c);
data_in =
(guint8 *) GST_VIDEO_FRAME_COMP_DATA (frame,
c) + (image->y0 / comp->dy) * sstride;
data_out = comp->data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out = *tmp;
data_out++;
tmp++;
}
data_in += sstride;
}
}
}
static void
fill_image_planar8_1 (opj_image_t * image, GstVideoFrame * frame)
{
gint x, y, w, h;
const guint8 *data_in, *tmp;
gint *data_out;
gint sstride;
opj_image_comp_t *comp = image->comps;
w = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
h = comp->h;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0);
data_in =
(guint8 *) GST_VIDEO_FRAME_COMP_DATA (frame,
0) + (image->y0 / comp->dy) * sstride;
data_out = image->comps[0].data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out = *tmp;
data_out++;
tmp++;
}
data_in += sstride;
}
}
static void
fill_image_planar16_1 (opj_image_t * image, GstVideoFrame * frame)
{
gint x, y, w, h;
const guint16 *data_in, *tmp;
gint *data_out;
gint sstride;
opj_image_comp_t *comp = image->comps;
w = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0);
h = comp->h;
sstride = GST_VIDEO_FRAME_PLANE_STRIDE (frame, 0) / 2;
data_in =
(guint16 *) GST_VIDEO_FRAME_COMP_DATA (frame,
0) + (image->y0 / comp->dy) * sstride;
data_out = comp->data;
for (y = 0; y < h; y++) {
tmp = data_in;
for (x = 0; x < w; x++) {
*data_out = *tmp;
data_out++;
tmp++;
}
data_in += sstride;
}
}
static gboolean
gst_openjpeg_enc_set_format (GstVideoEncoder * encoder,
GstVideoCodecState * state)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (encoder);
GstCaps *allowed_caps, *caps;
GstStructure *s;
const gchar *colorspace = NULL;
GstJPEG2000Sampling sampling = GST_JPEG2000_SAMPLING_NONE;
gint ncomps;
gboolean stripe_mode =
self->num_stripes != GST_OPENJPEG_ENC_DEFAULT_NUM_STRIPES;
GST_DEBUG_OBJECT (self, "Setting format: %" GST_PTR_FORMAT, state->caps);
if (self->input_state)
gst_video_codec_state_unref (self->input_state);
self->input_state = gst_video_codec_state_ref (state);
if (stripe_mode) {
GstCaps *template_caps = gst_caps_new_empty_simple ("image/x-jpc-striped");
GstCaps *my_caps;
my_caps = gst_pad_query_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder),
template_caps);
gst_caps_unref (template_caps);
allowed_caps = gst_pad_peer_query_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder),
my_caps);
gst_caps_unref (my_caps);
if (gst_caps_is_empty (allowed_caps)) {
gst_caps_unref (allowed_caps);
GST_WARNING_OBJECT (self, "Striped JPEG 2000 not accepted downstream");
return FALSE;
}
self->codec_format = OPJ_CODEC_J2K;
self->is_jp2c = FALSE;
allowed_caps = gst_caps_truncate (allowed_caps);
s = gst_caps_get_structure (allowed_caps, 0);
} else {
allowed_caps =
gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder));
allowed_caps = gst_caps_truncate (allowed_caps);
s = gst_caps_get_structure (allowed_caps, 0);
if (gst_structure_has_name (s, "image/jp2")) {
self->codec_format = OPJ_CODEC_JP2;
self->is_jp2c = FALSE;
} else if (gst_structure_has_name (s, "image/x-j2c")) {
self->codec_format = OPJ_CODEC_J2K;
self->is_jp2c = TRUE;
} else if (gst_structure_has_name (s, "image/x-jpc")) {
self->codec_format = OPJ_CODEC_J2K;
self->is_jp2c = FALSE;
} else {
g_return_val_if_reached (FALSE);
}
}
switch (state->info.finfo->format) {
case GST_VIDEO_FORMAT_ARGB64:
self->fill_image = fill_image_packed16_4;
ncomps = 4;
break;
case GST_VIDEO_FORMAT_ARGB:
case GST_VIDEO_FORMAT_AYUV:
self->fill_image = fill_image_packed8_4;
ncomps = 4;
break;
case GST_VIDEO_FORMAT_xRGB:
self->fill_image = fill_image_packed8_3;
ncomps = 3;
break;
case GST_VIDEO_FORMAT_AYUV64:
self->fill_image = fill_image_packed16_4;
ncomps = 4;
break;
case GST_VIDEO_FORMAT_Y444_10LE:
case GST_VIDEO_FORMAT_Y444_10BE:
case GST_VIDEO_FORMAT_I422_10LE:
case GST_VIDEO_FORMAT_I422_10BE:
case GST_VIDEO_FORMAT_I420_10LE:
case GST_VIDEO_FORMAT_I420_10BE:
self->fill_image = fill_image_planar16_3;
ncomps = 3;
break;
case GST_VIDEO_FORMAT_Y444:
case GST_VIDEO_FORMAT_Y42B:
case GST_VIDEO_FORMAT_I420:
case GST_VIDEO_FORMAT_Y41B:
case GST_VIDEO_FORMAT_YUV9:
self->fill_image = fill_image_planar8_3;
ncomps = 3;
break;
case GST_VIDEO_FORMAT_GRAY8:
self->fill_image = fill_image_planar8_1;
ncomps = 1;
break;
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
self->fill_image = fill_image_planar16_1;
ncomps = 1;
break;
default:
g_assert_not_reached ();
}
/* sampling */
/* note: encoder re-orders channels so that alpha channel is encoded as the last channel */
switch (state->info.finfo->format) {
case GST_VIDEO_FORMAT_ARGB64:
case GST_VIDEO_FORMAT_ARGB:
sampling = GST_JPEG2000_SAMPLING_RGBA;
break;
case GST_VIDEO_FORMAT_AYUV64:
case GST_VIDEO_FORMAT_AYUV:
sampling = GST_JPEG2000_SAMPLING_YBRA4444_EXT;
break;
case GST_VIDEO_FORMAT_xRGB:
sampling = GST_JPEG2000_SAMPLING_RGB;
break;
case GST_VIDEO_FORMAT_Y444_10LE:
case GST_VIDEO_FORMAT_Y444_10BE:
case GST_VIDEO_FORMAT_Y444:
sampling = GST_JPEG2000_SAMPLING_YBR444;
break;
case GST_VIDEO_FORMAT_I422_10LE:
case GST_VIDEO_FORMAT_I422_10BE:
case GST_VIDEO_FORMAT_Y42B:
sampling = GST_JPEG2000_SAMPLING_YBR422;
break;
case GST_VIDEO_FORMAT_YUV9:
sampling = GST_JPEG2000_SAMPLING_YBR410;
break;
case GST_VIDEO_FORMAT_Y41B:
sampling = GST_JPEG2000_SAMPLING_YBR411;
break;
case GST_VIDEO_FORMAT_I420_10LE:
case GST_VIDEO_FORMAT_I420_10BE:
case GST_VIDEO_FORMAT_I420:
sampling = GST_JPEG2000_SAMPLING_YBR420;
break;
case GST_VIDEO_FORMAT_GRAY8:
case GST_VIDEO_FORMAT_GRAY16_LE:
case GST_VIDEO_FORMAT_GRAY16_BE:
sampling = GST_JPEG2000_SAMPLING_GRAYSCALE;
break;
default:
break;
}
if ((state->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_YUV)) {
colorspace = "sYUV";
} else if ((state->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB)) {
colorspace = "sRGB";
} else if ((state->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_GRAY)) {
colorspace = "GRAY";
} else
g_return_val_if_reached (FALSE);
if (stripe_mode) {
caps = gst_caps_new_simple ("image/x-jpc-striped",
"colorspace", G_TYPE_STRING, colorspace,
"sampling", G_TYPE_STRING, gst_jpeg2000_sampling_to_string (sampling),
"num-components", G_TYPE_INT, ncomps,
"num-stripes", G_TYPE_INT, self->num_stripes,
"stripe-height", G_TYPE_INT,
get_stripe_height (self, 0,
GST_VIDEO_INFO_COMP_HEIGHT (&state->info, 0)), NULL);
} else if (sampling != GST_JPEG2000_SAMPLING_NONE) {
caps = gst_caps_new_simple (gst_structure_get_name (s),
"colorspace", G_TYPE_STRING, colorspace,
"sampling", G_TYPE_STRING, gst_jpeg2000_sampling_to_string (sampling),
"num-components", G_TYPE_INT, ncomps, NULL);
} else {
caps = gst_caps_new_simple (gst_structure_get_name (s),
"colorspace", G_TYPE_STRING, colorspace,
"num-components", G_TYPE_INT, ncomps, NULL);
}
gst_caps_unref (allowed_caps);
if (self->output_state)
gst_video_codec_state_unref (self->output_state);
self->output_state =
gst_video_encoder_set_output_state (encoder, caps, state);
gst_video_encoder_negotiate (GST_VIDEO_ENCODER (encoder));
return TRUE;
}
static opj_image_t *
gst_openjpeg_enc_fill_image (GstOpenJPEGEnc * self, GstVideoFrame * frame,
guint slice_num)
{
gint i, ncomps, temp, min_height = INT_MAX;
opj_image_cmptparm_t *comps;
OPJ_COLOR_SPACE colorspace;
opj_image_t *image;
ncomps = GST_VIDEO_FRAME_N_COMPONENTS (frame);
comps = g_new0 (opj_image_cmptparm_t, ncomps);
for (i = 0; i < ncomps; i++) {
comps[i].prec = GST_VIDEO_FRAME_COMP_DEPTH (frame, i);
comps[i].bpp = GST_VIDEO_FRAME_COMP_DEPTH (frame, i);
comps[i].sgnd = 0;
comps[i].w = GST_VIDEO_FRAME_COMP_WIDTH (frame, i);
comps[i].dx =
(guint) ((float) GST_VIDEO_FRAME_WIDTH (frame) /
GST_VIDEO_FRAME_COMP_WIDTH (frame, i) + 0.5f);
comps[i].dy =
(guint) ((float) GST_VIDEO_FRAME_HEIGHT (frame) /
GST_VIDEO_FRAME_COMP_HEIGHT (frame, i) + 0.5f);
temp =
(GST_VIDEO_FRAME_COMP_HEIGHT (frame,
i) / self->num_stripes) * comps[i].dy;
if (temp < min_height)
min_height = temp;
}
for (i = 0; i < ncomps; i++) {
gint nominal_height = min_height / comps[i].dy;
comps[i].h = (slice_num < self->num_stripes) ?
nominal_height
: GST_VIDEO_FRAME_COMP_HEIGHT (frame,
i) - (self->num_stripes - 1) * nominal_height;
}
if ((frame->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_YUV))
colorspace = OPJ_CLRSPC_SYCC;
else if ((frame->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB))
colorspace = OPJ_CLRSPC_SRGB;
else if ((frame->info.finfo->flags & GST_VIDEO_FORMAT_FLAG_GRAY))
colorspace = OPJ_CLRSPC_GRAY;
else
g_return_val_if_reached (NULL);
image = opj_image_create (ncomps, comps, colorspace);
if (!image) {
GST_WARNING_OBJECT (self,
"Unable to create a JPEG image. first component height=%d",
ncomps ? comps[0].h : 0);
return NULL;
}
g_free (comps);
image->x0 = 0;
image->x1 = GST_VIDEO_FRAME_WIDTH (frame);
image->y0 = (slice_num - 1) * min_height;
image->y1 =
(slice_num <
self->num_stripes) ? image->y0 +
min_height : GST_VIDEO_FRAME_HEIGHT (frame);
self->fill_image (image, frame);
return image;
}
static void
gst_openjpeg_enc_opj_error (const char *msg, void *userdata)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (userdata);
gchar *trimmed = g_strchomp (g_strdup (msg));
GST_TRACE_OBJECT (self, "openjpeg error: %s", trimmed);
g_free (trimmed);
}
static void
gst_openjpeg_enc_opj_warning (const char *msg, void *userdata)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (userdata);
gchar *trimmed = g_strchomp (g_strdup (msg));
GST_TRACE_OBJECT (self, "openjpeg warning: %s", trimmed);
g_free (trimmed);
}
static void
gst_openjpeg_enc_opj_info (const char *msg, void *userdata)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (userdata);
gchar *trimmed = g_strchomp (g_strdup (msg));
GST_TRACE_OBJECT (self, "openjpeg info: %s", trimmed);
g_free (trimmed);
}
typedef struct
{
guint8 *data;
guint allocsize;
guint offset;
guint size;
} MemStream;
static OPJ_SIZE_T
read_fn (void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
g_return_val_if_reached (-1);
}
static OPJ_SIZE_T
write_fn (void *p_buffer, OPJ_SIZE_T p_nb_bytes, void *p_user_data)
{
MemStream *mstream = p_user_data;
if (mstream->offset + p_nb_bytes > mstream->allocsize) {
while (mstream->offset + p_nb_bytes > mstream->allocsize)
mstream->allocsize *= 2;
mstream->data = g_realloc (mstream->data, mstream->allocsize);
}
memcpy (mstream->data + mstream->offset, p_buffer, p_nb_bytes);
if (mstream->offset + p_nb_bytes > mstream->size)
mstream->size = mstream->offset + p_nb_bytes;
mstream->offset += p_nb_bytes;
return p_nb_bytes;
}
static OPJ_OFF_T
skip_fn (OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
MemStream *mstream = p_user_data;
if (mstream->offset + p_nb_bytes > mstream->allocsize) {
while (mstream->offset + p_nb_bytes > mstream->allocsize)
mstream->allocsize *= 2;
mstream->data = g_realloc (mstream->data, mstream->allocsize);
}
if (mstream->offset + p_nb_bytes > mstream->size)
mstream->size = mstream->offset + p_nb_bytes;
mstream->offset += p_nb_bytes;
return p_nb_bytes;
}
static OPJ_BOOL
seek_fn (OPJ_OFF_T p_nb_bytes, void *p_user_data)
{
MemStream *mstream = p_user_data;
if (p_nb_bytes > mstream->size)
return OPJ_FALSE;
mstream->offset = p_nb_bytes;
return OPJ_TRUE;
}
static gboolean
gst_openjpeg_encode_is_last_subframe (GstVideoEncoder * enc, int stripe)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (enc);
return (stripe == self->num_stripes);
}
static GstOpenJPEGCodecMessage *
gst_openjpeg_encode_message_new (GstOpenJPEGEnc * self,
GstVideoCodecFrame * frame, int num_stripe)
{
GstOpenJPEGCodecMessage *message = g_slice_new0 (GstOpenJPEGCodecMessage);
message->frame = gst_video_codec_frame_ref (frame);
message->stripe = num_stripe;
message->last_error = OPENJPEG_ERROR_NONE;
return message;
}
static GstOpenJPEGCodecMessage *
gst_openjpeg_encode_message_free (GstOpenJPEGCodecMessage * message)
{
if (message) {
gst_video_codec_frame_unref (message->frame);
if (message->output_buffer)
gst_buffer_unref (message->output_buffer);
g_slice_free (GstOpenJPEGCodecMessage, message);
}
return NULL;
}
#define ENCODE_ERROR(encode_params, err_code) { \
encode_params->last_error = err_code; \
goto done; \
}
/* callback method to be called asynchronously or not*/
static void
gst_openjpeg_enc_encode_stripe (GstElement * element, gpointer user_data)
{
GstOpenJPEGCodecMessage *message = (GstOpenJPEGCodecMessage *) user_data;
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (element);
opj_codec_t *enc = NULL;
opj_stream_t *stream = NULL;
MemStream mstream;
opj_image_t *image = NULL;
GstVideoFrame vframe;
GST_INFO_OBJECT (self, "Encode stripe %d/%d", message->stripe,
self->num_stripes);
mstream.data = NULL;
enc = opj_create_compress (self->codec_format);
if (!enc)
ENCODE_ERROR (message, OPENJPEG_ERROR_INIT);
if (G_UNLIKELY (gst_debug_category_get_threshold (GST_CAT_DEFAULT) >=
GST_LEVEL_TRACE)) {
opj_set_info_handler (enc, gst_openjpeg_enc_opj_info, self);
opj_set_warning_handler (enc, gst_openjpeg_enc_opj_warning, self);
opj_set_error_handler (enc, gst_openjpeg_enc_opj_error, self);
} else {
opj_set_info_handler (enc, NULL, NULL);
opj_set_warning_handler (enc, NULL, NULL);
opj_set_error_handler (enc, NULL, NULL);
}
if (!gst_video_frame_map (&vframe, &self->input_state->info,
message->frame->input_buffer, GST_MAP_READ))
ENCODE_ERROR (message, OPENJPEG_ERROR_MAP_READ);
image = gst_openjpeg_enc_fill_image (self, &vframe, message->stripe);
gst_video_frame_unmap (&vframe);
if (!image)
ENCODE_ERROR (message, OPENJPEG_ERROR_FILL_IMAGE);
if (vframe.info.finfo->flags & GST_VIDEO_FORMAT_FLAG_RGB) {
self->params.tcp_mct = 1;
}
opj_setup_encoder (enc, &self->params, image);
stream = opj_stream_create (4096, OPJ_FALSE);
if (!stream)
ENCODE_ERROR (message, OPENJPEG_ERROR_OPEN);
mstream.allocsize = 4096;
mstream.data = g_malloc (mstream.allocsize);
mstream.offset = 0;
mstream.size = 0;
opj_stream_set_read_function (stream, read_fn);
opj_stream_set_write_function (stream, write_fn);
opj_stream_set_skip_function (stream, skip_fn);
opj_stream_set_seek_function (stream, seek_fn);
opj_stream_set_user_data (stream, &mstream, NULL);
opj_stream_set_user_data_length (stream, mstream.size);
if (!opj_start_compress (enc, image, stream))
ENCODE_ERROR (message, OPENJPEG_ERROR_ENCODE);
if (!opj_encode (enc, stream))
ENCODE_ERROR (message, OPENJPEG_ERROR_ENCODE);
if (!opj_end_compress (enc, stream))
ENCODE_ERROR (message, OPENJPEG_ERROR_ENCODE);
opj_image_destroy (image);
image = NULL;
opj_stream_destroy (stream);
stream = NULL;
opj_destroy_codec (enc);
enc = NULL;
message->output_buffer = gst_buffer_new ();
if (self->is_jp2c) {
GstMapInfo map;
GstMemory *mem;
mem = gst_allocator_alloc (NULL, 8, NULL);
gst_memory_map (mem, &map, GST_MAP_WRITE);
GST_WRITE_UINT32_BE (map.data, mstream.size + 8);
GST_WRITE_UINT32_BE (map.data + 4, GST_MAKE_FOURCC ('j', 'p', '2', 'c'));
gst_memory_unmap (mem, &map);
gst_buffer_append_memory (message->output_buffer, mem);
}
gst_buffer_append_memory (message->output_buffer,
gst_memory_new_wrapped (0, mstream.data, mstream.allocsize, 0,
mstream.size, mstream.data, (GDestroyNotify) g_free));
message->last_error = OPENJPEG_ERROR_NONE;
GST_INFO_OBJECT (self,
"Stripe %d encoded successfully, pass it to the streaming thread",
message->stripe);
done:
if (message->last_error != OPENJPEG_ERROR_NONE) {
if (mstream.data)
g_free (mstream.data);
if (enc)
opj_destroy_codec (enc);
if (image)
opj_image_destroy (image);
if (stream)
opj_stream_destroy (stream);
}
if (!message->direct) {
GST_OBJECT_LOCK (self);
g_queue_push_tail (&self->messages, message);
g_cond_signal (&self->messages_cond);
GST_OBJECT_UNLOCK (self);
}
}
static GstOpenJPEGCodecMessage *
gst_openjpeg_enc_wait_for_new_message (GstOpenJPEGEnc * self)
{
GstOpenJPEGCodecMessage *message = NULL;
GST_OBJECT_LOCK (self);
while (g_queue_is_empty (&self->messages))
g_cond_wait (&self->messages_cond, GST_OBJECT_GET_LOCK (self));
message = g_queue_pop_head (&self->messages);
GST_OBJECT_UNLOCK (self);
return message;
}
static GstFlowReturn
gst_openjpeg_enc_encode_frame_multiple (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (encoder);
GstFlowReturn ret = GST_FLOW_OK;
guint i;
guint encoded_stripes = 0;
guint enqueued_stripes = 0;
GstOpenJPEGCodecMessage *message = NULL;
/* The method receives a frame and split it into n stripes and
* and create a thread per stripe to encode it.
* As the number of stripes can be greater than the
* available threads to encode, there is two loop, one to
* count the enqueues stripes and one to count the encoded
* stripes.
*/
while (encoded_stripes < self->num_stripes) {
for (i = 1;
i <= self->available_threads
&& enqueued_stripes < (self->num_stripes - encoded_stripes); i++) {
message =
gst_openjpeg_encode_message_new (self, frame, i + encoded_stripes);
GST_LOG_OBJECT (self,
"About to enqueue an encoding message from frame %p stripe %d", frame,
message->stripe);
gst_element_call_async (GST_ELEMENT (self),
(GstElementCallAsyncFunc) gst_openjpeg_enc_encode_stripe, message,
NULL);
enqueued_stripes++;
}
while (enqueued_stripes > 0) {
message = gst_openjpeg_enc_wait_for_new_message (self);
if (!message)
continue;
enqueued_stripes--;
if (message->last_error == OPENJPEG_ERROR_NONE) {
GST_LOG_OBJECT (self,
"About to push frame %p stripe %d", frame, message->stripe);
frame->output_buffer = gst_buffer_ref (message->output_buffer);
if (gst_openjpeg_encode_is_last_subframe (encoder, encoded_stripes + 1)) {
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
ret = gst_video_encoder_finish_frame (encoder, frame);
} else
ret = gst_video_encoder_finish_subframe (encoder, frame);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT
(self, "An error occurred pushing the frame %s",
gst_flow_get_name (ret));
goto done;
}
encoded_stripes++;
message = gst_openjpeg_encode_message_free (message);
} else {
GST_WARNING_OBJECT
(self, "An error occurred %d during the JPEG encoding",
message->last_error);
gst_video_codec_frame_unref (frame);
self->last_error = message->last_error;
ret = GST_FLOW_ERROR;
goto done;
}
}
}
done:
gst_openjpeg_encode_message_free (message);
return ret;
}
static GstFlowReturn
gst_openjpeg_enc_encode_frame_single (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (encoder);
GstFlowReturn ret = GST_FLOW_OK;
guint i;
GstOpenJPEGCodecMessage *message = NULL;
for (i = 1; i <= self->num_stripes; ++i) {
message = gst_openjpeg_encode_message_new (self, frame, i);
message->direct = TRUE;
gst_openjpeg_enc_encode_stripe (GST_ELEMENT (self), message);
if (message->last_error != OPENJPEG_ERROR_NONE) {
GST_WARNING_OBJECT
(self, "An error occured %d during the JPEG encoding",
message->last_error);
gst_video_codec_frame_unref (frame);
self->last_error = message->last_error;
ret = GST_FLOW_ERROR;
goto done;
}
frame->output_buffer = gst_buffer_ref (message->output_buffer);
if (gst_openjpeg_encode_is_last_subframe (encoder, message->stripe)) {
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
ret = gst_video_encoder_finish_frame (encoder, frame);
} else
ret = gst_video_encoder_finish_subframe (encoder, frame);
if (ret != GST_FLOW_OK) {
GST_WARNING_OBJECT
(self, "An error occurred pushing the frame %s",
gst_flow_get_name (ret));
goto done;
}
message = gst_openjpeg_encode_message_free (message);
}
done:
gst_openjpeg_encode_message_free (message);
return ret;
}
static GstFlowReturn
gst_openjpeg_enc_handle_frame (GstVideoEncoder * encoder,
GstVideoCodecFrame * frame)
{
GstOpenJPEGEnc *self = GST_OPENJPEG_ENC (encoder);
GstFlowReturn ret = GST_FLOW_OK;
GstVideoFrame vframe;
gboolean subframe_mode =
self->num_stripes != GST_OPENJPEG_ENC_DEFAULT_NUM_STRIPES;
GST_DEBUG_OBJECT (self, "Handling frame");
if (subframe_mode) {
gint min_res;
/* due to limitations in openjpeg library,
* number of wavelet resolutions must not exceed floor(log(stripe height)) + 1 */
if (!gst_video_frame_map (&vframe, &self->input_state->info,
frame->input_buffer, GST_MAP_READ)) {
gst_video_codec_frame_unref (frame);
self->last_error = OPENJPEG_ERROR_MAP_READ;
goto error;
}
/* find stripe with least height */
min_res =
get_stripe_height (self, self->num_stripes - 1,
GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0));
min_res = MIN (min_res, get_stripe_height (self, 0,
GST_VIDEO_FRAME_COMP_HEIGHT (&vframe, 0)));
/* take log to find correct number of wavelet resolutions */
min_res = min_res > 1 ? (gint) log (min_res) + 1 : 1;
self->params.numresolution = MIN (min_res + 1, self->params.numresolution);
gst_video_frame_unmap (&vframe);
}
if (self->encode_frame (encoder, frame) != GST_FLOW_OK)
goto error;
return ret;
error:
switch (self->last_error) {
case OPENJPEG_ERROR_INIT:
GST_ELEMENT_ERROR (self, LIBRARY, INIT,
("Failed to initialize OpenJPEG encoder"), (NULL));
break;
case OPENJPEG_ERROR_MAP_READ:
GST_ELEMENT_ERROR (self, CORE, FAILED,
("Failed to map input buffer"), (NULL));
break;
case OPENJPEG_ERROR_FILL_IMAGE:
GST_ELEMENT_ERROR (self, LIBRARY, INIT,
("Failed to fill OpenJPEG image"), (NULL));
break;
case OPENJPEG_ERROR_OPEN:
GST_ELEMENT_ERROR (self, LIBRARY, INIT,
("Failed to open OpenJPEG data"), (NULL));
break;
case OPENJPEG_ERROR_ENCODE:
GST_ELEMENT_ERROR (self, LIBRARY, INIT,
("Failed to encode OpenJPEG data"), (NULL));
break;
default:
GST_ELEMENT_ERROR (self, LIBRARY, INIT,
("Failed to encode OpenJPEG data"), (NULL));
break;
}
gst_openjpeg_enc_flush_messages (self);
return GST_FLOW_ERROR;
}
static gboolean
gst_openjpeg_enc_propose_allocation (GstVideoEncoder * encoder,
GstQuery * query)
{
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
query);
}