• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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