2 * test-fei-enc-in.c - Test FEI input buffer submission
4 * Copyright (C) 2016 Intel Corporation
6 * Author: Sreerenj Balachandran <sreerenj.balachandran@intel.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1
11 * of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301 USA
23 /* sample pipeline: ./test-fei-enc-input -c h264 -o out.264 -e 4 -q 1 sample_i420.y4m */
27 #include <sys/types.h>
31 #include "gst/vaapi/sysdeps.h"
32 #include <gst/vaapi/gstvaapiencoder.h>
33 #include <gst/vaapi/gstvaapiencoder_h264_fei.h>
34 #include <gst/vaapi/gstvaapisurfacepool.h>
35 #include <gst/vaapi/gstvaapisurfaceproxy.h>
36 #include <gst/vaapi/gstvaapifei_objects.h>
38 #include "y4mreader.h"
41 static guint g_bitrate = 0;
42 static gchar *g_codec_str;
43 static gchar *g_output_file_name;
44 static char **g_input_files = NULL;
45 static gchar *input_mv_name = NULL;
46 static gchar *input_mbmode_name = NULL;
47 static guint input_mv_size;
48 static guint input_mbmode_size;
49 static guint input_qp;
50 static guint enable_mbcntrl;
51 static guint enable_mvpred;
52 static guint fei_mode;
54 #define SURFACE_NUM 16
58 #define ENC_PLUS_PAK 3
61 static GOptionEntry g_options[] = {
62 {"codec", 'c', 0, G_OPTION_ARG_STRING, &g_codec_str,
63 "codec to use for video encoding (h264)", NULL},
64 {"bitrate", 'b', 0, G_OPTION_ARG_INT, &g_bitrate,
65 "desired bitrate expressed in kbps", NULL},
66 {"output", 'o', 0, G_OPTION_ARG_FILENAME, &g_output_file_name,
67 "output file name", NULL},
68 {"imv", 'v', 0, G_OPTION_ARG_STRING, &input_mv_name,
69 "pak mv input file", NULL},
70 {"imbmode ", 'm', 0, G_OPTION_ARG_STRING, &input_mbmode_name,
71 "pak mbmode input file", NULL},
72 {"imvsize", 's', 0, G_OPTION_ARG_INT, &input_mv_size,
73 "input stream width", NULL},
74 {"imbmodesize", 'd', 0, G_OPTION_ARG_INT, &input_mbmode_size,
75 "input stream height", NULL},
76 {"iqp", 'q', 0, G_OPTION_ARG_INT, &input_qp,
77 "input qp val (it will get replicated for each macrobock)", NULL},
78 {"imbcntrl", 'l', 0, G_OPTION_ARG_INT, &enable_mbcntrl,
79 "enable macroblock control for each macrobock", NULL},
80 {"imbpred", 'p', 0, G_OPTION_ARG_INT, &enable_mvpred,
81 "enable mv predictor for each macroblock", NULL},
82 {"fei-mode", 'e', 0, G_OPTION_ARG_INT, &fei_mode,
83 "1:ENC 2:PAK 3:ENC+PAK 4:ENC_PAK", NULL},
85 {G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_FILENAME_ARRAY, &g_input_files,
86 "input file name", NULL},
92 GstVaapiDisplay *display;
93 GstVaapiEncoder *encoder;
102 guint input_mbmode_size;
103 guint input_stopped:1;
104 guint encode_failed:1;
107 static inline gchar *
108 generate_output_filename (const gchar * ext)
114 fn = g_strdup_printf ("temp%02d.%s", i, ext);
115 if (g_file_test (fn, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
127 parse_options (int *argc, char *argv[])
131 GError *error = NULL;
133 ctx = g_option_context_new (" - encoder test options");
137 g_option_context_add_group (ctx, gst_init_get_option_group ());
138 g_option_context_add_main_entries (ctx, g_options, NULL);
139 g_option_context_set_help_enabled (ctx, TRUE);
140 success = g_option_context_parse (ctx, argc, &argv, &error);
142 g_printerr ("Option parsing failed: %s\n", error->message);
143 g_error_free (error);
148 g_codec_str = g_strdup ("h264");
149 if (!g_output_file_name)
150 g_output_file_name = generate_output_filename (g_codec_str);
153 g_option_context_free (ctx);
158 print_yuv_info (App * app)
161 g_print ("Encode : %s\n", g_codec_str);
162 g_print ("Resolution : %dx%d\n", app->parser->width, app->parser->height);
163 g_print ("Source YUV : %s\n", g_input_files ? g_input_files[0] : "stdin");
164 g_print ("Frame Rate : %0.1f fps\n",
165 1.0 * app->parser->fps_n / app->parser->fps_d);
166 g_print ("Coded file : %s\n", g_output_file_name);
171 print_num_frame (App * app)
174 g_print ("read frames : %d\n", app->read_frames);
175 g_print ("encoded frames : %d\n", app->encoded_frames);
176 g_print ("saved frames : %d\n", app->saved_frames);
180 static GstVaapiEncoder *
181 encoder_new (GstVaapiDisplay * display)
183 GstVaapiEncoder *encoder = NULL;
185 if (!g_strcmp0 (g_codec_str, "h264")) {
186 encoder = gst_vaapi_encoder_h264_fei_new (display);
187 gst_vaapi_encoder_h264_fei_set_function_mode (GST_VAAPI_ENCODER_H264_FEI
188 (encoder), fei_mode);
189 gst_vaapi_encoder_h264_fei_set_max_profile (GST_VAAPI_ENCODER_H264_FEI
190 (encoder), GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE);
197 static inline GstVideoCodecState *
198 new_codec_state (gint width, gint height, gint fps_n, gint fps_d)
200 GstVideoCodecState *state;
202 state = g_slice_new0 (GstVideoCodecState);
203 state->ref_count = 1;
204 gst_video_info_set_format (&state->info, GST_VIDEO_FORMAT_ENCODED, width,
207 state->info.fps_n = fps_n;
208 state->info.fps_d = fps_d;
214 set_format (GstVaapiEncoder * encoder, gint width, gint height, gint fps_n,
217 GstVideoCodecState *in_state;
218 GstVaapiEncoderStatus status;
220 in_state = new_codec_state (width, height, fps_n, fps_d);
221 status = gst_vaapi_encoder_set_codec_state (encoder, in_state);
222 g_slice_free (GstVideoCodecState, in_state);
224 return (status == GST_VAAPI_ENCODER_STATUS_SUCCESS);
228 allocate_buffer (GstVaapiCodedBuffer * vbuf)
233 size = gst_vaapi_coded_buffer_get_size (vbuf);
236 g_warning ("Invalid VA buffer size (%zd)", size);
240 buf = gst_buffer_new_and_alloc (size);
242 g_warning ("Failed to create output buffer of size %zd", size);
246 if (!gst_vaapi_coded_buffer_copy_into (buf, vbuf)) {
247 g_warning ("Failed to copy VA buffer data");
248 gst_buffer_unref (buf);
255 static GstVaapiEncoderStatus
256 get_encoder_buffer (GstVaapiEncoder * encoder, GstBuffer ** buffer)
258 GstVaapiCodedBufferProxy *proxy = NULL;
259 GstVaapiEncoderStatus status;
261 status = gst_vaapi_encoder_get_buffer_with_timeout (encoder, &proxy, 50000);
262 if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS) {
263 g_warning ("Failed to get a buffer from encoder: %d", status);
265 } else if (status > GST_VAAPI_ENCODER_STATUS_SUCCESS) {
269 *buffer = allocate_buffer (GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (proxy));
270 gst_vaapi_coded_buffer_proxy_unref (proxy);
276 outputs_to_file (GstBuffer * buffer, FILE * file)
280 gboolean ret = FALSE;
282 if (!gst_buffer_map (buffer, &info, GST_MAP_READ))
285 if (info.size <= 0 || !info.data)
288 written = fwrite (info.data, 1, info.size, file);
289 if (written < info.size) {
290 g_warning ("write file error.");
297 gst_buffer_unmap (buffer, &info);
302 get_buffer_thread (gpointer data)
306 GstVaapiEncoderStatus ret;
311 ret = get_encoder_buffer (app->encoder, &obuf);
312 if (app->input_stopped && ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) {
313 break; /* finished */
314 } else if (ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) { /* another chance */
317 if (ret < GST_VAAPI_ENCODER_STATUS_SUCCESS) { /* fatal error */
318 app->encode_failed = TRUE;
322 app->encoded_frames++;
323 g_debug ("encoded frame %d, buffer = %p", app->encoded_frames, obuf);
325 if (app->output_file && outputs_to_file (obuf, app->output_file))
328 gst_buffer_unref (obuf);
331 gst_buffer_replace (&obuf, NULL);
339 g_return_if_fail (app);
342 y4m_reader_close (app->parser);
345 gst_vaapi_encoder_flush (app->encoder);
346 gst_object_unref (app->encoder);
350 gst_object_unref (app->display);
352 if (app->output_file)
353 fclose (app->output_file);
355 g_slice_free (App, app);
359 app_new (const gchar * input_fn, const gchar * output_fn)
361 App *app = g_slice_new0 (App);
364 app->parser = y4m_reader_open (input_fn);
366 g_warning ("Could not parse input stream.");
370 app->output_file = fopen (output_fn, "w");
371 if (app->output_file == NULL) {
372 g_warning ("Could not open file \"%s\" for writing: %s.", output_fn,
379 if (!input_mv_name || !input_mbmode_name) {
380 g_warning ("pak only mode need an mv and mbmode files as input");
385 app->mv_fd = open (input_mv_name, O_RDONLY, 0);
386 if (input_mbmode_name)
387 app->mbmode_fd = open (input_mbmode_name, O_RDONLY, 0);
389 assert (app->mv_fd >= 0);
390 assert (app->mbmode_fd >= 0);
393 app->display = video_output_create_display (NULL);
395 g_warning ("Could not create VA display.");
399 app->encoder = encoder_new (app->display);
401 g_warning ("Could not create encoder.");
405 if (!set_format (app->encoder, app->parser->width, app->parser->height,
406 app->parser->fps_n, app->parser->fps_d)) {
407 g_warning ("Could not set format.");
419 upload_frame (GstVaapiEncoder * encoder, GstVaapiSurfaceProxy * proxy)
421 GstVideoCodecFrame *frame;
422 GstVaapiEncoderStatus ret;
424 frame = g_slice_new0 (GstVideoCodecFrame);
425 gst_video_codec_frame_set_user_data (frame,
426 gst_vaapi_surface_proxy_ref (proxy),
427 (GDestroyNotify) gst_vaapi_surface_proxy_unref);
429 ret = gst_vaapi_encoder_put_frame (encoder, frame);
430 return (ret == GST_VAAPI_ENCODER_STATUS_SUCCESS);
434 load_frame (App * app, GstVaapiImage * image)
436 gboolean ret = FALSE;
438 if (!gst_vaapi_image_map (image))
441 ret = y4m_reader_load_image (app->parser, image);
443 if (!gst_vaapi_image_unmap (image))
452 GstVaapiImage *image;
453 GstVaapiVideoPool *pool;
454 GThread *buffer_thread;
458 int ret = EXIT_FAILURE;
459 image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420,
460 app->parser->width, app->parser->height);
464 gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
465 app->parser->width, app->parser->height);
466 pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0);
468 buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app);
471 GstVaapiSurfaceProxy *proxy;
472 GstVaapiSurface *surface;
473 gpointer data = NULL;
476 guint mb_width, mb_height, mb_size;
478 if (!load_frame (app, image))
481 if (!gst_vaapi_image_unmap (image))
485 gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool));
487 g_warning ("Could not get surface proxy from pool.");
490 surface = gst_vaapi_surface_proxy_get_surface (proxy);
492 g_warning ("Could not get surface from proxy.");
496 if (!gst_vaapi_surface_put_image (surface, image)) {
497 g_warning ("Could not update surface");
501 mb_width = (app->parser->width + 15) >> 4;
502 mb_height = (app->parser->height + 15) >> 4;
503 mb_size = mb_width * mb_height;
506 if (fei_mode == PAK) {
507 GstVaapiEncFeiMbCode *mbcode;
508 GstVaapiEncFeiMv *mv;
509 guint mv_size, mbmode_size;
511 mv_size = mb_width * mb_height * 128;
512 mbmode_size = mb_width * mb_height * 64;
515 assert (input_mv_size == mv_size);
517 if (input_mbmode_size)
518 assert (input_mbmode_size == mbmode_size);
520 /* Upload mbmode data */
521 mbcode = gst_vaapi_enc_fei_mb_code_new (app->encoder, NULL, mbmode_size);
522 rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (mbcode),
525 rt = read (app->mbmode_fd, data, mbmode_size);
529 mv = gst_vaapi_enc_fei_mv_new (app->encoder, NULL, mv_size);
530 rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (mv),
533 rt = read (app->mv_fd, data, mv_size);
536 /* assign mv and mbmode buffers to input surface proxy */
537 gst_vaapi_surface_proxy_set_fei_mb_code (proxy, mbcode);
538 gst_vaapi_surface_proxy_set_fei_mv (proxy, mv);
541 /* ENC, ENC+PAK and ENC_PAK */
544 GstVaapiEncFeiQp *qp = NULL;
545 VAEncQPBufferH264 *pqp = NULL;
548 qp_size = mb_width * mb_height * sizeof (VAEncQPBufferH264);
550 qp = gst_vaapi_enc_fei_qp_new (app->encoder, NULL, qp_size);
551 rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (qp),
555 pqp = (VAEncQPBufferH264 *) data;
556 for (i = 0; i < mb_size; i++) {
560 gst_vaapi_surface_proxy_set_fei_qp (proxy, qp);
563 if (enable_mbcntrl) {
564 GstVaapiEncFeiMbControl *mbcntrl = NULL;
565 VAEncFEIMBControlH264 *pmbcntrl = NULL;
566 guint mbcntrl_size = 0;
568 mbcntrl_size = mb_width * mb_height * sizeof (VAEncFEIMBControlH264);
570 gst_vaapi_enc_fei_mb_control_new (app->encoder, NULL, mbcntrl_size);
571 rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT
572 (mbcntrl), &data, &size);
575 pmbcntrl = (VAEncFEIMBControlH264 *) data;
576 for (i = 0; i < mb_size; i++) {
577 pmbcntrl->force_to_intra = 1;
578 pmbcntrl->force_to_skip = 0;
579 pmbcntrl->force_to_nonskip = 0;
580 pmbcntrl->enable_direct_bias_adjustment = 0;
581 pmbcntrl->enable_motion_bias_adjustment = 0;
582 pmbcntrl->ext_mv_cost_scaling_factor = 0;
583 pmbcntrl->target_size_in_word = 0xff;
584 pmbcntrl->max_size_in_word = 0xff;
587 gst_vaapi_surface_proxy_set_fei_mb_control (proxy, mbcntrl);
591 GstVaapiEncFeiMvPredictor *mvpred = NULL;
592 VAEncFEIMVPredictorH264 *pmvpred = NULL;
593 guint mvpred_size = 0, j;
595 mvpred_size = mb_width * mb_height * sizeof (VAEncFEIMVPredictorH264);
597 gst_vaapi_enc_fei_mv_predictor_new (app->encoder, NULL,
599 rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT
600 (mvpred), &data, &size);
603 pmvpred = (VAEncFEIMVPredictorH264 *) data;
604 for (i = 0; i < mb_size; i++) {
605 for (j = 0; i < 4; i++) {
606 pmvpred->ref_idx[j].ref_idx_l0 = 0;
607 pmvpred->ref_idx[j].ref_idx_l1 = 0;
609 pmvpred->mv[j].mv0[0] = 0x8000;
610 pmvpred->mv[j].mv0[1] = 0x8000;
611 pmvpred->mv[j].mv1[0] = 0x8000;
612 pmvpred->mv[j].mv1[1] = 0x8000;
616 gst_vaapi_surface_proxy_set_fei_mv_predictor (proxy, mvpred);
620 if (!upload_frame (app->encoder, proxy)) {
621 g_warning ("put frame failed");
626 id = gst_vaapi_surface_get_id (surface);
627 g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames,
630 gst_vaapi_surface_proxy_unref (proxy);
633 app->input_stopped = TRUE;
635 g_thread_join (buffer_thread);
637 if (!app->encode_failed && feof (app->parser->fp))
640 gst_vaapi_video_pool_replace (&pool, NULL);
641 gst_vaapi_object_unref (image);
646 main (int argc, char *argv[])
649 int ret = EXIT_FAILURE;
652 if (!parse_options (&argc, argv))
655 /* @TODO: iterate all the input files */
656 input_fn = g_input_files ? g_input_files[0] : NULL;
657 if (input_fn && !g_file_test (input_fn,
658 G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
659 g_warning ("input file \"%s\" doesn't exist", input_fn);
663 app = app_new (input_fn, g_output_file_name);
666 print_yuv_info (app);
668 print_num_frame (app);
673 g_free (g_codec_str);
674 g_free (g_output_file_name);
675 g_strfreev (g_input_files);