1 /* GStreamer
2 * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "gstmfvideobuffer.h"
21 #include <string.h>
22
23 /* *INDENT-OFF* */
24 G_BEGIN_DECLS
25
26 GST_DEBUG_CATEGORY_EXTERN (gst_mf_video_buffer_debug);
27 #define GST_CAT_DEFAULT gst_mf_video_buffer_debug
28
29 G_END_DECLS
30
IGstMFVideoBuffer()31 IGstMFVideoBuffer::IGstMFVideoBuffer ()
32 : ref_count_ (1)
33 , current_len_ (0)
34 , contiguous_len_ (0)
35 , data_ (nullptr)
36 , contiguous_data_ (nullptr)
37 , info_ (nullptr)
38 , contiguous_info_ (nullptr)
39 , locked_ (false)
40 , wrapped_ (false)
41 , user_data_ (nullptr)
42 , notify_ (nullptr)
43 {
44 }
45
~IGstMFVideoBuffer()46 IGstMFVideoBuffer::~IGstMFVideoBuffer ()
47 {
48 if (info_)
49 gst_video_info_free (info_);
50 if (contiguous_info_)
51 gst_video_info_free (contiguous_info_);
52
53 g_free (contiguous_data_);
54
55 if (!wrapped_)
56 g_free (data_);
57 }
58
59 HRESULT
CreateInstance(GstVideoInfo * info,IMFMediaBuffer ** buffer)60 IGstMFVideoBuffer::CreateInstance (GstVideoInfo * info,
61 IMFMediaBuffer ** buffer)
62 {
63 HRESULT hr = S_OK;
64 IGstMFVideoBuffer * self;
65
66 if (!info || !buffer)
67 return E_INVALIDARG;
68
69 self = new IGstMFVideoBuffer ();
70
71 if (!self)
72 return E_OUTOFMEMORY;
73
74 hr = self->Initialize (info);
75
76 if (SUCCEEDED (hr))
77 hr = self->QueryInterface (IID_PPV_ARGS (buffer));
78
79 self->Release ();
80
81 return hr;
82 }
83
84 HRESULT
CreateInstanceWrapped(GstVideoInfo * info,BYTE * data,DWORD length,IMFMediaBuffer ** buffer)85 IGstMFVideoBuffer::CreateInstanceWrapped (GstVideoInfo * info,
86 BYTE * data, DWORD length, IMFMediaBuffer ** buffer)
87 {
88 HRESULT hr = S_OK;
89 IGstMFVideoBuffer * self;
90
91 if (!info || !data || length == 0 || !buffer)
92 return E_INVALIDARG;
93
94 self = new IGstMFVideoBuffer ();
95
96 if (!self)
97 return E_OUTOFMEMORY;
98
99 hr = self->InitializeWrapped (info, data, length);
100
101 if (SUCCEEDED (hr))
102 hr = self->QueryInterface (IID_PPV_ARGS (buffer));
103
104 self->Release ();
105
106 return hr;
107 }
108
109 HRESULT
Initialize(GstVideoInfo * info)110 IGstMFVideoBuffer::Initialize (GstVideoInfo * info)
111 {
112 if (!info)
113 return E_INVALIDARG;
114
115 info_ = gst_video_info_copy (info);
116 contiguous_info_ = gst_video_info_new ();
117
118 /* check if padding is required */
119 gst_video_info_set_format (contiguous_info_, GST_VIDEO_INFO_FORMAT (info_),
120 GST_VIDEO_INFO_WIDTH (info_), GST_VIDEO_INFO_HEIGHT (info_));
121
122 contiguous_ = GST_VIDEO_INFO_SIZE (info_) ==
123 GST_VIDEO_INFO_SIZE (contiguous_info_);
124
125 contiguous_len_ = GST_VIDEO_INFO_SIZE (contiguous_info_);
126 /* NOTE: {Set,Get}CurrentLength will not be applied for
127 * IMF2DBuffer interface */
128
129 current_len_ = contiguous_len_;
130
131 data_ = (BYTE *) g_malloc0 (GST_VIDEO_INFO_SIZE (info_));
132
133 if (!data_)
134 return E_OUTOFMEMORY;
135
136 return S_OK;
137 }
138
139 HRESULT
InitializeWrapped(GstVideoInfo * info,BYTE * data,DWORD length)140 IGstMFVideoBuffer::InitializeWrapped (GstVideoInfo * info, BYTE * data,
141 DWORD length)
142 {
143 if (!info || !data || length == 0)
144 return E_INVALIDARG;
145
146 if (length < GST_VIDEO_INFO_SIZE (info))
147 return E_INVALIDARG;
148
149 info_ = gst_video_info_copy (info);
150 contiguous_info_ = gst_video_info_new ();
151
152 /* check if padding is required */
153 gst_video_info_set_format (contiguous_info_, GST_VIDEO_INFO_FORMAT (info_),
154 GST_VIDEO_INFO_WIDTH (info_), GST_VIDEO_INFO_HEIGHT (info_));
155
156 contiguous_ = GST_VIDEO_INFO_SIZE (info_) ==
157 GST_VIDEO_INFO_SIZE (contiguous_info_);
158
159 contiguous_len_ = GST_VIDEO_INFO_SIZE (contiguous_info_);
160
161 current_len_ = contiguous_len_;
162
163 data_ = data;
164 wrapped_ = true;
165
166 return S_OK;
167 }
168
169 HRESULT
SetUserData(gpointer user_data,GDestroyNotify notify)170 IGstMFVideoBuffer::SetUserData (gpointer user_data, GDestroyNotify notify)
171 {
172 GDestroyNotify old_notify = notify_;
173 gpointer old_user_data = user_data_;
174
175 if (old_notify)
176 old_notify (old_user_data);
177
178 user_data_ = user_data;
179 notify_ = notify;
180
181 return S_OK;
182 }
183
184 HRESULT
GetUserData(gpointer * user_data)185 IGstMFVideoBuffer::GetUserData (gpointer * user_data)
186 {
187 if (!user_data)
188 return E_INVALIDARG;
189
190 *user_data = user_data_;
191
192 return S_OK;
193 }
194
195 /* IUnknown interface */
STDMETHODIMP_(ULONG)196 STDMETHODIMP_ (ULONG)
197 IGstMFVideoBuffer::AddRef (void)
198 {
199 GST_TRACE ("%p, %d", this, ref_count_);
200 return InterlockedIncrement (&ref_count_);
201 }
202
STDMETHODIMP_(ULONG)203 STDMETHODIMP_ (ULONG)
204 IGstMFVideoBuffer::Release (void)
205 {
206 ULONG ref_count;
207
208 GST_TRACE ("%p, %d", this, ref_count_);
209 ref_count = InterlockedDecrement (&ref_count_);
210
211 if (ref_count == 0) {
212 GDestroyNotify old_notify = notify_;
213 gpointer old_user_data = user_data_;
214
215 GST_TRACE ("Delete instance %p", this);
216 delete this;
217
218 if (old_notify)
219 old_notify (old_user_data);
220 }
221
222 return ref_count;
223 }
224
225 STDMETHODIMP
QueryInterface(REFIID riid,void ** object)226 IGstMFVideoBuffer::QueryInterface (REFIID riid, void ** object)
227 {
228 if (!object)
229 return E_POINTER;
230
231 if (riid == IID_IUnknown) {
232 GST_TRACE ("query IUnknown interface %p", this);
233 *object = static_cast<IUnknown *> (static_cast<IMFMediaBuffer *> (this));
234 } else if (riid == __uuidof(IMFMediaBuffer)) {
235 GST_TRACE ("query IMFMediaBuffer interface %p", this);
236 *object = static_cast<IMFMediaBuffer *> (this);
237 } else if (riid == __uuidof(IMF2DBuffer)) {
238 GST_TRACE ("query IMF2DBuffer interface %p", this);
239 *object = static_cast<IMF2DBuffer *> (this);
240 } else if (riid == __uuidof(IGstMFVideoBuffer)) {
241 GST_TRACE ("query IGstMFVideoBuffer interface %p", this);
242 *object = this;
243 } else {
244 *object = NULL;
245 return E_NOINTERFACE;
246 }
247
248 AddRef();
249
250 return S_OK;
251 }
252
253 /* IMFMediaBuffer interface */
254 STDMETHODIMP
Lock(BYTE ** buffer,DWORD * max_length,DWORD * current_length)255 IGstMFVideoBuffer::Lock (BYTE ** buffer, DWORD * max_length,
256 DWORD * current_length)
257 {
258 std::lock_guard<std::mutex> lock(lock_);
259
260 GST_TRACE ("%p", this);
261
262 if (locked_) {
263 GST_LOG ("%p, Already locked", this);
264 return S_OK;
265 }
266
267 locked_ = true;
268
269 if (contiguous_) {
270 *buffer = data_;
271 goto done;
272 }
273
274 /* IMFMediaBuffer::Lock method should return contiguous memory */
275 if (!contiguous_data_)
276 contiguous_data_ = (BYTE *) g_malloc0 (contiguous_len_);
277
278 ContiguousCopyToUnlocked (contiguous_data_, contiguous_len_);
279 *buffer = contiguous_data_;
280
281 done:
282 if (max_length)
283 *max_length = contiguous_len_;
284 if (current_length)
285 *current_length = current_len_;
286
287 return S_OK;
288 }
289
290 STDMETHODIMP
Unlock(void)291 IGstMFVideoBuffer::Unlock (void)
292 {
293 std::lock_guard<std::mutex> lock(lock_);
294
295 GST_TRACE ("%p", this);
296
297 if (!locked_) {
298 GST_LOG ("%p, No previous Lock call", this);
299 return S_OK;
300 }
301
302 locked_ = false;
303
304 if (contiguous_) {
305 GST_TRACE ("%p, Have configured contiguous data", this);
306 return S_OK;
307 }
308
309 /* copy back to original data */
310 ContiguousCopyFromUnlocked (contiguous_data_, contiguous_len_);
311
312 return S_OK;
313 }
314
315 STDMETHODIMP
GetCurrentLength(DWORD * length)316 IGstMFVideoBuffer::GetCurrentLength (DWORD * length)
317 {
318 std::lock_guard<std::mutex> lock(lock_);
319
320 *length = current_len_;
321
322 GST_TRACE ("%p, %d", this, current_len_);
323
324 return S_OK;
325 }
326
327 STDMETHODIMP
SetCurrentLength(DWORD length)328 IGstMFVideoBuffer::SetCurrentLength (DWORD length)
329 {
330 std::lock_guard<std::mutex> lock(lock_);
331
332 GST_TRACE ("%p %d", this, length);
333
334 if (length > contiguous_len_) {
335 GST_LOG ("%p, Requested length %d is larger than contiguous_len %d",
336 this, length, contiguous_len_);
337 return E_INVALIDARG;
338 }
339
340 current_len_ = length;
341
342 return S_OK;
343 }
344
345 STDMETHODIMP
GetMaxLength(DWORD * length)346 IGstMFVideoBuffer::GetMaxLength (DWORD * length)
347 {
348 std::lock_guard<std::mutex> lock(lock_);
349
350 GST_TRACE ("%p", this);
351
352 *length = contiguous_len_;
353
354 return S_OK;
355 }
356
357 /* IMF2DBuffer */
358 STDMETHODIMP
Lock2D(BYTE ** buffer,LONG * pitch)359 IGstMFVideoBuffer::Lock2D (BYTE ** buffer, LONG * pitch)
360 {
361 std::lock_guard<std::mutex> lock(lock_);
362
363 GST_TRACE ("%p", this);
364
365 if (locked_) {
366 GST_LOG ("%p, Already locked", this);
367 return MF_E_INVALIDREQUEST;
368 }
369
370 locked_ = true;
371
372 *buffer = data_;
373 *pitch = GST_VIDEO_INFO_PLANE_STRIDE (info_, 0);
374
375 return S_OK;
376 }
377
378 STDMETHODIMP
Unlock2D(void)379 IGstMFVideoBuffer::Unlock2D (void)
380 {
381 std::lock_guard<std::mutex> lock(lock_);
382
383 GST_TRACE ("%p", this);
384
385 if (!locked_) {
386 GST_LOG ("%p, No previous Lock2D call", this);
387 return S_OK;
388 }
389
390 locked_ = false;
391
392 return S_OK;
393 }
394
395 STDMETHODIMP
GetScanline0AndPitch(BYTE ** buffer,LONG * pitch)396 IGstMFVideoBuffer::GetScanline0AndPitch (BYTE ** buffer, LONG * pitch)
397 {
398 std::lock_guard<std::mutex> lock(lock_);
399
400 GST_TRACE ("%p", this);
401
402 /* Lock2D must be called before */
403 if (!locked_) {
404 GST_LOG ("%p, Invalid call, Lock2D must be called before", this);
405 return ERROR_INVALID_FUNCTION;
406 }
407
408 *buffer = data_;
409 *pitch = GST_VIDEO_INFO_PLANE_STRIDE (info_, 0);
410
411 return S_OK;
412 }
413
414 STDMETHODIMP
IsContiguousFormat(BOOL * contiguous)415 IGstMFVideoBuffer::IsContiguousFormat (BOOL * contiguous)
416 {
417 std::lock_guard<std::mutex> lock(lock_);
418
419 GST_TRACE ("%p", this);
420
421 *contiguous = contiguous_;
422
423 return S_OK;
424 }
425
426 STDMETHODIMP
GetContiguousLength(DWORD * length)427 IGstMFVideoBuffer::GetContiguousLength (DWORD * length)
428 {
429 std::lock_guard<std::mutex> lock(lock_);
430
431 GST_TRACE ("%p", this);
432
433 *length = contiguous_len_;
434
435 return S_OK;
436 }
437
438 STDMETHODIMP
ContiguousCopyTo(BYTE * dest_buffer,DWORD dest_buffer_length)439 IGstMFVideoBuffer::ContiguousCopyTo (BYTE * dest_buffer,
440 DWORD dest_buffer_length)
441 {
442 std::lock_guard<std::mutex> lock(lock_);
443
444 return ContiguousCopyToUnlocked (dest_buffer, dest_buffer_length);
445 }
446
447 STDMETHODIMP
ContiguousCopyFrom(const BYTE * src_buffer,DWORD src_buffer_length)448 IGstMFVideoBuffer::ContiguousCopyFrom (const BYTE * src_buffer,
449 DWORD src_buffer_length)
450 {
451 std::lock_guard<std::mutex> lock(lock_);
452
453 GST_TRACE ("%p", this);
454
455 return ContiguousCopyFromUnlocked (src_buffer, src_buffer_length);
456 }
457
458 HRESULT
ContiguousCopyToUnlocked(BYTE * dest_buffer,DWORD dest_buffer_length)459 IGstMFVideoBuffer::ContiguousCopyToUnlocked (BYTE * dest_buffer,
460 DWORD dest_buffer_length)
461 {
462 GST_TRACE ("%p", this);
463
464 if (!dest_buffer || dest_buffer_length < contiguous_len_)
465 return E_INVALIDARG;
466
467 if (contiguous_) {
468 memcpy (dest_buffer, data_, current_len_);
469 return S_OK;
470 }
471
472 for (gint i = 0; i < GST_VIDEO_INFO_N_PLANES (info_); i++) {
473 BYTE *src, *dst;
474 guint src_stride, dst_stride;
475 guint width, height;
476
477 src = data_ + GST_VIDEO_INFO_PLANE_OFFSET (info_, i);
478 dst = dest_buffer + GST_VIDEO_INFO_PLANE_OFFSET (contiguous_info_, i);
479
480 src_stride = GST_VIDEO_INFO_PLANE_STRIDE (info_, i);
481 dst_stride = GST_VIDEO_INFO_PLANE_STRIDE (contiguous_info_, i);
482
483 width = GST_VIDEO_INFO_COMP_WIDTH (info_, i)
484 * GST_VIDEO_INFO_COMP_PSTRIDE (info_, i);
485 height = GST_VIDEO_INFO_COMP_HEIGHT (info_, i);
486
487 for (gint j = 0; j < height; j++) {
488 memcpy (dst, src, width);
489 src += src_stride;
490 dst += dst_stride;
491 }
492 }
493
494 return S_OK;
495 }
496
497 HRESULT
ContiguousCopyFromUnlocked(const BYTE * src_buffer,DWORD src_buffer_length)498 IGstMFVideoBuffer::ContiguousCopyFromUnlocked (const BYTE * src_buffer,
499 DWORD src_buffer_length)
500 {
501 gint offset;
502
503 GST_TRACE ("%p", this);
504
505 if (!src_buffer)
506 return E_INVALIDARG;
507
508 /* Nothing to copy */
509 if (src_buffer_length == 0)
510 return S_OK;
511
512 if (contiguous_) {
513 memcpy (data_, src_buffer, src_buffer_length);
514 return S_OK;
515 }
516
517 for (gint i = 0; i < GST_VIDEO_INFO_N_PLANES (info_); i++) {
518 BYTE *dst;
519 guint src_stride, dst_stride;
520 guint width, height;
521
522 offset = GST_VIDEO_INFO_PLANE_OFFSET (contiguous_info_, i);
523
524 dst = data_ + GST_VIDEO_INFO_PLANE_OFFSET (info_, i);
525
526 src_stride = GST_VIDEO_INFO_PLANE_STRIDE (contiguous_info_, i);
527 dst_stride = GST_VIDEO_INFO_PLANE_STRIDE (info_, i);
528
529 width = GST_VIDEO_INFO_COMP_WIDTH (info_, i)
530 * GST_VIDEO_INFO_COMP_PSTRIDE (info_, i);
531 height = GST_VIDEO_INFO_COMP_HEIGHT (info_, i);
532
533 for (gint j = 0; j < height; j++) {
534 gint to_copy = 0;
535
536 if (offset + width < src_buffer_length)
537 to_copy = width;
538 else
539 to_copy = (gint) src_buffer_length - offset;
540
541 if (to_copy <= 0)
542 return S_OK;
543
544 memcpy (dst, src_buffer + offset, to_copy);
545
546 offset += src_stride;
547 dst += dst_stride;
548 }
549 }
550
551 return S_OK;
552 }
553
554 /* *INDENT-ON* */
555