blob: 415eb39f650f4fd01c50a4bba41bd72f716ca708 [file] [log] [blame]
// Copyright (c) 2010 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS. All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#include <strmif.h>
#include "webmconstants.hpp"
#include "webmmuxcontext.hpp"
#include "webmmuxstreamaudiovorbis.hpp"
#include "vorbistypes.hpp"
#include "cmediatypes.hpp"
#include <cassert>
#include <uuids.h>
#include <vfwmsgs.h>
#if 0 //def _DEBUG
#include <odbgstream.hpp>
#include <iomanip>
using std::endl;
using std::hex;
using std::dec;
using std::setfill;
using std::setw;
#endif
namespace WebmMuxLib
{
void StreamAudioVorbis::GetMediaTypes(CMediaTypes& mtv)
{
AM_MEDIA_TYPE mt;
mt.majortype = MEDIATYPE_Audio;
mt.subtype = VorbisTypes::MEDIASUBTYPE_Vorbis2;
mt.bFixedSizeSamples = FALSE;
mt.bTemporalCompression = FALSE;
mt.lSampleSize = 0;
mt.formattype = GUID_NULL;
mt.pUnk = 0;
mt.cbFormat = 0;
mt.pbFormat = 0;
mtv.Add(mt);
mt.subtype = VorbisTypes::MEDIASUBTYPE_Vorbis2_Xiph_Lacing;
mtv.Add(mt);
}
bool StreamAudioVorbis::QueryAccept(const AM_MEDIA_TYPE& mt)
{
if (mt.majortype != MEDIATYPE_Audio)
return false;
if (mt.subtype == VorbisTypes::MEDIASUBTYPE_Vorbis2)
__noop;
else if (mt.subtype == VorbisTypes::MEDIASUBTYPE_Vorbis2_Xiph_Lacing)
__noop;
else
return false;
if (mt.formattype != VorbisTypes::FORMAT_Vorbis2)
return false;
using VorbisTypes::VORBISFORMAT2;
if (mt.cbFormat < sizeof(VORBISFORMAT2))
return false;
if (mt.pbFormat == 0)
return false;
const VORBISFORMAT2& fmt = (VORBISFORMAT2&)(*mt.pbFormat);
if (fmt.channels == 0)
return false;
if (fmt.channels > 255)
return false;
if (fmt.samplesPerSec == 0)
return false;
//TODO: fmt.dwBitsPerSample
const DWORD ident_len = fmt.headerSize[0];
if (ident_len != 30)
return false;
const DWORD comments_len = fmt.headerSize[1];
if (comments_len == 0) //TODO: req'd?
return false;
const DWORD setup_len = fmt.headerSize[2];
if (setup_len == 0)
return false;
const DWORD hdr_len = ident_len + comments_len + setup_len;
const DWORD cbFormat = sizeof(VORBISFORMAT2) + hdr_len;
if (mt.cbFormat < cbFormat)
return false;
return true;
}
#if 0
HRESULT StreamAudioVorbis::GetAllocatorRequirements(
const AM_MEDIA_TYPE&,
ALLOCATOR_PROPERTIES&)
{
//Here we assume a Vorbis audio packet is 20ms in duration,
//which makes 50 packets per one second of audio. We then buffer
//3 seconds worth of audio.
//TODO: determine what the duration of a Vorbis packet actually is.
//UPDATE: James Z. said that Vorbis packets have powers-of-two
//number of samples, from 64 - 8192. The number of buffers
//could be calculated as:
// number of buffers [=] samples/sec / 64 samples/buffer
//which is about 750 buffers/sec. We could either choose to
//fix the number of buffers we allocated, which determines
//how much we're able to buffer, or fix how much we want to
//buffer, and then copy the audio data from the media samples
//into a local buffer. (The latter approach is less likely
//to stall the graph, but it's not as efficient.)
props.cBuffers = 3 * 50;
props.cbBuffer = 0;
props.cbAlign = 0;
props.cbPrefix = 0;
return S_OK;
}
#endif
StreamAudio* StreamAudioVorbis::CreateStream(
Context& ctx,
const AM_MEDIA_TYPE& mt)
{
assert(QueryAccept(mt));
return new (std::nothrow) StreamAudioVorbis(ctx, mt);
}
#if 0
StreamAudioVorbis::VorbisFrame::VorbisFrame(
IMediaSample* pSample,
StreamAudioVorbis* pStream) :
m_pSample(pSample)
{
m_pSample->AddRef();
__int64 st, sp; //reftime units
const HRESULT hr = m_pSample->GetTime(&st, &sp);
assert(SUCCEEDED(hr));
assert(st >= 0);
const __int64 ns = st * 100; //nanoseconds
const Context& ctx = pStream->m_context;
const ULONG scale = ctx.GetTimecodeScale();
assert(scale >= 1);
//TODO: verify this when scale equals audio sampling rate
const __int64 tc = ns / scale;
assert(tc <= ULONG_MAX);
m_timecode = static_cast<ULONG>(tc);
}
#else
StreamAudioVorbis::VorbisFrame::VorbisFrame(
IMediaSample* pSample,
StreamAudioVorbis* pStream)
{
__int64 st, sp; //reftime units
HRESULT hr = pSample->GetTime(&st, &sp);
assert(SUCCEEDED(hr));
assert(st >= 0);
__int64 ns = st * 100; //nanoseconds
const Context& ctx = pStream->m_context;
const ULONG scale = ctx.GetTimecodeScale();
assert(scale >= 1);
//TODO: verify this when scale equals audio sampling rate
__int64 tc = ns / scale;
assert(tc <= ULONG_MAX);
m_timecode = static_cast<ULONG>(tc);
if ((hr == VFW_S_NO_STOP_TIME) || (sp <= st))
m_duration = 0;
else
{
ns = (sp - st) * 100; //duration (ns units)
tc = ns / scale;
m_duration = static_cast<ULONG>(tc);
}
const long size = pSample->GetActualDataLength();
assert(size > 0);
m_size = size;
BYTE* ptr;
hr = pSample->GetPointer(&ptr);
assert(SUCCEEDED(hr));
assert(ptr);
m_data = new (std::nothrow) BYTE[m_size];
assert(m_data); //TODO
memcpy(m_data, ptr, m_size);
const GUID& g = pStream->m_subtype;
if (g == VorbisTypes::MEDIASUBTYPE_Vorbis2_Xiph_Lacing)
m_lacing = 1;
else
m_lacing = 0;
}
#endif
StreamAudioVorbis::VorbisFrame::~VorbisFrame()
{
#if 0
const ULONG n = m_pSample->Release();
n;
#else
delete[] m_data;
#endif
}
const VorbisTypes::VORBISFORMAT2&
StreamAudioVorbis::GetFormat() const
{
using VorbisTypes::VORBISFORMAT2;
ULONG cb;
const void* const pv = StreamAudio::GetFormat(cb);
assert(pv);
assert(cb >= sizeof(VORBISFORMAT2));
const VORBISFORMAT2* const pfmt = static_cast<const VORBISFORMAT2*>(pv);
assert(pfmt);
const VORBISFORMAT2& fmt = *pfmt;
return fmt;
}
int StreamAudioVorbis::VorbisFrame::GetLacing() const
{
return m_lacing;
}
ULONG StreamAudioVorbis::GetSamplesPerSec() const
{
const VorbisTypes::VORBISFORMAT2& fmt = GetFormat();
assert(fmt.samplesPerSec > 0);
return fmt.samplesPerSec;
}
BYTE StreamAudioVorbis::GetChannels() const
{
const VorbisTypes::VORBISFORMAT2& fmt = GetFormat();
assert(fmt.channels > 0);
assert(fmt.channels <= 255);
return static_cast<BYTE>(fmt.channels);
}
ULONG StreamAudioVorbis::VorbisFrame::GetTimecode() const
{
return m_timecode;
}
ULONG StreamAudioVorbis::VorbisFrame::GetDuration() const
{
return m_duration;
}
ULONG StreamAudioVorbis::VorbisFrame::GetSize() const
{
#if 0
const long result = m_pSample->GetActualDataLength();
assert(result >= 0);
return result;
#else
return m_size;
#endif
}
const BYTE* StreamAudioVorbis::VorbisFrame::GetData() const
{
#if 0
BYTE* ptr;
const HRESULT hr = m_pSample->GetPointer(&ptr);
assert(SUCCEEDED(hr));
assert(ptr);
return ptr;
#else
return m_data;
#endif
}
StreamAudioVorbis::StreamAudioVorbis(
Context& ctx,
const AM_MEDIA_TYPE& mt) :
StreamAudio(ctx, mt.pbFormat, mt.cbFormat),
m_subtype(mt.subtype)
{
}
void StreamAudioVorbis::WriteTrackCodecID()
{
WebmUtil::EbmlScratchBuf& buf = m_context.m_buf;
buf.WriteID1(WebmUtil::kEbmlCodecIDID);
buf.Write1String("A_VORBIS");
}
void StreamAudioVorbis::WriteTrackCodecName()
{
WebmUtil::EbmlScratchBuf& buf = m_context.m_buf;
buf.WriteID3(WebmUtil::kEbmlCodecNameID);
buf.Write1UTF8(L"VORBIS");
}
void StreamAudioVorbis::WriteTrackCodecPrivate()
{
typedef VorbisTypes::VORBISFORMAT2 F;
ULONG cb;
const void* const pv = StreamAudio::GetFormat(cb);
assert(cb >= sizeof(F));
assert(pv);
const F* const pf = static_cast<const F*>(pv);
const F& f = *pf;
assert(f.channels > 0);
assert(f.samplesPerSec > 0);
const DWORD ident_len = f.headerSize[0];
assert(ident_len == 30);
const DWORD comment_len = f.headerSize[1];
assert(comment_len > 0);
const DWORD setup_len = f.headerSize[2];
assert(setup_len > 0);
const DWORD hdr_len = ident_len + comment_len + setup_len;
assert(cb == (sizeof(F) + hdr_len));
const BYTE* const pb = static_cast<const BYTE*>(pv);
const BYTE* const hdr_ptr = pb + sizeof(F);
ULONG len = 1 + 1;
//1 byte = number of headers - 1 (always 2)
//1 byte = ident len (always 30)
ULONG n = comment_len;
while (n >= 255)
{
++len;
n -= 255;
}
++len;
len += hdr_len;
WebmUtil::EbmlScratchBuf& buf = m_context.m_buf;
buf.WriteID2(WebmUtil::kEbmlCodecPrivateID);
buf.WriteUInt(len, 0);
uint8 val = 2; //number of headers - 1
buf.Write(&val, 1);
val = static_cast<uint8>(ident_len);
buf.Write(&val, 1);
n = comment_len;
while (n >= 255)
{
val = 255;
buf.Write(&val, 1);
n -= 255;
}
val = static_cast<uint8>(n);
buf.Write(&val, 1);
buf.Write(hdr_ptr, hdr_len);
}
HRESULT StreamAudioVorbis::Receive(IMediaSample* pSample)
{
assert(pSample);
EbmlIO::File& file = m_context.m_file;
if (file.GetStream() == 0)
return S_OK;
VorbisFrame* const pFrame = new (std::nothrow) VorbisFrame(pSample, this);
assert(pFrame); //TODO
m_context.NotifyAudioFrame(this, pFrame);
return S_OK;
}
int StreamAudioVorbis::EndOfStream()
{
return m_context.NotifyAudioEOS(this);
}
} //end namespace WebmMuxLib