*fps_num = num;
*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);
+}
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)
{
GstH264ParserResult res;
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 *
h264parser_suite (void)
{
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_invalid_sei);
+ tcase_add_test (tc_chain, test_h264_create_sei);
return s;
}