1 /*
2  * simple-encoder.c - Test GstVaapiencoder
3  *
4  * Copyright (C) 2015 Intel Corporation
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License
8  * as published by the Free Software Foundation; either version 2.1
9  * of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free
18  * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301 USA
20  */
21 
22 #include "gst/vaapi/sysdeps.h"
23 #include <gst/vaapi/gstvaapiencoder_mpeg2.h>
24 #include <gst/vaapi/gstvaapiencoder_h264.h>
25 #include <gst/vaapi/gstvaapisurfacepool.h>
26 #include <gst/vaapi/gstvaapisurfaceproxy.h>
27 
28 #include "output.h"
29 #include "y4mreader.h"
30 
31 static guint g_bitrate = 0;
32 static gchar *g_codec_str;
33 static gchar *g_output_file_name;
34 static char **g_input_files = NULL;
35 
36 #define SURFACE_NUM 16
37 
38 static GOptionEntry g_options[] = {
39   {"codec", 'c', 0, G_OPTION_ARG_STRING, &g_codec_str,
40       "codec to use for video encoding (h264/mpeg2)", NULL},
41   {"bitrate", 'b', 0, G_OPTION_ARG_INT, &g_bitrate,
42       "desired bitrate expressed in kbps", NULL},
43   {"output", 'o', 0, G_OPTION_ARG_FILENAME, &g_output_file_name,
44       "output file name", NULL},
45   {G_OPTION_REMAINING, ' ', 0, G_OPTION_ARG_FILENAME_ARRAY, &g_input_files,
46       "input file name", NULL},
47   {NULL}
48 };
49 
50 typedef struct
51 {
52   GstVaapiDisplay *display;
53   GstVaapiEncoder *encoder;
54   guint read_frames;
55   guint encoded_frames;
56   guint saved_frames;
57   Y4MReader *parser;
58   FILE *output_file;
59   guint input_stopped:1;
60   guint encode_failed:1;
61 } App;
62 
63 static inline gchar *
generate_output_filename(const gchar * ext)64 generate_output_filename (const gchar * ext)
65 {
66   gchar *fn;
67   int i = 0;
68 
69   while (1) {
70     fn = g_strdup_printf ("temp%02d.%s", i, ext);
71     if (g_file_test (fn, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
72       i++;
73       g_free (fn);
74     } else {
75       break;
76     }
77   }
78 
79   return fn;
80 }
81 
82 static gboolean
parse_options(int * argc,char * argv[])83 parse_options (int *argc, char *argv[])
84 {
85   GOptionContext *ctx;
86   gboolean success;
87   GError *error = NULL;
88 
89   ctx = g_option_context_new (" - encoder test options");
90   if (!ctx)
91     return FALSE;
92 
93   g_option_context_add_group (ctx, gst_init_get_option_group ());
94   g_option_context_add_main_entries (ctx, g_options, NULL);
95   g_option_context_set_help_enabled (ctx, TRUE);
96   success = g_option_context_parse (ctx, argc, &argv, &error);
97   if (!success) {
98     g_printerr ("Option parsing failed: %s\n", error->message);
99     g_error_free (error);
100     goto bail;
101   }
102 
103   if (!g_codec_str)
104     g_codec_str = g_strdup ("h264");
105   if (!g_output_file_name)
106     g_output_file_name = generate_output_filename (g_codec_str);
107 
108 bail:
109   g_option_context_free (ctx);
110   return success;
111 }
112 
113 static void
print_yuv_info(App * app)114 print_yuv_info (App * app)
115 {
116   g_print ("\n");
117   g_print ("Encode      : %s\n", g_codec_str);
118   g_print ("Resolution  : %dx%d\n", app->parser->width, app->parser->height);
119   g_print ("Source YUV  : %s\n", g_input_files ? g_input_files[0] : "stdin");
120   g_print ("Frame Rate  : %0.1f fps\n",
121       1.0 * app->parser->fps_n / app->parser->fps_d);
122   g_print ("Coded file  : %s\n", g_output_file_name);
123   g_print ("\n");
124 }
125 
126 static void
print_num_frame(App * app)127 print_num_frame (App * app)
128 {
129   g_print ("\n");
130   g_print ("read frames    : %d\n", app->read_frames);
131   g_print ("encoded frames : %d\n", app->encoded_frames);
132   g_print ("saved frames   : %d\n", app->saved_frames);
133   g_print ("\n");
134 }
135 
136 static GstVaapiEncoder *
encoder_new(GstVaapiDisplay * display)137 encoder_new (GstVaapiDisplay * display)
138 {
139   GstVaapiEncoder *encoder = NULL;
140 
141   if (!g_strcmp0 (g_codec_str, "mpeg2"))
142     encoder = gst_vaapi_encoder_mpeg2_new (display);
143   else if (!g_strcmp0 (g_codec_str, "h264"))
144     encoder = gst_vaapi_encoder_h264_new (display);
145   else
146     return NULL;
147 
148   gst_vaapi_encoder_set_bitrate (encoder, g_bitrate);
149 
150   return encoder;
151 }
152 
153 static inline GstVideoCodecState *
new_codec_state(gint width,gint height,gint fps_n,gint fps_d)154 new_codec_state (gint width, gint height, gint fps_n, gint fps_d)
155 {
156   GstVideoCodecState *state;
157 
158   state = g_slice_new0 (GstVideoCodecState);
159   state->ref_count = 1;
160   gst_video_info_set_format (&state->info, GST_VIDEO_FORMAT_ENCODED, width,
161       height);
162 
163   state->info.fps_n = fps_n;
164   state->info.fps_d = fps_d;
165 
166   return state;
167 }
168 
169 static gboolean
set_format(GstVaapiEncoder * encoder,gint width,gint height,gint fps_n,gint fps_d)170 set_format (GstVaapiEncoder * encoder, gint width, gint height, gint fps_n,
171     gint fps_d)
172 {
173   GstVideoCodecState *in_state;
174   GstVaapiEncoderStatus status;
175 
176   in_state = new_codec_state (width, height, fps_n, fps_d);
177   status = gst_vaapi_encoder_set_codec_state (encoder, in_state);
178   g_slice_free (GstVideoCodecState, in_state);
179 
180   return (status == GST_VAAPI_ENCODER_STATUS_SUCCESS);
181 }
182 
183 static GstBuffer *
allocate_buffer(GstVaapiCodedBuffer * vbuf)184 allocate_buffer (GstVaapiCodedBuffer * vbuf)
185 {
186   GstBuffer *buf;
187   gssize size;
188 
189   size = gst_vaapi_coded_buffer_get_size (vbuf);
190   if (size <= 0) {
191     g_warning ("Invalid VA buffer size (%zd)", size);
192     return NULL;
193   }
194 
195   buf = gst_buffer_new_and_alloc (size);
196   if (!buf) {
197     g_warning ("Failed to create output buffer of size %zd", size);
198     return NULL;
199   }
200 
201   if (!gst_vaapi_coded_buffer_copy_into (buf, vbuf)) {
202     g_warning ("Failed to copy VA buffer data");
203     gst_buffer_unref (buf);
204     return NULL;
205   }
206 
207   return buf;
208 }
209 
210 static GstVaapiEncoderStatus
get_encoder_buffer(GstVaapiEncoder * encoder,GstBuffer ** buffer)211 get_encoder_buffer (GstVaapiEncoder * encoder, GstBuffer ** buffer)
212 {
213   GstVaapiCodedBufferProxy *proxy = NULL;
214   GstVaapiEncoderStatus status;
215 
216   status = gst_vaapi_encoder_get_buffer_with_timeout (encoder, &proxy, 50000);
217   if (status < GST_VAAPI_ENCODER_STATUS_SUCCESS) {
218     g_warning ("Failed to get a buffer from encoder: %d", status);
219     return status;
220   } else if (status > GST_VAAPI_ENCODER_STATUS_SUCCESS) {
221     return status;
222   }
223 
224   *buffer = allocate_buffer (GST_VAAPI_CODED_BUFFER_PROXY_BUFFER (proxy));
225   gst_vaapi_coded_buffer_proxy_unref (proxy);
226 
227   return status;
228 }
229 
230 static gboolean
outputs_to_file(GstBuffer * buffer,FILE * file)231 outputs_to_file (GstBuffer * buffer, FILE * file)
232 {
233   GstMapInfo info;
234   size_t written;
235   gboolean ret = FALSE;
236 
237   if (!gst_buffer_map (buffer, &info, GST_MAP_READ))
238     return FALSE;
239 
240   if (info.size <= 0 || !info.data)
241     return FALSE;
242 
243   written = fwrite (info.data, 1, info.size, file);
244   if (written < info.size) {
245     g_warning ("write file error.");
246     goto bail;
247   }
248 
249   ret = TRUE;
250 
251 bail:
252   gst_buffer_unmap (buffer, &info);
253   return ret;
254 }
255 
256 static gpointer
get_buffer_thread(gpointer data)257 get_buffer_thread (gpointer data)
258 {
259   App *app = data;
260 
261   GstVaapiEncoderStatus ret;
262   GstBuffer *obuf;
263 
264   while (1) {
265     obuf = NULL;
266     ret = get_encoder_buffer (app->encoder, &obuf);
267     if (app->input_stopped && ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) {
268       break;                    /* finished */
269     } else if (ret > GST_VAAPI_ENCODER_STATUS_SUCCESS) {        /* another chance */
270       continue;
271     }
272     if (ret < GST_VAAPI_ENCODER_STATUS_SUCCESS) {       /* fatal error */
273       app->encode_failed = TRUE;
274       break;
275     }
276 
277     app->encoded_frames++;
278     g_debug ("encoded frame %d, buffer = %p", app->encoded_frames, obuf);
279 
280     if (app->output_file && outputs_to_file (obuf, app->output_file))
281       app->saved_frames++;
282 
283     gst_buffer_unref (obuf);
284   }
285 
286   if (obuf)
287     gst_buffer_replace (&obuf, NULL);
288 
289   return NULL;
290 }
291 
292 static void
app_free(App * app)293 app_free (App * app)
294 {
295   g_return_if_fail (app);
296 
297   if (app->parser)
298     y4m_reader_close (app->parser);
299 
300   if (app->encoder) {
301     gst_vaapi_encoder_flush (app->encoder);
302     gst_vaapi_encoder_unref (app->encoder);
303   }
304 
305   if (app->display)
306     gst_object_unref (app->display);
307 
308   if (app->output_file)
309     fclose (app->output_file);
310 
311   g_slice_free (App, app);
312 }
313 
314 static App *
app_new(const gchar * input_fn,const gchar * output_fn)315 app_new (const gchar * input_fn, const gchar * output_fn)
316 {
317   App *app = g_slice_new0 (App);
318   if (!app)
319     return NULL;
320 
321   app->parser = y4m_reader_open (input_fn);
322   if (!app->parser) {
323     g_warning ("Could not parse input stream.");
324     goto error;
325   }
326 
327   app->output_file = fopen (output_fn, "w");
328   if (app->output_file == NULL) {
329     g_warning ("Could not open file \"%s\" for writing: %s.", output_fn,
330         g_strerror (errno));
331     goto error;
332   }
333 
334   app->display = video_output_create_display (NULL);
335   if (!app->display) {
336     g_warning ("Could not create VA display.");
337     goto error;
338   }
339 
340   app->encoder = encoder_new (app->display);
341   if (!app->encoder) {
342     g_warning ("Could not create encoder.");
343     goto error;
344   }
345 
346   if (!set_format (app->encoder, app->parser->width, app->parser->height,
347           app->parser->fps_n, app->parser->fps_d)) {
348     g_warning ("Could not set format.");
349     goto error;
350   }
351 
352   return app;
353 
354 error:
355   app_free (app);
356   return NULL;
357 }
358 
359 static gboolean
upload_frame(GstVaapiEncoder * encoder,GstVaapiSurfaceProxy * proxy)360 upload_frame (GstVaapiEncoder * encoder, GstVaapiSurfaceProxy * proxy)
361 {
362   GstVideoCodecFrame *frame;
363   GstVaapiEncoderStatus ret;
364 
365   frame = g_slice_new0 (GstVideoCodecFrame);
366   gst_video_codec_frame_set_user_data (frame,
367       gst_vaapi_surface_proxy_ref (proxy),
368       (GDestroyNotify) gst_vaapi_surface_proxy_unref);
369 
370   ret = gst_vaapi_encoder_put_frame (encoder, frame);
371   return (ret == GST_VAAPI_ENCODER_STATUS_SUCCESS);
372 }
373 
374 static gboolean
load_frame(App * app,GstVaapiImage * image)375 load_frame (App * app, GstVaapiImage * image)
376 {
377   gboolean ret = FALSE;
378 
379   if (!gst_vaapi_image_map (image))
380     return FALSE;
381 
382   ret = y4m_reader_load_image (app->parser, image);
383 
384   if (!gst_vaapi_image_unmap (image))
385     return FALSE;
386 
387   return ret;
388 }
389 
390 static int
app_run(App * app)391 app_run (App * app)
392 {
393   GstVaapiImage *image;
394   GstVaapiVideoPool *pool;
395   GThread *buffer_thread;
396   gsize id;
397   int ret = EXIT_FAILURE;
398 
399   image = gst_vaapi_image_new (app->display, GST_VIDEO_FORMAT_I420,
400       app->parser->width, app->parser->height);
401 
402   {
403     GstVideoInfo vi;
404     gst_video_info_set_format (&vi, GST_VIDEO_FORMAT_ENCODED,
405         app->parser->width, app->parser->height);
406     pool = gst_vaapi_surface_pool_new_full (app->display, &vi, 0);
407   }
408 
409   buffer_thread = g_thread_new ("get buffer thread", get_buffer_thread, app);
410 
411   while (1) {
412     GstVaapiSurfaceProxy *proxy;
413     GstVaapiSurface *surface;
414 
415     if (!load_frame (app, image))
416       break;
417 
418     if (!gst_vaapi_image_unmap (image))
419       break;
420 
421     proxy =
422         gst_vaapi_surface_proxy_new_from_pool (GST_VAAPI_SURFACE_POOL (pool));
423     if (!proxy) {
424       g_warning ("Could not get surface proxy from pool.");
425       break;
426     }
427     surface = gst_vaapi_surface_proxy_get_surface (proxy);
428     if (!surface) {
429       g_warning ("Could not get surface from proxy.");
430       break;
431     }
432 
433     if (!gst_vaapi_surface_put_image (surface, image)) {
434       g_warning ("Could not update surface");
435       break;
436     }
437 
438     if (!upload_frame (app->encoder, proxy)) {
439       g_warning ("put frame failed");
440       break;
441     }
442 
443     app->read_frames++;
444     id = gst_vaapi_surface_get_id (surface);
445     g_debug ("input frame %d, surface id = %" G_GSIZE_FORMAT, app->read_frames,
446         id);
447 
448     gst_vaapi_surface_proxy_unref (proxy);
449   }
450 
451   app->input_stopped = TRUE;
452 
453   g_thread_join (buffer_thread);
454 
455   if (!app->encode_failed && feof (app->parser->fp))
456     ret = EXIT_SUCCESS;
457 
458   gst_vaapi_video_pool_replace (&pool, NULL);
459   gst_vaapi_object_unref (image);
460   return ret;
461 }
462 
463 int
main(int argc,char * argv[])464 main (int argc, char *argv[])
465 {
466   App *app;
467   int ret = EXIT_FAILURE;
468   gchar *input_fn;
469 
470   if (!parse_options (&argc, argv))
471     return EXIT_FAILURE;
472 
473   /* @TODO: iterate all the input files */
474   input_fn = g_input_files ? g_input_files[0] : NULL;
475   if (input_fn && !g_file_test (input_fn,
476           G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR)) {
477     g_warning ("input file \"%s\" doesn't exist", input_fn);
478     goto bail;
479   }
480 
481   app = app_new (input_fn, g_output_file_name);
482   if (!app)
483     goto bail;
484 
485   print_yuv_info (app);
486   ret = app_run (app);
487   print_num_frame (app);
488 
489   app_free (app);
490 
491 bail:
492   g_free (g_codec_str);
493   g_free (g_output_file_name);
494   g_strfreev (g_input_files);
495 
496   gst_deinit ();
497 
498   return ret;
499 }
500