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