2 * simple-decoder.c - Simple Decoder Application
4 * Copyright (C) 2013 Intel Corporation
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.
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.
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
23 * This is a really simple decoder application that only accepts raw
24 * bitstreams. So, it may be needed to suggest what codec to use to
28 #include "gst/vaapi/sysdeps.h"
30 #include <gst/vaapi/gstvaapidecoder.h>
31 #include <gst/vaapi/gstvaapidecoder_h264.h>
32 #include <gst/vaapi/gstvaapidecoder_jpeg.h>
33 #include <gst/vaapi/gstvaapidecoder_mpeg2.h>
34 #include <gst/vaapi/gstvaapidecoder_mpeg4.h>
35 #include <gst/vaapi/gstvaapidecoder_vc1.h>
36 #include <gst/vaapi/gstvaapiwindow.h>
37 #include <gst/vaapi/gstvaapipixmap.h>
41 static gchar *g_codec_str;
42 static gboolean g_use_pixmap;
43 static gboolean g_benchmark;
45 static GOptionEntry g_options[] = {
48 G_OPTION_ARG_STRING, &g_codec_str,
49 "suggested codec", NULL },
52 G_OPTION_ARG_NONE, &g_use_pixmap,
53 "use render-to-pixmap", NULL },
56 G_OPTION_ARG_NONE, &g_benchmark,
57 "benchmark mode", NULL },
74 GstVaapiSurfaceProxy *proxy;
76 GstClockTime duration;
86 GstVaapiDisplay *display;
87 GstVaapiDecoder *decoder;
88 GThread *decoder_thread;
89 volatile gboolean decoder_thread_cancel;
91 GAsyncQueue *decoder_queue;
95 guint32 frame_duration;
98 GstVaapiPixmap *pixmaps[2];
102 GstVaapiWindow *window;
105 GThread *render_thread;
106 volatile gboolean render_thread_cancel;
108 RenderFrame *last_frame;
116 static inline RenderFrame *
117 render_frame_new(void)
119 return g_slice_new(RenderFrame);
123 render_frame_free(RenderFrame *rfp)
125 if (G_UNLIKELY(!rfp))
127 gst_vaapi_surface_proxy_replace(&rfp->proxy, NULL);
128 g_slice_free(RenderFrame, rfp);
132 render_frame_replace(RenderFrame **rfp_ptr, RenderFrame *new_rfp)
135 render_frame_free(*rfp_ptr);
139 #define APP_ERROR app_error_quark()
141 app_error_quark(void)
143 static gsize g_quark;
145 if (g_once_init_enter(&g_quark)) {
146 gsize quark = (gsize)g_quark_from_static_string("AppError");
147 g_once_init_leave(&g_quark, quark);
153 app_send_error(App *app, GError *error)
155 g_mutex_lock(&app->mutex);
157 app->event = APP_GOT_ERROR;
158 g_cond_signal(&app->event_cond);
159 g_mutex_unlock(&app->mutex);
163 app_send_eos(App *app)
165 g_mutex_lock(&app->mutex);
166 app->event = APP_GOT_EOS;
167 g_cond_signal(&app->event_cond);
168 g_mutex_unlock(&app->mutex);
172 get_decoder_status_string(GstVaapiDecoderStatus status)
176 #define DEFINE_STATUS(status, status_string) \
177 case GST_VAAPI_DECODER_STATUS_##status: \
178 str = status_string; \
182 DEFINE_STATUS(SUCCESS, "<success>");
183 DEFINE_STATUS(END_OF_STREAM, "<EOS>");
184 DEFINE_STATUS(ERROR_ALLOCATION_FAILED, "allocation failed");
185 DEFINE_STATUS(ERROR_INIT_FAILED, "initialization failed");
186 DEFINE_STATUS(ERROR_UNSUPPORTED_CODEC, "unsupported codec");
187 DEFINE_STATUS(ERROR_NO_DATA, "not enough data");
188 DEFINE_STATUS(ERROR_NO_SURFACE, "no surface vailable");
189 DEFINE_STATUS(ERROR_INVALID_SURFACE, "invalid surface");
190 DEFINE_STATUS(ERROR_BITSTREAM_PARSER, "bitstream parser error");
191 DEFINE_STATUS(ERROR_UNSUPPORTED_PROFILE,
192 "unsupported profile");
193 DEFINE_STATUS(ERROR_UNSUPPORTED_CHROMA_FORMAT,
194 "unsupported chroma-format");
195 DEFINE_STATUS(ERROR_INVALID_PARAMETER, "invalid parameter");
206 get_error_string(AppError error)
210 #define DEFINE_ERROR(error, error_string) \
211 case APP_ERROR_##error: \
212 str = error_string; \
216 DEFINE_ERROR(NONE, "<none>");
217 DEFINE_ERROR(DECODER, "decoder");
218 DEFINE_ERROR(RENDERER, "renderer");
229 decoder_release(App *app)
231 g_mutex_lock(&app->mutex);
232 g_cond_signal(&app->decoder_ready);
233 g_mutex_unlock(&app->mutex);
237 decoder_thread(gpointer data)
239 App * const app = data;
240 GError *error = NULL;
241 GstVaapiDecoderStatus status;
242 GstVaapiSurfaceProxy *proxy;
246 gboolean got_surface, got_eos = FALSE;
250 g_print("Decoder thread started\n");
252 #define SEND_ERROR(...) \
254 error = g_error_new(APP_ERROR, APP_ERROR_DECODER, __VA_ARGS__); \
258 pts = g_get_monotonic_time();
260 while (!app->decoder_thread_cancel) {
261 if (G_UNLIKELY(ofs == app->file_size))
264 const gsize size = MIN(4096, app->file_size - ofs);
265 buffer = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY,
266 app->file_data, app->file_size, ofs, size, NULL, NULL);
268 SEND_ERROR("failed to allocate new buffer");
271 if (!gst_vaapi_decoder_put_buffer(app->decoder, buffer))
272 SEND_ERROR("failed to push buffer to decoder");
273 gst_buffer_replace(&buffer, NULL);
276 status = gst_vaapi_decoder_get_surface(app->decoder, &proxy);
278 case GST_VAAPI_DECODER_STATUS_SUCCESS:
279 gst_vaapi_surface_proxy_set_destroy_notify(proxy,
280 (GDestroyNotify)decoder_release, app);
281 rfp = render_frame_new();
283 SEND_ERROR("failed to allocate render frame");
286 rfp->duration = app->frame_duration;
287 pts += app->frame_duration;
288 g_async_queue_push(app->decoder_queue, rfp);
290 case GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA:
291 /* nothing to do, just continue to the next iteration */
293 case GST_VAAPI_DECODER_STATUS_END_OF_STREAM:
294 gst_vaapi_decoder_flush(app->decoder);
299 case GST_VAAPI_DECODER_STATUS_ERROR_NO_SURFACE:
300 end_time = g_get_monotonic_time() + G_TIME_SPAN_SECOND;
301 g_mutex_lock(&app->mutex);
302 got_surface = g_cond_wait_until(&app->decoder_ready, &app->mutex,
304 g_mutex_unlock(&app->mutex);
307 SEND_ERROR("failed to acquire a surface within one second");
310 SEND_ERROR("%s", get_decoder_status_string(status));
323 app_send_error(app, error);
328 app_set_framerate(App *app, guint fps_n, guint fps_d)
330 if (!fps_n || !fps_d)
333 g_mutex_lock(&app->mutex);
334 if (fps_n != app->fps_n || fps_d != app->fps_d) {
337 app->frame_duration = gst_util_uint64_scale(
338 GST_TIME_AS_USECONDS(GST_SECOND), fps_d, fps_n);
340 g_mutex_unlock(&app->mutex);
344 handle_decoder_state_changes(GstVaapiDecoder *decoder,
345 const GstVideoCodecState *codec_state, gpointer user_data)
347 App * const app = user_data;
349 g_assert(app->decoder == decoder);
350 app_set_framerate(app, codec_state->info.fps_n, codec_state->info.fps_d);
354 start_decoder(App *app)
358 app->file = g_mapped_file_new(app->file_name, FALSE, NULL);
362 app->file_size = g_mapped_file_get_length(app->file);
363 app->file_data = (guint8 *)g_mapped_file_get_contents(app->file);
367 caps = caps_from_codec(app->codec);
368 switch (app->codec) {
369 case GST_VAAPI_CODEC_H264:
370 app->decoder = gst_vaapi_decoder_h264_new(app->display, caps);
373 case GST_VAAPI_CODEC_JPEG:
374 app->decoder = gst_vaapi_decoder_jpeg_new(app->display, caps);
377 case GST_VAAPI_CODEC_MPEG2:
378 app->decoder = gst_vaapi_decoder_mpeg2_new(app->display, caps);
380 case GST_VAAPI_CODEC_MPEG4:
381 app->decoder = gst_vaapi_decoder_mpeg4_new(app->display, caps);
383 case GST_VAAPI_CODEC_VC1:
384 app->decoder = gst_vaapi_decoder_vc1_new(app->display, caps);
393 gst_vaapi_decoder_set_codec_state_changed_func(app->decoder,
394 handle_decoder_state_changes, app);
396 g_timer_start(app->timer);
398 app->decoder_thread = g_thread_create(decoder_thread, app, TRUE, NULL);
399 if (!app->decoder_thread)
405 stop_decoder(App *app)
407 g_timer_stop(app->timer);
409 app->decoder_thread_cancel = TRUE;
410 g_thread_join(app->decoder_thread);
411 g_print("Decoder thread stopped\n");
416 ensure_window_size(App *app, GstVaapiSurface *surface)
420 if (gst_vaapi_window_get_fullscreen(app->window))
423 gst_vaapi_surface_get_size(surface, &width, &height);
424 if (app->surface_width == width && app->surface_height == height)
426 app->surface_width = width;
427 app->surface_height = height;
429 gst_vaapi_window_set_size(app->window, width, height);
430 gst_vaapi_window_get_size(app->window,
431 &app->window_width, &app->window_height);
435 ensure_pixmaps(App *app, GstVaapiSurface *surface,
436 const GstVaapiRectangle *crop_rect)
438 GstVaapiPixmap *pixmaps[G_N_ELEMENTS(app->pixmaps)];
439 guint num_pixmaps, i, width, height;
440 gboolean success = FALSE;
443 width = crop_rect->width;
444 height = crop_rect->height;
447 gst_vaapi_surface_get_size(surface, &width, &height);
448 if (app->pixmap_width == width && app->pixmap_height == height)
451 for (i = 0, num_pixmaps = 0; i < G_N_ELEMENTS(pixmaps); i++) {
452 GstVaapiPixmap * const pixmap =
453 video_output_create_pixmap(app->display, GST_VIDEO_FORMAT_xRGB,
457 pixmaps[num_pixmaps++] = pixmap;
460 for (i = 0; i < num_pixmaps; i++)
461 gst_vaapi_pixmap_replace(&app->pixmaps[i], pixmaps[i]);
462 app->pixmap_width = width;
463 app->pixmap_height = height;
467 for (i = 0; i < num_pixmaps; i++)
468 gst_vaapi_pixmap_replace(&pixmaps[i], NULL);
473 renderer_wait_until(App *app, GstClockTime pts)
475 g_mutex_lock(&app->mutex);
477 } while (g_cond_wait_until(&app->render_ready, &app->mutex, pts));
478 g_mutex_unlock(&app->mutex);
482 renderer_process(App *app, RenderFrame *rfp)
484 GError *error = NULL;
485 GstVaapiSurface *surface;
486 const GstVaapiRectangle *crop_rect;
488 #define SEND_ERROR(...) \
490 error = g_error_new(APP_ERROR, APP_ERROR_RENDERER, __VA_ARGS__); \
494 surface = gst_vaapi_surface_proxy_get_surface(rfp->proxy);
496 SEND_ERROR("failed to get decoded surface from render frame");
498 ensure_window_size(app, surface);
500 crop_rect = gst_vaapi_surface_proxy_get_crop_rect(rfp->proxy);
501 if (!ensure_pixmaps(app, surface, crop_rect))
502 SEND_ERROR("failed to create intermediate pixmaps");
504 if (!gst_vaapi_surface_sync(surface))
505 SEND_ERROR("failed to sync decoded surface");
507 if (G_LIKELY(!g_benchmark))
508 renderer_wait_until(app, rfp->pts);
510 if (G_UNLIKELY(g_use_pixmap)) {
511 GstVaapiPixmap * const pixmap = app->pixmaps[app->pixmap_id];
513 if (!gst_vaapi_pixmap_put_surface(pixmap, surface, crop_rect,
514 GST_VAAPI_PICTURE_STRUCTURE_FRAME))
515 SEND_ERROR("failed to render to pixmap");
517 if (!gst_vaapi_window_put_pixmap(app->window, pixmap, NULL, NULL))
518 SEND_ERROR("failed to render surface %" GST_VAAPI_ID_FORMAT,
519 GST_VAAPI_ID_ARGS(pixmap));
521 app->pixmap_id = (app->pixmap_id + 1) % G_N_ELEMENTS(app->pixmaps);
523 else if (!gst_vaapi_window_put_surface(app->window, surface,
524 crop_rect, NULL, GST_VAAPI_PICTURE_STRUCTURE_FRAME))
525 SEND_ERROR("failed to render surface %" GST_VAAPI_ID_FORMAT,
526 GST_VAAPI_ID_ARGS(gst_vaapi_surface_get_id(surface)));
530 render_frame_replace(&app->last_frame, rfp);
536 app_send_error(app, error);
541 renderer_thread(gpointer data)
543 App * const app = data;
546 g_print("Render thread started\n");
548 while (!app->render_thread_cancel) {
549 rfp = g_async_queue_timeout_pop(app->decoder_queue, 1000000);
550 if (rfp && !renderer_process(app, rfp))
557 flush_decoder_queue(App *app)
561 /* Flush pending surfaces */
563 rfp = g_async_queue_try_pop(app->decoder_queue);
566 } while (renderer_process(app, rfp));
571 start_renderer(App *app)
573 app->render_thread = g_thread_create(renderer_thread, app, TRUE, NULL);
574 if (!app->render_thread)
580 stop_renderer(App *app)
582 app->render_thread_cancel = TRUE;
583 g_thread_join(app->render_thread);
585 g_print("Render thread stopped\n");
587 flush_decoder_queue(app);
588 render_frame_replace(&app->last_frame, NULL);
601 g_mapped_file_unref(app->file);
604 g_free(app->file_name);
606 for (i = 0; i < G_N_ELEMENTS(app->pixmaps); i++)
607 gst_vaapi_pixmap_replace(&app->pixmaps[i], NULL);
608 gst_vaapi_decoder_replace(&app->decoder, NULL);
609 gst_vaapi_window_replace(&app->window, NULL);
610 gst_vaapi_display_replace(&app->display, NULL);
612 if (app->decoder_queue) {
613 g_async_queue_unref(app->decoder_queue);
614 app->decoder_queue = NULL;
616 g_cond_clear(&app->decoder_ready);
619 g_timer_destroy(app->timer);
623 g_cond_clear(&app->render_ready);
624 g_cond_clear(&app->event_cond);
625 g_mutex_clear(&app->mutex);
626 g_slice_free(App, app);
634 app = g_slice_new0(App);
638 g_mutex_init(&app->mutex);
639 g_cond_init(&app->event_cond);
640 g_cond_init(&app->decoder_ready);
641 g_cond_init(&app->render_ready);
643 app_set_framerate(app, 60, 1);
644 app->window_width = 640;
645 app->window_height = 480;
647 app->decoder_queue = g_async_queue_new_full(
648 (GDestroyNotify)render_frame_free);
649 if (!app->decoder_queue)
652 app->timer = g_timer_new();
663 app_check_events(App *app)
665 GError *error = NULL;
666 gboolean stop = FALSE;
669 g_mutex_lock(&app->mutex);
670 while (app->event == APP_RUNNING)
671 g_cond_wait(&app->event_cond, &app->mutex);
673 switch (app->event) {
684 g_mutex_unlock(&app->mutex);
690 g_message("%s error: %s", get_error_string(error->code), error->message);
696 app_run(App *app, int argc, char *argv[])
699 g_message("no bitstream file specified");
702 app->file_name = g_strdup(argv[1]);
704 if (!g_file_test(app->file_name, G_FILE_TEST_IS_REGULAR)) {
705 g_message("failed to find file '%s'", app->file_name);
709 app->codec = identify_codec(app->file_name);
711 app->codec = identify_codec_from_string(g_codec_str);
713 g_message("failed to identify codec for '%s'", app->file_name);
718 g_print("Simple decoder (%s bitstream)\n", string_from_codec(app->codec));
720 app->display = video_output_create_display(NULL);
722 g_message("failed to create VA display");
726 app->window = video_output_create_window(app->display,
727 app->window_width, app->window_height);
729 g_message("failed to create window");
733 gst_vaapi_window_show(app->window);
735 if (!start_decoder(app)) {
736 g_message("failed to start decoder thread");
740 if (!start_renderer(app)) {
741 g_message("failed to start renderer thread");
745 app_check_events(app);
750 g_print("Decoded %u frames", app->num_frames);
752 const gdouble elapsed = g_timer_elapsed(app->timer, NULL);
753 g_print(" in %.2f sec (%.1f fps)\n",
754 elapsed, (gdouble)app->num_frames / elapsed);
761 main(int argc, char *argv[])
766 if (!video_output_init(&argc, argv, g_options))
767 g_error("failed to initialize video output subsystem");
771 g_error("failed to create application context");
773 ret = !app_run(app, argc, argv);