mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-20 08:41:07 +00:00
qtmux: support more of j2k
Reads the new caps added to qtdemux by commit
c917d65e6d
and adds its corresponding atoms.
Also adds support for image/x-jpc as it is the same
as image/x-jp2, except that the buffers need to be
boxed inside a jp2c isom box before muxing. To solve
this the QTPads now have a function that (if
not NULL) is called when a buffer is collected. This
function returns a replacement to the current collected
buffer.
Fixes #598916
This commit is contained in:
parent
ac48db3247
commit
1b2e0eae67
6 changed files with 236 additions and 34 deletions
|
@ -45,8 +45,8 @@
|
|||
#include <string.h>
|
||||
#include <glib.h>
|
||||
|
||||
/* only needed for gst_util_uint64_scale */
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/gstbytewriter.h>
|
||||
|
||||
/**
|
||||
* Creates a new AtomsContext for the given flavor.
|
||||
|
@ -3182,52 +3182,173 @@ build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * codec_data)
|
|||
}
|
||||
|
||||
AtomInfo *
|
||||
build_jp2h_extension (AtomTRAK * trak, gint width, gint height, guint32 fourcc)
|
||||
build_fiel_extension (gint fields)
|
||||
{
|
||||
AtomData *atom_data;
|
||||
GstBuffer *buf;
|
||||
|
||||
if (fields == 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = gst_buffer_new_and_alloc (1);
|
||||
GST_BUFFER_DATA (buf)[0] = (guint8) fields;
|
||||
|
||||
atom_data =
|
||||
atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('f', 'i', 'e', 'l'), buf);
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
|
||||
atom_data_free);
|
||||
}
|
||||
|
||||
AtomInfo *
|
||||
build_jp2x_extension (const GstBuffer * prefix)
|
||||
{
|
||||
AtomData *atom_data;
|
||||
|
||||
if (!prefix) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
atom_data =
|
||||
atom_data_new_from_gst_buffer (GST_MAKE_FOURCC ('j', 'p', '2', 'x'),
|
||||
prefix);
|
||||
|
||||
return build_atom_info_wrapper ((Atom *) atom_data, atom_data_copy_data,
|
||||
atom_data_free);
|
||||
}
|
||||
|
||||
AtomInfo *
|
||||
build_jp2h_extension (AtomTRAK * trak, gint width, gint height, guint32 fourcc,
|
||||
gint ncomp, const GValue * cmap_array, const GValue * cdef_array)
|
||||
{
|
||||
AtomData *atom_data;
|
||||
GstBuffer *buf;
|
||||
guint8 *data;
|
||||
guint8 cenum;
|
||||
gint i;
|
||||
gint idhr_size = 22;
|
||||
gint colr_size = 15;
|
||||
gint cmap_size = 0, cdef_size = 0;
|
||||
gint cmap_array_size = 0;
|
||||
gint cdef_array_size = 0;
|
||||
GstByteWriter writer;
|
||||
|
||||
g_return_val_if_fail (cmap_array == NULL ||
|
||||
GST_VALUE_HOLDS_ARRAY (cmap_array), NULL);
|
||||
g_return_val_if_fail (cdef_array == NULL ||
|
||||
GST_VALUE_HOLDS_ARRAY (cdef_array), NULL);
|
||||
|
||||
if (fourcc == GST_MAKE_FOURCC ('s', 'R', 'G', 'B')) {
|
||||
cenum = 0x10;
|
||||
if (ncomp == 0)
|
||||
ncomp = 3;
|
||||
} else if (fourcc == GST_MAKE_FOURCC ('G', 'R', 'A', 'Y')) {
|
||||
cenum = 0x11;
|
||||
if (ncomp == 0)
|
||||
ncomp = 1;
|
||||
} else if (fourcc == GST_MAKE_FOURCC ('s', 'Y', 'U', 'V')) {
|
||||
cenum = 0x12;
|
||||
if (ncomp == 0)
|
||||
ncomp = 3;
|
||||
} else
|
||||
return FALSE;
|
||||
return NULL;
|
||||
|
||||
buf = gst_buffer_new_and_alloc (22 + 15);
|
||||
data = GST_BUFFER_DATA (buf);
|
||||
if (cmap_array) {
|
||||
cmap_array_size = gst_value_array_get_size (cmap_array);
|
||||
cmap_size = 8 + cmap_array_size * 4;
|
||||
}
|
||||
if (cdef_array) {
|
||||
cdef_array_size = gst_value_array_get_size (cdef_array);
|
||||
cdef_size = 8 + 2 + cdef_array_size * 6;
|
||||
}
|
||||
|
||||
buf = gst_buffer_new_and_alloc (idhr_size + colr_size + cmap_size +
|
||||
cdef_size);
|
||||
gst_byte_writer_init_with_buffer (&writer, buf, FALSE);
|
||||
|
||||
/* ihdr = image header box */
|
||||
GST_WRITE_UINT32_BE (data, 22);
|
||||
GST_WRITE_UINT32_LE (data + 4, GST_MAKE_FOURCC ('i', 'h', 'd', 'r'));
|
||||
GST_WRITE_UINT32_BE (data + 8, height);
|
||||
GST_WRITE_UINT32_BE (data + 12, width);
|
||||
/* FIXME perhaps parse from stream,
|
||||
* though exactly 3 in any respectable colourspace */
|
||||
GST_WRITE_UINT16_BE (data + 16, 3);
|
||||
gst_byte_writer_put_uint32_be (&writer, 22);
|
||||
gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('i', 'h', 'd', 'r'));
|
||||
gst_byte_writer_put_uint32_be (&writer, height);
|
||||
gst_byte_writer_put_uint32_be (&writer, width);
|
||||
gst_byte_writer_put_uint16_be (&writer, ncomp);
|
||||
/* 8 bits per component, unsigned */
|
||||
GST_WRITE_UINT8 (data + 18, 0x7);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x7);
|
||||
/* compression type; reserved */
|
||||
GST_WRITE_UINT8 (data + 19, 0x7);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x7);
|
||||
/* colour space (un)known */
|
||||
GST_WRITE_UINT8 (data + 20, 0x0);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x0);
|
||||
/* intellectual property right (box present) */
|
||||
GST_WRITE_UINT8 (data + 21, 0x0);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x0);
|
||||
|
||||
/* colour specification box */
|
||||
data += 22;
|
||||
GST_WRITE_UINT32_BE (data, 15);
|
||||
GST_WRITE_UINT32_LE (data + 4, GST_MAKE_FOURCC ('c', 'o', 'l', 'r'));
|
||||
gst_byte_writer_put_uint32_be (&writer, 15);
|
||||
gst_byte_writer_put_uint32_le (&writer, GST_MAKE_FOURCC ('c', 'o', 'l', 'r'));
|
||||
|
||||
/* specification method: enumerated */
|
||||
GST_WRITE_UINT8 (data + 8, 0x1);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x1);
|
||||
/* precedence; reserved */
|
||||
GST_WRITE_UINT8 (data + 9, 0x0);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x0);
|
||||
/* approximation; reserved */
|
||||
GST_WRITE_UINT8 (data + 10, 0x0);
|
||||
gst_byte_writer_put_uint8 (&writer, 0x0);
|
||||
/* enumerated colourspace */
|
||||
GST_WRITE_UINT32_BE (data + 11, cenum);
|
||||
gst_byte_writer_put_uint32_be (&writer, cenum);
|
||||
|
||||
if (cmap_array) {
|
||||
gst_byte_writer_put_uint32_be (&writer, cmap_size);
|
||||
gst_byte_writer_put_uint32_le (&writer,
|
||||
GST_MAKE_FOURCC ('c', 'm', 'a', 'p'));
|
||||
for (i = 0; i < cmap_array_size; i++) {
|
||||
const GValue *item;
|
||||
gint value;
|
||||
guint16 cmp;
|
||||
guint8 mtyp;
|
||||
guint8 pcol;
|
||||
item = gst_value_array_get_value (cmap_array, i);
|
||||
value = g_value_get_int (item);
|
||||
|
||||
/* value is '(mtyp << 24) | (pcol << 16) | cmp' */
|
||||
cmp = value & 0xFFFF;
|
||||
mtyp = value >> 24;
|
||||
pcol = (value >> 16) & 0xFF;
|
||||
|
||||
if (mtyp == 1)
|
||||
GST_WARNING ("MTYP of cmap atom signals Pallete Mapping, but we don't "
|
||||
"handle Pallete mapping atoms yet");
|
||||
|
||||
gst_byte_writer_put_uint16_be (&writer, cmp);
|
||||
gst_byte_writer_put_uint8 (&writer, mtyp);
|
||||
gst_byte_writer_put_uint8 (&writer, pcol);
|
||||
}
|
||||
}
|
||||
|
||||
if (cdef_array) {
|
||||
gst_byte_writer_put_uint32_be (&writer, cdef_size);
|
||||
gst_byte_writer_put_uint32_le (&writer,
|
||||
GST_MAKE_FOURCC ('c', 'd', 'e', 'f'));
|
||||
gst_byte_writer_put_uint16_be (&writer, cdef_array_size);
|
||||
for (i = 0; i < cdef_array_size; i++) {
|
||||
const GValue *item;
|
||||
gint value;
|
||||
item = gst_value_array_get_value (cdef_array, i);
|
||||
value = g_value_get_int (item);
|
||||
|
||||
gst_byte_writer_put_uint16_be (&writer, i);
|
||||
if (value > 0) {
|
||||
gst_byte_writer_put_uint16_be (&writer, 0);
|
||||
gst_byte_writer_put_uint16_be (&writer, value);
|
||||
} else if (value < 0) {
|
||||
gst_byte_writer_put_uint16_be (&writer, -value);
|
||||
gst_byte_writer_put_uint16_be (&writer, 0); /* TODO what here? */
|
||||
} else {
|
||||
gst_byte_writer_put_uint16_be (&writer, 1);
|
||||
gst_byte_writer_put_uint16_be (&writer, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_assert (gst_byte_writer_get_remaining (&writer) == 0);
|
||||
|
||||
atom_data = atom_data_new_from_gst_buffer (FOURCC_jp2h, buf);
|
||||
gst_buffer_unref (buf);
|
||||
|
|
|
@ -670,7 +670,12 @@ AtomInfo * build_mov_aac_extension (AtomTRAK * trak, const GstBuffer * cod
|
|||
AtomInfo * build_esds_extension (AtomTRAK * trak, guint8 object_type,
|
||||
guint8 stream_type, const GstBuffer * codec_data);
|
||||
AtomInfo * build_jp2h_extension (AtomTRAK * trak, gint width, gint height,
|
||||
guint32 fourcc);
|
||||
guint32 fourcc, gint ncomp,
|
||||
const GValue * cmap_array,
|
||||
const GValue * cdef_array);
|
||||
|
||||
AtomInfo * build_jp2x_extension (const GstBuffer * prefix);
|
||||
AtomInfo * build_fiel_extension (gint fields);
|
||||
AtomInfo * build_amr_extension ();
|
||||
AtomInfo * build_h263_extension ();
|
||||
AtomInfo * build_gama_atom (gdouble gamma);
|
||||
|
|
|
@ -170,6 +170,7 @@ G_BEGIN_DECLS
|
|||
#define FOURCC_jpeg GST_MAKE_FOURCC('j','p','e','g')
|
||||
#define FOURCC_mjp2 GST_MAKE_FOURCC('m','j','p','2')
|
||||
#define FOURCC_jp2h GST_MAKE_FOURCC('j','p','2','h')
|
||||
#define FOURCC_jp2c GST_MAKE_FOURCC('j','p','2','c')
|
||||
#define FOURCC_gama GST_MAKE_FOURCC('g','a','m','a')
|
||||
|
||||
/* SVQ3 fourcc */
|
||||
|
|
|
@ -251,6 +251,7 @@ gst_qt_mux_pad_reset (GstQTPad * qtpad)
|
|||
qtpad->sync = FALSE;
|
||||
qtpad->last_dts = 0;
|
||||
qtpad->first_ts = GST_CLOCK_TIME_NONE;
|
||||
qtpad->prepare_buf_func = NULL;
|
||||
|
||||
if (qtpad->last_buf)
|
||||
gst_buffer_replace (&qtpad->last_buf, NULL);
|
||||
|
@ -355,6 +356,30 @@ gst_qt_mux_finalize (GObject * object)
|
|||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qt_mux_prepare_jpc_buffer (GstQTPad * qtpad, GstBuffer * buf,
|
||||
GstQTMux * qtmux)
|
||||
{
|
||||
GstBuffer *newbuf;
|
||||
|
||||
GST_LOG_OBJECT (qtmux, "Preparing jpc buffer");
|
||||
|
||||
if (buf == NULL)
|
||||
return NULL;
|
||||
|
||||
newbuf = gst_buffer_new_and_alloc (GST_BUFFER_SIZE (buf) + 8);
|
||||
gst_buffer_copy_metadata (newbuf, buf, GST_BUFFER_COPY_ALL);
|
||||
|
||||
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (newbuf), GST_BUFFER_SIZE (newbuf));
|
||||
GST_WRITE_UINT32_LE (GST_BUFFER_DATA (newbuf) + 4, FOURCC_jp2c);
|
||||
|
||||
memcpy (GST_BUFFER_DATA (newbuf) + 8, GST_BUFFER_DATA (buf),
|
||||
GST_BUFFER_SIZE (buf));
|
||||
gst_buffer_unref (buf);
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qt_mux_add_mp4_tag (GstQTMux * qtmux, const GstTagList * list,
|
||||
const char *tag, const char *tag2, guint32 fourcc)
|
||||
|
@ -1398,6 +1423,11 @@ gst_qt_mux_add_buffer (GstQTMux * qtmux, GstQTPad * pad, GstBuffer * buf)
|
|||
if (!pad->fourcc)
|
||||
goto not_negotiated;
|
||||
|
||||
/* if this pad has a prepare function, call it */
|
||||
if (pad->prepare_buf_func != NULL) {
|
||||
buf = pad->prepare_buf_func (pad, buf, qtmux);
|
||||
}
|
||||
|
||||
last_buf = pad->last_buf;
|
||||
if (last_buf == NULL) {
|
||||
#ifndef GST_DISABLE_GST_DEBUG
|
||||
|
@ -1668,6 +1698,8 @@ gst_qt_mux_audio_sink_set_caps (GstPad * pad, GstCaps * caps)
|
|||
qtpad = (GstQTPad *) gst_pad_get_element_private (pad);
|
||||
g_assert (qtpad);
|
||||
|
||||
qtpad->prepare_buf_func = NULL;
|
||||
|
||||
/* does not go well to renegotiate stream mid-way */
|
||||
if (qtpad->fourcc)
|
||||
goto refuse_renegotiation;
|
||||
|
@ -1916,6 +1948,8 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps)
|
|||
qtpad = (GstQTPad *) gst_pad_get_element_private (pad);
|
||||
g_assert (qtpad);
|
||||
|
||||
qtpad->prepare_buf_func = NULL;
|
||||
|
||||
/* does not go well to renegotiate stream mid-way */
|
||||
if (qtpad->fourcc)
|
||||
goto refuse_renegotiation;
|
||||
|
@ -2095,20 +2129,43 @@ gst_qt_mux_video_sink_set_caps (GstPad * pad, GstCaps * caps)
|
|||
} else if (strcmp (mimetype, "image/jpeg") == 0) {
|
||||
entry.fourcc = FOURCC_jpeg;
|
||||
sync = FALSE;
|
||||
} else if (strcmp (mimetype, "image/x-j2c") == 0) {
|
||||
} else if (strcmp (mimetype, "image/x-j2c") == 0 ||
|
||||
strcmp (mimetype, "image/x-jpc") == 0) {
|
||||
guint32 fourcc;
|
||||
const GValue *cmap_array;
|
||||
const GValue *cdef_array;
|
||||
gint ncomp = 0;
|
||||
gint fields = 1;
|
||||
|
||||
if (strcmp (mimetype, "image/x-jpc") == 0) {
|
||||
qtpad->prepare_buf_func = gst_qt_mux_prepare_jpc_buffer;
|
||||
}
|
||||
|
||||
gst_structure_get_int (structure, "num-components", &ncomp);
|
||||
gst_structure_get_int (structure, "fields", &fields);
|
||||
cmap_array = gst_structure_get_value (structure, "component-map");
|
||||
cdef_array = gst_structure_get_value (structure, "channel-definitions");
|
||||
|
||||
ext_atom = NULL;
|
||||
entry.fourcc = FOURCC_mjp2;
|
||||
sync = FALSE;
|
||||
if (!gst_structure_get_fourcc (structure, "fourcc", &fourcc) ||
|
||||
!(ext_atom =
|
||||
build_jp2h_extension (qtpad->trak, width, height, fourcc))) {
|
||||
if (gst_structure_get_fourcc (structure, "fourcc", &fourcc) &&
|
||||
(ext_atom =
|
||||
build_jp2h_extension (qtpad->trak, width, height, fourcc, ncomp,
|
||||
cmap_array, cdef_array)) != NULL) {
|
||||
ext_atom_list = g_list_append (ext_atom_list, ext_atom);
|
||||
|
||||
ext_atom = build_fiel_extension (fields);
|
||||
if (ext_atom)
|
||||
ext_atom_list = g_list_append (ext_atom_list, ext_atom);
|
||||
|
||||
ext_atom = build_jp2x_extension (codec_data);
|
||||
if (ext_atom)
|
||||
ext_atom_list = g_list_append (ext_atom_list, ext_atom);
|
||||
} else {
|
||||
GST_DEBUG_OBJECT (qtmux, "missing or invalid fourcc in jp2 caps");
|
||||
goto refuse_caps;
|
||||
}
|
||||
if (ext_atom)
|
||||
ext_atom_list = g_list_prepend (ext_atom_list, ext_atom);
|
||||
} else if (strcmp (mimetype, "video/x-qt-part") == 0) {
|
||||
guint32 fourcc;
|
||||
|
||||
|
|
|
@ -62,8 +62,22 @@ G_BEGIN_DECLS
|
|||
|
||||
typedef struct _GstQTMux GstQTMux;
|
||||
typedef struct _GstQTMuxClass GstQTMuxClass;
|
||||
typedef struct _GstQTPad GstQTPad;
|
||||
|
||||
typedef struct _GstQTPad
|
||||
/*
|
||||
* GstQTPadPrepareBufferFunc
|
||||
*
|
||||
* Receives a buffer (takes ref) and returns a new buffer that should
|
||||
* replace the passed one.
|
||||
*
|
||||
* Useful for when the pad/datatype needs some manipulation before
|
||||
* being muxed. (Originally added for image/x-jpc support, for which buffers
|
||||
* need to be wrapped into a isom box)
|
||||
*/
|
||||
typedef GstBuffer * (*GstQTPadPrepareBufferFunc) (GstQTPad * pad,
|
||||
GstBuffer * buf, GstQTMux * qtmux);
|
||||
|
||||
struct _GstQTPad
|
||||
{
|
||||
GstCollectData collect; /* we extend the CollectData */
|
||||
|
||||
|
@ -89,7 +103,10 @@ typedef struct _GstQTPad
|
|||
/* all the atom and chunk book-keeping is delegated here
|
||||
* unowned/uncounted reference, parent MOOV owns */
|
||||
AtomTRAK *trak;
|
||||
} GstQTPad;
|
||||
|
||||
/* if nothing is set, it won't be called */
|
||||
GstQTPadPrepareBufferFunc prepare_buf_func;
|
||||
};
|
||||
|
||||
typedef enum _GstQTMuxState
|
||||
{
|
||||
|
|
|
@ -199,7 +199,8 @@ GstQTMuxFormatProp gst_qt_mux_format_list[] = {
|
|||
"MJ2",
|
||||
"GstMJ2Mux",
|
||||
GST_STATIC_CAPS ("video/mj2"),
|
||||
GST_STATIC_CAPS ("image/x-j2c, " COMMON_VIDEO_CAPS),
|
||||
GST_STATIC_CAPS ("image/x-j2c, " COMMON_VIDEO_CAPS "; "
|
||||
"image/x-jpc, " COMMON_VIDEO_CAPS),
|
||||
GST_STATIC_CAPS (PCM_CAPS)
|
||||
}
|
||||
,
|
||||
|
|
Loading…
Reference in a new issue