1 /*******************************************************************************
3 * This file is part of the Ogg Vorbis DirectShow filter collection *
5 * Copyright (c) 2001, Tobias Waldvogel *
6 * All rights reserved. *
8 * Redistribution and use in source and binary forms, with or without *
9 * modification, are permitted provided that the following conditions are met: *
11 * - Redistributions of source code must retain the above copyright notice, *
12 * this list of conditions and the following disclaimer. *
14 * - Redistributions in binary form must reproduce the above copyright notice, *
15 * this list of conditions and the following disclaimer in the documentation *
16 * and/or other materials provided with the distribution. *
18 * - The names of the contributors may not be used to endorse or promote *
19 * products derived from this software without specific prior written *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" *
23 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE *
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE *
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE *
26 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF *
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN *
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) *
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
32 * POSSIBILITY OF SUCH DAMAGE. *
34 *******************************************************************************/
43 // To interleave all the streams and to save memory a stream
44 // is blocked in Receive() if other streams are behind.
45 // If we detect that blocking leads to a dead lock the corresponding
46 // stream is not blocked anymore and a queue is used
47 // (Basically if all streams are from the same source)
49 COggMux::COggMux(LPUNKNOWN pUnk
, HRESULT
* phr
) :
50 CBaseFilter(NAME("Ogg Mux"), pUnk
, &m_csFilter
, CLSID_OggMux
)
53 m_TimeCode
= TIME_FORMAT_MEDIA_TIME
;
57 COggMuxOutputPin
* pOut
= new COggMuxOutputPin(NAME("Output pin"), this,
58 &(this->m_csFilter
), phr
, L
"Ogg Stream");
60 if(SUCCEEDED(*phr
)) m_pOutput
= pOut
; else delete pOut
;
61 else *phr
= E_OUTOFMEMORY
;
63 *phr
= CheckFreeInputPin();
67 COggMux::~COggMux(void)
71 STDMETHODIMP
COggMux::NonDelegatingQueryInterface(REFIID riid
, void** ppv
)
73 if(riid
== IID_ISpecifyPropertyPages
)
75 CheckPointer(ppv
, E_POINTER
);
76 return GetInterface((ISpecifyPropertyPages
*)(this), ppv
);
78 if(riid
== IID_IMediaSeeking
)
80 CheckPointer(ppv
, E_POINTER
);
81 return GetInterface((IMediaSeeking
*)(this), ppv
);
83 return CBaseFilter::NonDelegatingQueryInterface(riid
, ppv
);
86 // Provide the about property page
87 STDMETHODIMP
COggMux::GetPages(CAUUID
* pPages
)
89 CheckPointer(pPages
, E_POINTER
);
91 pPages
->pElems
= (GUID
*)CoTaskMemAlloc(2 * sizeof(GUID
));
92 pPages
->pElems
[0] = CLSID_OggMuxPropPage
;
93 pPages
->pElems
[1] = CLSID_OggDSAboutPage
;
97 // Create a new input pin if there is no more left,
98 // which is not already connected
99 HRESULT
COggMux::CheckFreeInputPin()
103 for (int i
=0; i
<m_iInputs
; i
++)
104 if (!m_paInput
[i
]->IsConnected()) FreePins
++;
108 HRESULT hr
= NOERROR
;
111 COggMuxInputPin
*pIn
;
112 COggMuxInputPin
**paInput
;
114 wsprintfW(PinName
,L
"Stream %d", m_iInputs
);
115 wsprintf(PinID
, "Stream %d", m_iInputs
);
116 pIn
= new COggMuxInputPin(PinID
, this, &m_csFilter
, m_iInputs
, PinName
, &hr
);
117 if FAILED(hr
) return hr
;
118 paInput
= new COggMuxInputPin
*[m_iInputs
+1];
122 return E_OUTOFMEMORY
;
126 CopyMemory((void*)paInput
, (void*)m_paInput
, m_iInputs
* sizeof(m_paInput
[0]));
130 m_paInput
[m_iInputs
] = pIn
;
137 // Returns the number of pins this filter has
138 int COggMux::GetPinCount(void)
140 CAutoLock
lock(&m_csFilter
);
141 return m_iInputs
+ 1;
144 // Return a non-addref'd pointer to pin n
145 // needed by CBaseFilter
146 CBasePin
* COggMux::GetPin(int n
)
148 CAutoLock
lock(&m_csFilter
);
150 if (n
== 0) return m_pOutput
;
151 else if ((n
> 0) && (n
<= m_iInputs
)) return m_paInput
[n
-1];
155 COggMuxInputPin
*COggMux::GetEarliestPin(void)
157 REFERENCE_TIME TimeStamp
= _I64_MAX
;
158 REFERENCE_TIME rtCurrent
;
159 COggMuxInputPin
*pEarliestPin
;
161 // This loop starts with the highest stream number
162 // to give lower stream numbers higher priority
163 for (int i
=m_iInputs
-1; i
>=0; i
--)
164 if (m_paInput
[i
]->IsConnected())
166 rtCurrent
= m_paInput
[i
]->CurrentPos();
167 if (rtCurrent
<= TimeStamp
)
169 TimeStamp
= rtCurrent
;
170 pEarliestPin
= m_paInput
[i
]->OggPageQueuePages() != 0 ? m_paInput
[i
] : NULL
;
177 HRESULT
COggMux::NewSegment(REFERENCE_TIME tStart
, REFERENCE_TIME tStop
, double dRate
)
179 m_rtSegStart
= tStart
;
183 HRESULT
COggMux::EndOfStream(void)
185 CAutoLock
lock(&m_csFilter
);
187 for (int i
=0; i
<m_iInputs
; i
++)
188 if (m_paInput
[i
]->IsConnected() && !m_paInput
[i
]->EOSReceived())
191 // Send EOS if all pins have received already EOS
192 if (!ActivePins
) return m_pOutput
->DeliverEndOfStream();
196 HRESULT
COggMux::Interleave()
198 CAutoLock
lock(&m_csWriteOutPages
);
201 COggMuxInputPin
* pPin
;
203 while (pPin
= GetEarliestPin())
205 IMediaSample
*pOutSample
;
207 tPageNode
* pPage
= pPin
->GetPage();
210 CAutoLock
lock(&m_csPosition
);
211 m_rtPosition
= pPage
->rtPos
;
213 hr
= m_pOutput
->GetSample(&pOutSample
, pPage
->og
.header_len
+pPage
->og
.body_len
);
214 if FAILED(hr
) return hr
;
216 // Buffer overflow shoudn't occur but to be on the safe side ...
217 if (pPage
->og
.header_len
+pPage
->og
.body_len
> pOutSample
->GetSize())
218 return VFW_E_BUFFER_OVERFLOW
;
220 pOutSample
->GetPointer(&buffer
);
221 memcpy(buffer
, pPage
->og
.header
, pPage
->og
.header_len
);
222 memcpy(buffer
+pPage
->og
.header_len
, pPage
->og
.body
, pPage
->og
.body_len
);
223 pOutSample
->SetActualDataLength(pPage
->og
.header_len
+pPage
->og
.body_len
);
224 pPin
->OggPageRelease(pPage
);
225 pOutSample
->SetDiscontinuity(FALSE
);
226 pOutSample
->SetPreroll(FALSE
);
227 pOutSample
->SetSyncPoint(TRUE
);
228 hr
= m_pOutput
->Deliver(pOutSample
);
229 pOutSample
->Release();
230 if FAILED(hr
) return hr
;
235 void COggMux::ResetBlockedState()
237 for (int i
=0; i
<m_iInputs
; i
++)
238 m_paInput
[i
]->ResetBlockedState();
241 STDMETHODIMP
COggMux::Stop()
244 hr
= CBaseFilter::Stop();
246 for (int i
=0; i
<m_iInputs
; i
++)
248 // Delete all page buffers ...
249 m_paInput
[i
]->OggPageQueueFlush();
250 stream_state_clear(&m_paInput
[i
]->m_ss
);
251 if (m_paInput
[i
]->m_sh
)
253 free(m_paInput
[i
]->m_sh
);
254 m_paInput
[i
]->m_sh
= NULL
;
262 CUnknown
* COggMux::CreateInstance(LPUNKNOWN pUnk
, HRESULT
* phr
)
264 return new COggMux(pUnk
, phr
);