diff options
author | Sreerenj Balachandran <sreerenj.balachandran@intel.com> | 2017-08-09 18:46:09 -0700 |
---|---|---|
committer | Víctor Manuel Jáquez Leal <vjaquez@igalia.com> | 2017-09-01 13:15:05 +0200 |
commit | 9f98a02a0563f6d93c15553124ce3b8c4f4d3ad4 (patch) | |
tree | e38515ca15dfdc14d141671ea82f71db5ce373a5 /tests | |
parent | 5750bd7850256dcb812dcac7ff23eed910c62e84 (diff) |
FEI: Add test applications to showcase fei use case
test-fei-enc-out: A simple fei encoding application to output mv, mbcode and distortion
eg:
./test-fei-enc-out -i sample_320x240.nv12 -w 320 -h 240 -o out.264 -v mv.out -d out.dist -m out.mbcode -e 1
test-fei-enc-in: A simple fei encoding application for testing input fei buffers
eg:
./test-fei-enc-in -c h264 -o out.264 -e 4 -q 1 sample_i420.y4m
Fixme: Running test-fei-enc-in in PAK mode with mv and mbcode input buffers
from saved files is still not working
People contributed:
Wang, Yi <yi.a.wang@intel.com>
Leilei <leilei.shang@intel.com>
Zhong, Xiaoxia <xiaoxia.zhong@intel.com>
xiaominc <xiaomin.chen@intel.com>
Li, Jing B <jing.b.li@intel.com>
https://bugzilla.gnome.org/show_bug.cgi?id=785712
https://bugzilla.gnome.org/show_bug.cgi?id=784667
Diffstat (limited to 'tests')
-rw-r--r-- | tests/Makefile.am | 19 | ||||
-rw-r--r-- | tests/test-fei-enc-in.c | 679 | ||||
-rw-r--r-- | tests/test-fei-enc-out.c | 300 |
3 files changed, 998 insertions, 0 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am index ca587979..892e3966 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -14,6 +14,13 @@ noinst_PROGRAMS += \ $(NULL) endif +if USE_H264_FEI_ENCODER +noinst_PROGRAMS += \ + test-fei-enc-out \ + test-fei-enc-in \ + $(NULL) +endif + if USE_GLX noinst_PROGRAMS += \ test-textures \ @@ -136,6 +143,18 @@ test_textures_CFLAGS = $(TEST_CFLAGS) test_textures_LDFLAGS = $(GST_VAAPI_LIBS) test_textures_LDADD = libutils.la $(TEST_LIBS) +test_fei_enc_out_SOURCES = test-fei-enc-out.c ../gst/vaapi/gstvaapifeivideometa.h +test_fei_enc_out_CFLAGS = $(TEST_CFLAGS) +test_fei_enc_out_LDFLAGS = $(GST_VAAPI_LIBS) +test_fei_enc_out_LDADD = libutils.la $(TEST_LIBS) + +test_fei_enc_in_sources_c = test-fei-enc-in.c ../gst/vaapi/gstvaapifeivideometa.h y4mreader.c +test_fei_enc_in_sources_h = y4mreader.h +test_fei_enc_in_SOURCES = $(test_fei_enc_in_sources_c) +test_fei_enc_in_CFLAGS = $(TEST_CFLAGS) $(GST_VIDEO_CFLAGS) +test_fei_enc_in_LDFLAGS = $(GST_VAAPI_LIBS) +test_fei_enc_in_LDADD = libutils.la $(TEST_LIBS) $(GST_VIDEO_LIBS) + simple_decoder_source_c = simple-decoder.c simple_decoder_source_h = simple_decoder_SOURCES = $(simple_decoder_source_c) diff --git a/tests/test-fei-enc-in.c b/tests/test-fei-enc-in.c new file mode 100644 index 00000000..2f7d698e --- /dev/null +++ b/tests/test-fei-enc-in.c @@ -0,0 +1,679 @@ +/* + * test-fei-enc-in.c - Test FEI input buffer submission + * + * Copyright (C) 2016 Intel Corporation + * + * Author: Sreerenj Balachandran <sreerenj.balachandran@intel.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ +/* sample pipeline: ./test-fei-enc-input -c h264 -o out.264 -e 4 -q 1 sample_i420.y4m */ + +#include <assert.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include "gst/vaapi/sysdeps.h" +#include <gst/vaapi/gstvaapiencoder.h> +#include <gst/vaapi/gstvaapiencoder_h264_fei.h> +#include <gst/vaapi/gstvaapisurfacepool.h> +#include <gst/vaapi/gstvaapisurfaceproxy.h> +#include <gst/vaapi/gstvaapifei_objects.h> +#include "output.h" +#include "y4mreader.h" +#include <va/va.h> + +static guint g_bitrate = 0; +static gchar *g_codec_str; +static gchar *g_output_file_name; +static char **g_input_files = NULL; +static gchar *input_mv_name = NULL; +static gchar *input_mbmode_name = NULL; +static guint input_mv_size; +static guint input_mbmode_size; +static guint input_qp; +static guint enable_mbcntrl; +static guint enable_mvpred; +static guint fei_mode; + +#define SURFACE_NUM 16 + +#define ENC 1 +#define PAK 2 +#define ENC_PLUS_PAK 3 +#define ENC_PAK 4 + +static GOptionEntry g_options[] = { + {"codec", 'c', 0, G_OPTION_ARG_STRING, &g_codec_str, + "codec to use for video encoding (h264)", NULL}, + {"bitrate", 'b', 0, G_OPTION_ARG_INT, &g_bitrate, + "desired bitrate expressed in kbps", NULL}, + {"output", 'o', 0, G_OPTION_ARG_FILENAME, &g_output_file_name, + "output file name", NULL}, + {"imv", 'v', 0, G_OPTION_ARG_STRING, &input_mv_name, + "pak mv input file", NULL}, + {"imbmode ", 'm', 0, G_OPTION_ARG_STRING, &input_mbmode_name, + "pak mbmode input file", NULL}, + {"imvsize", 's', 0, G_OPTION_ARG_INT, &input_mv_size, + "input stream width", NULL}, + {"imbmodesize", 'd', 0, G_OPTION_ARG_INT, &input_mbmode_size, + "input stream height", NULL}, + {"iqp", 'q', 0, G_OPTION_ARG_INT, &input_qp, + "input qp val (it will get replicated for each macrobock)", NULL}, + {"imbcntrl", 'l', 0, G_OPTION_ARG_INT, &enable_mbcntrl, + "enable macroblock control for each macrobock", NULL}, + {"imbpred", 'p', 0, G_OPTION_ARG_INT, &enable_mvpred, + "enable mv predictor for each macroblock", NULL}, + {"fei-mode", 'e', 0, G_OPTION_ARG_INT, &fei_mode, + "1:ENC 2:PAK 3:ENC+PAK 4:ENC_PAK", NULL}, + + {G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_FILENAME_ARRAY, &g_input_files, + "input file name", NULL}, + {NULL} +}; + +typedef struct +{ + GstVaapiDisplay *display; + GstVaapiEncoder *encoder; + guint read_frames; + guint encoded_frames; + guint saved_frames; + Y4MReader *parser; + FILE *output_file; + int mv_fd; + int mbmode_fd; + guint input_mv_size; + guint input_mbmode_size; + guint input_stopped:1; + guint encode_failed:1; +} App; + +static inline gchar * +generate_output_filename (const gchar * ext) +{ + gchar *fn; + int i = 0; + + while (1) { + fn = g_strdup_printf ("temp%02d.%s", i, ext); + if (g_file_test (fn, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { + i++; + g_free (fn); + } else { + break; + } + } + + return fn; +} + +static gboolean +parse_options (int *argc, char *argv[]) +{ + GOptionContext *ctx; + gboolean success; + GError *error = NULL; + + ctx = g_option_context_new (" - encoder test options"); + if (!ctx) + return FALSE; + + g_option_context_add_group (ctx, gst_init_get_option_group ()); + g_option_context_add_main_entries (ctx, g_options, NULL); + g_option_context_set_help_enabled (ctx, TRUE); + success = g_option_context_parse (ctx, argc, &argv, &error); + if (!success) { + g_printerr ("Option parsing failed: %s\n", error->message); + g_error_free (error); + goto bail; + } + + if (!g_codec_str) + g_codec_str = g_strdup ("h264"); + if (!g_output_file_name) + g_output_file_name = generate_output_filename (g_codec_str); + +bail: + g_option_context_free (ctx); + return success; +} + +static void +print_yuv_info (App * app) +{ + g_print ("\n"); + g_print ("Encode : %s\n", g_codec_str); + g_print ("Resolution : %dx%d\n", app->parser->width, app->parser->height); + g_print ("Source YUV : %s\n", g_input_files ? g_input_files[0] : "stdin"); + g_print ("Frame Rate : %0.1f fps\n", + 1.0 * app->parser->fps_n / app->parser->fps_d); + g_print ("Coded file : %s\n", g_output_file_name); + g_print ("\n"); +} + +static void +print_num_frame (App * app) +{ + g_print ("\n"); + g_print ("read frames : %d\n", app->read_frames); + g_print ("encoded frames : %d\n", app->encoded_frames); + g_print ("saved frames : %d\n", app->saved_frames); + g_print ("\n"); +} + +static GstVaapiEncoder * +encoder_new (GstVaapiDisplay * display) +{ + GstVaapiEncoder *encoder = NULL; + + if (!g_strcmp0 (g_codec_str, "h264")) { + encoder = gst_vaapi_encoder_h264_fei_new (display); + gst_vaapi_encoder_h264_fei_set_function_mode (GST_VAAPI_ENCODER_H264_FEI + (encoder), fei_mode); + gst_vaapi_encoder_h264_fei_set_max_profile (GST_VAAPI_ENCODER_H264_FEI + (encoder), GST_VAAPI_PROFILE_H264_CONSTRAINED_BASELINE); + } else + return NULL; + + return encoder; +} + +static inline GstVideoCodecState * +new_codec_state (gint width, gint height, gint fps_n, gint fps_d) +{ + GstVideoCodecState *state; + + state = g_slice_new0 (GstVideoCodecState); + state->ref_count = 1; + gst_video_info_set_format (&state->info, GST_VIDEO_FORMAT_ENCODED, width, + height); + + state->info.fps_n = fps_n; + state->info.fps_d = fps_d; + + return state; +} + +static gboolean +set_format (GstVaapiEncoder * encoder, gint width, gint height, gint fps_n, + gint fps_d) +{ + GstVideoCodecState *in_state; + GstVaapiEncoderStatus status; + + in_state = new_codec_state (width, height, fps_n, fps_d); + status = gst_vaapi_encoder_set_codec_state (encoder, in_state); + g_slice_free (GstVideoCodecState, in_state); + + return (status == GST_VAAPI_ENCODER_STATUS_SUCCESS); +} + +static GstBuffer * +allocate_buffer (GstVaapiCodedBuffer * vbuf) +{ + GstBuffer *buf; + gssize size; + + size = gst_vaapi_coded_buffer_get_size (vbuf); + + if (size <= 0) { + g_warning ("Invalid VA buffer size (%zd)", size); + return NULL; + } + + buf = gst_buffer_new_and_alloc (size); + if (!buf) { + g_warning ("Failed to create output buffer of size %zd", size); + return NULL; + } + + if (!gst_vaapi_coded_buffer_copy_into (buf, vbuf)) { + g_warning ("Failed to copy VA buffer data"); + gst_buffer_unref (buf); + return NULL; + } + + return buf; +} + +static GstVaapiEncoderStatus +get_encoder_buffer (GstVaapiEncoder * encoder, GstBuffer ** buffer) +{ + GstVaapiCodedBufferProxy *proxy = NULL; + GstVaapiEncoderStatus status; + + status = gst_vaapi_encoder_get_buffer_with_timeout (encoder, &proxy, 50000); + if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS) { + g_warning ("Failed to get a buffer from encoder: %d", status); + return status; + } else if (status > GST_VAAPI_ENCODER_STATUS_SUCCESS) { + return status; + } + + *buffer = allocate_buffer (GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (proxy)); + gst_vaapi_coded_buffer_proxy_unref (proxy); + + return status; +} + +static gboolean +outputs_to_file (GstBuffer * buffer, FILE * file) +{ + GstMapInfo info; + size_t written; + gboolean ret = FALSE; + + gst_buffer_map (buffer, &info, GST_MAP_READ); + + if (info.size <= 0 || !info.data) + return FALSE; + + written = fwrite (info.data, 1, info.size, file); + if (written < info.size) { + g_warning ("write file error."); + goto bail; + } + + ret = TRUE; + +bail: + gst_buffer_unmap (buffer, &info); + return ret; +} + +static gpointer +get_buffer_thread (gpointer data) +{ + App *app = data; + + GstVaapiEncoderStatus ret; + GstBuffer *obuf; + + while (1) { + obuf = NULL; + ret = get_encoder_buffer (app->encoder, &obuf); + if (app->input_stopped && ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) { + break; /* finished */ + } else if (ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) { /* another chance */ + continue; + } + if (ret < GST_VAAPI_ENCODER_STATUS_SUCCESS) { /* fatal error */ + app->encode_failed = TRUE; + break; + } + + app->encoded_frames++; + g_debug ("encoded frame %d, buffer = %p", app->encoded_frames, obuf); + + if (app->output_file && outputs_to_file (obuf, app->output_file)) + app->saved_frames++; + + gst_buffer_unref (obuf); + } + if (obuf) + gst_buffer_replace (&obuf, NULL); + + return NULL; +} + +static void +app_free (App * app) +{ + g_return_if_fail (app); + + if (app->parser) + y4m_reader_close (app->parser); + + if (app->encoder) { + gst_vaapi_encoder_flush (app->encoder); + gst_vaapi_encoder_unref (app->encoder); + } + + if (app->display) + gst_vaapi_display_unref (app->display); + + if (app->output_file) + fclose (app->output_file); + + g_slice_free (App, app); +} + +static App * +app_new (const gchar * input_fn, const gchar * output_fn) +{ + App *app = g_slice_new0 (App); + if (!app) + return NULL; + app->parser = y4m_reader_open (input_fn); + if (!app->parser) { + g_warning ("Could not parse input stream."); + goto error; + } + + app->output_file = fopen (output_fn, "w"); + if (app->output_file == NULL) { + g_warning ("Could not open file \"%s\" for writing: %s.", output_fn, + g_strerror (errno)); + goto error; + } + + /* if PAK only */ + if (fei_mode == 2) { + if (!input_mv_name || !input_mbmode_name) { + g_warning ("pak only mode need an mv and mbmode files as input"); + assert (0); + } + + if (input_mv_name) + app->mv_fd = open (input_mv_name, O_RDONLY, 0); + if (input_mbmode_name) + app->mbmode_fd = open (input_mbmode_name, O_RDONLY, 0); + + assert (app->mv_fd >= 0); + assert (app->mbmode_fd >= 0); + } + + app->display = video_output_create_display (NULL); + if (!app->display) { + g_warning ("Could not create VA display."); + goto error; + } + + app->encoder = encoder_new (app->display); + if (!app->encoder) { + g_warning ("Could not create encoder."); + goto error; + } + + if (!set_format (app->encoder, app->parser->width, app->parser->height, + app->parser->fps_n, app->parser->fps_d)) { + g_warning ("Could not set format."); + goto error; + } + + return app; + +error: + app_free (app); + return NULL; +} + +static gboolean +upload_frame (GstVaapiEncoder * encoder, GstVaapiSurfaceProxy * proxy) +{ + GstVideoCodecFrame *frame; + GstVaapiEncoderStatus ret; + + frame = g_slice_new0 (GstVideoCodecFrame); + gst_video_codec_frame_set_user_data (frame, + gst_vaapi_surface_proxy_ref (proxy), + (GDestroyNotify) gst_vaapi_surface_proxy_unref); + + ret = gst_vaapi_encoder_put_frame (encoder, frame); + return (ret == GST_VAAPI_ENCODER_STATUS_SUCCESS); +} + +static gboolean +load_frame (App * app, GstVaapiImage * image) +{ + gboolean ret = FALSE; + + if (!gst_vaapi_image_map (image)) + return FALSE; + + ret = y4m_reader_load_image (app->parser, image); + + if (!gst_vaapi_image_unmap (image)) + return FALSE; + + return ret; +} + +static int +app_run (App * app) +{ + GstVaapiImage *image; + GstVaapiVideoPool *pool; + GThread *buffer_thread; + gsize id; + gint i; + + int ret = EXIT_FAILURE; + image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420, + app->parser->width, app->parser->height); + + { + GstVideoInfo vi; + gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED, + app->parser->width, app->parser->height); + pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0); + } + buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app); + + while (1) { + GstVaapiSurfaceProxy *proxy; + GstVaapiSurface *surface; + gpointer data = NULL; + guint size = 0; + gint rt = 0; + guint mb_width, mb_height, mb_size; + + if (!load_frame (app, image)) + break; + + if (!gst_vaapi_image_unmap (image)) + break; + + proxy = + gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool)); + if (!proxy) { + g_warning ("Could not get surface proxy from pool."); + break; + } + surface = gst_vaapi_surface_proxy_get_surface (proxy); + if (!surface) { + g_warning ("Could not get surface from proxy."); + break; + } + + if (!gst_vaapi_surface_put_image (surface, image)) { + g_warning ("Could not update surface"); + break; + } + + mb_width = (app->parser->width + 15) >> 4; + mb_height = (app->parser->height + 15) >> 4; + mb_size = mb_width * mb_height; + + /* PAK Only */ + if (fei_mode == PAK) { + GstVaapiEncFeiMbCode *mbcode; + GstVaapiEncFeiMv *mv; + guint mv_size, mbmode_size; + + mv_size = mb_width * mb_height * 128; + mbmode_size = mb_width * mb_height * 64; + + if (input_mv_size) + assert (input_mv_size == mv_size); + + if (input_mbmode_size) + assert (input_mbmode_size == mbmode_size); + + /* Upload mbmode data */ + mbcode = gst_vaapi_enc_fei_mb_code_new (app->encoder, NULL, mbmode_size); + rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (mbcode), + &data, &size); + assert (rt == 1); + rt = read (app->mbmode_fd, data, mbmode_size); + assert (rt >= 0); + + /* Upload mv data */ + mv = gst_vaapi_enc_fei_mv_new (app->encoder, NULL, mv_size); + rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (mv), + &data, &size); + assert (rt == 1); + rt = read (app->mv_fd, data, mv_size); + assert (rt >= 0); + + /* assign mv and mbmode buffers to input surface proxy */ + gst_vaapi_surface_proxy_set_fei_mb_code (proxy, mbcode); + gst_vaapi_surface_proxy_set_fei_mv (proxy, mv); + + } else { + /* ENC, ENC+PAK and ENC_PAK */ + + if (input_qp) { + GstVaapiEncFeiQp *qp = NULL; + VAEncQPBufferH264 *pqp = NULL; + guint qp_size = 0; + + qp_size = mb_width * mb_height * sizeof (VAEncQPBufferH264); + + qp = gst_vaapi_enc_fei_qp_new (app->encoder, NULL, qp_size); + rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT (qp), + &data, &size); + assert (rt == 1); + + pqp = (VAEncQPBufferH264 *) data; + for (i = 0; i < mb_size; i++) { + pqp->qp = input_qp; + pqp++; + } + gst_vaapi_surface_proxy_set_fei_qp (proxy, qp); + } + + if (enable_mbcntrl) { + GstVaapiEncFeiMbControl *mbcntrl = NULL; + VAEncFEIMBControlH264 *pmbcntrl = NULL; + guint mbcntrl_size = 0; + + mbcntrl_size = mb_width * mb_height * sizeof (VAEncFEIMBControlH264); + mbcntrl = + gst_vaapi_enc_fei_mb_control_new (app->encoder, NULL, mbcntrl_size); + rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT + (mbcntrl), &data, &size); + assert (rt == 1); + + pmbcntrl = (VAEncFEIMBControlH264 *) data; + for (i = 0; i < mb_size; i++) { + pmbcntrl->force_to_intra = 1; + pmbcntrl->force_to_skip = 0; + pmbcntrl->force_to_nonskip = 0; + pmbcntrl->enable_direct_bias_adjustment = 0; + pmbcntrl->enable_motion_bias_adjustment = 0; + pmbcntrl->ext_mv_cost_scaling_factor = 0; + pmbcntrl->target_size_in_word = 0xff; + pmbcntrl->max_size_in_word = 0xff; + pmbcntrl++; + } + gst_vaapi_surface_proxy_set_fei_mb_control (proxy, mbcntrl); + } + + if (enable_mvpred) { + GstVaapiEncFeiMvPredictor *mvpred = NULL; + VAEncFEIMVPredictorH264 *pmvpred = NULL; + guint mvpred_size = 0, j; + + mvpred_size = mb_width * mb_height * sizeof (VAEncFEIMVPredictorH264); + mvpred = + gst_vaapi_enc_fei_mv_predictor_new (app->encoder, NULL, + mvpred_size); + rt = gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT + (mvpred), &data, &size); + assert (rt == 1); + + pmvpred = (VAEncFEIMVPredictorH264 *) data; + for (i = 0; i < mb_size; i++) { + for (j = 0; i < 4; i++) { + pmvpred->ref_idx[j].ref_idx_l0 = 0; + pmvpred->ref_idx[j].ref_idx_l1 = 0; + + pmvpred->mv[j].mv0[0] = 0x8000; + pmvpred->mv[j].mv0[1] = 0x8000; + pmvpred->mv[j].mv1[0] = 0x8000; + pmvpred->mv[j].mv1[1] = 0x8000; + } + pmvpred++; + } + gst_vaapi_surface_proxy_set_fei_mv_predictor (proxy, mvpred); + } + } + + if (!upload_frame (app->encoder, proxy)) { + g_warning ("put frame failed"); + break; + } + + app->read_frames++; + id = gst_vaapi_surface_get_id (surface); + g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames, + id); + + gst_vaapi_surface_proxy_unref (proxy); + } + + app->input_stopped = TRUE; + + g_thread_join (buffer_thread); + + if (!app->encode_failed && feof (app->parser->fp)) + ret = EXIT_SUCCESS; + + gst_vaapi_video_pool_replace (&pool, NULL); + gst_vaapi_object_unref (image); + return ret; +} + +int +main (int argc, char *argv[]) +{ + App *app; + int ret = EXIT_FAILURE; + gchar *input_fn; + + if (!parse_options (&argc, argv)) + return EXIT_FAILURE; + + /* @TODO: iterate all the input files */ + input_fn = g_input_files ? g_input_files[0] : NULL; + if (input_fn && !g_file_test (input_fn, + G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) { + g_warning ("input file \"%s\" doesn't exist", input_fn); + goto bail; + } + + app = app_new (input_fn, g_output_file_name); + if (!app) + goto bail; + print_yuv_info (app); + ret = app_run (app); + print_num_frame (app); + + app_free (app); + +bail: + g_free (g_codec_str); + g_free (g_output_file_name); + g_strfreev (g_input_files); + + gst_deinit (); + + return ret; +} diff --git a/tests/test-fei-enc-out.c b/tests/test-fei-enc-out.c new file mode 100644 index 00000000..32326434 --- /dev/null +++ b/tests/test-fei-enc-out.c @@ -0,0 +1,300 @@ +/* + * test-fei-enc-out.c - FEI Encoder Test application to dump output buffers + * + * Copyright (C) 2017 Intel Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + */ + +/* ./test-fei-enc -i sample_320x240.nv12 -f nv12 -w 320 -h 240 -o out.264 -v mv.out -d dist.out -m mbcode.out -e 1 */ + +#include <stdio.h> +#include <gst/gst.h> +#include <gst/app/gstappsink.h> +#include <stdlib.h> +#include "../gst/vaapi/gstvaapifeivideometa.h" +#include <gst/video/video.h> + +int +main (int argc, char *argv[]) +{ + GstElement *pipeline, *filesrc, *videoparse, *enc, *capsfilter, *appsink; + GError *err = NULL; + GstStateChangeReturn ret; + GstSample *sample; + GstVideoFormat raw_format = GST_VIDEO_FORMAT_NV12; + GOptionContext *ctx; + FILE *file = NULL; + FILE *mv_file = NULL; + FILE *dist_file = NULL; + FILE *mbcode_file = NULL; + FILE *fei_stat_file = NULL; + gchar *input_file_name = NULL; + gchar *output_file_name = NULL; + gchar *output_mv_name = NULL; + gchar *output_distortion_name = NULL; + gchar *output_mbcode_name = NULL; + gchar *input_format; + guint input_width; + guint input_height; + guint enc_frame_num = 0; + guint block_size = 0; + guint fei_mode = 1; + guint fei_mode_flag = 0x00000004; + gboolean link_ok = FALSE; + guint mv_buffer_size = 0; + guint mbcode_buffer_size = 0; + guint dist_buffer_size = 0; + gpointer mapped_data = NULL; + guint mapped_data_size = 0; + const gchar *caps_string = "video/x-h264, profile=constrained-baseline"; + GstCaps *filter_caps = NULL; + + GOptionEntry options[] = { + {"input file", 'i', 0, G_OPTION_ARG_STRING, &input_file_name, + "file to encode", NULL}, + {"output file", 'o', 0, G_OPTION_ARG_STRING, &output_file_name, + "encpak output file", NULL}, + {"output mv file", 'v', 0, G_OPTION_ARG_STRING, &output_mv_name, + "encpak mv output file", NULL}, + {"output distortion file", 'd', 0, G_OPTION_ARG_STRING, + &output_distortion_name, + "encpak distortion output file", NULL}, + {"output mbcode file", 'm', 0, G_OPTION_ARG_STRING, &output_mbcode_name, + "encpak mbcode output file", NULL}, + {"format", 'f', 0, G_OPTION_ARG_STRING, &input_format, + "input raw format: nv12 or i420", NULL}, + {"width", 'w', 0, G_OPTION_ARG_INT, &input_width, + "input stream width", NULL}, + {"height", 'h', 0, G_OPTION_ARG_INT, &input_height, + "input stream height", NULL}, + {"frame-num", 'n', 0, G_OPTION_ARG_INT, &enc_frame_num, + "numumber of buffers to be encoded", NULL}, + {"blocksize", 's', 0, G_OPTION_ARG_INT, &block_size, + "single buffer size of input stream", NULL}, + {"fei-mode", 'e', 0, G_OPTION_ARG_INT, &fei_mode, + "1: ENC_PAK 2: ENC+PAK", NULL}, + {NULL} + }; + + ctx = + g_option_context_new + ("encpak with element filesrc, videoparse, vaapih264feienc, appsink"); + g_option_context_add_main_entries (ctx, options, NULL); + g_option_context_add_group (ctx, gst_init_get_option_group ()); + + if (!g_option_context_parse (ctx, &argc, &argv, &err)) { + g_print ("Error intializing: %s\n", err->message); + g_option_context_free (ctx); + g_clear_error (&err); + return -1; + } + + if (input_file_name == NULL || output_file_name == NULL) { + g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL)); + g_option_context_free (ctx); + return -1; + } + + if (!g_strcmp0 (input_format, "nv12")) + raw_format = GST_VIDEO_FORMAT_NV12; + else if (!g_strcmp0 (input_format, "i420")) + raw_format = GST_VIDEO_FORMAT_I420; + else + return -1; + + if (!input_width || !input_height) { + g_print ("%s", g_option_context_get_help (ctx, TRUE, NULL)); + g_option_context_free (ctx); + return -1; + } + + switch (fei_mode) { + case 1: + fei_mode_flag = 0x00000004; + break; + case 2: + fei_mode_flag = 0x00000001 | 0x00000002; + break; + default: + printf ("Unknown fei mode \n"); + g_assert (0); + break; + } + + g_option_context_free (ctx); + + gst_init (&argc, &argv); + + /* create pipeline */ + pipeline = gst_pipeline_new ("pipeline"); + filesrc = gst_element_factory_make ("filesrc", "source"); + videoparse = gst_element_factory_make ("videoparse", "videoparse"); + enc = gst_element_factory_make ("vaapih264feienc", "encpak"); + capsfilter = gst_element_factory_make ("capsfilter", "enccaps"); + appsink = gst_element_factory_make ("appsink", "sink"); + + /* element prop setup */ + g_object_set (G_OBJECT (filesrc), "location", input_file_name, NULL); + g_object_set (G_OBJECT (videoparse), "format", raw_format, + "width", input_width, "height", input_height, NULL); + + if (enc_frame_num != 0) + g_object_set (G_OBJECT (filesrc), "num-buffers", enc_frame_num, NULL); + if (block_size != 0) + g_object_set (G_OBJECT (filesrc), "blocksize", block_size, NULL); + + g_object_set (G_OBJECT (enc), "fei-mode", fei_mode_flag, NULL); + g_object_set (G_OBJECT (enc), "search-window", 5, NULL); + g_object_set (G_OBJECT (enc), "max-bframes", 0, NULL); + + filter_caps = gst_caps_from_string (caps_string); + if (filter_caps) + g_object_set (G_OBJECT (capsfilter), "caps", filter_caps, NULL); + gst_caps_unref (filter_caps); + + gst_bin_add_many (GST_BIN (pipeline), filesrc, videoparse, enc, capsfilter, + appsink, NULL); + + link_ok = + gst_element_link_many (filesrc, videoparse, enc, capsfilter, appsink, + NULL); + if (!link_ok) { + g_print ("filesrc, enc and appsink link fail"); + return -1; + } + + file = fopen (output_file_name, "wb"); + + if (output_mv_name != NULL) + mv_file = fopen (output_mv_name, "wb"); + + if (output_mbcode_name != NULL) + mbcode_file = fopen (output_mbcode_name, "wb"); + + if (output_distortion_name != NULL) + dist_file = fopen (output_distortion_name, "wb"); + + ret = gst_element_set_state (pipeline, GST_STATE_PLAYING); + if (ret == GST_STATE_CHANGE_FAILURE) { + g_printerr ("Unable to set the pipeline to the playing state.\n"); + gst_object_unref (pipeline); + return -1; + } + + /* pull sample from pipeline */ + while (1) { + g_signal_emit_by_name (appsink, "pull-sample", &sample, NULL); + if (sample) { + GstBuffer *buffer = NULL; + GstMapInfo map, info; + GstMemory *mem; + GstVaapiFeiVideoMeta *meta = NULL; + GstMeta *m = NULL; + const GstMetaInfo *meta_info; + GType api; + + g_debug ("appsink received sample.\n"); + buffer = gst_sample_get_buffer (sample); + if (gst_buffer_map (buffer, &map, GST_MAP_READ)) { + mem = gst_buffer_peek_memory (buffer, 0); + if (gst_memory_map (mem, &info, GST_MAP_READ)) + fwrite (info.data, 1, info.size, file); + + gst_memory_unmap (mem, &info); + gst_buffer_unmap (buffer, &map); + } + + meta_info = gst_meta_get_info ("GstVaapiFeiVideoMeta"); + api = meta_info->api; + m = gst_buffer_get_meta (buffer, api); + if (m != NULL) + meta = ((GstVaapiFeiVideoMetaHolder *) (m))->meta; + + if (meta != NULL) { + + if (mv_file != NULL) { + mapped_data = NULL; + mapped_data_size = 0; + if (gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT + (meta->mv), &mapped_data, &mapped_data_size)) { + fwrite (mapped_data, 1, mapped_data_size, mv_file); + gst_vaapi_fei_codec_object_unmap (GST_VAAPI_FEI_CODEC_OBJECT + (meta->mv)); + mv_buffer_size = mapped_data_size; + } + } + + if (mbcode_file != NULL) { + mapped_data = NULL; + mapped_data_size = 0; + if (gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT + (meta->mbcode), &mapped_data, &mapped_data_size)) { + fwrite (mapped_data, 1, mapped_data_size, mbcode_file); + gst_vaapi_fei_codec_object_unmap (GST_VAAPI_FEI_CODEC_OBJECT + (meta->mbcode)); + mbcode_buffer_size = mapped_data_size; + } + } + + if (dist_file != NULL) { + mapped_data = NULL; + mapped_data_size = 0; + if (gst_vaapi_fei_codec_object_map (GST_VAAPI_FEI_CODEC_OBJECT + (meta->dist), &mapped_data, &mapped_data_size)) { + fwrite (mapped_data, 1, mapped_data_size, dist_file); + gst_vaapi_fei_codec_object_unmap (GST_VAAPI_FEI_CODEC_OBJECT + (meta->dist)); + dist_buffer_size = mapped_data_size; + } + } + } + + gst_sample_unref (sample); + } else { + g_print ("appsink finished receive sample.\n"); + break; + } + } + + /* Fixme: Currently assuming the input video has only one resoultion + * which may not be true */ + /* create a status file for dumping size of each fei output buffer */ + if (output_mv_name || output_mbcode_name || output_distortion_name) { + fei_stat_file = fopen ("fei_stat.out", "wb"); + fprintf (fei_stat_file, "Frame_MotionVectorData_Buffer_Size => %d \n", + mv_buffer_size); + fprintf (fei_stat_file, "Frame_MacroblcokCode_Buffer_Size => %d \n", + mbcode_buffer_size); + fprintf (fei_stat_file, "Frame_Distortion_Buffer_Size => %d \n", + dist_buffer_size); + } + + /* free */ + fclose (file); + if (mv_file != NULL) + fclose (mv_file); + if (mbcode_file != NULL) + fclose (mbcode_file); + if (dist_file != NULL) + fclose (dist_file); + if (fei_stat_file) + fclose (fei_stat_file); + + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (pipeline); + return 0; +} |