winegstreamer: Create wg_sample from IMFSample within wg_transform_push_mf.
[wine.git] / dlls / winegstreamer / video_processor.c
blobfcbccb3c3efa67a13f5c0d0443bca527583adf62
1 /* Copyright 2022 RĂ©mi Bernon for CodeWeavers
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2.1 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 #include "gst_private.h"
20 #include "mfapi.h"
21 #include "mferror.h"
22 #include "mfobjects.h"
23 #include "mftransform.h"
24 #include "wmcodecdsp.h"
26 #include "wine/debug.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(mfplat);
29 WINE_DECLARE_DEBUG_CHANNEL(winediag);
31 static const GUID *const input_types[] =
33 &MFVideoFormat_IYUV,
34 &MFVideoFormat_YV12,
35 &MFVideoFormat_NV12,
36 &MFVideoFormat_420O,
37 &MFVideoFormat_UYVY,
38 &MFVideoFormat_YUY2,
39 &MEDIASUBTYPE_P208,
40 &MFVideoFormat_NV11,
41 &MFVideoFormat_AYUV,
42 &MFVideoFormat_ARGB32,
43 &MFVideoFormat_RGB32,
44 &MFVideoFormat_RGB24,
45 &MFVideoFormat_I420,
46 &MFVideoFormat_YVYU,
47 &MFVideoFormat_RGB555,
48 &MFVideoFormat_RGB565,
49 &MFVideoFormat_RGB8,
50 &MFVideoFormat_Y216,
51 &MFVideoFormat_v410,
52 &MFVideoFormat_Y41P,
53 &MFVideoFormat_Y41T,
54 &MFVideoFormat_Y42T,
56 static const GUID *const output_types[] =
58 &MFVideoFormat_YUY2,
59 &MFVideoFormat_IYUV,
60 &MFVideoFormat_I420,
61 &MFVideoFormat_NV12,
62 &MFVideoFormat_RGB24,
63 &MFVideoFormat_ARGB32,
64 &MFVideoFormat_RGB32,
65 &MFVideoFormat_YV12,
66 &MFVideoFormat_AYUV,
67 &MFVideoFormat_RGB555,
68 &MFVideoFormat_RGB565,
71 struct video_processor
73 IMFTransform IMFTransform_iface;
74 LONG refcount;
76 IMFMediaType *input_type;
77 IMFMediaType *output_type;
78 IMFAttributes *attributes;
79 IMFAttributes *output_attributes;
81 struct wg_transform *wg_transform;
82 struct wg_sample_queue *wg_sample_queue;
85 static HRESULT try_create_wg_transform(struct video_processor *impl)
87 struct wg_format input_format, output_format;
89 if (impl->wg_transform)
90 wg_transform_destroy(impl->wg_transform);
91 impl->wg_transform = NULL;
93 mf_media_type_to_wg_format(impl->input_type, &input_format);
94 if (input_format.major_type == WG_MAJOR_TYPE_UNKNOWN)
95 return MF_E_INVALIDMEDIATYPE;
97 mf_media_type_to_wg_format(impl->output_type, &output_format);
98 if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN)
99 return MF_E_INVALIDMEDIATYPE;
101 if (!(impl->wg_transform = wg_transform_create(&input_format, &output_format)))
102 return E_FAIL;
104 return S_OK;
107 static struct video_processor *impl_from_IMFTransform(IMFTransform *iface)
109 return CONTAINING_RECORD(iface, struct video_processor, IMFTransform_iface);
112 static HRESULT WINAPI video_processor_QueryInterface(IMFTransform *iface, REFIID iid, void **out)
114 struct video_processor *impl = impl_from_IMFTransform(iface);
116 TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
118 if (IsEqualGUID(iid, &IID_IMFTransform))
119 *out = &impl->IMFTransform_iface;
120 else
122 *out = NULL;
123 WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid));
124 return E_NOINTERFACE;
127 IUnknown_AddRef((IUnknown *)*out);
128 return S_OK;
131 static ULONG WINAPI video_processor_AddRef(IMFTransform *iface)
133 struct video_processor *impl = impl_from_IMFTransform(iface);
134 ULONG refcount = InterlockedIncrement(&impl->refcount);
136 TRACE("iface %p increasing refcount to %lu.\n", iface, refcount);
138 return refcount;
141 static ULONG WINAPI video_processor_Release(IMFTransform *iface)
143 struct video_processor *impl = impl_from_IMFTransform(iface);
144 ULONG refcount = InterlockedDecrement(&impl->refcount);
146 TRACE("iface %p decreasing refcount to %lu.\n", iface, refcount);
148 if (!refcount)
150 if (impl->wg_transform)
151 wg_transform_destroy(impl->wg_transform);
152 if (impl->input_type)
153 IMFMediaType_Release(impl->input_type);
154 if (impl->output_type)
155 IMFMediaType_Release(impl->output_type);
156 if (impl->attributes)
157 IMFAttributes_Release(impl->attributes);
158 if (impl->output_attributes)
159 IMFAttributes_Release(impl->output_attributes);
161 wg_sample_queue_destroy(impl->wg_sample_queue);
162 free(impl);
165 return refcount;
168 static HRESULT WINAPI video_processor_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum,
169 DWORD *input_maximum, DWORD *output_minimum, DWORD *output_maximum)
171 TRACE("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p.\n",
172 iface, input_minimum, input_maximum, output_minimum, output_maximum);
173 *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1;
174 return S_OK;
177 static HRESULT WINAPI video_processor_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs)
179 TRACE("iface %p, inputs %p, outputs %p.\n", iface, inputs, outputs);
180 *inputs = *outputs = 1;
181 return S_OK;
184 static HRESULT WINAPI video_processor_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs,
185 DWORD output_size, DWORD *outputs)
187 FIXME("iface %p, input_size %lu, inputs %p, output_size %lu, outputs %p stub!\n", iface,
188 input_size, inputs, output_size, outputs);
189 return E_NOTIMPL;
192 static HRESULT WINAPI video_processor_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info)
194 struct video_processor *impl = impl_from_IMFTransform(iface);
195 UINT32 sample_size;
196 UINT64 framesize;
197 GUID subtype;
198 HRESULT hr;
200 TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
202 if (id)
203 return MF_E_INVALIDSTREAMNUMBER;
205 if (impl->input_type && SUCCEEDED(hr = IMFMediaType_GetGUID(impl->input_type, &MF_MT_SUBTYPE, &subtype))
206 && SUCCEEDED(hr = IMFMediaType_GetUINT64(impl->input_type, &MF_MT_FRAME_SIZE, &framesize)))
207 MFCalculateImageSize(&subtype, framesize >> 32, (UINT32)framesize, &sample_size);
208 else
209 sample_size = 0;
211 info->dwFlags = 0;
212 info->cbSize = sample_size;
213 info->cbAlignment = 0;
214 info->hnsMaxLatency = 0;
215 info->cbMaxLookahead = 0;
217 return S_OK;
220 static HRESULT WINAPI video_processor_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info)
222 struct video_processor *impl = impl_from_IMFTransform(iface);
223 UINT32 sample_size;
224 UINT64 framesize;
225 GUID subtype;
226 HRESULT hr;
228 TRACE("iface %p, id %#lx, info %p.\n", iface, id, info);
230 if (id)
231 return MF_E_INVALIDSTREAMNUMBER;
233 if (impl->output_type && SUCCEEDED(hr = IMFMediaType_GetGUID(impl->output_type, &MF_MT_SUBTYPE, &subtype))
234 && SUCCEEDED(hr = IMFMediaType_GetUINT64(impl->output_type, &MF_MT_FRAME_SIZE, &framesize)))
235 MFCalculateImageSize(&subtype, framesize >> 32, (UINT32)framesize, &sample_size);
236 else
237 sample_size = 0;
239 info->dwFlags = 0;
240 info->cbSize = sample_size;
241 info->cbAlignment = 0;
243 return S_OK;
246 static HRESULT WINAPI video_processor_GetAttributes(IMFTransform *iface, IMFAttributes **attributes)
248 struct video_processor *impl = impl_from_IMFTransform(iface);
250 FIXME("iface %p, attributes %p stub!\n", iface, attributes);
252 IMFAttributes_AddRef((*attributes = impl->attributes));
253 return S_OK;
256 static HRESULT WINAPI video_processor_GetInputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes)
258 FIXME("iface %p, id %#lx, attributes %p stub!\n", iface, id, attributes);
259 return E_NOTIMPL;
262 static HRESULT WINAPI video_processor_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, IMFAttributes **attributes)
264 struct video_processor *impl = impl_from_IMFTransform(iface);
266 FIXME("iface %p, id %#lx, attributes %p stub!\n", iface, id, attributes);
268 IMFAttributes_AddRef((*attributes = impl->output_attributes));
269 return S_OK;
272 static HRESULT WINAPI video_processor_DeleteInputStream(IMFTransform *iface, DWORD id)
274 FIXME("iface %p, id %#lx stub!\n", iface, id);
275 return E_NOTIMPL;
278 static HRESULT WINAPI video_processor_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids)
280 FIXME("iface %p, streams %lu, ids %p stub!\n", iface, streams, ids);
281 return E_NOTIMPL;
284 static HRESULT WINAPI video_processor_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
285 IMFMediaType **type)
287 IMFMediaType *media_type;
288 const GUID *subtype;
289 HRESULT hr;
291 TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
293 *type = NULL;
295 if (index >= ARRAY_SIZE(input_types))
296 return MF_E_NO_MORE_TYPES;
297 subtype = input_types[index];
299 if (FAILED(hr = MFCreateMediaType(&media_type)))
300 return hr;
302 if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)))
303 goto done;
304 if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype)))
305 goto done;
307 IMFMediaType_AddRef((*type = media_type));
309 done:
310 IMFMediaType_Release(media_type);
311 return hr;
314 static HRESULT WINAPI video_processor_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index,
315 IMFMediaType **type)
317 struct video_processor *impl = impl_from_IMFTransform(iface);
318 IMFMediaType *media_type;
319 UINT64 frame_size;
320 GUID subtype;
321 HRESULT hr;
323 TRACE("iface %p, id %#lx, index %#lx, type %p.\n", iface, id, index, type);
325 *type = NULL;
327 if (!impl->input_type)
328 return MF_E_NO_MORE_TYPES;
330 if (FAILED(hr = IMFMediaType_GetGUID(impl->input_type, &MF_MT_SUBTYPE, &subtype))
331 || FAILED(hr = IMFMediaType_GetUINT64(impl->input_type, &MF_MT_FRAME_SIZE, &frame_size)))
332 return hr;
334 if (index > ARRAY_SIZE(output_types))
335 return MF_E_NO_MORE_TYPES;
336 if (index > 0)
337 subtype = *output_types[index - 1];
339 if (FAILED(hr = MFCreateMediaType(&media_type)))
340 return hr;
342 if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)))
343 goto done;
344 if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &subtype)))
345 goto done;
346 if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, frame_size)))
347 goto done;
349 IMFMediaType_AddRef((*type = media_type));
351 done:
352 IMFMediaType_Release(media_type);
353 return hr;
356 static HRESULT WINAPI video_processor_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
358 struct video_processor *impl = impl_from_IMFTransform(iface);
359 GUID major, subtype;
360 UINT64 frame_size;
361 HRESULT hr;
362 ULONG i;
364 TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
366 if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major))
367 || !IsEqualGUID(&major, &MFMediaType_Video))
368 return E_INVALIDARG;
369 if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
370 return MF_E_INVALIDMEDIATYPE;
371 if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
372 return hr;
374 for (i = 0; i < ARRAY_SIZE(input_types); ++i)
375 if (IsEqualGUID(&subtype, input_types[i]))
376 break;
377 if (i == ARRAY_SIZE(input_types))
378 return MF_E_INVALIDMEDIATYPE;
379 if (flags & MFT_SET_TYPE_TEST_ONLY)
380 return S_OK;
382 if (impl->input_type)
383 IMFMediaType_Release(impl->input_type);
384 IMFMediaType_AddRef((impl->input_type = type));
386 if (impl->output_type && FAILED(hr = try_create_wg_transform(impl)))
388 IMFMediaType_Release(impl->input_type);
389 impl->input_type = NULL;
392 return hr;
395 static HRESULT WINAPI video_processor_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags)
397 struct video_processor *impl = impl_from_IMFTransform(iface);
398 GUID major, subtype;
399 UINT64 frame_size;
400 HRESULT hr;
401 ULONG i;
403 TRACE("iface %p, id %#lx, type %p, flags %#lx.\n", iface, id, type, flags);
405 if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major))
406 || !IsEqualGUID(&major, &MFMediaType_Video))
407 return E_INVALIDARG;
408 if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype)))
409 return MF_E_INVALIDMEDIATYPE;
410 if (FAILED(hr = IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size)))
411 return hr;
413 for (i = 0; i < ARRAY_SIZE(output_types); ++i)
414 if (IsEqualGUID(&subtype, output_types[i]))
415 break;
416 if (i == ARRAY_SIZE(output_types))
417 return MF_E_INVALIDMEDIATYPE;
418 if (flags & MFT_SET_TYPE_TEST_ONLY)
419 return S_OK;
421 if (impl->output_type)
422 IMFMediaType_Release(impl->output_type);
423 IMFMediaType_AddRef((impl->output_type = type));
425 if (impl->input_type && FAILED(hr = try_create_wg_transform(impl)))
427 IMFMediaType_Release(impl->output_type);
428 impl->output_type = NULL;
431 return hr;
434 static HRESULT WINAPI video_processor_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
436 struct video_processor *impl = impl_from_IMFTransform(iface);
437 HRESULT hr;
439 TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
441 if (id != 0)
442 return MF_E_INVALIDSTREAMNUMBER;
444 if (!impl->input_type)
445 return MF_E_TRANSFORM_TYPE_NOT_SET;
447 if (FAILED(hr = MFCreateMediaType(type)))
448 return hr;
450 if (FAILED(hr = IMFMediaType_CopyAllItems(impl->input_type, (IMFAttributes *)*type)))
451 IMFMediaType_Release(*type);
453 return hr;
456 static HRESULT WINAPI video_processor_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type)
458 struct video_processor *impl = impl_from_IMFTransform(iface);
459 HRESULT hr;
461 TRACE("iface %p, id %#lx, type %p.\n", iface, id, type);
463 if (id != 0)
464 return MF_E_INVALIDSTREAMNUMBER;
466 if (!impl->output_type)
467 return MF_E_TRANSFORM_TYPE_NOT_SET;
469 if (FAILED(hr = MFCreateMediaType(type)))
470 return hr;
472 if (FAILED(hr = IMFMediaType_CopyAllItems(impl->output_type, (IMFAttributes *)*type)))
473 IMFMediaType_Release(*type);
475 return hr;
478 static HRESULT WINAPI video_processor_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags)
480 struct video_processor *impl = impl_from_IMFTransform(iface);
482 FIXME("iface %p, id %#lx, flags %p stub!\n", iface, id, flags);
484 if (!impl->input_type)
485 return MF_E_TRANSFORM_TYPE_NOT_SET;
487 *flags = MFT_INPUT_STATUS_ACCEPT_DATA;
488 return S_OK;
491 static HRESULT WINAPI video_processor_GetOutputStatus(IMFTransform *iface, DWORD *flags)
493 struct video_processor *impl = impl_from_IMFTransform(iface);
495 FIXME("iface %p, flags %p stub!\n", iface, flags);
497 if (!impl->output_type)
498 return MF_E_TRANSFORM_TYPE_NOT_SET;
500 return E_NOTIMPL;
503 static HRESULT WINAPI video_processor_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper)
505 FIXME("iface %p, lower %I64d, upper %I64d stub!\n", iface, lower, upper);
506 return E_NOTIMPL;
509 static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event)
511 FIXME("iface %p, id %#lx, event %p stub!\n", iface, id, event);
512 return E_NOTIMPL;
515 static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param)
517 FIXME("iface %p, message %#x, param %#Ix stub!\n", iface, message, param);
518 return S_OK;
521 static HRESULT WINAPI video_processor_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags)
523 struct video_processor *impl = impl_from_IMFTransform(iface);
525 TRACE("iface %p, id %#lx, sample %p, flags %#lx.\n", iface, id, sample, flags);
527 if (!impl->wg_transform)
528 return MF_E_TRANSFORM_TYPE_NOT_SET;
530 return wg_transform_push_mf(impl->wg_transform, sample, impl->wg_sample_queue);
533 static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count,
534 MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status)
536 struct video_processor *impl = impl_from_IMFTransform(iface);
537 MFT_OUTPUT_STREAM_INFO info;
538 struct wg_sample *wg_sample;
539 HRESULT hr;
541 TRACE("iface %p, flags %#lx, count %lu, samples %p, status %p.\n", iface, flags, count, samples, status);
543 if (count != 1)
544 return E_INVALIDARG;
546 if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info)))
547 return hr;
549 if (!impl->wg_transform)
550 return MF_E_TRANSFORM_TYPE_NOT_SET;
552 samples[0].dwStatus = 0;
553 if (!samples[0].pSample) return E_INVALIDARG;
555 if (FAILED(hr = wg_sample_create_mf(samples[0].pSample, &wg_sample)))
556 return hr;
558 if (wg_sample->max_size < info.cbSize)
560 wg_sample_release(wg_sample);
561 return MF_E_BUFFERTOOSMALL;
564 if (SUCCEEDED(hr = wg_transform_read_mf(impl->wg_transform, wg_sample, NULL,
565 &samples[0].dwStatus)))
566 wg_sample_queue_flush(impl->wg_sample_queue, false);
568 wg_sample_release(wg_sample);
570 return hr;
573 static const IMFTransformVtbl video_processor_vtbl =
575 video_processor_QueryInterface,
576 video_processor_AddRef,
577 video_processor_Release,
578 video_processor_GetStreamLimits,
579 video_processor_GetStreamCount,
580 video_processor_GetStreamIDs,
581 video_processor_GetInputStreamInfo,
582 video_processor_GetOutputStreamInfo,
583 video_processor_GetAttributes,
584 video_processor_GetInputStreamAttributes,
585 video_processor_GetOutputStreamAttributes,
586 video_processor_DeleteInputStream,
587 video_processor_AddInputStreams,
588 video_processor_GetInputAvailableType,
589 video_processor_GetOutputAvailableType,
590 video_processor_SetInputType,
591 video_processor_SetOutputType,
592 video_processor_GetInputCurrentType,
593 video_processor_GetOutputCurrentType,
594 video_processor_GetInputStatus,
595 video_processor_GetOutputStatus,
596 video_processor_SetOutputBounds,
597 video_processor_ProcessEvent,
598 video_processor_ProcessMessage,
599 video_processor_ProcessInput,
600 video_processor_ProcessOutput,
603 HRESULT video_processor_create(REFIID riid, void **ret)
605 static const struct wg_format input_format =
607 .major_type = WG_MAJOR_TYPE_VIDEO,
608 .u.video =
610 .format = WG_VIDEO_FORMAT_I420,
611 .width = 1920,
612 .height = 1080,
615 static const struct wg_format output_format =
617 .major_type = WG_MAJOR_TYPE_VIDEO,
618 .u.video =
620 .format = WG_VIDEO_FORMAT_NV12,
621 .width = 1920,
622 .height = 1080,
625 struct wg_transform *transform;
626 struct video_processor *impl;
627 HRESULT hr;
629 TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret);
631 if (!(transform = wg_transform_create(&input_format, &output_format)))
633 ERR_(winediag)("GStreamer doesn't support video conversion, please install appropriate plugins.\n");
634 return E_FAIL;
636 wg_transform_destroy(transform);
638 if (!(impl = calloc(1, sizeof(*impl))))
639 return E_OUTOFMEMORY;
641 if (FAILED(hr = MFCreateAttributes(&impl->attributes, 0)))
642 goto failed;
643 if (FAILED(hr = MFCreateAttributes(&impl->output_attributes, 0)))
644 goto failed;
645 if (FAILED(hr = wg_sample_queue_create(&impl->wg_sample_queue)))
646 goto failed;
648 impl->IMFTransform_iface.lpVtbl = &video_processor_vtbl;
649 impl->refcount = 1;
651 *ret = &impl->IMFTransform_iface;
652 TRACE("Created %p\n", *ret);
653 return S_OK;
655 failed:
656 if (impl->output_attributes)
657 IMFAttributes_Release(impl->output_attributes);
658 if (impl->attributes)
659 IMFAttributes_Release(impl->attributes);
660 free(impl);
661 return hr;