h264parser: Add a helper method to create and inject raw SEI data

Add an API to create raw SEI nal unit. This would be useful in case
an user want to create SEI nal data and inject the SEI nal data
into bitstream.
This commit is contained in:
Seungha Yang 2020-03-25 17:20:13 +09:00 committed by GStreamer Merge Bot
parent f94157c949
commit a1e8109aa8
3 changed files with 743 additions and 0 deletions

View file

@ -2639,3 +2639,483 @@ gst_h264_video_calculate_framerate (const GstH264SPS * sps,
*fps_num = num; *fps_num = num;
*fps_den = den; *fps_den = den;
} }
static gboolean
gst_h264_write_sei_registered_user_data (NalWriter * nw,
GstH264RegisteredUserData * rud)
{
WRITE_UINT8 (nw, rud->country_code, 8);
if (rud->country_code == 0xff)
WRITE_UINT8 (nw, rud->country_code_extension, 8);
WRITE_BYTES (nw, rud->data, rud->size);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h264_write_sei_frame_packing (NalWriter * nw,
GstH264FramePacking * frame_packing)
{
WRITE_UE (nw, frame_packing->frame_packing_id);
WRITE_UINT8 (nw, frame_packing->frame_packing_cancel_flag, 1);
if (!frame_packing->frame_packing_cancel_flag) {
WRITE_UINT8 (nw, frame_packing->frame_packing_type, 7);
WRITE_UINT8 (nw, frame_packing->quincunx_sampling_flag, 1);
WRITE_UINT8 (nw, frame_packing->content_interpretation_type, 6);
WRITE_UINT8 (nw, frame_packing->spatial_flipping_flag, 1);
WRITE_UINT8 (nw, frame_packing->frame0_flipped_flag, 1);
WRITE_UINT8 (nw, frame_packing->field_views_flag, 1);
WRITE_UINT8 (nw, frame_packing->current_frame_is_frame0_flag, 1);
WRITE_UINT8 (nw, frame_packing->frame0_self_contained_flag, 1);
WRITE_UINT8 (nw, frame_packing->frame1_self_contained_flag, 1);
if (!frame_packing->quincunx_sampling_flag &&
frame_packing->frame_packing_type !=
GST_H264_FRAME_PACKING_TEMPORAL_INTERLEAVING) {
WRITE_UINT8 (nw, frame_packing->frame0_grid_position_x, 4);
WRITE_UINT8 (nw, frame_packing->frame0_grid_position_y, 4);
WRITE_UINT8 (nw, frame_packing->frame1_grid_position_x, 4);
WRITE_UINT8 (nw, frame_packing->frame1_grid_position_y, 4);
}
/* frame_packing_arrangement_reserved_byte */
WRITE_UINT8 (nw, 0, 8);
WRITE_UE (nw, frame_packing->frame_packing_repetition_period);
}
/* frame_packing_arrangement_extension_flag */
WRITE_UINT8 (nw, 0, 1);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h264_write_sei_mastering_display_colour_volume (NalWriter * nw,
GstH264MasteringDisplayColourVolume * mdcv)
{
gint i;
for (i = 0; i < 3; i++) {
WRITE_UINT16 (nw, mdcv->display_primaries_x[i], 16);
WRITE_UINT16 (nw, mdcv->display_primaries_y[i], 16);
}
WRITE_UINT16 (nw, mdcv->white_point_x, 16);
WRITE_UINT16 (nw, mdcv->white_point_y, 16);
WRITE_UINT32 (nw, mdcv->max_display_mastering_luminance, 32);
WRITE_UINT32 (nw, mdcv->min_display_mastering_luminance, 32);
return TRUE;
error:
return FALSE;
}
static gboolean
gst_h264_write_sei_content_light_level_info (NalWriter * nw,
GstH264ContentLightLevel * cll)
{
WRITE_UINT16 (nw, cll->max_content_light_level, 16);
WRITE_UINT16 (nw, cll->max_pic_average_light_level, 16);
return TRUE;
error:
return FALSE;
}
static GstMemory *
gst_h264_create_sei_memory_internal (guint8 nal_prefix_size,
gboolean packetized, GArray * messages)
{
NalWriter nw;
gint i;
gboolean have_written_data = FALSE;
nal_writer_init (&nw, nal_prefix_size, packetized);
if (messages->len == 0)
goto error;
GST_DEBUG ("Create SEI nal from array, len: %d", messages->len);
/* nal header */
/* forbidden_zero_bit */
WRITE_UINT8 (&nw, 0, 1);
/* nal_ref_idc, zero for sei nalu */
WRITE_UINT8 (&nw, 0, 2);
/* nal_unit_type */
WRITE_UINT8 (&nw, GST_H264_NAL_SEI, 5);
for (i = 0; i < messages->len; i++) {
GstH264SEIMessage *msg = &g_array_index (messages, GstH264SEIMessage, i);
guint32 payload_size_data = 0;
guint32 payload_size_in_bits = 0;
guint32 payload_type_data = msg->payloadType;
gboolean need_align = FALSE;
switch (payload_type_data) {
case GST_H264_SEI_REGISTERED_USER_DATA:{
GstH264RegisteredUserData *rud = &msg->payload.registered_user_data;
/* itu_t_t35_country_code: 8 bits */
payload_size_data = 1;
if (rud->country_code == 0xff) {
/* itu_t_t35_country_code_extension_byte */
payload_size_data++;
}
payload_size_data += rud->size;
break;
}
case GST_H264_SEI_FRAME_PACKING:{
GstH264FramePacking *frame_packing = &msg->payload.frame_packing;
guint leading_zeros, rest;
/* frame_packing_arrangement_id: exp-golomb bits */
count_exp_golomb_bits (frame_packing->frame_packing_id,
&leading_zeros, &rest);
payload_size_in_bits = leading_zeros + rest;
/* frame_packing_arrangement_cancel_flag: 1 bit */
payload_size_in_bits++;
if (!frame_packing->frame_packing_cancel_flag) {
/* frame_packing_arrangement_type: 7 bits
* quincunx_sampling_flag: 1 bit
* content_interpretation_type: 6 bit
* spatial_flipping_flag: 1 bit
* frame0_flipped_flag: 1 bit
* field_views_flag: 1 bit
* current_frame_is_frame0_flag: 1 bit
* frame0_self_contained_flag: 1 bit
* frame1_self_contained_flag: 1 bit
*/
payload_size_in_bits += 20;
if (!frame_packing->quincunx_sampling_flag &&
frame_packing->frame_packing_type !=
GST_H264_FRAME_PACKING_TEMPORAL_INTERLEAVING) {
/* frame0_grid_position_x: 4bits
* frame0_grid_position_y: 4bits
* frame1_grid_position_x: 4bits
* frame1_grid_position_y: 4bits
*/
payload_size_in_bits += 16;
}
/* frame_packing_arrangement_reserved_byte: 8 bits */
payload_size_in_bits += 8;
/* frame_packing_arrangement_repetition_period: exp-golomb bits */
count_exp_golomb_bits (frame_packing->frame_packing_repetition_period,
&leading_zeros, &rest);
payload_size_in_bits += (leading_zeros + rest);
}
/* frame_packing_arrangement_extension_flag: 1 bit */
payload_size_in_bits++;
payload_size_data = payload_size_in_bits >> 3;
if ((payload_size_in_bits & 0x7) != 0) {
GST_INFO ("Bits for Frame Packing SEI is not byte aligned");
payload_size_data++;
need_align = TRUE;
}
break;
}
case GST_H264_SEI_MASTERING_DISPLAY_COLOUR_VOLUME:
/* x, y 16 bits per RGB channel
* x, y 16 bits white point
* max, min luminance 32 bits
*
* (2 * 2 * 3) + (2 * 2) + (4 * 2) = 24 bytes
*/
payload_size_data = 24;
break;
case GST_H264_SEI_CONTENT_LIGHT_LEVEL:
/* maxCLL and maxFALL per 16 bits
*
* 2 * 2 = 4 bytes
*/
payload_size_data = 4;
break;
default:
break;
}
if (payload_size_data == 0) {
GST_FIXME ("Unsupported SEI type %d", msg->payloadType);
continue;
}
/* write payload type bytes */
while (payload_type_data >= 0xff) {
WRITE_UINT8 (&nw, 0xff, 8);
payload_type_data -= -0xff;
}
WRITE_UINT8 (&nw, payload_type_data, 8);
/* write payload size bytes */
while (payload_size_data >= 0xff) {
WRITE_UINT8 (&nw, 0xff, 8);
payload_size_data -= -0xff;
}
WRITE_UINT8 (&nw, payload_size_data, 8);
switch (msg->payloadType) {
case GST_H264_SEI_REGISTERED_USER_DATA:
GST_DEBUG ("Writing \"Registered user data\" done");
if (!gst_h264_write_sei_registered_user_data (&nw,
&msg->payload.registered_user_data)) {
GST_WARNING ("Failed to write \"Registered user data\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H264_SEI_FRAME_PACKING:
GST_DEBUG ("Writing \"Frame packing\" done");
if (!gst_h264_write_sei_frame_packing (&nw,
&msg->payload.frame_packing)) {
GST_WARNING ("Failed to write \"Frame packing\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H264_SEI_MASTERING_DISPLAY_COLOUR_VOLUME:
GST_DEBUG ("Wrtiting \"Mastering display colour volume\"");
if (!gst_h264_write_sei_mastering_display_colour_volume (&nw,
&msg->payload.mastering_display_colour_volume)) {
GST_WARNING ("Failed to write \"Mastering display colour volume\"");
goto error;
}
have_written_data = TRUE;
break;
case GST_H264_SEI_CONTENT_LIGHT_LEVEL:
GST_DEBUG ("Writing \"Content light level\" done");
if (!gst_h264_write_sei_content_light_level_info (&nw,
&msg->payload.content_light_level)) {
GST_WARNING ("Failed to write \"Content light level\"");
goto error;
}
have_written_data = TRUE;
break;
default:
break;
}
if (need_align && !nal_writer_do_rbsp_trailing_bits (&nw)) {
GST_WARNING ("Cannot insert traling bits");
goto error;
}
}
if (!have_written_data) {
GST_WARNING ("No written sei data");
goto error;
}
if (!nal_writer_do_rbsp_trailing_bits (&nw)) {
GST_WARNING ("Failed to insert rbsp trailing bits");
goto error;
}
return nal_writer_reset_and_get_memory (&nw);
error:
nal_writer_reset (&nw);
return NULL;
}
/**
* gst_h264_create_sei_memory:
* @start_code_prefix_length: a length of start code prefix, must be 3 or 4
* @messages: (transfer none): a GArray of #GstH264SEIMessage
*
* Creates raw byte-stream format (a.k.a Annex B type) SEI nal unit data
* from @messages
*
* Returns: a #GstMemory containing a SEI nal unit
*
* Since: 1.18
*/
GstMemory *
gst_h264_create_sei_memory (guint8 start_code_prefix_length, GArray * messages)
{
g_return_val_if_fail (start_code_prefix_length == 3
|| start_code_prefix_length == 4, NULL);
g_return_val_if_fail (messages != NULL, NULL);
g_return_val_if_fail (messages->len > 0, NULL);
return gst_h264_create_sei_memory_internal (start_code_prefix_length,
FALSE, messages);
}
/**
* gst_h264_create_sei_memory_avc:
* @nal_length_size: a size of nal length field, allowed range is [1, 4]
* @messages: (transfer none): a GArray of #GstH264SEIMessage
*
* Creates raw packetized format SEI nal unit data from @messages
*
* Returns: a #GstMemory containing a SEI nal unit
*
* Since: 1.18
*/
GstMemory *
gst_h264_create_sei_memory_avc (guint8 nal_length_size, GArray * messages)
{
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5, NULL);
g_return_val_if_fail (messages != NULL, NULL);
g_return_val_if_fail (messages->len > 0, NULL);
return gst_h264_create_sei_memory_internal (nal_length_size, TRUE, messages);
}
static GstBuffer *
gst_h264_parser_insert_sei_internal (GstH264NalParser * nalparser,
guint8 nal_prefix_size, gboolean packetized, GstBuffer * au,
GstMemory * sei)
{
GstH264NalUnit nalu;
GstMapInfo info;
GstH264ParserResult pres;
guint offset = 0;
GstBuffer *new_buffer = NULL;
if (!gst_buffer_map (au, &info, GST_MAP_READ)) {
GST_ERROR ("Cannot map au buffer");
return NULL;
}
/* Find the offset of the first slice */
do {
if (packetized) {
pres = gst_h264_parser_identify_nalu_avc (nalparser,
info.data, offset, info.size, nal_prefix_size, &nalu);
} else {
pres = gst_h264_parser_identify_nalu (nalparser,
info.data, offset, info.size, &nalu);
}
if (pres != GST_H264_PARSER_OK && pres != GST_H264_PARSER_NO_NAL_END) {
GST_DEBUG ("Failed to identify nal unit, ret: %d", pres);
gst_buffer_unmap (au, &info);
return NULL;
}
if ((nalu.type >= GST_H264_NAL_SLICE && nalu.type <= GST_H264_NAL_SLICE_IDR)
|| (nalu.type >= GST_H264_NAL_SLICE_AUX
&& nalu.type <= GST_H264_NAL_SLICE_DEPTH)) {
GST_DEBUG ("Found slice nal type %d at offset %d",
nalu.type, nalu.sc_offset);
break;
}
offset = nalu.offset + nalu.size;
} while (pres == GST_H264_PARSER_OK);
gst_buffer_unmap (au, &info);
/* found the best position now, create new buffer */
new_buffer = gst_buffer_new ();
/* copy all metadata */
if (!gst_buffer_copy_into (new_buffer, au, GST_BUFFER_COPY_METADATA, 0, -1)) {
GST_ERROR ("Failed to copy metadata into new buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
/* copy non-slice nal */
if (nalu.sc_offset > 0) {
if (!gst_buffer_copy_into (new_buffer, au,
GST_BUFFER_COPY_MEMORY, 0, nalu.sc_offset)) {
GST_ERROR ("Failed to copy buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
}
/* insert sei */
gst_buffer_append_memory (new_buffer, gst_memory_ref (sei));
/* copy the rest */
if (!gst_buffer_copy_into (new_buffer, au,
GST_BUFFER_COPY_MEMORY, nalu.sc_offset, -1)) {
GST_ERROR ("Failed to copy buffer");
gst_clear_buffer (&new_buffer);
goto out;
}
out:
return new_buffer;
}
/**
* gst_h264_parser_insert_sei:
* @nalparser: a #GstH264NalParser
* @au: (transfer none): a #GstBuffer containing AU data
* @sei: (transfer none): a #GstMemory containing a SEI nal
*
* Copy @au into new #GstBuffer and insert @sei into the #GstBuffer.
* The validation for completeness of @au and @sei is caller's responsibility.
* Both @au and @sei must be byte-stream formatted
*
* Returns: (nullable): a SEI inserted #GstBuffer or %NULL
* if cannot figure out proper position to insert a @sei
*
* Since: 1.18
*/
GstBuffer *
gst_h264_parser_insert_sei (GstH264NalParser * nalparser, GstBuffer * au,
GstMemory * sei)
{
g_return_val_if_fail (nalparser != NULL, NULL);
g_return_val_if_fail (GST_IS_BUFFER (au), NULL);
g_return_val_if_fail (sei != NULL, NULL);
/* the size of start code prefix (3 or 4) is not matter since it will be
* scanned */
return gst_h264_parser_insert_sei_internal (nalparser, 4, FALSE, au, sei);
}
/**
* gst_h264_parser_insert_sei_avc:
* @nalparser: a #GstH264NalParser
* @nal_length_size: a size of nal length field, allowed range is [1, 4]
* @au: (transfer none): a #GstBuffer containing AU data
* @sei: (transfer none): a #GstMemory containing a SEI nal
*
* Copy @au into new #GstBuffer and insert @sei into the #GstBuffer.
* The validation for completeness of @au and @sei is caller's responsibility.
* Nal prefix type of both @au and @sei must be packetized, and
* also the size of nal length field must be identical to @nal_length_size
*
* Returns: (nullable): a SEI inserted #GstBuffer or %NULL
* if cannot figure out proper position to insert a @sei
*
* Since: 1.18
*/
GstBuffer *
gst_h264_parser_insert_sei_avc (GstH264NalParser * nalparser,
guint8 nal_length_size, GstBuffer * au, GstMemory * sei)
{
g_return_val_if_fail (nalparser != NULL, NULL);
g_return_val_if_fail (nal_length_size > 0 && nal_length_size < 5, NULL);
g_return_val_if_fail (GST_IS_BUFFER (au), NULL);
g_return_val_if_fail (sei != NULL, NULL);
/* the size of start code prefix (3 or 4) is not matter since it will be
* scanned */
return gst_h264_parser_insert_sei_internal (nalparser, nal_length_size, TRUE,
au, sei);
}

View file

@ -1223,6 +1223,25 @@ GST_CODEC_PARSERS_API
void gst_h264_video_calculate_framerate (const GstH264SPS * sps, guint field_pic_flag, void gst_h264_video_calculate_framerate (const GstH264SPS * sps, guint field_pic_flag,
guint pic_struct, gint * fps_num, gint * fps_den); guint pic_struct, gint * fps_num, gint * fps_den);
GST_CODEC_PARSERS_API
GstMemory * gst_h264_create_sei_memory (guint8 start_code_prefix_size,
GArray * messages);
GST_CODEC_PARSERS_API
GstMemory * gst_h264_create_sei_memory_avc (guint8 nal_length_size,
GArray * messages);
GST_CODEC_PARSERS_API
GstBuffer * gst_h264_parser_insert_sei (GstH264NalParser * nalparser,
GstBuffer * au,
GstMemory * sei);
GST_CODEC_PARSERS_API
GstBuffer * gst_h264_parser_insert_sei_avc (GstH264NalParser * nalparser,
guint8 nal_length_size,
GstBuffer * au,
GstMemory * sei);
G_END_DECLS G_END_DECLS
#endif #endif

View file

@ -245,6 +245,40 @@ static guint8 nalu_chained_sei[] = {
0x06, 0x01, 0xc4, 0x80 0x06, 0x01, 0xc4, 0x80
}; };
/* Content light level information SEI message */
static guint8 h264_sei_cll[] = {
0x00, 0x00, 0x00, 0x01, 0x06, 0x90, 0x04, 0x03, 0xe8, 0x01, 0x90, 0x80
};
/* Mastering display colour volume information SEI message */
static guint8 h264_sei_mdcv[] = {
0x00, 0x00, 0x00, 0x01, 0x06, 0x89, 0x18, 0x84,
0xd0, 0x3e, 0x80, 0x33, 0x90, 0x86, 0xc4, 0x1d,
0x4c, 0x0b, 0xb8, 0x3d, 0x13, 0x40, 0x42, 0x00,
0x98, 0x96, 0x80, 0x00, 0x00, 0x03, 0x00, 0x01,
0x80
};
/* closed caption data */
static guint8 h264_sei_user_data_registered[] = {
0x00, 0x00, 0x00, 0x01, 0x06, 0x04, 0x47, 0xb5, 0x00, 0x31, 0x47, 0x41,
0x39, 0x34, 0x03, 0xd4,
0xff, 0xfc, 0x80, 0x80, 0xfd, 0x80, 0x80, 0xfa, 0x00, 0x00, 0xfa, 0x00,
0x00, 0xfa, 0x00, 0x00,
0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00,
0xfa, 0x00, 0x00, 0xfa,
0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa,
0x00, 0x00, 0xfa, 0x00,
0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00, 0x00, 0xfa, 0x00,
0x00, 0xff, 0x80
};
/* frame packing, side-by-side */
static guint8 h264_sei_frame_packing[] = {
0x00, 0x00, 0x00, 0x01, 0x06, 0x2d, 0x07, 0x81, 0x81, 0x00, 0x00, 0x03,
0x00, 0x01, 0x20, 0x80
};
GST_START_TEST (test_h264_parse_invalid_sei) GST_START_TEST (test_h264_parse_invalid_sei)
{ {
GstH264ParserResult res; GstH264ParserResult res;
@ -313,6 +347,215 @@ GST_START_TEST (test_h264_parse_invalid_sei)
GST_END_TEST; GST_END_TEST;
typedef gboolean (*SEICheckFunc) (gconstpointer a, gconstpointer b);
static gboolean
check_sei_user_data_registered (const GstH264RegisteredUserData * a,
const GstH264RegisteredUserData * b)
{
if (a->country_code != b->country_code)
return FALSE;
if ((a->country_code == 0xff) &&
(a->country_code_extension != b->country_code_extension))
return FALSE;
if (a->size != b->size)
return FALSE;
return !memcmp (a->data, b->data, a->size);
}
static gboolean
check_sei_frame_packing (const GstH264FramePacking * a,
const GstH264FramePacking * b)
{
if ((a->frame_packing_id != b->frame_packing_id) ||
(a->frame_packing_cancel_flag != b->frame_packing_cancel_flag))
return FALSE;
if (!a->frame_packing_cancel_flag) {
if ((a->frame_packing_type != b->frame_packing_type) ||
(a->quincunx_sampling_flag != b->quincunx_sampling_flag) ||
(a->content_interpretation_type != b->content_interpretation_type) ||
(a->spatial_flipping_flag != b->spatial_flipping_flag) ||
(a->frame0_flipped_flag != b->frame0_flipped_flag) ||
(a->field_views_flag != b->field_views_flag) ||
(a->current_frame_is_frame0_flag != b->current_frame_is_frame0_flag) ||
(a->frame0_self_contained_flag != b->frame0_self_contained_flag) ||
(a->frame1_self_contained_flag != b->frame1_self_contained_flag))
return FALSE;
if (!a->quincunx_sampling_flag &&
a->frame_packing_type != GST_H264_FRAME_PACKING_TEMPORAL_INTERLEAVING) {
if ((a->frame0_grid_position_x != b->frame0_grid_position_x) ||
(a->frame0_grid_position_y != b->frame0_grid_position_y) ||
(a->frame1_grid_position_x != b->frame1_grid_position_x) ||
(a->frame1_grid_position_y != b->frame1_grid_position_y))
return FALSE;
}
if (a->frame_packing_repetition_period !=
b->frame_packing_repetition_period)
return FALSE;
}
return TRUE;
}
static gboolean
check_sei_mdcv (const GstH264MasteringDisplayColourVolume * a,
const GstH264MasteringDisplayColourVolume * b)
{
gint i;
for (i = 0; i < 3; i++) {
if (a->display_primaries_x[i] != b->display_primaries_x[i] ||
a->display_primaries_y[i] != b->display_primaries_y[i])
return FALSE;
}
return (a->white_point_x == b->white_point_x) &&
(a->white_point_y == b->white_point_y) &&
(a->max_display_mastering_luminance == b->max_display_mastering_luminance)
&& (a->min_display_mastering_luminance ==
b->min_display_mastering_luminance);
}
static gboolean
check_sei_cll (const GstH264ContentLightLevel * a,
const GstH264ContentLightLevel * b)
{
return (a->max_content_light_level == b->max_content_light_level) &&
(a->max_pic_average_light_level == b->max_pic_average_light_level);
}
GST_START_TEST (test_h264_create_sei)
{
GstH264NalParser *parser;
GstH264ParserResult parse_ret;
GstH264NalUnit nalu;
GArray *msg_array = NULL;
GstMemory *mem;
gint i;
GstMapInfo info;
struct
{
guint8 *raw_data;
guint len;
GstH264SEIPayloadType type;
GstH264SEIMessage parsed_message;
SEICheckFunc check_func;
} test_list[] = {
/* *INDENT-OFF* */
{h264_sei_user_data_registered, G_N_ELEMENTS (h264_sei_user_data_registered),
GST_H264_SEI_REGISTERED_USER_DATA, {0,},
(SEICheckFunc) check_sei_user_data_registered},
{h264_sei_frame_packing, G_N_ELEMENTS (h264_sei_frame_packing),
GST_H264_SEI_FRAME_PACKING, {0,},
(SEICheckFunc) check_sei_frame_packing},
{h264_sei_mdcv, G_N_ELEMENTS (h264_sei_mdcv),
GST_H264_SEI_MASTERING_DISPLAY_COLOUR_VOLUME, {0,},
(SEICheckFunc) check_sei_mdcv},
{h264_sei_cll, G_N_ELEMENTS (h264_sei_cll),
GST_H264_SEI_CONTENT_LIGHT_LEVEL, {0,},
(SEICheckFunc) check_sei_cll},
/* *INDENT-ON* */
};
parser = gst_h264_nal_parser_new ();
/* test single sei message per sei nal unit */
for (i = 0; i < G_N_ELEMENTS (test_list); i++) {
gsize nal_size;
parse_ret = gst_h264_parser_identify_nalu_unchecked (parser,
test_list[i].raw_data, 0, test_list[i].len, &nalu);
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
assert_equals_int (nalu.type, GST_H264_NAL_SEI);
parse_ret = gst_h264_parser_parse_sei (parser, &nalu, &msg_array);
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
assert_equals_int (msg_array->len, 1);
/* test bytestream */
mem = gst_h264_create_sei_memory (4, msg_array);
fail_unless (mem != NULL);
fail_unless (gst_memory_map (mem, &info, GST_MAP_READ));
GST_MEMDUMP ("created sei nal", info.data, info.size);
GST_MEMDUMP ("original sei nal", test_list[i].raw_data, test_list[i].len);
assert_equals_int (info.size, test_list[i].len);
fail_if (memcmp (info.data, test_list[i].raw_data, test_list[i].len));
gst_memory_unmap (mem, &info);
gst_memory_unref (mem);
/* test packetized */
mem = gst_h264_create_sei_memory_avc (4, msg_array);
fail_unless (mem != NULL);
fail_unless (gst_memory_map (mem, &info, GST_MAP_READ));
assert_equals_int (info.size, test_list[i].len);
fail_if (memcmp (info.data + 4, test_list[i].raw_data + 4,
test_list[i].len - 4));
nal_size = GST_READ_UINT32_BE (info.data);
assert_equals_int (nal_size, info.size - 4);
gst_memory_unmap (mem, &info);
gst_memory_unref (mem);
/* store parsed SEI for following tests */
test_list[i].parsed_message =
g_array_index (msg_array, GstH264SEIMessage, 0);
if (test_list[i].type == GST_H264_SEI_REGISTERED_USER_DATA) {
GstH264RegisteredUserData *dst_rud =
&test_list[i].parsed_message.payload.registered_user_data;
const GstH264SEIMessage *src_msg =
&g_array_index (msg_array, GstH264SEIMessage, 0);
const GstH264RegisteredUserData *src_rud =
&src_msg->payload.registered_user_data;
dst_rud->data = g_malloc (src_rud->size);
memcpy ((guint8 *) dst_rud->data, src_rud->data, src_rud->size);
}
g_array_unref (msg_array);
}
/* test multiple SEI messages in a nal unit */
msg_array = g_array_new (FALSE, FALSE, sizeof (GstH264SEIMessage));
for (i = 0; i < G_N_ELEMENTS (test_list); i++)
g_array_append_val (msg_array, test_list[i].parsed_message);
mem = gst_h264_create_sei_memory (4, msg_array);
fail_unless (mem != NULL);
g_array_unref (msg_array);
/* parse sei message from buffer */
fail_unless (gst_memory_map (mem, &info, GST_MAP_READ));
parse_ret = gst_h264_parser_identify_nalu_unchecked (parser,
info.data, 0, info.size, &nalu);
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
assert_equals_int (nalu.type, GST_H264_NAL_SEI);
parse_ret = gst_h264_parser_parse_sei (parser, &nalu, &msg_array);
gst_memory_unmap (mem, &info);
gst_memory_unref (mem);
assert_equals_int (parse_ret, GST_H264_PARSER_OK);
assert_equals_int (msg_array->len, G_N_ELEMENTS (test_list));
for (i = 0; i < msg_array->len; i++) {
GstH264SEIMessage *msg = &g_array_index (msg_array, GstH264SEIMessage, i);
assert_equals_int (msg->payloadType, test_list[i].type);
fail_unless (test_list[i].check_func (&msg->payload,
&test_list[i].parsed_message.payload));
}
/* clean up */
for (i = 0; i < G_N_ELEMENTS (test_list); i++)
gst_h264_sei_clear (&test_list[i].parsed_message);
g_array_unref (msg_array);
gst_h264_nal_parser_free (parser);
}
GST_END_TEST;
static Suite * static Suite *
h264parser_suite (void) h264parser_suite (void)
{ {
@ -325,6 +568,7 @@ h264parser_suite (void)
tcase_add_test (tc_chain, test_h264_parse_slice_eoseq_slice); tcase_add_test (tc_chain, test_h264_parse_slice_eoseq_slice);
tcase_add_test (tc_chain, test_h264_parse_slice_5bytes); tcase_add_test (tc_chain, test_h264_parse_slice_5bytes);
tcase_add_test (tc_chain, test_h264_parse_invalid_sei); tcase_add_test (tc_chain, test_h264_parse_invalid_sei);
tcase_add_test (tc_chain, test_h264_create_sei);
return s; return s;
} }