Recognizes if input is ogg or not.
[xiph/unicode.git] / oggds / OggMuxDS / OggMuxDS.cpp
blob5bfe0b9a2ddf51f41c6ff5abfb0dc6d0867b6f3f
1 /*******************************************************************************
2 * *
3 * This file is part of the Ogg Vorbis DirectShow filter collection *
4 * *
5 * Copyright (c) 2001, Tobias Waldvogel *
6 * All rights reserved. *
7 * *
8 * Redistribution and use in source and binary forms, with or without *
9 * modification, are permitted provided that the following conditions are met: *
10 * *
11 * - Redistributions of source code must retain the above copyright notice, *
12 * this list of conditions and the following disclaimer. *
13 * *
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. *
17 * *
18 * - The names of the contributors may not be used to endorse or promote *
19 * products derived from this software without specific prior written *
20 * permission. *
21 * *
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. *
33 * *
34 *******************************************************************************/
36 #include "OggMuxDS.h"
37 #include <limits.h>
38 #include <atlbase.h>
41 // Strategy:
42 //
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)
52 m_iInputs = 0;
53 m_TimeCode = TIME_FORMAT_MEDIA_TIME;
55 if (SUCCEEDED(*phr))
57 COggMuxOutputPin* pOut = new COggMuxOutputPin(NAME("Output pin"), this,
58 &(this->m_csFilter), phr, L"Ogg Stream");
59 if(pOut)
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);
90 pPages->cElems = 2;
91 pPages->pElems = (GUID*)CoTaskMemAlloc(2 * sizeof(GUID));
92 pPages->pElems[0] = CLSID_OggMuxPropPage;
93 pPages->pElems[1] = CLSID_OggDSAboutPage;
94 return NOERROR;
97 // Create a new input pin if there is no more left,
98 // which is not already connected
99 HRESULT COggMux::CheckFreeInputPin()
101 int FreePins = 0;
103 for (int i=0; i<m_iInputs; i++)
104 if (!m_paInput[i]->IsConnected()) FreePins++;
106 if (FreePins < 1)
107 { // Create new pin
108 HRESULT hr = NOERROR;
109 WCHAR PinName[256];
110 CHAR PinID[256];
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];
119 if (!paInput)
121 delete pIn;
122 return E_OUTOFMEMORY;
124 if (m_paInput)
126 CopyMemory((void*)paInput, (void*)m_paInput, m_iInputs * sizeof(m_paInput[0]));
127 delete [] m_paInput;
129 m_paInput = paInput;
130 m_paInput[m_iInputs] = pIn;
131 m_iInputs++;
133 return NOERROR;
136 // GetPinCount
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];
152 return NULL;
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;
174 return pEarliestPin;
177 HRESULT COggMux::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate)
179 m_rtSegStart = tStart;
180 return NOERROR;
183 HRESULT COggMux::EndOfStream(void)
185 CAutoLock lock(&m_csFilter);
186 int ActivePins = 0;
187 for (int i=0; i<m_iInputs; i++)
188 if (m_paInput[i]->IsConnected() && !m_paInput[i]->EOSReceived())
189 ActivePins++;
191 // Send EOS if all pins have received already EOS
192 if (!ActivePins) return m_pOutput->DeliverEndOfStream();
193 return NOERROR;
196 HRESULT COggMux::Interleave()
198 CAutoLock lock(&m_csWriteOutPages);
200 HRESULT hr;
201 COggMuxInputPin* pPin;
203 while (pPin = GetEarliestPin())
205 IMediaSample *pOutSample;
206 BYTE *buffer;
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;
232 return NOERROR;
235 void COggMux::ResetBlockedState()
237 for (int i=0; i<m_iInputs; i++)
238 m_paInput[i]->ResetBlockedState();
241 STDMETHODIMP COggMux::Stop()
243 HRESULT hr;
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;
259 return hr;
262 CUnknown* COggMux::CreateInstance(LPUNKNOWN pUnk, HRESULT* phr)
264 return new COggMux(pUnk, phr);