1 /* GStreamer
2 *
3 * Copyright (C) 2009 Carl-Anton Ingmarsson <ca.ingmarsson@gmail.com>.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #include "gstvdpdecoder.h"
26 #include "gstvdpvideomemory.h"
27 #include "gstvdpvideobufferpool.h"
28 
29 GST_DEBUG_CATEGORY_STATIC (gst_vdp_decoder_debug);
30 #define GST_CAT_DEFAULT gst_vdp_decoder_debug
31 
32 #define DEBUG_INIT \
33     GST_DEBUG_CATEGORY_INIT (gst_vdp_decoder_debug, "vdpdecoder", 0, \
34     "VDPAU decoder base class");
35 #define gst_vdp_decoder_parent_class parent_class
36 G_DEFINE_TYPE_WITH_CODE (GstVdpDecoder, gst_vdp_decoder, GST_TYPE_VIDEO_DECODER,
37     DEBUG_INIT);
38 
39 enum
40 {
41   PROP_0,
42   PROP_DISPLAY
43 };
44 
45 void
gst_vdp_decoder_post_error(GstVdpDecoder * decoder,GError * error)46 gst_vdp_decoder_post_error (GstVdpDecoder * decoder, GError * error)
47 {
48   GstMessage *message;
49 
50   g_return_if_fail (GST_IS_VDP_DECODER (decoder));
51   g_return_if_fail (decoder != NULL);
52 
53   message = gst_message_new_error (GST_OBJECT (decoder), error, NULL);
54   gst_element_post_message (GST_ELEMENT (decoder), message);
55   g_error_free (error);
56 }
57 
58 
59 GstFlowReturn
gst_vdp_decoder_render(GstVdpDecoder * vdp_decoder,VdpPictureInfo * info,guint n_bufs,VdpBitstreamBuffer * bufs,GstVideoCodecFrame * frame)60 gst_vdp_decoder_render (GstVdpDecoder * vdp_decoder, VdpPictureInfo * info,
61     guint n_bufs, VdpBitstreamBuffer * bufs, GstVideoCodecFrame * frame)
62 {
63   GstFlowReturn ret;
64 
65   VdpStatus status;
66 
67   GstVdpVideoMemory *vmem;
68 #ifndef GST_DISABLE_GST_DEBUG
69   GstClockTime before, after;
70 #endif
71 
72   GST_DEBUG_OBJECT (vdp_decoder, "n_bufs:%d, frame:%d", n_bufs,
73       frame->system_frame_number);
74 
75   ret =
76       gst_video_decoder_allocate_output_frame (GST_VIDEO_DECODER (vdp_decoder),
77       frame);
78   if (ret != GST_FLOW_OK)
79     goto fail_alloc;
80 
81   vmem = (GstVdpVideoMemory *) gst_buffer_get_memory (frame->output_buffer, 0);
82   if (!vmem
83       || !gst_memory_is_type ((GstMemory *) vmem,
84           GST_VDP_VIDEO_MEMORY_ALLOCATOR))
85     goto no_mem;
86 
87   GST_DEBUG_OBJECT (vdp_decoder, "Calling VdpDecoderRender()");
88 #ifndef GST_DISABLE_GST_DEBUG
89   before = gst_util_get_timestamp ();
90 #endif
91   status =
92       vdp_decoder->device->vdp_decoder_render (vdp_decoder->decoder,
93       vmem->surface, info, n_bufs, bufs);
94 #ifndef GST_DISABLE_GST_DEBUG
95   after = gst_util_get_timestamp ();
96 #endif
97   if (status != VDP_STATUS_OK)
98     goto decode_error;
99 
100   GST_DEBUG_OBJECT (vdp_decoder, "VdpDecoderRender() took %" GST_TIME_FORMAT,
101       GST_TIME_ARGS (after - before));
102 
103   return GST_FLOW_OK;
104 
105 decode_error:
106   GST_ELEMENT_ERROR (vdp_decoder, RESOURCE, READ,
107       ("Could not decode"),
108       ("Error returned from vdpau was: %s",
109           vdp_decoder->device->vdp_get_error_string (status)));
110 
111   gst_video_decoder_drop_frame (GST_VIDEO_DECODER (vdp_decoder), frame);
112 
113   return GST_FLOW_ERROR;
114 
115 fail_alloc:
116   {
117     GST_WARNING_OBJECT (vdp_decoder, "Failed to get an output frame");
118     return ret;
119   }
120 
121 no_mem:
122   {
123     GST_ERROR_OBJECT (vdp_decoder, "Didn't get VdpVideoSurface backed buffer");
124     return GST_FLOW_ERROR;
125   }
126 }
127 
128 GstFlowReturn
gst_vdp_decoder_init_decoder(GstVdpDecoder * vdp_decoder,VdpDecoderProfile profile,guint32 max_references,GstVideoCodecState * output_state)129 gst_vdp_decoder_init_decoder (GstVdpDecoder * vdp_decoder,
130     VdpDecoderProfile profile, guint32 max_references,
131     GstVideoCodecState * output_state)
132 {
133   GstVdpDevice *device;
134 
135   VdpStatus status;
136 
137   device = vdp_decoder->device;
138 
139   if (vdp_decoder->decoder != VDP_INVALID_HANDLE) {
140     status = device->vdp_decoder_destroy (vdp_decoder->decoder);
141     if (status != VDP_STATUS_OK)
142       goto destroy_decoder_error;
143   }
144 
145   GST_DEBUG_OBJECT (vdp_decoder,
146       "device:%u, profile:%d, width:%d, height:%d, max_references:%d",
147       device->device, profile, output_state->info.width,
148       output_state->info.height, max_references);
149 
150   status = device->vdp_decoder_create (device->device, profile,
151       output_state->info.width, output_state->info.height, max_references,
152       &vdp_decoder->decoder);
153   if (status != VDP_STATUS_OK)
154     goto create_decoder_error;
155 
156   return GST_FLOW_OK;
157 
158 destroy_decoder_error:
159   GST_ELEMENT_ERROR (vdp_decoder, RESOURCE, READ,
160       ("Could not destroy vdpau decoder"),
161       ("Error returned from vdpau was: %s",
162           device->vdp_get_error_string (status)));
163 
164   return GST_FLOW_ERROR;
165 
166 create_decoder_error:
167   GST_ELEMENT_ERROR (vdp_decoder, RESOURCE, READ,
168       ("Could not create vdpau decoder"),
169       ("Error returned from vdpau was: %s",
170           device->vdp_get_error_string (status)));
171 
172   return GST_FLOW_ERROR;
173 }
174 
175 static gboolean
gst_vdp_decoder_decide_allocation(GstVideoDecoder * video_decoder,GstQuery * query)176 gst_vdp_decoder_decide_allocation (GstVideoDecoder * video_decoder,
177     GstQuery * query)
178 {
179   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (video_decoder);
180   GstCaps *outcaps;
181   GstBufferPool *pool = NULL;
182   guint size, min = 0, max = 0;
183   GstStructure *config;
184   GstVideoInfo vinfo;
185   gboolean update_pool;
186 
187   gst_query_parse_allocation (query, &outcaps, NULL);
188   gst_video_info_init (&vinfo);
189   gst_video_info_from_caps (&vinfo, outcaps);
190 
191   if (gst_query_get_n_allocation_pools (query) > 0) {
192     gst_query_parse_nth_allocation_pool (query, 0, &pool, &size, &min, &max);
193     size = MAX (size, vinfo.size);
194     update_pool = TRUE;
195   } else {
196     pool = NULL;
197     size = vinfo.size;
198     min = max = 0;
199 
200     update_pool = FALSE;
201   }
202 
203   if (pool == NULL
204       || !gst_buffer_pool_has_option (pool,
205           GST_BUFFER_POOL_OPTION_VDP_VIDEO_META)) {
206     if (pool)
207       gst_object_unref (pool);
208     /* no pool or pool doesn't support GstVdpVideoMeta, we can make our own */
209     GST_DEBUG_OBJECT (video_decoder,
210         "no pool or doesn't support GstVdpVideoMeta, making new pool");
211     pool = gst_vdp_video_buffer_pool_new (vdp_decoder->device);
212   }
213 
214   /* now configure */
215   config = gst_buffer_pool_get_config (pool);
216   gst_buffer_pool_config_set_params (config, outcaps, size, min, max);
217   gst_buffer_pool_config_add_option (config,
218       GST_BUFFER_POOL_OPTION_VDP_VIDEO_META);
219   gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META);
220   gst_buffer_pool_set_config (pool, config);
221 
222   if (update_pool)
223     gst_query_set_nth_allocation_pool (query, 0, pool, size, min, max);
224   else
225     gst_query_add_allocation_pool (query, pool, size, min, max);
226 
227   if (pool)
228     gst_object_unref (pool);
229 
230   return TRUE;
231 
232 }
233 
234 static gboolean
gst_vdp_decoder_start(GstVideoDecoder * video_decoder)235 gst_vdp_decoder_start (GstVideoDecoder * video_decoder)
236 {
237   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (video_decoder);
238   GError *err = NULL;
239 
240   GST_DEBUG_OBJECT (video_decoder, "Starting");
241 
242   vdp_decoder->device = gst_vdp_get_device (vdp_decoder->display, &err);
243   if (G_UNLIKELY (!vdp_decoder->device))
244     goto device_error;
245 
246   vdp_decoder->decoder = VDP_INVALID_HANDLE;
247 
248   return TRUE;
249 
250 device_error:
251   gst_vdp_decoder_post_error (vdp_decoder, err);
252   return FALSE;
253 }
254 
255 static gboolean
gst_vdp_decoder_stop(GstVideoDecoder * video_decoder)256 gst_vdp_decoder_stop (GstVideoDecoder * video_decoder)
257 {
258   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (video_decoder);
259 
260   if (vdp_decoder->decoder != VDP_INVALID_HANDLE) {
261     GstVdpDevice *device = vdp_decoder->device;
262     VdpStatus status;
263 
264     status = device->vdp_decoder_destroy (vdp_decoder->decoder);
265     if (status != VDP_STATUS_OK) {
266       GST_ELEMENT_ERROR (vdp_decoder, RESOURCE, READ,
267           ("Could not destroy vdpau decoder"),
268           ("Error returned from vdpau was: %s",
269               device->vdp_get_error_string (status)));
270       return FALSE;
271     }
272   }
273 
274   g_object_unref (vdp_decoder->device);
275 
276   return TRUE;
277 }
278 
279 static void
gst_vdp_decoder_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)280 gst_vdp_decoder_get_property (GObject * object, guint prop_id,
281     GValue * value, GParamSpec * pspec)
282 {
283   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (object);
284 
285   switch (prop_id) {
286     case PROP_DISPLAY:
287       g_value_set_string (value, vdp_decoder->display);
288       break;
289 
290     default:
291       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
292       break;
293   }
294 }
295 
296 static void
gst_vdp_decoder_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)297 gst_vdp_decoder_set_property (GObject * object, guint prop_id,
298     const GValue * value, GParamSpec * pspec)
299 {
300   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (object);
301 
302   switch (prop_id) {
303     case PROP_DISPLAY:
304       g_free (vdp_decoder->display);
305       vdp_decoder->display = g_value_dup_string (value);
306       break;
307 
308     default:
309       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
310       break;
311   }
312 }
313 
314 static void
gst_vdp_decoder_finalize(GObject * object)315 gst_vdp_decoder_finalize (GObject * object)
316 {
317   GstVdpDecoder *vdp_decoder = GST_VDP_DECODER (object);
318 
319   g_free (vdp_decoder->display);
320 
321   G_OBJECT_CLASS (parent_class)->finalize (object);
322 }
323 
324 static void
gst_vdp_decoder_init(GstVdpDecoder * vdp_decoder)325 gst_vdp_decoder_init (GstVdpDecoder * vdp_decoder)
326 {
327 
328 }
329 
330 static void
gst_vdp_decoder_class_init(GstVdpDecoderClass * klass)331 gst_vdp_decoder_class_init (GstVdpDecoderClass * klass)
332 {
333   GObjectClass *object_class;
334   GstVideoDecoderClass *video_decoder_class;
335   GstElementClass *element_class;
336 
337   GstCaps *src_caps;
338   GstPadTemplate *src_template;
339 
340   object_class = G_OBJECT_CLASS (klass);
341   element_class = GST_ELEMENT_CLASS (klass);
342   video_decoder_class = GST_VIDEO_DECODER_CLASS (klass);
343 
344   object_class->get_property = gst_vdp_decoder_get_property;
345   object_class->set_property = gst_vdp_decoder_set_property;
346   object_class->finalize = gst_vdp_decoder_finalize;
347 
348   video_decoder_class->start = gst_vdp_decoder_start;
349   video_decoder_class->stop = gst_vdp_decoder_stop;
350   video_decoder_class->decide_allocation = gst_vdp_decoder_decide_allocation;
351 
352   GST_FIXME ("Actually create srcpad template from hw capabilities");
353   src_caps =
354       gst_caps_from_string (GST_VIDEO_CAPS_MAKE_WITH_FEATURES
355       (GST_CAPS_FEATURE_MEMORY_VDPAU,
356           "{ YV12 }") ";" GST_VIDEO_CAPS_MAKE ("{ YV12 }"));
357   src_template =
358       gst_pad_template_new (GST_VIDEO_DECODER_SRC_NAME, GST_PAD_SRC,
359       GST_PAD_ALWAYS, src_caps);
360 
361   gst_element_class_add_pad_template (element_class, src_template);
362 
363   if (src_caps)
364     gst_caps_unref (src_caps);
365 
366   g_object_class_install_property (object_class,
367       PROP_DISPLAY, g_param_spec_string ("display", "Display", "X Display name",
368           NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
369 }
370