2 * Copyright 2022 RĂ©mi Bernon for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
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 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "gst_private.h"
21 #include "wmcodecdsp.h"
24 #include "wine/debug.h"
25 #include "wine/list.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(mfplat
);
28 WINE_DECLARE_DEBUG_CHANNEL(quartz
);
30 struct wg_sample_queue
38 void (*destroy
)(struct wg_sample
*sample
);
43 struct wg_sample wg_sample
;
45 const struct wg_sample_ops
*ops
;
53 IMFMediaBuffer
*buffer
;
62 static const struct wg_sample_ops mf_sample_ops
;
64 static inline struct sample
*unsafe_mf_from_wg_sample(struct wg_sample
*wg_sample
)
66 struct sample
*sample
= CONTAINING_RECORD(wg_sample
, struct sample
, wg_sample
);
67 if (sample
->ops
!= &mf_sample_ops
) return NULL
;
71 static void mf_sample_destroy(struct wg_sample
*wg_sample
)
73 struct sample
*sample
= unsafe_mf_from_wg_sample(wg_sample
);
75 TRACE_(mfplat
)("wg_sample %p.\n", wg_sample
);
77 IMFMediaBuffer_Unlock(sample
->u
.mf
.buffer
);
78 IMFMediaBuffer_Release(sample
->u
.mf
.buffer
);
79 IMFSample_Release(sample
->u
.mf
.sample
);
82 static const struct wg_sample_ops mf_sample_ops
=
87 HRESULT
wg_sample_create_mf(IMFSample
*mf_sample
, struct wg_sample
**out
)
89 DWORD current_length
, max_length
;
90 struct sample
*sample
;
94 if (!(sample
= calloc(1, sizeof(*sample
))))
96 if (FAILED(hr
= IMFSample_ConvertToContiguousBuffer(mf_sample
, &sample
->u
.mf
.buffer
)))
98 if (FAILED(hr
= IMFMediaBuffer_Lock(sample
->u
.mf
.buffer
, &buffer
, &max_length
, ¤t_length
)))
101 IMFSample_AddRef((sample
->u
.mf
.sample
= mf_sample
));
102 sample
->wg_sample
.data
= buffer
;
103 sample
->wg_sample
.size
= current_length
;
104 sample
->wg_sample
.max_size
= max_length
;
105 sample
->ops
= &mf_sample_ops
;
107 *out
= &sample
->wg_sample
;
108 TRACE_(mfplat
)("Created wg_sample %p for IMFSample %p.\n", *out
, mf_sample
);
112 if (sample
->u
.mf
.buffer
)
113 IMFMediaBuffer_Release(sample
->u
.mf
.buffer
);
118 static const struct wg_sample_ops quartz_sample_ops
;
120 static inline struct sample
*unsafe_quartz_from_wg_sample(struct wg_sample
*wg_sample
)
122 struct sample
*sample
= CONTAINING_RECORD(wg_sample
, struct sample
, wg_sample
);
123 if (sample
->ops
!= &quartz_sample_ops
) return NULL
;
127 static void quartz_sample_destroy(struct wg_sample
*wg_sample
)
129 struct sample
*sample
= unsafe_quartz_from_wg_sample(wg_sample
);
131 TRACE_(quartz
)("wg_sample %p.\n", wg_sample
);
133 IMediaSample_Release(sample
->u
.quartz
.sample
);
136 static const struct wg_sample_ops quartz_sample_ops
=
138 quartz_sample_destroy
,
141 HRESULT
wg_sample_create_quartz(IMediaSample
*media_sample
, struct wg_sample
**out
)
143 DWORD current_length
, max_length
;
144 struct sample
*sample
;
148 if (FAILED(hr
= IMediaSample_GetPointer(media_sample
, &buffer
)))
150 current_length
= IMediaSample_GetActualDataLength(media_sample
);
151 max_length
= IMediaSample_GetSize(media_sample
);
153 if (!(sample
= calloc(1, sizeof(*sample
))))
154 return E_OUTOFMEMORY
;
156 IMediaSample_AddRef((sample
->u
.quartz
.sample
= media_sample
));
157 sample
->wg_sample
.data
= buffer
;
158 sample
->wg_sample
.size
= current_length
;
159 sample
->wg_sample
.max_size
= max_length
;
160 sample
->ops
= &quartz_sample_ops
;
162 TRACE_(quartz
)("Created wg_sample %p for IMediaSample %p.\n", &sample
->wg_sample
, media_sample
);
163 *out
= &sample
->wg_sample
;
167 void wg_sample_release(struct wg_sample
*wg_sample
)
169 struct sample
*sample
= CONTAINING_RECORD(wg_sample
, struct sample
, wg_sample
);
171 if (InterlockedOr(&wg_sample
->refcount
, 0))
173 ERR("wg_sample %p is still in use, trouble ahead!\n", wg_sample
);
177 sample
->ops
->destroy(wg_sample
);
182 static void wg_sample_queue_begin_append(struct wg_sample_queue
*queue
, struct wg_sample
*wg_sample
)
184 struct sample
*sample
= CONTAINING_RECORD(wg_sample
, struct sample
, wg_sample
);
186 /* make sure a concurrent wg_sample_queue_flush call won't release the sample until we're done */
187 InterlockedIncrement(&wg_sample
->refcount
);
188 sample
->wg_sample
.flags
|= WG_SAMPLE_FLAG_HAS_REFCOUNT
;
190 EnterCriticalSection(&queue
->cs
);
191 list_add_tail(&queue
->samples
, &sample
->entry
);
192 LeaveCriticalSection(&queue
->cs
);
195 static void wg_sample_queue_end_append(struct wg_sample_queue
*queue
, struct wg_sample
*wg_sample
)
197 /* release temporary ref taken in wg_sample_queue_begin_append */
198 InterlockedDecrement(&wg_sample
->refcount
);
200 wg_sample_queue_flush(queue
, false);
203 void wg_sample_queue_flush(struct wg_sample_queue
*queue
, bool all
)
205 struct sample
*sample
, *next
;
207 EnterCriticalSection(&queue
->cs
);
209 LIST_FOR_EACH_ENTRY_SAFE(sample
, next
, &queue
->samples
, struct sample
, entry
)
211 if (!InterlockedOr(&sample
->wg_sample
.refcount
, 0) || all
)
213 list_remove(&sample
->entry
);
214 wg_sample_release(&sample
->wg_sample
);
218 LeaveCriticalSection(&queue
->cs
);
221 HRESULT
wg_sample_queue_create(struct wg_sample_queue
**out
)
223 struct wg_sample_queue
*queue
;
225 if (!(queue
= calloc(1, sizeof(*queue
))))
226 return E_OUTOFMEMORY
;
228 InitializeCriticalSection(&queue
->cs
);
229 queue
->cs
.DebugInfo
->Spare
[0] = (DWORD_PTR
)(__FILE__
": cs");
230 list_init(&queue
->samples
);
232 TRACE("Created wg_sample_queue %p.\n", queue
);
238 void wg_sample_queue_destroy(struct wg_sample_queue
*queue
)
240 wg_sample_queue_flush(queue
, true);
242 queue
->cs
.DebugInfo
->Spare
[0] = 0;
243 InitializeCriticalSection(&queue
->cs
);
248 HRESULT
wg_transform_push_mf(struct wg_transform
*transform
, struct wg_sample
*wg_sample
,
249 struct wg_sample_queue
*queue
)
251 struct sample
*sample
= unsafe_mf_from_wg_sample(wg_sample
);
252 LONGLONG time
, duration
;
256 TRACE_(mfplat
)("transform %p, wg_sample %p, queue %p.\n", transform
, wg_sample
, queue
);
258 if (SUCCEEDED(IMFSample_GetSampleTime(sample
->u
.mf
.sample
, &time
)))
260 sample
->wg_sample
.flags
|= WG_SAMPLE_FLAG_HAS_PTS
;
261 sample
->wg_sample
.pts
= time
;
263 if (SUCCEEDED(IMFSample_GetSampleDuration(sample
->u
.mf
.sample
, &duration
)))
265 sample
->wg_sample
.flags
|= WG_SAMPLE_FLAG_HAS_DURATION
;
266 sample
->wg_sample
.duration
= duration
;
268 if (SUCCEEDED(IMFSample_GetUINT32(sample
->u
.mf
.sample
, &MFSampleExtension_CleanPoint
, &value
)) && value
)
269 sample
->wg_sample
.flags
|= WG_SAMPLE_FLAG_SYNC_POINT
;
271 wg_sample_queue_begin_append(queue
, wg_sample
);
272 hr
= wg_transform_push_data(transform
, wg_sample
);
273 wg_sample_queue_end_append(queue
, wg_sample
);
278 HRESULT
wg_transform_read_mf(struct wg_transform
*transform
, struct wg_sample
*wg_sample
,
279 struct wg_format
*format
)
281 struct sample
*sample
= unsafe_mf_from_wg_sample(wg_sample
);
284 TRACE_(mfplat
)("transform %p, wg_sample %p, format %p.\n", transform
, wg_sample
, format
);
286 if (FAILED(hr
= wg_transform_read_data(transform
, wg_sample
, format
)))
289 if (FAILED(hr
= IMFMediaBuffer_SetCurrentLength(sample
->u
.mf
.buffer
, wg_sample
->size
)))
292 if (wg_sample
->flags
& WG_SAMPLE_FLAG_HAS_PTS
)
293 IMFSample_SetSampleTime(sample
->u
.mf
.sample
, wg_sample
->pts
);
294 if (wg_sample
->flags
& WG_SAMPLE_FLAG_HAS_DURATION
)
295 IMFSample_SetSampleDuration(sample
->u
.mf
.sample
, wg_sample
->duration
);
296 if (wg_sample
->flags
& WG_SAMPLE_FLAG_SYNC_POINT
)
297 IMFSample_SetUINT32(sample
->u
.mf
.sample
, &MFSampleExtension_CleanPoint
, 1);