dllhost: Add ISurrogate stub implementation.
[wine.git] / dlls / qcap / avimux.c
blob2e8c2a96681f9023940188f90483e3e9e42d3830
1 /*
2 * Copyright (C) 2013 Piotr Caban 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 "qcap_private.h"
20 #include "vfw.h"
21 #include "aviriff.h"
23 WINE_DEFAULT_DEBUG_CHANNEL(quartz);
25 #define MAX_PIN_NO 128
26 #define AVISUPERINDEX_ENTRIES 2000
27 #define AVISTDINDEX_ENTRIES 4000
28 #define ALIGN(x) ((x+1)/2*2)
30 typedef struct {
31 struct strmbase_sink pin;
32 IAMStreamControl IAMStreamControl_iface;
33 IPropertyBag IPropertyBag_iface;
34 IQualityControl IQualityControl_iface;
36 REFERENCE_TIME avg_time_per_frame;
37 REFERENCE_TIME stop;
38 int stream_id;
39 LONGLONG stream_time;
41 /* strl chunk */
42 AVISTREAMHEADER strh;
43 struct {
44 FOURCC fcc;
45 DWORD cb;
46 BYTE data[1];
47 } *strf;
48 AVISUPERINDEX *indx;
49 BYTE indx_data[FIELD_OFFSET(AVISUPERINDEX, aIndex[AVISUPERINDEX_ENTRIES])];
51 /* movi chunk */
52 int ix_off;
53 AVISTDINDEX *ix;
54 BYTE ix_data[FIELD_OFFSET(AVISTDINDEX, aIndex[AVISTDINDEX_ENTRIES])];
56 IMediaSample *samples_head;
57 IMemAllocator *samples_allocator;
58 } AviMuxIn;
60 typedef struct {
61 struct strmbase_filter filter;
62 IConfigAviMux IConfigAviMux_iface;
63 IConfigInterleaving IConfigInterleaving_iface;
64 IMediaSeeking IMediaSeeking_iface;
65 IPersistMediaPropertyBag IPersistMediaPropertyBag_iface;
66 ISpecifyPropertyPages ISpecifyPropertyPages_iface;
68 InterleavingMode mode;
69 REFERENCE_TIME interleave;
70 REFERENCE_TIME preroll;
72 struct strmbase_source source;
73 IQualityControl IQualityControl_iface;
75 int input_pin_no;
76 AviMuxIn *in[MAX_PIN_NO-1];
78 REFERENCE_TIME start, stop;
79 AVIMAINHEADER avih;
81 int idx1_entries;
82 int idx1_size;
83 AVIINDEXENTRY *idx1;
85 int cur_stream;
86 LONGLONG cur_time;
88 int buf_pos;
89 BYTE buf[65536];
91 int movi_off;
92 int out_pos;
93 int size;
94 IStream *stream;
95 } AviMux;
97 static HRESULT create_input_pin(AviMux*);
99 static inline AviMux* impl_from_strmbase_filter(struct strmbase_filter *filter)
101 return CONTAINING_RECORD(filter, AviMux, filter);
104 static struct strmbase_pin *avi_mux_get_pin(struct strmbase_filter *iface, unsigned int index)
106 AviMux *filter = impl_from_strmbase_filter(iface);
108 if (!index)
109 return &filter->source.pin;
110 else if (index <= filter->input_pin_no)
111 return &filter->in[index - 1]->pin.pin;
112 return NULL;
115 static void avi_mux_destroy(struct strmbase_filter *iface)
117 AviMux *filter = impl_from_strmbase_filter(iface);
118 int i;
120 strmbase_source_cleanup(&filter->source);
122 for (i = 0; i < filter->input_pin_no; ++i)
124 IPin_Disconnect(&filter->in[i]->pin.pin.IPin_iface);
125 IMemAllocator_Release(filter->in[i]->samples_allocator);
126 filter->in[i]->samples_allocator = NULL;
127 strmbase_sink_cleanup(&filter->in[i]->pin);
128 free(filter->in[i]);
131 free(filter->idx1);
132 strmbase_filter_cleanup(&filter->filter);
133 free(filter);
136 static HRESULT avi_mux_query_interface(struct strmbase_filter *iface, REFIID iid, void **out)
138 AviMux *filter = impl_from_strmbase_filter(iface);
140 if (IsEqualGUID(iid, &IID_IConfigAviMux))
141 *out = &filter->IConfigAviMux_iface;
142 else if (IsEqualGUID(iid, &IID_IConfigInterleaving))
143 *out = &filter->IConfigInterleaving_iface;
144 else if (IsEqualGUID(iid, &IID_IMediaSeeking))
145 *out = &filter->IMediaSeeking_iface;
146 else if (IsEqualGUID(iid, &IID_IPersistMediaPropertyBag))
147 *out = &filter->IPersistMediaPropertyBag_iface;
148 else if (IsEqualGUID(iid, &IID_ISpecifyPropertyPages))
149 *out = &filter->ISpecifyPropertyPages_iface;
150 else
151 return E_NOINTERFACE;
153 IUnknown_AddRef((IUnknown *)*out);
154 return S_OK;
157 static HRESULT out_flush(AviMux *This)
159 ULONG written;
160 HRESULT hr;
162 if(!This->buf_pos)
163 return S_OK;
165 hr = IStream_Write(This->stream, This->buf, This->buf_pos, &written);
166 if(FAILED(hr))
167 return hr;
168 if (written != This->buf_pos)
169 return E_FAIL;
171 This->buf_pos = 0;
172 return S_OK;
175 static HRESULT out_seek(AviMux *This, int pos)
177 LARGE_INTEGER li;
178 HRESULT hr;
180 hr = out_flush(This);
181 if(FAILED(hr))
182 return hr;
184 li.QuadPart = pos;
185 hr = IStream_Seek(This->stream, li, STREAM_SEEK_SET, NULL);
186 if(FAILED(hr))
187 return hr;
189 This->out_pos = pos;
190 if(This->out_pos > This->size)
191 This->size = This->out_pos;
192 return hr;
195 static HRESULT out_write(AviMux *This, const void *data, int size)
197 int chunk_size;
198 HRESULT hr;
200 while(1) {
201 if (size > sizeof(This->buf) - This->buf_pos)
202 chunk_size = sizeof(This->buf) - This->buf_pos;
203 else
204 chunk_size = size;
206 memcpy(This->buf + This->buf_pos, data, chunk_size);
207 size -= chunk_size;
208 data = (const BYTE*)data + chunk_size;
209 This->buf_pos += chunk_size;
210 This->out_pos += chunk_size;
211 if (This->out_pos > This->size)
212 This->size = This->out_pos;
214 if(!size)
215 break;
216 hr = out_flush(This);
217 if(FAILED(hr))
218 return hr;
221 return S_OK;
224 static inline HRESULT idx1_add_entry(AviMux *avimux, DWORD ckid, DWORD flags, DWORD off, DWORD len)
226 if(avimux->idx1_entries == avimux->idx1_size) {
227 AVIINDEXENTRY *new_idx = realloc(avimux->idx1, sizeof(*avimux->idx1) * 2 * avimux->idx1_size);
228 if(!new_idx)
229 return E_OUTOFMEMORY;
231 avimux->idx1_size *= 2;
232 avimux->idx1 = new_idx;
235 avimux->idx1[avimux->idx1_entries].ckid = ckid;
236 avimux->idx1[avimux->idx1_entries].dwFlags = flags;
237 avimux->idx1[avimux->idx1_entries].dwChunkOffset = off;
238 avimux->idx1[avimux->idx1_entries].dwChunkLength = len;
239 avimux->idx1_entries++;
240 return S_OK;
243 static HRESULT flush_queue(AviMux *avimux, AviMuxIn *avimuxin, BOOL closing)
245 IMediaSample *sample, **prev, **head_prev;
246 BYTE *data;
247 RIFFCHUNK rf;
248 DWORD size;
249 DWORD flags;
250 HRESULT hr;
252 if (avimux->cur_stream != avimuxin->stream_id)
253 return S_OK;
255 while(avimuxin->samples_head) {
256 hr = IMediaSample_GetPointer(avimuxin->samples_head, (BYTE**)&head_prev);
257 if(FAILED(hr))
258 return hr;
259 head_prev--;
261 hr = IMediaSample_GetPointer(*head_prev, (BYTE**)&prev);
262 if(FAILED(hr))
263 return hr;
264 prev--;
266 sample = *head_prev;
267 size = IMediaSample_GetActualDataLength(sample);
268 hr = IMediaSample_GetPointer(sample, &data);
269 if(FAILED(hr))
270 return hr;
271 flags = IMediaSample_IsDiscontinuity(sample)==S_OK ? AM_SAMPLE_TIMEDISCONTINUITY : 0;
272 if(IMediaSample_IsSyncPoint(sample) == S_OK)
273 flags |= AM_SAMPLE_SPLICEPOINT;
275 if (avimuxin->stream_time + (closing ? 0 : avimuxin->strh.dwScale) > avimux->cur_time
276 && !(flags & AM_SAMPLE_TIMEDISCONTINUITY))
278 if(closing)
279 break;
281 avimux->cur_stream++;
282 if(avimux->cur_stream >= avimux->input_pin_no-1) {
283 avimux->cur_time += avimux->interleave;
284 avimux->cur_stream = 0;
286 avimuxin = avimux->in[avimux->cur_stream];
287 continue;
290 if(avimuxin->ix->nEntriesInUse == AVISTDINDEX_ENTRIES) {
291 /* TODO: use output pins Deliver/Receive method */
292 hr = out_seek(avimux, avimuxin->ix_off);
293 if(FAILED(hr))
294 return hr;
295 hr = out_write(avimux, avimuxin->ix, sizeof(avimuxin->ix_data));
296 if(FAILED(hr))
297 return hr;
299 avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].qwOffset = avimuxin->ix_off;
300 avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].dwSize = sizeof(avimuxin->ix_data);
301 avimuxin->indx->aIndex[avimuxin->indx->nEntriesInUse].dwDuration = AVISTDINDEX_ENTRIES;
302 avimuxin->indx->nEntriesInUse++;
304 memset(avimuxin->ix->aIndex, 0, sizeof(avimuxin->ix->aIndex)*avimuxin->ix->nEntriesInUse);
305 avimuxin->ix->nEntriesInUse = 0;
306 avimuxin->ix->qwBaseOffset = 0;
308 avimuxin->ix_off = avimux->size;
309 avimux->size += sizeof(avimuxin->ix_data);
312 if(*head_prev == avimuxin->samples_head)
313 avimuxin->samples_head = NULL;
314 else
315 *head_prev = *prev;
317 avimuxin->stream_time += avimuxin->strh.dwScale;
318 avimuxin->strh.dwLength++;
319 if(!(flags & AM_SAMPLE_TIMEDISCONTINUITY)) {
320 if(!avimuxin->ix->qwBaseOffset)
321 avimuxin->ix->qwBaseOffset = avimux->size;
322 avimuxin->ix->aIndex[avimuxin->ix->nEntriesInUse].dwOffset =
323 avimux->size + sizeof(RIFFCHUNK) - avimuxin->ix->qwBaseOffset;
325 hr = out_seek(avimux, avimux->size);
326 if(FAILED(hr)) {
327 IMediaSample_Release(sample);
328 return hr;
331 avimuxin->ix->aIndex[avimuxin->ix->nEntriesInUse].dwSize = size |
332 (flags & AM_SAMPLE_SPLICEPOINT ? 0 : AVISTDINDEX_DELTAFRAME);
333 avimuxin->ix->nEntriesInUse++;
335 rf.fcc = FCC('0'+avimuxin->stream_id/10, '0'+avimuxin->stream_id%10,
336 'd', flags & AM_SAMPLE_SPLICEPOINT ? 'b' : 'c');
337 rf.cb = size;
338 hr = idx1_add_entry(avimux, rf.fcc, flags & AM_SAMPLE_SPLICEPOINT ? AVIIF_KEYFRAME : 0,
339 flags & AM_SAMPLE_TIMEDISCONTINUITY ?
340 avimux->idx1[avimux->idx1_entries-1].dwChunkOffset : avimux->size, size);
341 if(FAILED(hr)) {
342 IMediaSample_Release(sample);
343 return hr;
346 if(!(flags & AM_SAMPLE_TIMEDISCONTINUITY)) {
347 hr = out_write(avimux, &rf, sizeof(rf));
348 if(FAILED(hr)) {
349 IMediaSample_Release(sample);
350 return hr;
352 hr = out_write(avimux, data, size);
353 if(FAILED(hr)) {
354 IMediaSample_Release(sample);
355 return hr;
357 flags = 0;
358 hr = out_write(avimux, &flags, ALIGN(rf.cb)-rf.cb);
359 if(FAILED(hr)) {
360 IMediaSample_Release(sample);
361 return hr;
364 IMediaSample_Release(sample);
366 return S_OK;
369 static HRESULT queue_sample(AviMux *avimux, AviMuxIn *avimuxin, IMediaSample *sample)
371 IMediaSample **prev, **head_prev;
372 HRESULT hr;
374 hr = IMediaSample_GetPointer(sample, (BYTE**)&prev);
375 if(FAILED(hr))
376 return hr;
377 prev--;
379 if(avimuxin->samples_head) {
380 hr = IMediaSample_GetPointer(avimuxin->samples_head, (BYTE**)&head_prev);
381 if(FAILED(hr))
382 return hr;
383 head_prev--;
385 *prev = *head_prev;
386 *head_prev = sample;
387 }else {
388 *prev = sample;
390 avimuxin->samples_head = sample;
391 IMediaSample_AddRef(sample);
393 return flush_queue(avimux, avimuxin, FALSE);
396 static HRESULT avi_mux_cleanup_stream(struct strmbase_filter *iface)
398 AviMux *This = impl_from_strmbase_filter(iface);
399 HRESULT hr;
400 int i;
402 if (This->stream)
404 AVIEXTHEADER dmlh;
405 RIFFCHUNK rc;
406 RIFFLIST rl;
407 int idx1_off, empty_stream;
409 empty_stream = This->cur_stream;
410 for(i=empty_stream+1; ; i++) {
411 if(i >= This->input_pin_no-1)
412 i = 0;
413 if(i == empty_stream)
414 break;
416 This->cur_stream = i;
417 hr = flush_queue(This, This->in[This->cur_stream], TRUE);
418 if(FAILED(hr))
419 return hr;
422 idx1_off = This->size;
423 rc.fcc = ckidAVIOLDINDEX;
424 rc.cb = This->idx1_entries * sizeof(*This->idx1);
425 hr = out_write(This, &rc, sizeof(rc));
426 if(FAILED(hr))
427 return hr;
428 hr = out_write(This, This->idx1, This->idx1_entries * sizeof(*This->idx1));
429 if(FAILED(hr))
430 return hr;
431 /* native writes 8 '\0' characters after the end of RIFF data */
432 i = 0;
433 hr = out_write(This, &i, sizeof(i));
434 if(FAILED(hr))
435 return hr;
436 hr = out_write(This, &i, sizeof(i));
437 if(FAILED(hr))
438 return hr;
440 for(i=0; i<This->input_pin_no; i++) {
441 if(!This->in[i]->pin.pin.peer)
442 continue;
444 hr = out_seek(This, This->in[i]->ix_off);
445 if(FAILED(hr))
446 return hr;
448 This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].qwOffset = This->in[i]->ix_off;
449 This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwSize = sizeof(This->in[i]->ix_data);
450 This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwDuration = This->in[i]->strh.dwLength;
451 if(This->in[i]->indx->nEntriesInUse) {
452 This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse].dwDuration -=
453 This->in[i]->indx->aIndex[This->in[i]->indx->nEntriesInUse-1].dwDuration;
455 This->in[i]->indx->nEntriesInUse++;
456 hr = out_write(This, This->in[i]->ix, sizeof(This->in[i]->ix_data));
457 if(FAILED(hr))
458 return hr;
461 hr = out_seek(This, 0);
462 if(FAILED(hr))
463 return hr;
465 rl.fcc = FCC('R','I','F','F');
466 rl.cb = This->size - sizeof(RIFFCHUNK) - 2 * sizeof(int);
467 rl.fccListType = FCC('A','V','I',' ');
468 hr = out_write(This, &rl, sizeof(rl));
469 if(FAILED(hr))
470 return hr;
472 rl.fcc = FCC('L','I','S','T');
473 rl.cb = This->movi_off - sizeof(RIFFLIST) - sizeof(RIFFCHUNK);
474 rl.fccListType = FCC('h','d','r','l');
475 hr = out_write(This, &rl, sizeof(rl));
476 if(FAILED(hr))
477 return hr;
479 /* FIXME: set This->avih.dwMaxBytesPerSec value */
480 This->avih.dwTotalFrames = (This->stop-This->start) / 10 / This->avih.dwMicroSecPerFrame;
481 hr = out_write(This, &This->avih, sizeof(This->avih));
482 if(FAILED(hr))
483 return hr;
485 for(i=0; i<This->input_pin_no; i++) {
486 if(!This->in[i]->pin.pin.peer)
487 continue;
489 rl.cb = sizeof(FOURCC) + sizeof(AVISTREAMHEADER) + sizeof(RIFFCHUNK) +
490 This->in[i]->strf->cb + sizeof(This->in[i]->indx_data);
491 rl.fccListType = ckidSTREAMLIST;
492 hr = out_write(This, &rl, sizeof(rl));
493 if(FAILED(hr))
494 return hr;
496 hr = out_write(This, &This->in[i]->strh, sizeof(AVISTREAMHEADER));
497 if(FAILED(hr))
498 return hr;
500 hr = out_write(This, This->in[i]->strf, sizeof(RIFFCHUNK) + This->in[i]->strf->cb);
501 if(FAILED(hr))
502 return hr;
504 hr = out_write(This, This->in[i]->indx, sizeof(This->in[i]->indx_data));
505 if(FAILED(hr))
506 return hr;
509 rl.cb = sizeof(dmlh) + sizeof(FOURCC);
510 rl.fccListType = ckidODML;
511 hr = out_write(This, &rl, sizeof(rl));
512 if(FAILED(hr))
513 return hr;
515 memset(&dmlh, 0, sizeof(dmlh));
516 dmlh.fcc = ckidAVIEXTHEADER;
517 dmlh.cb = sizeof(dmlh) - sizeof(RIFFCHUNK);
518 dmlh.dwGrandFrames = This->in[0]->strh.dwLength;
519 hr = out_write(This, &dmlh, sizeof(dmlh));
521 rl.cb = idx1_off - This->movi_off - sizeof(RIFFCHUNK);
522 rl.fccListType = FCC('m','o','v','i');
523 out_write(This, &rl, sizeof(rl));
524 out_flush(This);
526 IStream_Release(This->stream);
527 This->stream = NULL;
530 return S_OK;
533 static HRESULT avi_mux_init_stream(struct strmbase_filter *iface)
535 AviMux *This = impl_from_strmbase_filter(iface);
536 HRESULT hr;
537 int i, stream_id;
539 if(This->mode != INTERLEAVE_FULL) {
540 FIXME("mode not supported (%d)\n", This->mode);
541 return E_NOTIMPL;
544 for(i=0; i<This->input_pin_no; i++) {
545 IMediaSeeking *ms;
546 LONGLONG cur, stop;
548 if(!This->in[i]->pin.pin.peer)
549 continue;
551 hr = IPin_QueryInterface(This->in[i]->pin.pin.peer,
552 &IID_IMediaSeeking, (void**)&ms);
553 if(FAILED(hr))
554 continue;
556 hr = IMediaSeeking_GetPositions(ms, &cur, &stop);
557 if(FAILED(hr)) {
558 IMediaSeeking_Release(ms);
559 continue;
562 FIXME("Use IMediaSeeking to fill stream header\n");
563 IMediaSeeking_Release(ms);
566 if (This->source.pMemInputPin)
568 hr = IMemInputPin_QueryInterface(This->source.pMemInputPin,
569 &IID_IStream, (void **)&This->stream);
570 if(FAILED(hr))
571 return hr;
574 This->idx1_entries = 0;
575 if(!This->idx1_size) {
576 This->idx1_size = 1024;
577 if (!(This->idx1 = malloc(sizeof(*This->idx1) * This->idx1_size)))
578 return E_OUTOFMEMORY;
581 This->size = 3*sizeof(RIFFLIST) + sizeof(AVIMAINHEADER) + sizeof(AVIEXTHEADER);
582 This->start = -1;
583 This->stop = -1;
584 memset(&This->avih, 0, sizeof(This->avih));
585 for(i=0; i<This->input_pin_no; i++) {
586 if(!This->in[i]->pin.pin.peer)
587 continue;
589 This->avih.dwStreams++;
590 This->size += sizeof(RIFFLIST) + sizeof(AVISTREAMHEADER) + sizeof(RIFFCHUNK)
591 + This->in[i]->strf->cb + sizeof(This->in[i]->indx_data);
593 This->in[i]->strh.dwScale = MulDiv(This->in[i]->avg_time_per_frame, This->interleave, 10000000);
594 This->in[i]->strh.dwRate = This->interleave;
596 hr = IMemAllocator_Commit(This->in[i]->pin.pAllocator);
597 if(FAILED(hr)) {
598 if (This->stream)
600 IStream_Release(This->stream);
601 This->stream = NULL;
603 return hr;
607 This->movi_off = This->size;
608 This->size += sizeof(RIFFLIST);
610 idx1_add_entry(This, FCC('7','F','x','x'), 0, This->movi_off + sizeof(RIFFLIST), 0);
612 stream_id = 0;
613 for(i=0; i<This->input_pin_no; i++) {
614 if(!This->in[i]->pin.pin.peer)
615 continue;
617 This->in[i]->ix_off = This->size;
618 This->size += sizeof(This->in[i]->ix_data);
619 This->in[i]->ix->fcc = FCC('i','x','0'+stream_id/10,'0'+stream_id%10);
620 This->in[i]->ix->cb = sizeof(This->in[i]->ix_data) - sizeof(RIFFCHUNK);
621 This->in[i]->ix->wLongsPerEntry = 2;
622 This->in[i]->ix->bIndexSubType = 0;
623 This->in[i]->ix->bIndexType = AVI_INDEX_OF_CHUNKS;
624 This->in[i]->ix->dwChunkId = FCC('0'+stream_id/10,'0'+stream_id%10,'d','b');
625 This->in[i]->ix->qwBaseOffset = 0;
627 This->in[i]->indx->fcc = ckidAVISUPERINDEX;
628 This->in[i]->indx->cb = sizeof(This->in[i]->indx_data) - sizeof(RIFFCHUNK);
629 This->in[i]->indx->wLongsPerEntry = 4;
630 This->in[i]->indx->bIndexSubType = 0;
631 This->in[i]->indx->bIndexType = AVI_INDEX_OF_INDEXES;
632 This->in[i]->indx->dwChunkId = This->in[i]->ix->dwChunkId;
633 This->in[i]->stream_id = stream_id++;
636 This->buf_pos = 0;
637 This->out_pos = 0;
639 This->avih.fcc = ckidMAINAVIHEADER;
640 This->avih.cb = sizeof(AVIMAINHEADER) - sizeof(RIFFCHUNK);
641 /* TODO: Use first video stream */
642 This->avih.dwMicroSecPerFrame = This->in[0]->avg_time_per_frame/10;
643 This->avih.dwPaddingGranularity = 1;
644 This->avih.dwFlags = AVIF_TRUSTCKTYPE | AVIF_HASINDEX;
645 This->avih.dwWidth = ((BITMAPINFOHEADER*)This->in[0]->strf->data)->biWidth;
646 This->avih.dwHeight = ((BITMAPINFOHEADER*)This->in[0]->strf->data)->biHeight;
648 return S_OK;
651 static const struct strmbase_filter_ops filter_ops =
653 .filter_get_pin = avi_mux_get_pin,
654 .filter_destroy = avi_mux_destroy,
655 .filter_query_interface = avi_mux_query_interface,
656 .filter_init_stream = avi_mux_init_stream,
657 .filter_cleanup_stream = avi_mux_cleanup_stream,
660 static inline AviMux* impl_from_IConfigAviMux(IConfigAviMux *iface)
662 return CONTAINING_RECORD(iface, AviMux, IConfigAviMux_iface);
665 static HRESULT WINAPI ConfigAviMux_QueryInterface(
666 IConfigAviMux *iface, REFIID riid, void **ppv)
668 AviMux *This = impl_from_IConfigAviMux(iface);
669 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
672 static ULONG WINAPI ConfigAviMux_AddRef(IConfigAviMux *iface)
674 AviMux *This = impl_from_IConfigAviMux(iface);
675 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
678 static ULONG WINAPI ConfigAviMux_Release(IConfigAviMux *iface)
680 AviMux *This = impl_from_IConfigAviMux(iface);
681 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
684 static HRESULT WINAPI ConfigAviMux_SetMasterStream(IConfigAviMux *iface, LONG iStream)
686 AviMux *This = impl_from_IConfigAviMux(iface);
687 FIXME("filter %p, index %ld, stub!\n", This, iStream);
688 return E_NOTIMPL;
691 static HRESULT WINAPI ConfigAviMux_GetMasterStream(IConfigAviMux *iface, LONG *pStream)
693 AviMux *This = impl_from_IConfigAviMux(iface);
694 FIXME("(%p)->(%p)\n", This, pStream);
695 return E_NOTIMPL;
698 static HRESULT WINAPI ConfigAviMux_SetOutputCompatibilityIndex(
699 IConfigAviMux *iface, BOOL fOldIndex)
701 AviMux *This = impl_from_IConfigAviMux(iface);
702 FIXME("(%p)->(%x)\n", This, fOldIndex);
703 return E_NOTIMPL;
706 static HRESULT WINAPI ConfigAviMux_GetOutputCompatibilityIndex(
707 IConfigAviMux *iface, BOOL *pfOldIndex)
709 AviMux *This = impl_from_IConfigAviMux(iface);
710 FIXME("(%p)->(%p)\n", This, pfOldIndex);
711 return E_NOTIMPL;
714 static const IConfigAviMuxVtbl ConfigAviMuxVtbl = {
715 ConfigAviMux_QueryInterface,
716 ConfigAviMux_AddRef,
717 ConfigAviMux_Release,
718 ConfigAviMux_SetMasterStream,
719 ConfigAviMux_GetMasterStream,
720 ConfigAviMux_SetOutputCompatibilityIndex,
721 ConfigAviMux_GetOutputCompatibilityIndex
724 static inline AviMux* impl_from_IConfigInterleaving(IConfigInterleaving *iface)
726 return CONTAINING_RECORD(iface, AviMux, IConfigInterleaving_iface);
729 static HRESULT WINAPI ConfigInterleaving_QueryInterface(
730 IConfigInterleaving *iface, REFIID riid, void **ppv)
732 AviMux *This = impl_from_IConfigInterleaving(iface);
733 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
736 static ULONG WINAPI ConfigInterleaving_AddRef(IConfigInterleaving *iface)
738 AviMux *This = impl_from_IConfigInterleaving(iface);
739 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
742 static ULONG WINAPI ConfigInterleaving_Release(IConfigInterleaving *iface)
744 AviMux *This = impl_from_IConfigInterleaving(iface);
745 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
748 static HRESULT WINAPI ConfigInterleaving_put_Mode(
749 IConfigInterleaving *iface, InterleavingMode mode)
751 AviMux *This = impl_from_IConfigInterleaving(iface);
753 TRACE("(%p)->(%d)\n", This, mode);
755 if(mode>INTERLEAVE_NONE_BUFFERED)
756 return E_INVALIDARG;
758 if(This->mode != mode) {
759 if(This->source.pin.peer) {
760 HRESULT hr = IFilterGraph_Reconnect(This->filter.graph, &This->source.pin.IPin_iface);
761 if(FAILED(hr))
762 return hr;
765 This->mode = mode;
768 return S_OK;
771 static HRESULT WINAPI ConfigInterleaving_get_Mode(
772 IConfigInterleaving *iface, InterleavingMode *pMode)
774 AviMux *This = impl_from_IConfigInterleaving(iface);
775 FIXME("(%p)->(%p)\n", This, pMode);
776 return E_NOTIMPL;
779 static HRESULT WINAPI ConfigInterleaving_put_Interleaving(IConfigInterleaving *iface,
780 const REFERENCE_TIME *prtInterleave, const REFERENCE_TIME *prtPreroll)
782 AviMux *This = impl_from_IConfigInterleaving(iface);
784 TRACE("(%p)->(%p %p)\n", This, prtInterleave, prtPreroll);
786 if(prtInterleave)
787 This->interleave = *prtInterleave;
788 if(prtPreroll)
789 This->preroll = *prtPreroll;
790 return S_OK;
793 static HRESULT WINAPI ConfigInterleaving_get_Interleaving(IConfigInterleaving *iface,
794 REFERENCE_TIME *prtInterleave, REFERENCE_TIME *prtPreroll)
796 AviMux *This = impl_from_IConfigInterleaving(iface);
797 FIXME("(%p)->(%p %p)\n", This, prtInterleave, prtPreroll);
798 return E_NOTIMPL;
801 static const IConfigInterleavingVtbl ConfigInterleavingVtbl = {
802 ConfigInterleaving_QueryInterface,
803 ConfigInterleaving_AddRef,
804 ConfigInterleaving_Release,
805 ConfigInterleaving_put_Mode,
806 ConfigInterleaving_get_Mode,
807 ConfigInterleaving_put_Interleaving,
808 ConfigInterleaving_get_Interleaving
811 static inline AviMux* impl_from_IMediaSeeking(IMediaSeeking *iface)
813 return CONTAINING_RECORD(iface, AviMux, IMediaSeeking_iface);
816 static HRESULT WINAPI MediaSeeking_QueryInterface(
817 IMediaSeeking *iface, REFIID riid, void **ppv)
819 AviMux *This = impl_from_IMediaSeeking(iface);
820 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
823 static ULONG WINAPI MediaSeeking_AddRef(IMediaSeeking *iface)
825 AviMux *This = impl_from_IMediaSeeking(iface);
826 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
829 static ULONG WINAPI MediaSeeking_Release(IMediaSeeking *iface)
831 AviMux *This = impl_from_IMediaSeeking(iface);
832 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
835 static HRESULT WINAPI MediaSeeking_GetCapabilities(
836 IMediaSeeking *iface, DWORD *pCapabilities)
838 AviMux *This = impl_from_IMediaSeeking(iface);
839 FIXME("(%p)->(%p)\n", This, pCapabilities);
840 return E_NOTIMPL;
843 static HRESULT WINAPI MediaSeeking_CheckCapabilities(
844 IMediaSeeking *iface, DWORD *pCapabilities)
846 AviMux *This = impl_from_IMediaSeeking(iface);
847 FIXME("(%p)->(%p)\n", This, pCapabilities);
848 return E_NOTIMPL;
851 static HRESULT WINAPI MediaSeeking_IsFormatSupported(
852 IMediaSeeking *iface, const GUID *pFormat)
854 AviMux *This = impl_from_IMediaSeeking(iface);
855 FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat));
856 return E_NOTIMPL;
859 static HRESULT WINAPI MediaSeeking_QueryPreferredFormat(
860 IMediaSeeking *iface, GUID *pFormat)
862 AviMux *This = impl_from_IMediaSeeking(iface);
863 FIXME("(%p)->(%p)\n", This, pFormat);
864 return E_NOTIMPL;
867 static HRESULT WINAPI MediaSeeking_GetTimeFormat(
868 IMediaSeeking *iface, GUID *pFormat)
870 AviMux *This = impl_from_IMediaSeeking(iface);
871 FIXME("(%p)->(%p)\n", This, pFormat);
872 return E_NOTIMPL;
875 static HRESULT WINAPI MediaSeeking_IsUsingTimeFormat(
876 IMediaSeeking *iface, const GUID *pFormat)
878 AviMux *This = impl_from_IMediaSeeking(iface);
879 FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat));
880 return E_NOTIMPL;
883 static HRESULT WINAPI MediaSeeking_SetTimeFormat(
884 IMediaSeeking *iface, const GUID *pFormat)
886 AviMux *This = impl_from_IMediaSeeking(iface);
887 FIXME("(%p)->(%s)\n", This, debugstr_guid(pFormat));
888 return E_NOTIMPL;
891 static HRESULT WINAPI MediaSeeking_GetDuration(
892 IMediaSeeking *iface, LONGLONG *pDuration)
894 AviMux *This = impl_from_IMediaSeeking(iface);
895 FIXME("(%p)->(%p)\n", This, pDuration);
896 return E_NOTIMPL;
899 static HRESULT WINAPI MediaSeeking_GetStopPosition(
900 IMediaSeeking *iface, LONGLONG *pStop)
902 AviMux *This = impl_from_IMediaSeeking(iface);
903 FIXME("(%p)->(%p)\n", This, pStop);
904 return E_NOTIMPL;
907 static HRESULT WINAPI MediaSeeking_GetCurrentPosition(
908 IMediaSeeking *iface, LONGLONG *pCurrent)
910 AviMux *This = impl_from_IMediaSeeking(iface);
911 FIXME("(%p)->(%p)\n", This, pCurrent);
912 return E_NOTIMPL;
915 static HRESULT WINAPI MediaSeeking_ConvertTimeFormat(IMediaSeeking *iface, LONGLONG *pTarget,
916 const GUID *pTargetFormat, LONGLONG Source, const GUID *pSourceFormat)
918 AviMux *This = impl_from_IMediaSeeking(iface);
919 FIXME("(%p)->(%p %s %s %s)\n", This, pTarget, debugstr_guid(pTargetFormat),
920 wine_dbgstr_longlong(Source), debugstr_guid(pSourceFormat));
921 return E_NOTIMPL;
924 static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG *pCurrent,
925 DWORD dwCurrentFlags, LONGLONG *pStop, DWORD dwStopFlags)
927 AviMux *This = impl_from_IMediaSeeking(iface);
928 FIXME("(%p)->(%p %#lx %p %#lx)\n", This, pCurrent, dwCurrentFlags, pStop, dwStopFlags);
929 return E_NOTIMPL;
932 static HRESULT WINAPI MediaSeeking_GetPositions(IMediaSeeking *iface,
933 LONGLONG *pCurrent, LONGLONG *pStop)
935 AviMux *This = impl_from_IMediaSeeking(iface);
936 FIXME("(%p)->(%p %p)\n", This, pCurrent, pStop);
937 return E_NOTIMPL;
940 static HRESULT WINAPI MediaSeeking_GetAvailable(IMediaSeeking *iface,
941 LONGLONG *pEarliest, LONGLONG *pLatest)
943 AviMux *This = impl_from_IMediaSeeking(iface);
944 FIXME("(%p)->(%p %p)\n", This, pEarliest, pLatest);
945 return E_NOTIMPL;
948 static HRESULT WINAPI MediaSeeking_SetRate(IMediaSeeking *iface, double dRate)
950 AviMux *This = impl_from_IMediaSeeking(iface);
951 FIXME("(%p)->(%lf)\n", This, dRate);
952 return E_NOTIMPL;
955 static HRESULT WINAPI MediaSeeking_GetRate(IMediaSeeking *iface, double *pdRate)
957 AviMux *This = impl_from_IMediaSeeking(iface);
958 FIXME("(%p)->(%p)\n", This, pdRate);
959 return E_NOTIMPL;
962 static HRESULT WINAPI MediaSeeking_GetPreroll(IMediaSeeking *iface, LONGLONG *pllPreroll)
964 AviMux *This = impl_from_IMediaSeeking(iface);
965 FIXME("(%p)->(%p)\n", This, pllPreroll);
966 return E_NOTIMPL;
969 static const IMediaSeekingVtbl MediaSeekingVtbl = {
970 MediaSeeking_QueryInterface,
971 MediaSeeking_AddRef,
972 MediaSeeking_Release,
973 MediaSeeking_GetCapabilities,
974 MediaSeeking_CheckCapabilities,
975 MediaSeeking_IsFormatSupported,
976 MediaSeeking_QueryPreferredFormat,
977 MediaSeeking_GetTimeFormat,
978 MediaSeeking_IsUsingTimeFormat,
979 MediaSeeking_SetTimeFormat,
980 MediaSeeking_GetDuration,
981 MediaSeeking_GetStopPosition,
982 MediaSeeking_GetCurrentPosition,
983 MediaSeeking_ConvertTimeFormat,
984 MediaSeeking_SetPositions,
985 MediaSeeking_GetPositions,
986 MediaSeeking_GetAvailable,
987 MediaSeeking_SetRate,
988 MediaSeeking_GetRate,
989 MediaSeeking_GetPreroll
992 static inline AviMux* impl_from_IPersistMediaPropertyBag(IPersistMediaPropertyBag *iface)
994 return CONTAINING_RECORD(iface, AviMux, IPersistMediaPropertyBag_iface);
997 static HRESULT WINAPI PersistMediaPropertyBag_QueryInterface(
998 IPersistMediaPropertyBag *iface, REFIID riid, void **ppv)
1000 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1001 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1004 static ULONG WINAPI PersistMediaPropertyBag_AddRef(IPersistMediaPropertyBag *iface)
1006 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1007 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1010 static ULONG WINAPI PersistMediaPropertyBag_Release(IPersistMediaPropertyBag *iface)
1012 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1013 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1016 static HRESULT WINAPI PersistMediaPropertyBag_GetClassID(
1017 IPersistMediaPropertyBag *iface, CLSID *pClassID)
1019 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1020 return IBaseFilter_GetClassID(&This->filter.IBaseFilter_iface, pClassID);
1023 static HRESULT WINAPI PersistMediaPropertyBag_InitNew(IPersistMediaPropertyBag *iface)
1025 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1026 FIXME("(%p)->()\n", This);
1027 return E_NOTIMPL;
1030 static HRESULT WINAPI PersistMediaPropertyBag_Load(IPersistMediaPropertyBag *iface,
1031 IMediaPropertyBag *pPropBag, IErrorLog *pErrorLog)
1033 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1034 FIXME("(%p)->()\n", This);
1035 return E_NOTIMPL;
1038 static HRESULT WINAPI PersistMediaPropertyBag_Save(IPersistMediaPropertyBag *iface,
1039 IMediaPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
1041 AviMux *This = impl_from_IPersistMediaPropertyBag(iface);
1042 FIXME("(%p)->()\n", This);
1043 return E_NOTIMPL;
1046 static const IPersistMediaPropertyBagVtbl PersistMediaPropertyBagVtbl = {
1047 PersistMediaPropertyBag_QueryInterface,
1048 PersistMediaPropertyBag_AddRef,
1049 PersistMediaPropertyBag_Release,
1050 PersistMediaPropertyBag_GetClassID,
1051 PersistMediaPropertyBag_InitNew,
1052 PersistMediaPropertyBag_Load,
1053 PersistMediaPropertyBag_Save
1056 static inline AviMux* impl_from_ISpecifyPropertyPages(ISpecifyPropertyPages *iface)
1058 return CONTAINING_RECORD(iface, AviMux, ISpecifyPropertyPages_iface);
1061 static HRESULT WINAPI SpecifyPropertyPages_QueryInterface(
1062 ISpecifyPropertyPages *iface, REFIID riid, void **ppv)
1064 AviMux *This = impl_from_ISpecifyPropertyPages(iface);
1065 return IBaseFilter_QueryInterface(&This->filter.IBaseFilter_iface, riid, ppv);
1068 static ULONG WINAPI SpecifyPropertyPages_AddRef(ISpecifyPropertyPages *iface)
1070 AviMux *This = impl_from_ISpecifyPropertyPages(iface);
1071 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1074 static ULONG WINAPI SpecifyPropertyPages_Release(ISpecifyPropertyPages *iface)
1076 AviMux *This = impl_from_ISpecifyPropertyPages(iface);
1077 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1080 static HRESULT WINAPI SpecifyPropertyPages_GetPages(
1081 ISpecifyPropertyPages *iface, CAUUID *pPages)
1083 AviMux *This = impl_from_ISpecifyPropertyPages(iface);
1084 FIXME("(%p)->(%p)\n", This, pPages);
1085 return E_NOTIMPL;
1088 static const ISpecifyPropertyPagesVtbl SpecifyPropertyPagesVtbl = {
1089 SpecifyPropertyPages_QueryInterface,
1090 SpecifyPropertyPages_AddRef,
1091 SpecifyPropertyPages_Release,
1092 SpecifyPropertyPages_GetPages
1095 static inline AviMux *impl_from_source_pin(struct strmbase_pin *iface)
1097 return CONTAINING_RECORD(iface, AviMux, source.pin);
1100 static HRESULT source_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
1102 AviMux *filter = impl_from_source_pin(iface);
1104 if (IsEqualGUID(iid, &IID_IQualityControl))
1105 *out = &filter->IQualityControl_iface;
1106 else
1107 return E_NOINTERFACE;
1109 IUnknown_AddRef((IUnknown *)*out);
1110 return S_OK;
1113 static HRESULT source_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt)
1115 if (!IsEqualGUID(&mt->majortype, &GUID_NULL) && !IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream))
1116 return S_FALSE;
1117 if (!IsEqualGUID(&mt->subtype, &GUID_NULL) && !IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_Avi))
1118 return S_FALSE;
1119 return S_OK;
1122 static HRESULT WINAPI AviMuxOut_AttemptConnection(struct strmbase_source *iface,
1123 IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
1125 AviMux *filter = impl_from_source_pin(&iface->pin);
1126 PIN_DIRECTION dir;
1127 unsigned int i;
1128 HRESULT hr;
1130 hr = IPin_QueryDirection(pReceivePin, &dir);
1131 if(hr==S_OK && dir!=PINDIR_INPUT)
1132 return VFW_E_INVALID_DIRECTION;
1134 if (FAILED(hr = BaseOutputPinImpl_AttemptConnection(iface, pReceivePin, pmt)))
1135 return hr;
1137 for (i = 0; i < filter->input_pin_no; ++i)
1139 if (!filter->in[i]->pin.pin.peer)
1140 continue;
1142 hr = IFilterGraph_Reconnect(filter->filter.graph, &filter->in[i]->pin.pin.IPin_iface);
1143 if (FAILED(hr))
1145 IPin_Disconnect(&iface->pin.IPin_iface);
1146 break;
1150 return hr;
1153 static HRESULT source_get_media_type(struct strmbase_pin *base, unsigned int iPosition, AM_MEDIA_TYPE *amt)
1155 TRACE("(%p)->(%d %p)\n", base, iPosition, amt);
1157 if(iPosition > 0)
1158 return VFW_S_NO_MORE_ITEMS;
1160 amt->majortype = MEDIATYPE_Stream;
1161 amt->subtype = MEDIASUBTYPE_Avi;
1162 amt->bFixedSizeSamples = TRUE;
1163 amt->bTemporalCompression = FALSE;
1164 amt->lSampleSize = 1;
1165 amt->formattype = GUID_NULL;
1166 amt->pUnk = NULL;
1167 amt->cbFormat = 0;
1168 amt->pbFormat = NULL;
1169 return S_OK;
1172 static HRESULT WINAPI AviMuxOut_DecideAllocator(struct strmbase_source *base,
1173 IMemInputPin *pPin, IMemAllocator **pAlloc)
1175 ALLOCATOR_PROPERTIES req, actual;
1176 HRESULT hr;
1178 TRACE("(%p)->(%p %p)\n", base, pPin, pAlloc);
1180 if (FAILED(hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL,
1181 CLSCTX_INPROC_SERVER, &IID_IMemAllocator, (void **)pAlloc)))
1183 ERR("Failed to create allocator, hr %#lx.\n", hr);
1184 return hr;
1187 hr = IMemInputPin_GetAllocatorRequirements(pPin, &req);
1188 if(FAILED(hr))
1189 req.cbAlign = 1;
1190 req.cBuffers = 32;
1191 req.cbBuffer = 0;
1192 req.cbPrefix = 0;
1194 hr = IMemAllocator_SetProperties(*pAlloc, &req, &actual);
1195 if(FAILED(hr))
1196 return hr;
1198 return IMemInputPin_NotifyAllocator(pPin, *pAlloc, TRUE);
1201 static const struct strmbase_source_ops source_ops =
1203 .base.pin_query_interface = source_query_interface,
1204 .base.pin_query_accept = source_query_accept,
1205 .base.pin_get_media_type = source_get_media_type,
1206 .pfnAttemptConnection = AviMuxOut_AttemptConnection,
1207 .pfnDecideAllocator = AviMuxOut_DecideAllocator,
1210 static inline AviMux* impl_from_out_IQualityControl(IQualityControl *iface)
1212 return CONTAINING_RECORD(iface, AviMux, IQualityControl_iface);
1215 static HRESULT WINAPI AviMuxOut_QualityControl_QueryInterface(
1216 IQualityControl *iface, REFIID riid, void **ppv)
1218 AviMux *This = impl_from_out_IQualityControl(iface);
1219 return IPin_QueryInterface(&This->source.pin.IPin_iface, riid, ppv);
1222 static ULONG WINAPI AviMuxOut_QualityControl_AddRef(IQualityControl *iface)
1224 AviMux *This = impl_from_out_IQualityControl(iface);
1225 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1228 static ULONG WINAPI AviMuxOut_QualityControl_Release(IQualityControl *iface)
1230 AviMux *This = impl_from_out_IQualityControl(iface);
1231 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1234 static HRESULT WINAPI AviMuxOut_QualityControl_Notify(IQualityControl *iface,
1235 IBaseFilter *sender, Quality q)
1237 AviMux *filter = impl_from_out_IQualityControl(iface);
1239 FIXME("filter %p, sender %p, type %#x, proportion %ld, late %s, timestamp %s, stub!\n",
1240 filter, sender, q.Type, q.Proportion,
1241 wine_dbgstr_longlong(q.Late), wine_dbgstr_longlong(q.TimeStamp));
1243 return E_NOTIMPL;
1246 static HRESULT WINAPI AviMuxOut_QualityControl_SetSink(
1247 IQualityControl *iface, IQualityControl *piqc)
1249 AviMux *This = impl_from_out_IQualityControl(iface);
1250 FIXME("(%p)->(%p)\n", This, piqc);
1251 return E_NOTIMPL;
1254 static const IQualityControlVtbl AviMuxOut_QualityControlVtbl = {
1255 AviMuxOut_QualityControl_QueryInterface,
1256 AviMuxOut_QualityControl_AddRef,
1257 AviMuxOut_QualityControl_Release,
1258 AviMuxOut_QualityControl_Notify,
1259 AviMuxOut_QualityControl_SetSink
1262 static inline AviMuxIn *impl_sink_from_strmbase_pin(struct strmbase_pin *iface)
1264 return CONTAINING_RECORD(iface, AviMuxIn, pin.pin);
1267 static HRESULT sink_query_interface(struct strmbase_pin *iface, REFIID iid, void **out)
1269 AviMuxIn *pin = impl_sink_from_strmbase_pin(iface);
1271 if (IsEqualGUID(iid, &IID_IAMStreamControl))
1272 *out = &pin->IAMStreamControl_iface;
1273 else if (IsEqualGUID(iid, &IID_IMemInputPin))
1274 *out = &pin->pin.IMemInputPin_iface;
1275 else if (IsEqualGUID(iid, &IID_IPropertyBag))
1276 *out = &pin->IPropertyBag_iface;
1277 else if (IsEqualGUID(iid, &IID_IQualityControl))
1278 *out = &pin->IQualityControl_iface;
1279 else
1280 return E_NOINTERFACE;
1282 IUnknown_AddRef((IUnknown *)*out);
1283 return S_OK;
1286 static HRESULT sink_query_accept(struct strmbase_pin *base, const AM_MEDIA_TYPE *pmt)
1288 if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Audio) &&
1289 IsEqualIID(&pmt->formattype, &FORMAT_WaveFormatEx))
1290 return S_OK;
1291 if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Interleaved) &&
1292 IsEqualIID(&pmt->formattype, &FORMAT_DvInfo))
1293 return S_OK;
1294 if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Video) &&
1295 (IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo) ||
1296 IsEqualIID(&pmt->formattype, &FORMAT_DvInfo)))
1297 return S_OK;
1298 return S_FALSE;
1301 static HRESULT WINAPI AviMuxIn_Receive(struct strmbase_sink *base, IMediaSample *pSample)
1303 AviMux *avimux = impl_from_strmbase_filter(base->pin.filter);
1304 AviMuxIn *avimuxin = CONTAINING_RECORD(base, AviMuxIn, pin);
1305 REFERENCE_TIME start, stop;
1306 IMediaSample *sample;
1307 int frames_no;
1308 IMediaSample2 *ms2;
1309 BYTE *frame, *buf;
1310 DWORD max_size, size;
1311 DWORD flags;
1312 HRESULT hr;
1314 TRACE("pin %p, pSample %p.\n", avimuxin, pSample);
1316 hr = IMediaSample_QueryInterface(pSample, &IID_IMediaSample2, (void**)&ms2);
1317 if(SUCCEEDED(hr)) {
1318 AM_SAMPLE2_PROPERTIES props;
1320 memset(&props, 0, sizeof(props));
1321 hr = IMediaSample2_GetProperties(ms2, sizeof(props), (BYTE*)&props);
1322 IMediaSample2_Release(ms2);
1323 if(FAILED(hr))
1324 return hr;
1326 flags = props.dwSampleFlags;
1327 frame = props.pbBuffer;
1328 size = props.lActual;
1329 }else {
1330 flags = IMediaSample_IsSyncPoint(pSample) == S_OK ? AM_SAMPLE_SPLICEPOINT : 0;
1331 hr = IMediaSample_GetPointer(pSample, &frame);
1332 if(FAILED(hr))
1333 return hr;
1334 size = IMediaSample_GetActualDataLength(pSample);
1337 if(!avimuxin->pin.pin.mt.bTemporalCompression)
1338 flags |= AM_SAMPLE_SPLICEPOINT;
1340 hr = IMediaSample_GetTime(pSample, &start, &stop);
1341 if(FAILED(hr))
1342 return hr;
1344 if(avimuxin->stop>stop)
1345 return VFW_E_START_TIME_AFTER_END;
1347 if(avimux->start == -1)
1348 avimux->start = start;
1349 if(avimux->stop < stop)
1350 avimux->stop = stop;
1352 if(avimux->avih.dwSuggestedBufferSize < ALIGN(size)+sizeof(RIFFCHUNK))
1353 avimux->avih.dwSuggestedBufferSize = ALIGN(size) + sizeof(RIFFCHUNK);
1354 if(avimuxin->strh.dwSuggestedBufferSize < ALIGN(size)+sizeof(RIFFCHUNK))
1355 avimuxin->strh.dwSuggestedBufferSize = ALIGN(size) + sizeof(RIFFCHUNK);
1357 frames_no = 1;
1358 if(avimuxin->stop!=-1 && start > avimuxin->stop) {
1359 frames_no += (double)(start - avimuxin->stop) / 10000000
1360 * avimuxin->strh.dwRate / avimuxin->strh.dwScale + 0.5;
1362 avimuxin->stop = stop;
1364 while(--frames_no) {
1365 /* TODO: store all control frames in one buffer */
1366 hr = IMemAllocator_GetBuffer(avimuxin->samples_allocator, &sample, NULL, NULL, 0);
1367 if(FAILED(hr))
1368 return hr;
1369 hr = IMediaSample_SetActualDataLength(sample, 0);
1370 if(SUCCEEDED(hr))
1371 hr = IMediaSample_SetDiscontinuity(sample, TRUE);
1372 if(SUCCEEDED(hr))
1373 hr = IMediaSample_SetSyncPoint(sample, FALSE);
1374 if(SUCCEEDED(hr))
1375 hr = queue_sample(avimux, avimuxin, sample);
1376 IMediaSample_Release(sample);
1377 if(FAILED(hr))
1378 return hr;
1381 hr = IMemAllocator_GetBuffer(avimuxin->samples_allocator, &sample, NULL, NULL, 0);
1382 if(FAILED(hr))
1383 return hr;
1384 max_size = IMediaSample_GetSize(sample);
1385 if(size > max_size)
1386 size = max_size;
1387 hr = IMediaSample_SetActualDataLength(sample, size);
1388 if(SUCCEEDED(hr))
1389 hr = IMediaSample_SetDiscontinuity(sample, FALSE);
1390 if(SUCCEEDED(hr))
1391 hr = IMediaSample_SetSyncPoint(sample, flags & AM_SAMPLE_SPLICEPOINT);
1392 /* TODO: avoid unnecessary copying */
1393 if(SUCCEEDED(hr))
1394 hr = IMediaSample_GetPointer(sample, &buf);
1395 if(SUCCEEDED(hr)) {
1396 memcpy(buf, frame, size);
1397 hr = queue_sample(avimux, avimuxin, sample);
1399 IMediaSample_Release(sample);
1401 return hr;
1404 static HRESULT avi_mux_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *pmt)
1406 AviMuxIn *avimuxin = impl_sink_from_strmbase_pin(&iface->pin);
1407 AviMux *This = impl_from_strmbase_filter(iface->pin.filter);
1408 HRESULT hr;
1410 if(!pmt)
1411 return E_POINTER;
1413 if(IsEqualIID(&pmt->majortype, &MEDIATYPE_Video) &&
1414 IsEqualIID(&pmt->formattype, &FORMAT_VideoInfo)) {
1415 ALLOCATOR_PROPERTIES req, act;
1416 VIDEOINFOHEADER *vih;
1417 int size;
1419 vih = (VIDEOINFOHEADER*)pmt->pbFormat;
1420 avimuxin->strh.fcc = ckidSTREAMHEADER;
1421 avimuxin->strh.cb = sizeof(AVISTREAMHEADER) - FIELD_OFFSET(AVISTREAMHEADER, fccType);
1422 avimuxin->strh.fccType = streamtypeVIDEO;
1423 /* FIXME: fccHandler should be set differently */
1424 avimuxin->strh.fccHandler = vih->bmiHeader.biCompression ?
1425 vih->bmiHeader.biCompression : FCC('D','I','B',' ');
1426 avimuxin->avg_time_per_frame = vih->AvgTimePerFrame;
1427 avimuxin->stop = -1;
1429 req.cBuffers = 32;
1430 req.cbBuffer = vih->bmiHeader.biSizeImage;
1431 req.cbAlign = 1;
1432 req.cbPrefix = sizeof(void*);
1433 hr = IMemAllocator_SetProperties(avimuxin->samples_allocator, &req, &act);
1434 if(SUCCEEDED(hr))
1435 hr = IMemAllocator_Commit(avimuxin->samples_allocator);
1436 if (FAILED(hr))
1437 return hr;
1439 size = pmt->cbFormat - FIELD_OFFSET(VIDEOINFOHEADER, bmiHeader);
1440 avimuxin->strf = malloc(sizeof(RIFFCHUNK) + ALIGN(FIELD_OFFSET(BITMAPINFO, bmiColors[vih->bmiHeader.biClrUsed])));
1441 avimuxin->strf->fcc = ckidSTREAMFORMAT;
1442 avimuxin->strf->cb = FIELD_OFFSET(BITMAPINFO, bmiColors[vih->bmiHeader.biClrUsed]);
1443 if(size > avimuxin->strf->cb)
1444 size = avimuxin->strf->cb;
1445 memcpy(avimuxin->strf->data, &vih->bmiHeader, size);
1446 }else {
1447 FIXME("format not supported: %s %s\n", debugstr_guid(&pmt->majortype),
1448 debugstr_guid(&pmt->formattype));
1449 return E_NOTIMPL;
1452 return create_input_pin(This);
1455 static void avi_mux_sink_disconnect(struct strmbase_sink *iface)
1457 AviMuxIn *avimuxin = impl_sink_from_strmbase_pin(&iface->pin);
1458 IMediaSample **prev, *cur;
1460 IMemAllocator_Decommit(avimuxin->samples_allocator);
1461 while(avimuxin->samples_head) {
1462 cur = avimuxin->samples_head;
1463 if (FAILED(IMediaSample_GetPointer(cur, (BYTE **)&prev)))
1464 break;
1465 prev--;
1467 cur = avimuxin->samples_head;
1468 avimuxin->samples_head = *prev;
1469 IMediaSample_Release(cur);
1471 if(cur == avimuxin->samples_head)
1472 avimuxin->samples_head = NULL;
1474 free(avimuxin->strf);
1475 avimuxin->strf = NULL;
1478 static const struct strmbase_sink_ops sink_ops =
1480 .base.pin_query_interface = sink_query_interface,
1481 .base.pin_query_accept = sink_query_accept,
1482 .pfnReceive = AviMuxIn_Receive,
1483 .sink_connect = avi_mux_sink_connect,
1484 .sink_disconnect = avi_mux_sink_disconnect,
1487 static inline AviMux* impl_from_in_IPin(IPin *iface)
1489 struct strmbase_pin *pin = CONTAINING_RECORD(iface, struct strmbase_pin, IPin_iface);
1490 return impl_from_strmbase_filter(pin->filter);
1493 static inline AviMuxIn* AviMuxIn_from_IAMStreamControl(IAMStreamControl *iface)
1495 return CONTAINING_RECORD(iface, AviMuxIn, IAMStreamControl_iface);
1498 static HRESULT WINAPI AviMuxIn_AMStreamControl_QueryInterface(
1499 IAMStreamControl *iface, REFIID riid, void **ppv)
1501 AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface);
1502 return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv);
1505 static ULONG WINAPI AviMuxIn_AMStreamControl_AddRef(IAMStreamControl *iface)
1507 AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface);
1508 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1509 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1512 static ULONG WINAPI AviMuxIn_AMStreamControl_Release(IAMStreamControl *iface)
1514 AviMuxIn *avimuxin = AviMuxIn_from_IAMStreamControl(iface);
1515 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1516 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1519 static HRESULT WINAPI AviMuxIn_AMStreamControl_StartAt(IAMStreamControl *iface,
1520 const REFERENCE_TIME *start, DWORD cookie)
1522 FIXME("iface %p, start %p, cookie %#lx, stub!\n", iface, start, cookie);
1523 return E_NOTIMPL;
1526 static HRESULT WINAPI AviMuxIn_AMStreamControl_StopAt(IAMStreamControl *iface,
1527 const REFERENCE_TIME *stop, BOOL send_extra, DWORD cookie)
1529 FIXME("iface %p, stop %p, send_extra %d, cookie %#lx, stub!\n", iface, stop, send_extra, cookie);
1530 return E_NOTIMPL;
1533 static HRESULT WINAPI AviMuxIn_AMStreamControl_GetInfo(IAMStreamControl *iface,
1534 AM_STREAM_INFO *info)
1536 FIXME("iface %p, info %p, stub!\n", iface, info);
1537 return E_NOTIMPL;
1540 static const IAMStreamControlVtbl AviMuxIn_AMStreamControlVtbl = {
1541 AviMuxIn_AMStreamControl_QueryInterface,
1542 AviMuxIn_AMStreamControl_AddRef,
1543 AviMuxIn_AMStreamControl_Release,
1544 AviMuxIn_AMStreamControl_StartAt,
1545 AviMuxIn_AMStreamControl_StopAt,
1546 AviMuxIn_AMStreamControl_GetInfo
1549 static inline AviMuxIn* AviMuxIn_from_IMemInputPin(IMemInputPin *iface)
1551 return CONTAINING_RECORD(iface, AviMuxIn, pin.IMemInputPin_iface);
1554 static HRESULT WINAPI AviMuxIn_MemInputPin_QueryInterface(
1555 IMemInputPin *iface, REFIID riid, void **ppv)
1557 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1558 return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv);
1561 static ULONG WINAPI AviMuxIn_MemInputPin_AddRef(IMemInputPin *iface)
1563 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1564 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1565 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1568 static ULONG WINAPI AviMuxIn_MemInputPin_Release(IMemInputPin *iface)
1570 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1571 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1572 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1575 static HRESULT WINAPI AviMuxIn_MemInputPin_GetAllocator(
1576 IMemInputPin *iface, IMemAllocator **ppAllocator)
1578 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1580 TRACE("pin %p, ppAllocator %p.\n", avimuxin, ppAllocator);
1582 if(!ppAllocator)
1583 return E_POINTER;
1585 IMemAllocator_AddRef(avimuxin->pin.pAllocator);
1586 *ppAllocator = avimuxin->pin.pAllocator;
1587 return S_OK;
1590 static HRESULT WINAPI AviMuxIn_MemInputPin_NotifyAllocator(
1591 IMemInputPin *iface, IMemAllocator *pAllocator, BOOL bReadOnly)
1593 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1594 ALLOCATOR_PROPERTIES props;
1595 HRESULT hr;
1597 TRACE("pin %p, pAllocator %p, bReadOnly %d.\n", avimuxin, pAllocator, bReadOnly);
1599 if(!pAllocator)
1600 return E_POINTER;
1602 memset(&props, 0, sizeof(props));
1603 hr = IMemAllocator_GetProperties(pAllocator, &props);
1604 if(FAILED(hr))
1605 return hr;
1607 props.cbAlign = 1;
1608 props.cbPrefix = 8;
1609 return IMemAllocator_SetProperties(avimuxin->pin.pAllocator, &props, &props);
1612 static HRESULT WINAPI AviMuxIn_MemInputPin_GetAllocatorRequirements(
1613 IMemInputPin *iface, ALLOCATOR_PROPERTIES *pProps)
1615 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1617 TRACE("pin %p, pProps %p.\n", avimuxin, pProps);
1619 if(!pProps)
1620 return E_POINTER;
1622 pProps->cbAlign = 1;
1623 pProps->cbPrefix = 8;
1624 return S_OK;
1627 static HRESULT WINAPI AviMuxIn_MemInputPin_Receive(
1628 IMemInputPin *iface, IMediaSample *pSample)
1630 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1632 TRACE("pin %p, pSample %p.\n", avimuxin, pSample);
1634 return avimuxin->pin.pFuncsTable->pfnReceive(&avimuxin->pin, pSample);
1637 static HRESULT WINAPI AviMuxIn_MemInputPin_ReceiveMultiple(IMemInputPin *iface,
1638 IMediaSample **pSamples, LONG nSamples, LONG *nSamplesProcessed)
1640 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1641 HRESULT hr = S_OK;
1643 TRACE("pin %p, pSamples %p, nSamples %ld, nSamplesProcessed %p.\n",
1644 avimuxin, pSamples, nSamples, nSamplesProcessed);
1646 for(*nSamplesProcessed=0; *nSamplesProcessed<nSamples; (*nSamplesProcessed)++)
1648 hr = avimuxin->pin.pFuncsTable->pfnReceive(&avimuxin->pin, pSamples[*nSamplesProcessed]);
1649 if(hr != S_OK)
1650 break;
1653 return hr;
1656 static HRESULT WINAPI AviMuxIn_MemInputPin_ReceiveCanBlock(IMemInputPin *iface)
1658 AviMuxIn *avimuxin = AviMuxIn_from_IMemInputPin(iface);
1659 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1660 HRESULT hr;
1662 TRACE("avimuxin %p.\n", avimuxin);
1664 if(!This->source.pMemInputPin)
1665 return S_FALSE;
1667 hr = IMemInputPin_ReceiveCanBlock(This->source.pMemInputPin);
1668 return hr != S_FALSE ? S_OK : S_FALSE;
1671 static const IMemInputPinVtbl AviMuxIn_MemInputPinVtbl = {
1672 AviMuxIn_MemInputPin_QueryInterface,
1673 AviMuxIn_MemInputPin_AddRef,
1674 AviMuxIn_MemInputPin_Release,
1675 AviMuxIn_MemInputPin_GetAllocator,
1676 AviMuxIn_MemInputPin_NotifyAllocator,
1677 AviMuxIn_MemInputPin_GetAllocatorRequirements,
1678 AviMuxIn_MemInputPin_Receive,
1679 AviMuxIn_MemInputPin_ReceiveMultiple,
1680 AviMuxIn_MemInputPin_ReceiveCanBlock
1683 static inline AviMuxIn* AviMuxIn_from_IPropertyBag(IPropertyBag *iface)
1685 return CONTAINING_RECORD(iface, AviMuxIn, IPropertyBag_iface);
1688 static HRESULT WINAPI AviMuxIn_PropertyBag_QueryInterface(
1689 IPropertyBag *iface, REFIID riid, void **ppv)
1691 AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface);
1692 return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv);
1695 static ULONG WINAPI AviMuxIn_PropertyBag_AddRef(IPropertyBag *iface)
1697 AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface);
1698 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1699 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1702 static ULONG WINAPI AviMuxIn_PropertyBag_Release(IPropertyBag *iface)
1704 AviMuxIn *avimuxin = AviMuxIn_from_IPropertyBag(iface);
1705 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1706 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1709 static HRESULT WINAPI AviMuxIn_PropertyBag_Read(IPropertyBag *iface,
1710 const WCHAR *name, VARIANT *value, IErrorLog *error_log)
1712 FIXME("iface %p, name %s, value %p, error_log %p, stub!\n",
1713 iface, debugstr_w(name), value, error_log);
1714 return E_NOTIMPL;
1717 static HRESULT WINAPI AviMuxIn_PropertyBag_Write(IPropertyBag *iface,
1718 const WCHAR *name, VARIANT *value)
1720 FIXME("iface %p, name %s, value %s, stub!\n",
1721 iface, debugstr_w(name), debugstr_variant(value));
1722 return E_NOTIMPL;
1725 static const IPropertyBagVtbl AviMuxIn_PropertyBagVtbl = {
1726 AviMuxIn_PropertyBag_QueryInterface,
1727 AviMuxIn_PropertyBag_AddRef,
1728 AviMuxIn_PropertyBag_Release,
1729 AviMuxIn_PropertyBag_Read,
1730 AviMuxIn_PropertyBag_Write
1733 static inline AviMuxIn* AviMuxIn_from_IQualityControl(IQualityControl *iface)
1735 return CONTAINING_RECORD(iface, AviMuxIn, IQualityControl_iface);
1738 static HRESULT WINAPI AviMuxIn_QualityControl_QueryInterface(
1739 IQualityControl *iface, REFIID riid, void **ppv)
1741 AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface);
1742 return IPin_QueryInterface(&avimuxin->pin.pin.IPin_iface, riid, ppv);
1745 static ULONG WINAPI AviMuxIn_QualityControl_AddRef(IQualityControl *iface)
1747 AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface);
1748 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1749 return IBaseFilter_AddRef(&This->filter.IBaseFilter_iface);
1752 static ULONG WINAPI AviMuxIn_QualityControl_Release(IQualityControl *iface)
1754 AviMuxIn *avimuxin = AviMuxIn_from_IQualityControl(iface);
1755 AviMux *This = impl_from_in_IPin(&avimuxin->pin.pin.IPin_iface);
1756 return IBaseFilter_Release(&This->filter.IBaseFilter_iface);
1759 static HRESULT WINAPI AviMuxIn_QualityControl_Notify(IQualityControl *iface,
1760 IBaseFilter *filter, Quality q)
1762 FIXME("iface %p, filter %p, type %u, proportion %ld, late %s, timestamp %s, stub!\n",
1763 iface, filter, q.Type, q.Proportion, wine_dbgstr_longlong(q.Late),
1764 wine_dbgstr_longlong(q.TimeStamp));
1765 return E_NOTIMPL;
1768 static HRESULT WINAPI AviMuxIn_QualityControl_SetSink(IQualityControl *iface, IQualityControl *sink)
1770 FIXME("iface %p, sink %p, stub!\n", iface, sink);
1771 return E_NOTIMPL;
1774 static const IQualityControlVtbl AviMuxIn_QualityControlVtbl = {
1775 AviMuxIn_QualityControl_QueryInterface,
1776 AviMuxIn_QualityControl_AddRef,
1777 AviMuxIn_QualityControl_Release,
1778 AviMuxIn_QualityControl_Notify,
1779 AviMuxIn_QualityControl_SetSink
1782 static HRESULT create_input_pin(AviMux *avimux)
1784 AviMuxIn *object;
1785 WCHAR name[19];
1786 HRESULT hr;
1788 if(avimux->input_pin_no >= MAX_PIN_NO-1)
1789 return E_FAIL;
1791 swprintf(name, ARRAY_SIZE(name), L"Input %02u", avimux->input_pin_no + 1);
1793 if (!(object = calloc(1, sizeof(*object))))
1794 return E_OUTOFMEMORY;
1796 strmbase_sink_init(&object->pin, &avimux->filter, name, &sink_ops, NULL);
1797 object->pin.IMemInputPin_iface.lpVtbl = &AviMuxIn_MemInputPinVtbl;
1798 object->IAMStreamControl_iface.lpVtbl = &AviMuxIn_AMStreamControlVtbl;
1799 object->IPropertyBag_iface.lpVtbl = &AviMuxIn_PropertyBagVtbl;
1800 object->IQualityControl_iface.lpVtbl = &AviMuxIn_QualityControlVtbl;
1802 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER,
1803 &IID_IMemAllocator, (void **)&object->samples_allocator);
1804 if (FAILED(hr))
1806 strmbase_sink_cleanup(&object->pin);
1807 free(object);
1808 return hr;
1811 hr = CoCreateInstance(&CLSID_MemoryAllocator, NULL, CLSCTX_INPROC_SERVER,
1812 &IID_IMemAllocator, (void **)&object->pin.pAllocator);
1813 if (FAILED(hr))
1815 IMemAllocator_Release(object->samples_allocator);
1816 strmbase_sink_cleanup(&object->pin);
1817 free(object);
1818 return hr;
1821 object->indx = (AVISUPERINDEX *)&object->indx_data;
1822 object->ix = (AVISTDINDEX *)object->ix_data;
1824 avimux->in[avimux->input_pin_no++] = object;
1825 return S_OK;
1828 HRESULT avi_mux_create(IUnknown *outer, IUnknown **out)
1830 AviMux *avimux;
1831 HRESULT hr;
1833 if (!(avimux = calloc(1, sizeof(AviMux))))
1834 return E_OUTOFMEMORY;
1836 strmbase_filter_init(&avimux->filter, outer, &CLSID_AviDest, &filter_ops);
1837 avimux->IConfigAviMux_iface.lpVtbl = &ConfigAviMuxVtbl;
1838 avimux->IConfigInterleaving_iface.lpVtbl = &ConfigInterleavingVtbl;
1839 avimux->IMediaSeeking_iface.lpVtbl = &MediaSeekingVtbl;
1840 avimux->IPersistMediaPropertyBag_iface.lpVtbl = &PersistMediaPropertyBagVtbl;
1841 avimux->ISpecifyPropertyPages_iface.lpVtbl = &SpecifyPropertyPagesVtbl;
1843 strmbase_source_init(&avimux->source, &avimux->filter, L"AVI Out", &source_ops);
1844 avimux->IQualityControl_iface.lpVtbl = &AviMuxOut_QualityControlVtbl;
1845 avimux->cur_stream = 0;
1846 avimux->cur_time = 0;
1847 avimux->stream = NULL;
1849 hr = create_input_pin(avimux);
1850 if(FAILED(hr)) {
1851 strmbase_source_cleanup(&avimux->source);
1852 strmbase_filter_cleanup(&avimux->filter);
1853 free(avimux);
1854 return hr;
1857 avimux->interleave = 10000000;
1859 TRACE("Created AVI mux %p.\n", avimux);
1860 *out = &avimux->filter.IUnknown_inner;
1861 return S_OK;