/* GStreamer * * Copyright (C) 2014 Samsung Electronics. All rights reserved. * Author: Thiago Santos * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include static GstPad *mysrcpad, *mysinkpad; static GstElement *enc; static GList *events = NULL; #define TEST_VIDEO_WIDTH 640 #define TEST_VIDEO_HEIGHT 480 #define TEST_VIDEO_FPS_N 30 #define TEST_VIDEO_FPS_D 1 #define GST_VIDEO_ENCODER_TESTER_TYPE gst_video_encoder_tester_get_type() #define GST_VIDEO_ENCODER_TESTER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_VIDEO_ENCODER_TESTER_TYPE, GstVideoEncoderTester)) static GType gst_video_encoder_tester_get_type (void); typedef struct _GstVideoEncoderTester GstVideoEncoderTester; typedef struct _GstVideoEncoderTesterClass GstVideoEncoderTesterClass; struct _GstVideoEncoderTester { GstVideoEncoder parent; GstFlowReturn pre_push_result; gint num_subframes; gint current_subframe; gboolean send_headers; gboolean key_frame_sent; gboolean enable_step_by_step; gboolean negotiate_in_set_format; GstVideoCodecFrame *last_frame; }; struct _GstVideoEncoderTesterClass { GstFlowReturn (*step_by_step) (GstVideoEncoder * encoder, GstVideoCodecFrame * frame, int steps); GstVideoEncoderClass parent_class; }; G_DEFINE_TYPE (GstVideoEncoderTester, gst_video_encoder_tester, GST_TYPE_VIDEO_ENCODER); static gboolean gst_video_encoder_tester_start (GstVideoEncoder * enc) { return TRUE; } static gboolean gst_video_encoder_tester_stop (GstVideoEncoder * enc) { return TRUE; } static gboolean gst_video_encoder_tester_set_format (GstVideoEncoder * enc, GstVideoCodecState * state) { GstVideoEncoderTester *enc_tester = GST_VIDEO_ENCODER_TESTER (enc); GstVideoCodecState *res = gst_video_encoder_set_output_state (enc, gst_caps_new_simple ("video/x-test-custom", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, NULL), state); gst_video_codec_state_unref (res); if (enc_tester->negotiate_in_set_format) { gst_video_encoder_negotiate (enc); } return TRUE; } static GstFlowReturn gst_video_encoder_push_subframe (GstVideoEncoder * enc, GstVideoCodecFrame * frame, int current_subframe) { guint8 *data; GstMapInfo map; guint64 input_num; GstVideoEncoderTester *enc_tester = GST_VIDEO_ENCODER_TESTER (enc); if (enc_tester->send_headers) { GstBuffer *hdr; GList *headers = NULL; hdr = gst_buffer_new_and_alloc (0); GST_BUFFER_FLAG_SET (hdr, GST_BUFFER_FLAG_HEADER); headers = g_list_append (headers, hdr); gst_video_encoder_set_headers (enc, headers); enc_tester->send_headers = FALSE; } gst_buffer_map (frame->input_buffer, &map, GST_MAP_READ); input_num = *((guint64 *) map.data); gst_buffer_unmap (frame->input_buffer, &map); if (!enc_tester->key_frame_sent || GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (frame)) { GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame); enc_tester->key_frame_sent = TRUE; } data = g_malloc (sizeof (guint64)); *(guint64 *) data = input_num; frame->output_buffer = gst_buffer_new_wrapped (data, sizeof (guint64)); frame->pts = GST_BUFFER_PTS (frame->input_buffer); frame->duration = GST_BUFFER_DURATION (frame->input_buffer); if (current_subframe < enc_tester->num_subframes - 1) return gst_video_encoder_finish_subframe (enc, frame); else return gst_video_encoder_finish_frame (enc, frame); } static GstFlowReturn gst_video_encoder_tester_output_step_by_step (GstVideoEncoder * enc, GstVideoCodecFrame * frame, gint steps) { GstVideoEncoderTester *enc_tester = GST_VIDEO_ENCODER_TESTER (enc); GstFlowReturn ret = GST_FLOW_OK; int i; for (i = enc_tester->current_subframe; i < MIN (steps + enc_tester->current_subframe, enc_tester->num_subframes); i++) { ret = gst_video_encoder_push_subframe (enc, frame, i); } enc_tester->current_subframe = i; if (enc_tester->current_subframe >= enc_tester->num_subframes) { enc_tester->current_subframe = 0; gst_video_codec_frame_unref (enc_tester->last_frame); } return ret; } static GstFlowReturn gst_video_encoder_tester_handle_frame (GstVideoEncoder * enc, GstVideoCodecFrame * frame) { GstClockTimeDiff deadline; GstVideoEncoderTester *enc_tester = GST_VIDEO_ENCODER_TESTER (enc); deadline = gst_video_encoder_get_max_encode_time (enc, frame); if (deadline < 0) { /* Calling finish_frame() with frame->output_buffer == NULL means to drop it */ return gst_video_encoder_finish_frame (enc, frame); } enc_tester->last_frame = gst_video_codec_frame_ref (frame); if (enc_tester->enable_step_by_step) return GST_FLOW_OK; return gst_video_encoder_tester_output_step_by_step (enc, frame, enc_tester->num_subframes); } static GstFlowReturn gst_video_encoder_tester_pre_push (GstVideoEncoder * enc, GstVideoCodecFrame * frame) { GstVideoEncoderTester *tester = (GstVideoEncoderTester *) enc; return tester->pre_push_result; } static void gst_video_encoder_tester_class_init (GstVideoEncoderTesterClass * klass) { GstElementClass *element_class = GST_ELEMENT_CLASS (klass); GstVideoEncoderClass *videoencoder_class = GST_VIDEO_ENCODER_CLASS (klass); static GstStaticPadTemplate sink_templ = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw")); static GstStaticPadTemplate src_templ = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-test-custom")); gst_element_class_add_static_pad_template (element_class, &sink_templ); gst_element_class_add_static_pad_template (element_class, &src_templ); gst_element_class_set_metadata (element_class, "VideoEncoderTester", "Encoder/Video", "yep", "me"); videoencoder_class->start = gst_video_encoder_tester_start; videoencoder_class->stop = gst_video_encoder_tester_stop; videoencoder_class->handle_frame = gst_video_encoder_tester_handle_frame; videoencoder_class->pre_push = gst_video_encoder_tester_pre_push; videoencoder_class->set_format = gst_video_encoder_tester_set_format; } static void gst_video_encoder_tester_init (GstVideoEncoderTester * tester) { tester->pre_push_result = GST_FLOW_OK; /* One subframe is considered as a whole single frame. */ tester->num_subframes = 1; } static gboolean _mysinkpad_event (GstPad * pad, GstObject * parent, GstEvent * event) { events = g_list_append (events, event); return TRUE; } static void setup_videoencodertester (void) { static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-test-custom") ); static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_SRC, GST_PAD_ALWAYS, GST_STATIC_CAPS ("video/x-raw") ); enc = g_object_new (GST_VIDEO_ENCODER_TESTER_TYPE, NULL); mysrcpad = gst_check_setup_src_pad (enc, &srctemplate); mysinkpad = gst_check_setup_sink_pad (enc, &sinktemplate); gst_pad_set_event_function (mysinkpad, _mysinkpad_event); } static void setup_videoencodertester_with_subframes (int num_subframes) { GstVideoEncoderTester *enc_tester; setup_videoencodertester (); enc_tester = GST_VIDEO_ENCODER_TESTER (enc); enc_tester->num_subframes = num_subframes; enc_tester->send_headers = TRUE; } static void cleanup_videoencodertest (void) { gst_pad_set_active (mysrcpad, FALSE); gst_pad_set_active (mysinkpad, FALSE); gst_element_set_state (enc, GST_STATE_NULL); gst_check_teardown_src_pad (enc); gst_check_teardown_sink_pad (enc); gst_check_teardown_element (enc); g_list_free_full (events, (GDestroyNotify) gst_event_unref); events = NULL; } static GstBuffer * create_test_buffer (guint64 num) { GstBuffer *buffer; guint64 *data = g_malloc (sizeof (guint64)); *data = num; buffer = gst_buffer_new_wrapped (data, sizeof (guint64)); GST_BUFFER_PTS (buffer) = gst_util_uint64_scale_round (num, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N); GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale_round (GST_SECOND, TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N); return buffer; } static GstCaps * create_test_caps (void) { return gst_caps_new_simple ("video/x-raw", "width", G_TYPE_INT, TEST_VIDEO_WIDTH, "height", G_TYPE_INT, TEST_VIDEO_HEIGHT, "framerate", GST_TYPE_FRACTION, TEST_VIDEO_FPS_N, TEST_VIDEO_FPS_D, "format", G_TYPE_STRING, "GRAY8", NULL); } static void send_startup_events (void) { GstCaps *caps; fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("randomvalue"))); /* push caps */ caps = create_test_caps (); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps))); gst_caps_unref (caps); } #define NUM_BUFFERS 100 GST_START_TEST (videoencoder_playback) { GstSegment segment; GstBuffer *buffer; guint64 i; GList *iter; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push buffers, the data is actually a number so we can track them */ for (i = 0; i < NUM_BUFFERS; i++) { buffer = create_test_buffer (i); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); } fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); /* check that all buffers were received by our source pad */ fail_unless (g_list_length (buffers) == NUM_BUFFERS); i = 0; for (iter = buffers; iter; iter = g_list_next (iter)) { GstMapInfo map; guint64 num; buffer = iter->data; gst_buffer_map (buffer, &map, GST_MAP_READ); num = *(guint64 *) map.data; fail_unless (i == num); fail_unless (GST_BUFFER_PTS (buffer) == gst_util_uint64_scale_round (i, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N)); fail_unless (GST_BUFFER_DURATION (buffer) == gst_util_uint64_scale_round (GST_SECOND, TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N)); gst_buffer_unmap (buffer, &map); i++; } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; /* make sure tags sent right before eos are pushed */ GST_START_TEST (videoencoder_tags_before_eos) { GstSegment segment; GstBuffer *buffer; GstTagList *tags; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push buffer */ buffer = create_test_buffer (0); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* clean received events list */ g_list_free_full (events, (GDestroyNotify) gst_event_unref); events = NULL; /* push a tag event */ tags = gst_tag_list_new (GST_TAG_COMMENT, "test-comment", NULL); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_tag (tags))); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); /* check that the tag was received */ { GstEvent *tag_event = events->data; gchar *str; fail_unless (GST_EVENT_TYPE (tag_event) == GST_EVENT_TAG); gst_event_parse_tag (tag_event, &tags); fail_unless (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &str)); fail_unless (strcmp (str, "test-comment") == 0); g_free (str); } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; g_list_free_full (events, (GDestroyNotify) gst_event_unref); events = NULL; cleanup_videoencodertest (); } GST_END_TEST; /* make sure events sent right before eos are pushed */ GST_START_TEST (videoencoder_events_before_eos) { GstSegment segment; GstBuffer *buffer; GstMessage *msg; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push buffer */ buffer = create_test_buffer (0); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* clean received events list */ g_list_free_full (events, (GDestroyNotify) gst_event_unref); events = NULL; /* push a serialized event */ msg = gst_message_new_element (GST_OBJECT (mysrcpad), gst_structure_new_empty ("test")); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_sink_message ("sink-test", msg))); gst_message_unref (msg); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); /* check that the tag was received */ { GstEvent *msg_event = events->data; const GstStructure *structure; fail_unless (GST_EVENT_TYPE (msg_event) == GST_EVENT_SINK_MESSAGE); fail_unless (gst_event_has_name (msg_event, "sink-test")); gst_event_parse_sink_message (msg_event, &msg); structure = gst_message_get_structure (msg); fail_unless (gst_structure_has_name (structure, "test")); gst_message_unref (msg); } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; g_list_free_full (events, (GDestroyNotify) gst_event_unref); events = NULL; cleanup_videoencodertest (); } GST_END_TEST; GST_START_TEST (videoencoder_flush_events) { GstSegment segment; GstBuffer *buffer; guint i; GList *events_iter; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push buffers, the data is actually a number so we can track them */ for (i = 0; i < NUM_BUFFERS; i++) { if (i % 10 == 0) { GstTagList *tags; tags = gst_tag_list_new (GST_TAG_TRACK_NUMBER, i, NULL); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_tag (tags))); } else { buffer = create_test_buffer (i); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); } } fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); events_iter = events; /* make sure the usual events have been received */ { GstEvent *sstart = events_iter->data; fail_unless (GST_EVENT_TYPE (sstart) == GST_EVENT_STREAM_START); events_iter = g_list_next (events_iter); } { GstEvent *caps_event = events_iter->data; fail_unless (GST_EVENT_TYPE (caps_event) == GST_EVENT_CAPS); events_iter = g_list_next (events_iter); } { GstEvent *segment_event = events_iter->data; fail_unless (GST_EVENT_TYPE (segment_event) == GST_EVENT_SEGMENT); events_iter = g_list_next (events_iter); } /* check that EOS was received */ fail_unless (GST_PAD_IS_EOS (mysrcpad)); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_flush_start ())); fail_unless (GST_PAD_IS_EOS (mysrcpad)); /* Check that we have tags */ { GstEvent *tags = gst_pad_get_sticky_event (mysrcpad, GST_EVENT_TAG, 0); fail_unless (tags != NULL); gst_event_unref (tags); } /* Check that we still have a segment set */ { GstEvent *segment = gst_pad_get_sticky_event (mysrcpad, GST_EVENT_SEGMENT, 0); fail_unless (segment != NULL); gst_event_unref (segment); } fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_flush_stop (TRUE))); fail_if (GST_PAD_IS_EOS (mysrcpad)); /* Check that the segment was flushed on FLUSH_STOP */ { GstEvent *segment = gst_pad_get_sticky_event (mysrcpad, GST_EVENT_SEGMENT, 0); fail_unless (segment == NULL); } /* Check the tags were not lost on FLUSH_STOP */ { GstEvent *tags = gst_pad_get_sticky_event (mysrcpad, GST_EVENT_TAG, 0); fail_unless (tags != NULL); gst_event_unref (tags); } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; /* When pre_push fails the correct GstFlowReturn should be returned and there * should be no leaks */ GST_START_TEST (videoencoder_pre_push_fails) { GstVideoEncoderTester *tester; GstHarness *h; GstFlowReturn ret; tester = g_object_new (GST_VIDEO_ENCODER_TESTER_TYPE, NULL); tester->pre_push_result = GST_FLOW_ERROR; h = gst_harness_new_with_element (GST_ELEMENT (tester), "sink", "src"); gst_harness_set_src_caps (h, create_test_caps ()); ret = gst_harness_push (h, create_test_buffer (0)); fail_unless_equals_int (ret, GST_FLOW_ERROR); gst_harness_teardown (h); gst_object_unref (tester); } GST_END_TEST; GST_START_TEST (videoencoder_qos) { GstSegment segment; GstBuffer *buffer; GstClockTime ts, rt; GstBus *bus; GstMessage *msg; setup_videoencodertester (); gst_video_encoder_set_qos_enabled (GST_VIDEO_ENCODER (enc), TRUE); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); bus = gst_bus_new (); gst_element_set_bus (enc, bus); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push the first buffer */ buffer = create_test_buffer (0); ts = GST_BUFFER_PTS (buffer); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; /* pretend this buffer was late in the sink */ rt = gst_segment_to_running_time (&segment, GST_FORMAT_TIME, ts); fail_unless (gst_pad_push_event (mysinkpad, gst_event_new_qos (GST_QOS_TYPE_UNDERFLOW, 1.5, 500 * GST_MSECOND, rt))); /* push a second buffer which will be dropped as it's already late */ buffer = create_test_buffer (1); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; /* A QoS message was sent by the encoder */ msg = gst_bus_pop_filtered (bus, GST_MESSAGE_QOS); g_assert (msg != NULL); gst_message_unref (msg); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); gst_bus_set_flushing (bus, TRUE); gst_object_unref (bus); g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; #define NUM_BUFFERS 100 GST_START_TEST (videoencoder_playback_subframes) { GstSegment segment; GstBuffer *buffer; guint64 i; GList *iter; int subframes = 4; setup_videoencodertester_with_subframes (subframes); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push buffers, the data is actually a number so we can track them */ for (i = 0; i < NUM_BUFFERS; i++) { buffer = create_test_buffer (i); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); } fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); /* check that all buffers (plus one header buffer) were received by our source pad */ fail_unless (g_list_length (buffers) == NUM_BUFFERS * subframes + 1); /* check that first buffer is an header */ buffer = buffers->data; fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)); /* check the other buffers */ i = 0; for (iter = g_list_next (buffers); iter; iter = g_list_next (iter)) { /* first buffer should be the header */ GstMapInfo map; guint64 num; buffer = iter->data; fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)); gst_buffer_map (buffer, &map, GST_MAP_READ); num = *(guint64 *) map.data; fail_unless (i / subframes == num); if (i % subframes) fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)); fail_unless (GST_BUFFER_PTS (buffer) == gst_util_uint64_scale_round (i / subframes, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N)); fail_unless (GST_BUFFER_DURATION (buffer) == gst_util_uint64_scale_round (GST_SECOND, TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N)); gst_buffer_unmap (buffer, &map); i++; } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; GST_START_TEST (videoencoder_playback_events_subframes) { GstSegment segment; GstBuffer *buffer; GList *iter; gint subframes = 4; gint i, header_found; GstVideoEncoderTester *enc_tester; setup_videoencodertester_with_subframes (subframes); enc_tester = GST_VIDEO_ENCODER_TESTER (enc); enc_tester->send_headers = TRUE; enc_tester->enable_step_by_step = TRUE; gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment -> no new buffer and no new events (still pending two custom events) */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); fail_unless (g_list_length (buffers) == 0 && g_list_length (events) == 0); /* push a first buffer -> no new buffer and no new events (still pending two custom events) */ buffer = create_test_buffer (0); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); fail_unless (g_list_length (buffers) == 0 && g_list_length (events) == 0); /* ouput only one subframe -> 2 buffers(header + subframe) and 3 events (stream-start, caps, segment) */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 1); fail_unless (g_list_length (buffers) == 2 && g_list_length (events) == 3); fail_unless (GST_BUFFER_FLAG_IS_SET ((GstBuffer *) buffers->data, GST_BUFFER_FLAG_HEADER)); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 0)->data)) == GST_EVENT_STREAM_START); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 1)->data)) == GST_EVENT_CAPS); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 2)->data)) == GST_EVENT_SEGMENT); /* output 3 last subframes -> 2 more buffers and no new events */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 3); fail_unless (g_list_length (buffers) == 5 && g_list_length (events) == 3); /* push a new buffer -> no new buffer and no new events */ buffer = create_test_buffer (1); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); fail_unless (g_list_length (buffers) == 5 && g_list_length (events) == 3); /* push an event in between -> no new buffer and no new event */ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new_empty ("custom1")))); fail_unless (g_list_length (buffers) == 5 && g_list_length (events) == 3); /* output 1 subframe -> one new buffer and no new events */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 1); fail_unless (g_list_length (buffers) == 6 && g_list_length (events) == 3); /* push another custom event in between , no new event should appear until the next frame is handled */ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new_empty ("custom2")))); fail_unless (g_list_length (buffers) == 6 && g_list_length (events) == 3); /* output 2 subframes -> 2 new buffers and no new events */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 2); fail_unless (g_list_length (buffers) == 8 && g_list_length (events) == 3); /* output 1 last subframe -> 1 new buffers and no new events */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 1); fail_unless (g_list_length (buffers) == 9 && g_list_length (events) == 3); /* push a third buffer -> no new buffer and no new events (still pending two custom events) */ buffer = create_test_buffer (2); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); fail_unless (g_list_length (buffers) == 9 && g_list_length (events) == 3); /* output 1 subframes -> 1 new buffer and 2 custom events from the last input frame */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 1); fail_unless (g_list_length (buffers) == 10 && g_list_length (events) == 5); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 3)->data)) == GST_EVENT_CUSTOM_DOWNSTREAM); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 4)->data)) == GST_EVENT_CUSTOM_DOWNSTREAM); /* push another custom event in between , no new event should appear until eos */ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new_empty ("custom3")))); fail_unless (g_list_length (buffers) == 10 && g_list_length (events) == 5); /* output 3 subframes -> 3 new buffer and no new events */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 3); fail_unless (g_list_length (buffers) == 13 && g_list_length (events) == 5); /* push a force key-unit event */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); /* Create a new buffer which should be a key unit -> no new buffer and no new event */ buffer = create_test_buffer (3); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); fail_unless (g_list_length (buffers) == 13 && g_list_length (events) == 5); /* output 2 subframes -> 3 new buffer(one header and two subframes and two events key-unit and custom3 */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 2); fail_unless (g_list_length (buffers) == 16 && g_list_length (events) == 7); /* output 2 subframes -> 2 new buffer corresponding the two last subframes */ gst_video_encoder_tester_output_step_by_step (GST_VIDEO_ENCODER (enc), enc_tester->last_frame, 2); fail_unless (g_list_length (buffers) == 18 && g_list_length (events) == 7); /* push eos event -> 1 new event ( eos) */ fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ())); fail_unless (g_list_length (buffers) == 18 && g_list_length (events) == 8); /* check the order of the last events received */ fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 6)->data)) == GST_EVENT_CUSTOM_DOWNSTREAM); fail_unless (GST_EVENT_TYPE ((GstEvent *) (g_list_nth (events, 7)->data)) == GST_EVENT_EOS); /* check that only last subframe owns the GST_VIDEO_BUFFER_FLAG_MARKER flag */ header_found = 0; for (iter = buffers, i = 0; iter; iter = g_list_next (iter), i++) { buffer = (GstBuffer *) (iter->data); if (!GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_HEADER)) { if ((i - header_found) % subframes == (subframes - 1)) fail_unless (GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_MARKER)); else fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_MARKER)); } else { fail_unless (!GST_BUFFER_FLAG_IS_SET (buffer, GST_VIDEO_BUFFER_FLAG_MARKER)); header_found++; } /* Only the 0th (header), 1st, 13th (header) and 14th buffer should be keyframes */ if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT)) { fail_if (i == 0 || i == 1 || i == 13 || i == 14); } else { fail_unless (i == 0 || i == 1 || i == 13 || i == 14); } } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; GST_START_TEST (videoencoder_force_keyunit_handling) { GstSegment segment; GstBuffer *buffer; GList *l; gint i; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push the first buffer */ buffer = create_test_buffer (0); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 1); buffer = create_test_buffer (1); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 2); /* send a force-keyunit event, the next buffer should be a keyframe now */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); buffer = create_test_buffer (2); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 3); buffer = create_test_buffer (3); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 4); /* send multiple force-keyunit events now, this should still only cause a * single keyframe */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); buffer = create_test_buffer (4); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 5); buffer = create_test_buffer (5); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 6); /* send a force-keyunit event for the running time of the next buffer */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (6, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); buffer = create_test_buffer (6); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 7); buffer = create_test_buffer (7); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 8); /* send a force-keyunit event for the running time of the next buffer * and another one right before. This should only cause a single keyframe * again */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (8, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (8, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N) - 10 * GST_MSECOND, TRUE, 1))); buffer = create_test_buffer (8); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 9); buffer = create_test_buffer (9); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 10); /* send a force-keyunit event for the 12th buffer, see below */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (12, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); /* send two force-keyunit events. This should only cause a single keyframe * again */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (10, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (10, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N) - 10 * GST_MSECOND, TRUE, 1))); buffer = create_test_buffer (10); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 11); buffer = create_test_buffer (11); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 12); /* we already sent a force-keyunit event for the 12th buffer long ago */ buffer = create_test_buffer (12); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 13); /* we already received a keyframe after the given time, so the next frame * is not going to be another keyframe */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (12, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); buffer = create_test_buffer (13); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = NULL; fail_unless_equals_int (g_list_length (buffers), 14); /* every second buffer should be a keyframe */ for (l = buffers, i = 0; l; l = l->next, i++) { if (i % 2 == 0) fail_if (GST_BUFFER_FLAG_IS_SET (l->data, GST_BUFFER_FLAG_DELTA_UNIT)); else fail_unless (GST_BUFFER_FLAG_IS_SET (l->data, GST_BUFFER_FLAG_DELTA_UNIT)); } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; GST_START_TEST (videoencoder_force_keyunit_min_interval) { GstSegment segment; GstBuffer *buffer; GList *l; gint i; setup_videoencodertester (); gst_pad_set_active (mysrcpad, TRUE); /* Only one keyframe request every 3 frames at most */ g_object_set (enc, "min-force-key-unit-interval", 100 * GST_MSECOND, NULL); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); send_startup_events (); /* push a new segment */ gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); /* push the first two buffers */ buffer = create_test_buffer (0); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (1); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* send a force-keyunit event, the next buffer should not be a keyframe yet */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); buffer = create_test_buffer (2); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* this buffer should be a keyframe */ buffer = create_test_buffer (3); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* send two force-keyunit event, the 6th buffer should be a keyframe */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE, TRUE, 1))); buffer = create_test_buffer (4); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (5); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (6); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* send a force-keyunit event for the 9th buffer, this should happen */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (9, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); buffer = create_test_buffer (7); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (8); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (9); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); /* send a force-keyunit event for the 11th buffer, this should happen on the * 12th */ fail_unless (gst_pad_push_event (mysinkpad, gst_video_event_new_upstream_force_key_unit (gst_util_uint64_scale_round (11, GST_SECOND * TEST_VIDEO_FPS_D, TEST_VIDEO_FPS_N), TRUE, 1))); buffer = create_test_buffer (10); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (11); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); buffer = create_test_buffer (12); fail_unless (gst_pad_push (mysrcpad, buffer) == GST_FLOW_OK); fail_unless_equals_int (g_list_length (buffers), 13); /* every third buffer should be a keyframe */ for (l = buffers, i = 0; l; l = l->next, i++) { if (i % 3 == 0) fail_if (GST_BUFFER_FLAG_IS_SET (l->data, GST_BUFFER_FLAG_DELTA_UNIT)); else fail_unless (GST_BUFFER_FLAG_IS_SET (l->data, GST_BUFFER_FLAG_DELTA_UNIT)); } g_list_free_full (buffers, (GDestroyNotify) gst_buffer_unref); buffers = NULL; cleanup_videoencodertest (); } GST_END_TEST; GST_START_TEST (videoencoder_hdr_metadata) { const gchar *mdi_str = "35399:14599:8500:39850:6550:2300:15634:16450:10000000:1"; const gchar *cll_str = "1000:50"; gint i; /* Check that HDR metadata get passed to src pad no matter if negotiate gets * called from gst_video_encoder_finish_frame() or GstVideoEncoder::set_format */ for (i = 1; i >= 0; --i) { GstVideoMasteringDisplayInfo mdi; GstVideoContentLightLevel cll; GstSegment segment; GstCaps *caps; GstStructure *s; const gchar *str; setup_videoencodertester (); GST_VIDEO_ENCODER_TESTER (enc)->negotiate_in_set_format = i; gst_pad_set_active (mysrcpad, TRUE); gst_element_set_state (enc, GST_STATE_PLAYING); gst_pad_set_active (mysinkpad, TRUE); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("id"))); gst_video_mastering_display_info_from_string (&mdi, mdi_str); gst_video_content_light_level_from_string (&cll, cll_str); caps = create_test_caps (); gst_video_mastering_display_info_add_to_caps (&mdi, caps); gst_video_content_light_level_add_to_caps (&cll, caps); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_caps (caps))); gst_caps_unref (caps); gst_segment_init (&segment, GST_FORMAT_TIME); fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment))); gst_pad_push (mysrcpad, create_test_buffer (0)); caps = gst_pad_get_current_caps (mysinkpad); s = gst_caps_get_structure (caps, 0); fail_unless (str = gst_structure_get_string (s, "mastering-display-info")); fail_unless_equals_string (str, mdi_str); fail_unless (str = gst_structure_get_string (s, "content-light-level")); fail_unless_equals_string (str, cll_str); gst_caps_unref (caps); cleanup_videoencodertest (); } } GST_END_TEST; static Suite * gst_videoencoder_suite (void) { Suite *s = suite_create ("GstVideoEncoder"); TCase *tc = tcase_create ("general"); suite_add_tcase (s, tc); tcase_add_test (tc, videoencoder_playback); tcase_add_test (tc, videoencoder_tags_before_eos); tcase_add_test (tc, videoencoder_events_before_eos); tcase_add_test (tc, videoencoder_flush_events); tcase_add_test (tc, videoencoder_pre_push_fails); tcase_add_test (tc, videoencoder_qos); tcase_add_test (tc, videoencoder_playback_subframes); tcase_add_test (tc, videoencoder_playback_events_subframes); tcase_add_test (tc, videoencoder_force_keyunit_handling); tcase_add_test (tc, videoencoder_force_keyunit_min_interval); tcase_add_test (tc, videoencoder_hdr_metadata); return s; } GST_CHECK_MAIN (gst_videoencoder);