Recognizes if input is ogg or not.
[xiph.git] / oggds / OggSplitterDS / OggSplitStream.cpp
blob90af8cc1d10a2ac5f807b1cff02e72e5de1937c4
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 "OggSplitterDS.h"
38 COggStream::COggStream(int iStreamID, COggSplitter *pOggSplitter, bool bDummyStream) :
39 m_evWaitForData(FALSE)
41 m_pOggSplitter = pOggSplitter;
42 m_iStreamID = iStreamID;
43 m_bIdentified = false;
44 m_iStreamType = cnts_UNKNOWN;
45 m_pPin = NULL;
47 stream_state_init(&m_ss, iStreamID);
48 m_sh = NULL;
49 vorbis_info_init(&m_vi);
50 vorbis_comment_init(&m_vc);
51 memset(&m_VorbisHeader, 0, sizeof(ogg_packet) * 3);
52 m_bIsDummyStream = bDummyStream;
54 if (bDummyStream)
56 m_mt.InitMediaType();
57 m_mt.SetType(&MEDIATYPE_Text);
58 m_iBufferSize = 1024;
59 vorbis_comment_add_tag(&m_vc, "LANGUAGE", "off");
63 COggStream::~COggStream()
65 stream_state_clear(&m_ss);
66 vorbis_info_clear(&m_vi);
67 vorbis_comment_clear(&m_vc);
68 if (m_sh) free(m_sh);
71 HRESULT COggStream::IdentifyType(ogg_page *og)
73 if (m_bIdentified) return S_OK; // Everything is already done
75 ogg_packet op;
77 ogg_stream_pagein(&m_ss.os, og);
78 while (ogg_stream_packetout(&m_ss.os, &op) > 0)
80 if ((*op.packet & PACKET_TYPE_HEADER) == 0)
82 m_bIdentified = true; // This is already a data packet
83 return S_OK; // We are done
86 switch (*op.packet & PACKET_TYPE_BITS)
88 case PACKET_TYPE_HEADER:
89 if (stream_header_in(&m_sh, &op) == E_SUCCESS) // => it is our stream format
91 SetMTFromSH(m_sh, &m_mt, &m_iLowWatermark, &m_iHighWatermark);
92 m_iBufferSize = m_sh->buffersize;
94 m_vi.rate = 1; // otherwhise vorbis_synthesis_headerin will fail
95 m_iStreamType = cnts_OGGSTREAM;
97 else if (vorbis_synthesis_headerin(&m_vi, &m_vc, &op) >= 0) // => it is Ogg/Vorbis
99 CopyOggPacket(&m_VorbisHeader[0], &op);
101 SetMTFromVI(&m_vi, &m_mt);
102 m_iBufferSize = 8192; // Vorbis packets shoudn't contain more than
104 m_iHighWatermark = 32;
105 m_iLowWatermark = 16;
107 m_iStreamType = cnts_VORBIS;
109 else // No idea what's that
111 m_mt.SetType(&MEDIATYPE_NULL);
112 m_bIdentified = true; // Unknown type
113 m_iStreamType = cnts_UNKNOWN;
114 return S_FALSE;
116 break;
118 case PACKET_TYPE_COMMENT:
119 vorbis_synthesis_headerin(&m_vi, &m_vc, &op);
120 CopyOggPacket(&m_VorbisHeader[1], &op);
121 break;
123 case PACKET_TYPE_CODEBOOK:
124 if (m_iStreamType == cnts_VORBIS)
126 vorbis_synthesis_headerin(&m_vi, &m_vc, &op);
127 CopyOggPacket(&m_VorbisHeader[2], &op);
128 m_bIdentified = true;
129 return S_OK;
131 } // case
133 return S_OK;
136 bool COggStream::PageInFromQueue()
138 tPageNode* pPage;
140 pPage = OggPageDequeue();
141 if (!pPage) return false;
143 ogg_stream_pagein(&m_ss.os, &pPage->og);
144 OggPageRelease(pPage);
146 if (OggPageQueuePages() < m_iLowWatermark)
147 m_pOggSplitter->m_pInput->NotifyLowFilllevel();
149 return true;
152 // Add a page in the list and return if HighWatermark was reached
153 bool COggStream::DeliverPage(ogg_page *pog)
155 // If the group is not connected we are not interested in the page
156 if (!(m_pPin->IsConnected())) return false;
158 if (m_bAfterReset)
160 if (ogg_page_packets(pog) == 0) return false; // This would cause a problem in the Ogg lib
161 m_bAfterReset = false;
164 OggPageEnqueue(pog, NULL);
165 m_evWaitForData.Set(); // Signal the stream thread that we have received a page
167 return OggPageQueuePages() >= m_iHighWatermark;
170 HRESULT COggStream::Active()
172 if (m_pOggSplitter->IsActive())
173 return S_FALSE;
174 if (!m_pPin->IsConnected())
175 return NOERROR;
176 if (!Create())
177 return E_FAIL;
178 CallWorker(CMD_RUN);
180 return NOERROR;
183 HRESULT COggStream::Inactive()
185 if (!ThreadExists())
186 return NOERROR;
188 m_bAbort = true;
189 m_evWaitForData.Set(); // signal the events to be sure that
190 m_evWaitForGroup.Set(); // the thread is not blocked anymore
191 CallWorker(CMD_EXIT);
192 Close(); // Wait for the thread to exit, then tidy up.
194 OggPageQueueFlush();
195 return NOERROR;
198 HRESULT COggStream::BeginFlush()
200 if (!ThreadExists())
201 return NOERROR;
203 m_bAbort = true;
204 m_evWaitForData.Set();
205 m_evWaitForGroup.Set();
206 CallWorker(CMD_STOP);
207 return NOERROR;
210 HRESULT COggStream::EndFlush()
212 OggPageQueueFlush();
214 if (ThreadExists())
215 CallWorker(CMD_RUN);
217 return NOERROR;
220 DWORD COggStream::ThreadProc(void)
222 Command com;
225 com = (Command)GetRequest();
227 switch (com)
229 case CMD_EXIT:
230 Reply(NOERROR);
231 break;
233 case CMD_STOP:
234 Reply(NOERROR);
235 break;
237 case CMD_RUN:
238 if (!m_bIsDummyStream)
239 SendSampleLoop();
240 else
241 SendDummySampleLoop();
242 break;
244 default:
245 Reply((DWORD) E_NOTIMPL);
246 break;
248 } while (com != CMD_EXIT);
250 return NOERROR;
253 bool COggStream::GetSample(REFERENCE_TIME* prtStart, REFERENCE_TIME* prtStop,
254 ogg_int64_t* pmtStart, ogg_int64_t* pmtStop,
255 bool* pbSyncPoint, bool* pbEOS,
256 unsigned char** ppbBuffer, int* pcbBuffer)
260 int or;
262 if (m_iStreamType == cnts_VORBIS)
264 or = stream_sampleout_vorbis(&m_ss, &m_vi, pbEOS,
265 pmtStart, prtStart, ppbBuffer, pcbBuffer);
266 *prtStop = *prtStart;
267 *pmtStop = *pmtStart;
268 *pbSyncPoint = true;
270 else
272 or = stream_sampleout(&m_ss, m_sh, pbEOS, pbSyncPoint,
273 pmtStart, pmtStop, prtStart, prtStop, ppbBuffer, pcbBuffer);
274 *pmtStop += *pmtStart;
277 if (or > 0)
278 return true; // Got a sample => we are done
280 if (or == 0)
281 if (!PageInFromQueue())
282 return false; // If there are no more pages in the queue then
283 // there is no sample available
285 } while(true);
288 void COggStream::SendDummySample()
290 IMediaSample* pSample;
291 BYTE* pSampBuffer;
292 REFERENCE_TIME rtStart = 0;
293 REFERENCE_TIME rtStop = 1;
295 if FAILED(m_pPin->GetDeliveryBuffer(&pSample,NULL,NULL, 0)) return;
297 pSample->GetPointer(&pSampBuffer);
298 *pSampBuffer = 0;
299 pSample->SetActualDataLength(1);
301 pSample->SetTime(&rtStart,&rtStop);
302 pSample->SetPreroll(FALSE);
303 pSample->SetDiscontinuity(TRUE);
304 pSample->SetSyncPoint(TRUE);
305 m_pPin->Deliver(pSample);
306 pSample->Release();
309 HRESULT COggStream::SendSampleLoop()
311 HRESULT hr;
312 Command com;
314 stream_state_reset(&m_ss);
315 m_bAfterReset = true;
317 m_bAbort = false;
318 m_evWaitForData.Reset();
319 m_evWaitForGroup.Reset();
320 m_bDiscontinuity = true;
322 REFERENCE_TIME rtSegLen = m_pOggSplitter->m_rtStop - m_pOggSplitter->m_rtStart;
324 Reply(NOERROR);
326 if (m_bEnabled)
328 m_pPin->DeliverNewSegment(m_pOggSplitter->m_rtStart, m_pOggSplitter->m_rtStop,
329 m_pOggSplitter->m_dRate);
330 // If there is currently no subtitle then no sample is sent to the
331 // subsequent filter. But DirectShow expect at least one sample to
332 // go into pause state and the graph would block. This is a work around
333 // We send just a 100ns sample with just a 0 Byte.
334 if (m_iStreamType == cnts_OGGSTREAM)
335 if (strcmp(m_sh->streamtype, "text") == 0)
336 SendDummySample();
341 REFERENCE_TIME rtLastPos;
342 REFERENCE_TIME rtStart, rtStop;
343 __int64 mtStart, mtStop;
345 bool bSyncPoint;
346 bool bEOS;
348 unsigned char *pbBuffer;
349 int cbBuffer;
351 if (GetSample(&rtStart, &rtStop, &mtStart, &mtStop, &bSyncPoint, &bEOS, &pbBuffer, &cbBuffer))
353 if (!m_bDiscontinuity || bSyncPoint) // => we have valid times
355 rtStart -= m_pOggSplitter->m_rtStart;
356 rtStop -= m_pOggSplitter->m_rtStart;
357 m_pPin->GetLastPos(&rtLastPos);
359 if ((rtStart >= 0) && (rtStart > rtLastPos))
361 if (m_bEnabled)
363 if (m_bDiscontinuity && m_iStreamType == cnts_VORBIS)
364 m_pPin->SendVorbisHeaderPackets(&(m_VorbisHeader[0]));
366 IMediaSample* pSample;
367 BYTE* pSampBuffer;
369 hr = m_pPin->GetDeliveryBuffer(&pSample,NULL,NULL, bSyncPoint ? 0 : AM_GBF_NOTASYNCPOINT);
370 if FAILED(hr) return hr;
372 pSample->GetPointer(&pSampBuffer);
373 memcpy(pSampBuffer, pbBuffer, cbBuffer);
374 pSample->SetActualDataLength(cbBuffer);
376 pSample->SetTime(&rtStart,&rtStop);
377 pSample->SetMediaTime(&mtStart, &mtStop);
378 pSample->SetPreroll(FALSE);
379 pSample->SetDiscontinuity(m_bDiscontinuity ? TRUE : FALSE);
380 pSample->SetSyncPoint(bSyncPoint ? TRUE : FALSE);
381 m_bDiscontinuity = false;
382 hr = m_pPin->Deliver(pSample);
383 pSample->Release();
384 if FAILED(hr) return hr;
386 // inform the other streams ...
387 m_pOggSplitter->NotifyGroup(m_iGroupID, m_pPin, rtStart);
389 if ((rtStop > rtSegLen) || bEOS)
390 return m_pPin->DeliverEndOfStream();
394 else // Stream is not enabled
396 m_evWaitForGroup.Wait(INFINITE);
397 m_bDiscontinuity = TRUE;
398 if (rtStop > rtSegLen)
399 return NOERROR;
405 else
407 // There was no sample available
408 if (m_pOggSplitter->IsEOF())
410 if (m_bEnabled)
411 return m_pPin->DeliverEndOfStream();
412 else return NOERROR;
414 m_evWaitForData.Wait(INFINITE); // wait until we get more data
417 } while (!CheckRequest((DWORD*)&com) && !m_bAbort);
418 return NOERROR;
421 // Just sends empty samples
422 // Used for the dummy subtitle stream otherwise
423 // The internal script renderer would block
424 HRESULT COggStream::SendDummySampleLoop()
426 HRESULT hr;
427 Command com;
428 REFERENCE_TIME rtStreamStart, rtStreamStop;
429 double dRate;
431 m_bAbort = false;
432 m_evWaitForGroup.Reset();
433 m_bDiscontinuity = TRUE;
435 m_pOggSplitter->GetPositions(&rtStreamStart, &rtStreamStop);
436 m_pOggSplitter->GetRate(&dRate);
437 Reply(NOERROR);
439 if (m_bEnabled)
440 m_pPin->DeliverNewSegment(rtStreamStart, rtStreamStop, dRate);
442 REFERENCE_TIME rtStart, rtStop;
444 rtStart = 0;
448 rtStop = rtStart + SEC_IN_REFTIME;
450 REFERENCE_TIME rtLastPos;
451 m_pPin->GetLastPos(&rtLastPos);
453 if (m_bEnabled)
455 if (rtStart > rtLastPos)
457 IMediaSample *pSample;
458 BYTE* pSampBuffer;
460 hr = m_pPin->GetDeliveryBuffer(&pSample,NULL,NULL,0);
461 if FAILED(hr) return (hr);
463 pSample->GetPointer(&pSampBuffer);
464 *pSampBuffer = '\0';
466 pSample->SetDiscontinuity(m_bDiscontinuity);
467 pSample->SetSyncPoint(true);
468 pSample->SetTime(&rtStart,&rtStop);
469 pSample->SetPreroll(rtStart < 0);
470 pSample->SetActualDataLength(1);
471 hr = m_pPin->Deliver(pSample);
472 pSample->Release();
473 if FAILED(hr) return (hr);
474 m_bDiscontinuity = FALSE;
476 // inform the other streams ...
477 m_pOggSplitter->NotifyGroup(m_iGroupID, m_pPin, rtStart);
480 if ((rtStop > (rtStreamStop - rtStreamStart)) || m_pOggSplitter->IsEOF())
481 return m_pPin->DeliverEndOfStream();
483 else // Not enabled
485 if (rtStart > rtLastPos)
486 m_evWaitForGroup.Wait(INFINITE);
487 m_bDiscontinuity = TRUE;
488 if ((rtStop > (rtStreamStop - rtStreamStart)) || m_pOggSplitter->IsEOF())
489 return NOERROR;
492 rtStart = rtStop;
494 } while (!CheckRequest((DWORD*)&com) && !m_bAbort);
495 return NOERROR;
498 void COggStream::Enable(bool bEnabled)
500 m_bDiscontinuity = TRUE;
501 m_bEnabled = bEnabled;
502 if (m_pPin)
503 m_pPin->m_pStream = this;
506 __int64 COggStream::MediaTimeToRefTime(__int64 iMediaTime)
508 if (m_iStreamType == cnts_UNKNOWN)
509 return -1;
510 else if (m_iStreamType == cnts_VORBIS)
511 return mediatime_to_reference_time(SEC_IN_REFTIME, m_vi.rate, iMediaTime);
512 return mediatime_to_reference_time(m_sh->time_unit, m_sh->samples_per_unit, iMediaTime);
515 // This is the fourcc mapper feature
516 void COggStream::TranslateFourCC(char* FOURCC)
518 HKEY hReg;
519 HKEY hFourCCReg;
521 hReg = m_pOggSplitter->OpenRegistry();
522 if (!hReg) return;
524 if (RegOpenKeyEx(hReg, idFourCCMapping, 0, KEY_READ, &hFourCCReg) != ERROR_SUCCESS)
526 RegCloseKey(hReg);
527 return;
530 // Now the registry is opened
532 char srcFOURCC[11];
533 DWORD dstFOURCC;
534 DWORD* pdwFOURCC = (DWORD*) FOURCC;
535 DWORD dwSize;
536 int iReturn;
538 wsprintf(srcFOURCC, "0x%08x", *pdwFOURCC);
539 dstFOURCC = *pdwFOURCC;
543 dwSize = sizeof(dstFOURCC);
545 iReturn = RegQueryValueEx(hFourCCReg, srcFOURCC, NULL, NULL, (BYTE*)&dstFOURCC, &dwSize);
546 if (iReturn == ERROR_SUCCESS)
548 if (dstFOURCC == *pdwFOURCC) // To avoid circular conversions
550 *pdwFOURCC = strtol(srcFOURCC, NULL, 16);
551 RegCloseKey(hFourCCReg);
552 RegCloseKey(hReg);
553 return;
557 if (iReturn != ERROR_SUCCESS)
559 // There was no entry => we are done
560 *pdwFOURCC = dstFOURCC;
561 RegCloseKey(hFourCCReg);
562 RegCloseKey(hReg);
563 return;
566 wsprintf(srcFOURCC, "0x%08x", dstFOURCC);
567 } while (true);
570 void COggStream::SetMTFromSH(stream_header *sh, CMediaType *pmt, int* pHighWM, int* pLowWM)
572 if (strncmp((char*)&sh->streamtype, MT_Video, strlen(MT_Video)) == 0)
574 TranslateFourCC(&sh->subtype[0]);
576 pmt->InitMediaType();
577 pmt->SetType(&MEDIATYPE_Video);
578 pmt->SetSubtype(&MEDIASUBTYPE_YVYU);
579 pmt->subtype.Data1 = *(ogg_int32_t*)&sh->subtype;
581 if (*(pmt->Subtype()) != MEDIASUBTYPE_RGB565 &&
582 *(pmt->Subtype()) != MEDIASUBTYPE_RGB555 &&
583 *(pmt->Subtype()) != MEDIASUBTYPE_RGB24 &&
584 *(pmt->Subtype()) != MEDIASUBTYPE_RGB32 &&
585 *(pmt->Subtype()) != MEDIASUBTYPE_ARGB32 &&
586 *(pmt->Subtype()) != MEDIASUBTYPE_YUY2 &&
587 *(pmt->Subtype()) != MEDIASUBTYPE_UYVY &&
588 *(pmt->Subtype()) != MEDIASUBTYPE_YVYU)
590 pmt->SetTemporalCompression(TRUE);
591 pmt->SetVariableSize();
593 pmt->SetFormatType(&FORMAT_VideoInfo);
594 VIDEOINFO* pvi = (VIDEOINFO*)pmt->AllocFormatBuffer(sizeof(VIDEOINFO));
595 memset(pvi, 0, sizeof(*pvi));
596 pvi->AvgTimePerFrame = sh->time_unit;
597 pvi->bmiHeader.biBitCount = sh->bits_per_sample;
598 pvi->bmiHeader.biPlanes = 1;
599 pvi->bmiHeader.biSize = sizeof(pvi->bmiHeader);
600 pvi->bmiHeader.biWidth = sh->video.width;
601 pvi->bmiHeader.biHeight = sh->video.height;
602 pvi->bmiHeader.biCompression = *(ogg_int32_t*)&sh->subtype;
604 *pHighWM = sh->buffersize * 10 / 4096;
605 *pLowWM = *pHighWM / 2;
607 return;
610 if (strncmp((char*)&sh->streamtype, MT_Audio, strlen(MT_Audio)) == 0)
612 unsigned __int16 dwFormatTag = 0;
613 unsigned __int16 mult = 0x1000;
615 for (int i=0; i<4; i++)
617 if (sh->subtype[i] >= '0' && sh->subtype[i] <= '9')
618 dwFormatTag += (sh->subtype[i] - '0') * mult;
619 else if (sh->subtype[i] >= 'A' && sh->subtype[i] <= 'F')
620 dwFormatTag += (sh->subtype[i] - 'A' + 10) * mult;
621 else if (sh->subtype[i] >= 'a' && sh->subtype[i] <= 'f')
622 dwFormatTag += (sh->subtype[i] - 'a' + 10) * mult;
623 mult >>= 4;
626 pmt->InitMediaType();
627 pmt->SetType(&MEDIATYPE_Audio);
628 pmt->SetSubtype(&MEDIASUBTYPE_PCM);
629 pmt->subtype.Data1 = dwFormatTag;
631 pmt->SetFormatType(&FORMAT_WaveFormatEx);
633 int extralen = sh->size - sizeof(stream_header);
634 WAVEFORMATEX* pwfx = (WAVEFORMATEX*)pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX) + extralen);
635 memset(pwfx, 0, sizeof(WAVEFORMATEX));
637 pwfx->cbSize = extralen;
638 memcpy(pwfx+1, sh+1, extralen);
640 pwfx->nAvgBytesPerSec = sh->audio.avgbytespersec;
641 pwfx->nBlockAlign = sh->audio.blockalign;
642 pwfx->nChannels = sh->audio.channels;
643 pwfx->nSamplesPerSec = (__int32)sh->samples_per_unit;
644 pwfx->wBitsPerSample = sh->bits_per_sample;
645 pwfx->wFormatTag = dwFormatTag;
647 *pHighWM = sh->buffersize * 10 / 4096;
648 *pLowWM = *pHighWM / 2;
650 return;
653 if (strncmp((char*)&sh->streamtype, MT_Text, strlen(MT_Text)) == 0)
655 pmt->InitMediaType();
656 pmt->SetType(&MEDIATYPE_Text);
658 *pHighWM = 20;
659 *pLowWM = -1;
660 return;
664 void COggStream::SetMTFromVI(vorbis_info *vi, CMediaType *pmt)
666 pmt->InitMediaType();
667 pmt->SetType(&MEDIATYPE_Audio);
668 pmt->SetSubtype(&MEDIASUBTYPE_Vorbis);
669 pmt->SetFormatType(&FORMAT_VorbisFormat);
671 VORBISFORMAT *pfmt = (VORBISFORMAT*) (pmt->AllocFormatBuffer(sizeof(VORBISFORMAT)));
673 pfmt->nChannels = vi->channels;
674 pfmt->nSamplesPerSec = vi->rate;
675 pfmt->nMinBitsPerSec = vi->bitrate_lower;
676 pfmt->nAvgBitsPerSec = vi->bitrate_nominal;
677 pfmt->nMaxBitsPerSec = vi->bitrate_upper;
680 // used by the seek function
681 bool COggStream::FindKeyFrame(ogg_page *og, CRefTime *rtFrame)
683 bool bSyncPoint;
684 REFERENCE_TIME rtStart;
686 ogg_stream_pagein(&m_ss.os, og);
688 while (stream_sampleout(&m_ss, m_sh, NULL, &bSyncPoint, NULL, NULL,
689 &rtStart, NULL, NULL, NULL) > 0)
691 if (bSyncPoint && (rtStart >= *rtFrame))
693 *rtFrame = rtStart;
694 return true;
697 return false;