| // 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. |
| |
| // TODO(tomfinegan): Sort out the include order dependency issues that result |
| // in the following include order being required. |
| |
| // TODO(tomfinegan): Get rid of this warning. |
| #pragma warning( once : 4505 ) // unreferenced local function has been removed |
| |
| #include <amvideo.h> |
| #include <dvdmedia.h> |
| #include <evcode.h> |
| #include <uuids.h> |
| #include <vfwmsgs.h> |
| |
| #include <cassert> |
| |
| #include "vp8decoderfilter.h" |
| #include "vp8decoderinpin.h" |
| #include "vp8decoderoutpin.h" |
| |
| #include "mediatypeutil.h" |
| #include "vpx/vp8dx.h" |
| |
| #include "graphutil.h" |
| #include "webmtypes.h" |
| |
| #ifdef _DEBUG |
| #include <iomanip> |
| #include "odbgstream.h" |
| using std::endl; |
| using std::hex; |
| using std::dec; |
| using std::fixed; |
| using std::setprecision; |
| #endif |
| |
| namespace VP8DecoderLib { |
| |
| Inpin::Inpin(Filter* p) |
| : Pin(p, PINDIR_INPUT, L"input"), m_bEndOfStream(false), m_bFlush(false) { |
| AM_MEDIA_TYPE mt; |
| |
| mt.majortype = MEDIATYPE_Video; |
| mt.subtype = WebmTypes::MEDIASUBTYPE_VP80; |
| mt.bFixedSizeSamples = FALSE; |
| mt.bTemporalCompression = TRUE; |
| mt.lSampleSize = 0; |
| mt.formattype = GUID_NULL; |
| mt.pUnk = 0; |
| mt.cbFormat = 0; |
| mt.pbFormat = 0; |
| |
| m_preferred_mtv.Add(mt); |
| } |
| |
| HRESULT Inpin::QueryInterface(const IID& iid, void** ppv) { |
| if (ppv == 0) |
| return E_POINTER; |
| |
| IUnknown*& pUnk = reinterpret_cast<IUnknown*&>(*ppv); |
| |
| if (iid == __uuidof(IUnknown)) { |
| pUnk = static_cast<IPin*>(this); |
| } else if (iid == __uuidof(IPin)) { |
| pUnk = static_cast<IPin*>(this); |
| } else if (iid == __uuidof(IMemInputPin)) { |
| pUnk = static_cast<IMemInputPin*>(this); |
| } else { |
| pUnk = 0; |
| return E_NOINTERFACE; |
| } |
| |
| pUnk->AddRef(); |
| return S_OK; |
| } |
| |
| ULONG Inpin::AddRef() { |
| return m_pFilter->AddRef(); |
| } |
| |
| ULONG Inpin::Release() { |
| return m_pFilter->Release(); |
| } |
| |
| HRESULT Inpin::Connect(IPin*, const AM_MEDIA_TYPE*) { |
| return E_UNEXPECTED; // for output pins only |
| } |
| |
| HRESULT Inpin::QueryInternalConnections(IPin** pa, ULONG* pn) { |
| if (pn == 0) |
| return E_POINTER; |
| |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| const ULONG m = 1; // number of output pins |
| |
| ULONG& n = *pn; |
| |
| if (n == 0) { |
| if (pa == 0) { |
| // query for required number |
| n = m; |
| return S_OK; |
| } |
| |
| return S_FALSE; // means "insufficient number of array elements" |
| } |
| |
| if (n < m) { |
| n = 0; |
| return S_FALSE; // means "insufficient number of array elements" |
| } |
| |
| if (pa == 0) { |
| n = 0; |
| return E_POINTER; |
| } |
| |
| IPin*& pin = pa[0]; |
| |
| pin = &m_pFilter->m_outpin; |
| pin->AddRef(); |
| |
| n = m; |
| return S_OK; |
| } |
| |
| HRESULT Inpin::ReceiveConnection(IPin* pin, const AM_MEDIA_TYPE* pmt) { |
| if (pin == 0) |
| return E_INVALIDARG; |
| |
| if (pmt == 0) |
| return E_INVALIDARG; |
| |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (m_pFilter->GetStateLocked() != State_Stopped) |
| return VFW_E_NOT_STOPPED; |
| |
| if (bool(m_pPinConnection)) |
| return VFW_E_ALREADY_CONNECTED; |
| |
| m_connection_mtv.Clear(); |
| |
| hr = QueryAccept(pmt); |
| |
| if (hr != S_OK) |
| return VFW_E_TYPE_NOT_ACCEPTED; |
| |
| const AM_MEDIA_TYPE& mt = *pmt; |
| |
| hr = m_connection_mtv.Add(mt); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| m_pPinConnection = pin; |
| |
| // TODO: init decompressor here? |
| |
| m_pFilter->m_outpin.OnInpinConnect(mt); |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::EndOfStream() { |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (!bool(m_pPinConnection)) |
| return VFW_E_NOT_CONNECTED; |
| |
| m_bEndOfStream = true; |
| |
| if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { |
| lock.Release(); |
| |
| #ifdef _DEBUG |
| odbgstream os; |
| os << "vp8decoder::inpin::EOS: calling pin->EOS" << endl; |
| #endif |
| |
| const HRESULT hr = pPin->EndOfStream(); |
| |
| #ifdef _DEBUG |
| os << "vp8decoder::inpin::EOS: called pin->EOS; hr=0x" << hex << hr << dec |
| << endl; |
| #endif |
| |
| return hr; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::BeginFlush() { |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (!bool(m_pPinConnection)) |
| return VFW_E_NOT_CONNECTED; |
| |
| #if 0 // def _DEBUG |
| odbgstream os; |
| os << "vp8decoder::inpin::beginflush" << endl; |
| #endif |
| |
| m_bFlush = true; |
| |
| if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { |
| lock.Release(); |
| |
| const HRESULT hr = pPin->BeginFlush(); |
| return hr; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::EndFlush() { |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (!bool(m_pPinConnection)) |
| return VFW_E_NOT_CONNECTED; |
| |
| #if 0 // def _DEBUG |
| odbgstream os; |
| os << "vp8decoder::inpin::endflush" << endl; |
| #endif |
| |
| m_bFlush = false; |
| m_bEndOfStream = false; |
| |
| if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { |
| lock.Release(); |
| |
| const HRESULT hr = pPin->EndFlush(); |
| return hr; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::NewSegment(REFERENCE_TIME st, REFERENCE_TIME sp, double r) { |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (!bool(m_pPinConnection)) |
| return VFW_E_NOT_CONNECTED; |
| |
| if (IPin* pPin = m_pFilter->m_outpin.m_pPinConnection) { |
| lock.Release(); |
| |
| const HRESULT hr = pPin->NewSegment(st, sp, r); |
| return hr; |
| } |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::QueryAccept(const AM_MEDIA_TYPE* pmt) { |
| if (pmt == 0) |
| return E_INVALIDARG; |
| |
| const AM_MEDIA_TYPE& mt = *pmt; |
| |
| if (mt.majortype != MEDIATYPE_Video) |
| return S_FALSE; |
| |
| if (mt.subtype != WebmTypes::MEDIASUBTYPE_VP80) |
| return S_FALSE; |
| |
| if (mt.formattype != FORMAT_VideoInfo) // TODO: liberalize |
| return S_FALSE; |
| |
| if (mt.pbFormat == 0) |
| return S_FALSE; |
| |
| if (mt.cbFormat < sizeof(VIDEOINFOHEADER)) |
| return S_FALSE; |
| |
| const VIDEOINFOHEADER& vih = (VIDEOINFOHEADER&)(*mt.pbFormat); |
| const BITMAPINFOHEADER& bmih = vih.bmiHeader; |
| |
| if (bmih.biSize != sizeof(BITMAPINFOHEADER)) // TODO: liberalize |
| return S_FALSE; |
| |
| if (bmih.biWidth <= 0) |
| return S_FALSE; |
| |
| if (bmih.biHeight <= 0) |
| return S_FALSE; |
| |
| if (bmih.biCompression != WebmTypes::MEDIASUBTYPE_VP80.Data1) //"VP80" |
| return S_FALSE; |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::GetAllocator(IMemAllocator** p) { |
| if (p) |
| *p = 0; |
| |
| return VFW_E_NO_ALLOCATOR; |
| } |
| |
| HRESULT Inpin::NotifyAllocator(IMemAllocator* pAllocator, BOOL) { |
| if (pAllocator == 0) |
| return E_INVALIDARG; |
| |
| ALLOCATOR_PROPERTIES props; |
| const HRESULT hr = pAllocator->GetProperties(&props); |
| |
| if (FAILED(hr)) { |
| assert(SUCCEEDED(hr)); |
| return hr; |
| } |
| |
| #ifdef _DEBUG |
| wodbgstream os; |
| os << "vp8dec::inpin::NotifyAllocator: props.cBuffers=" << props.cBuffers |
| << " cbBuffer=" << props.cbBuffer << " cbAlign=" << props.cbAlign |
| << " cbPrefix=" << props.cbPrefix << endl; |
| #endif |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::GetAllocatorRequirements(ALLOCATOR_PROPERTIES* pp) { |
| if (pp == NULL) |
| return E_POINTER; |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::Receive(IMediaSample* pInSample) { |
| if (pInSample == 0) |
| return E_INVALIDARG; |
| |
| //#define DEBUG_RECEIVE |
| |
| #ifdef DEBUG_RECEIVE |
| __int64 start_reftime, stop_reftime; |
| const HRESULT hr_debug = pInSample->GetTime(&start_reftime, &stop_reftime); |
| |
| odbgstream os; |
| os << "vp8dec::inpin::receive: "; |
| |
| os << std::fixed << std::setprecision(3); |
| |
| if (hr_debug == S_OK) { |
| os << "start[ms]=" << double(start_reftime) / 10000 |
| << "; stop[ms]=" << double(stop_reftime) / 10000 |
| << "; dt[ms]=" << double(stop_reftime - start_reftime) / 10000; |
| } else if (hr_debug == VFW_S_NO_STOP_TIME) { |
| os << "start[ms]=" << double(start_reftime) / 10000; |
| } |
| |
| os << endl; |
| #endif // DEBUG_RECEIVE |
| |
| Filter::Lock lock; |
| |
| HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| #ifdef DEBUG_RECEIVE |
| wodbgstream os; |
| os << L"vp8dec::inpin::Receive: THREAD=0x" |
| << std::hex << GetCurrentThreadId() << std::dec |
| << endl; |
| #endif // DEBUG_RECEIVE |
| |
| if (!bool(m_pPinConnection)) |
| return VFW_E_NOT_CONNECTED; |
| |
| Outpin& outpin = m_pFilter->m_outpin; |
| |
| if (!bool(outpin.m_pPinConnection)) |
| return S_FALSE; |
| |
| if (!bool(outpin.m_pAllocator)) |
| return VFW_E_NO_ALLOCATOR; |
| |
| if (m_pFilter->GetStateLocked() == State_Stopped) |
| return VFW_E_NOT_RUNNING; |
| |
| if (m_bEndOfStream) |
| return VFW_E_SAMPLE_REJECTED_EOS; |
| |
| if (m_bFlush) |
| return S_FALSE; |
| |
| BYTE* buf; |
| |
| hr = pInSample->GetPointer(&buf); |
| assert(SUCCEEDED(hr)); |
| assert(buf); |
| |
| const long len = pInSample->GetActualDataLength(); |
| assert(len >= 0); |
| |
| const vpx_codec_err_t err = vpx_codec_decode(&m_ctx, buf, len, 0, 0); |
| |
| if (err != VPX_CODEC_OK) |
| return m_pFilter->OnDecodeFailureLocked(); |
| |
| hr = pInSample->IsSyncPoint(); |
| |
| m_pFilter->OnDecodeSuccessLocked(hr == S_OK); |
| |
| if (pInSample->IsPreroll() == S_OK) |
| return S_OK; |
| |
| lock.Release(); |
| |
| GraphUtil::IMediaSamplePtr pOutSample; |
| |
| hr = outpin.m_pAllocator->GetBuffer(&pOutSample, 0, 0, 0); |
| |
| if (FAILED(hr)) |
| return S_FALSE; |
| |
| assert(bool(pOutSample)); |
| |
| hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return hr; |
| |
| if (m_pFilter->GetStateLocked() == State_Stopped) |
| return VFW_E_NOT_RUNNING; |
| |
| if (!bool(outpin.m_pPinConnection)) // should never happen |
| return S_FALSE; |
| |
| if (!bool(outpin.m_pInputPin)) // should never happen |
| return S_FALSE; |
| |
| vpx_codec_iter_t iter = 0; |
| |
| vpx_image_t* const f = vpx_codec_get_frame(&m_ctx, &iter); |
| |
| if (f == 0) |
| return S_OK; |
| |
| AM_MEDIA_TYPE* pmt; |
| |
| hr = pOutSample->GetMediaType(&pmt); |
| |
| if (SUCCEEDED(hr) && (pmt != 0)) { |
| hr = outpin.QueryAccept(pmt); |
| |
| if (hr != S_OK) { |
| assert(hr == S_OK); |
| return hr; |
| } |
| |
| outpin.m_connection_mtv.Clear(); |
| outpin.m_connection_mtv.Add(*pmt); |
| |
| MediaTypeUtil::Free(pmt); |
| pmt = 0; |
| } |
| |
| const AM_MEDIA_TYPE& mt = outpin.m_connection_mtv[0]; |
| |
| const BITMAPINFOHEADER* bmih_ptr; |
| const RECT* rc_ptr; |
| |
| if (mt.formattype == FORMAT_VideoInfo) { |
| assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER)); |
| assert(mt.pbFormat); |
| |
| const VIDEOINFOHEADER& vih_out = (VIDEOINFOHEADER&)(*mt.pbFormat); |
| const RECT& rc_out = vih_out.rcSource; |
| const BITMAPINFOHEADER& bmih_out = vih_out.bmiHeader; |
| |
| bmih_ptr = &bmih_out; |
| rc_ptr = &rc_out; |
| } else if (mt.formattype == FORMAT_VideoInfo2) { |
| assert(mt.cbFormat >= sizeof(VIDEOINFOHEADER2)); |
| assert(mt.pbFormat); |
| |
| const VIDEOINFOHEADER2& vih2_out = (VIDEOINFOHEADER2&)(*mt.pbFormat); |
| const RECT& rc_out = vih2_out.rcSource; |
| const BITMAPINFOHEADER& bmih_out = vih2_out.bmiHeader; |
| |
| bmih_ptr = &bmih_out; |
| rc_ptr = &rc_out; |
| } else { |
| return E_FAIL; |
| } |
| |
| if (mt.subtype == MEDIASUBTYPE_NV12) |
| CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); |
| |
| else if (mt.subtype == MEDIASUBTYPE_YV12) |
| CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); |
| |
| else if (mt.subtype == WebmTypes::MEDIASUBTYPE_I420) |
| CopyToPlanar(f, pOutSample, mt.subtype, *bmih_ptr); |
| |
| else if (mt.subtype == MEDIASUBTYPE_UYVY) |
| CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); |
| |
| else if (mt.subtype == MEDIASUBTYPE_YUY2) |
| CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); |
| |
| else if (mt.subtype == MEDIASUBTYPE_YUYV) |
| CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); |
| |
| else if (mt.subtype == MEDIASUBTYPE_YVYU) |
| CopyToPacked(f, pOutSample, mt.subtype, *rc_ptr, *bmih_ptr); |
| |
| else |
| return E_FAIL; |
| |
| __int64 st, sp; |
| |
| hr = pInSample->GetTime(&st, &sp); |
| |
| if (FAILED(hr)) { |
| hr = pOutSample->SetTime(0, 0); |
| assert(SUCCEEDED(hr)); |
| } else if (hr == S_OK) { |
| hr = pOutSample->SetTime(&st, &sp); |
| assert(SUCCEEDED(hr)); |
| } else { |
| hr = pOutSample->SetTime(&st, 0); |
| assert(SUCCEEDED(hr)); |
| } |
| |
| hr = pOutSample->SetSyncPoint(TRUE); |
| assert(SUCCEEDED(hr)); |
| |
| hr = pOutSample->SetPreroll(FALSE); |
| assert(SUCCEEDED(hr)); |
| |
| hr = pInSample->IsDiscontinuity(); |
| hr = pOutSample->SetDiscontinuity(hr == S_OK); |
| |
| hr = pOutSample->SetMediaTime(0, 0); |
| |
| #if 0 |
| __int64 st, sp; |
| hr = pOutSample->GetTime(&st, &sp); |
| assert(SUCCEEDED(hr)); |
| |
| odbgstream os; |
| os << "V: " << fixed << setprecision(3) << (double(st)/10000000.0) << endl; |
| #endif |
| |
| lock.Release(); |
| |
| return outpin.m_pInputPin->Receive(pOutSample); |
| } |
| |
| HRESULT Inpin::ReceiveMultiple(IMediaSample** pSamples, |
| long n, // in |
| long* pm) { // out |
| if (pm == 0) |
| return E_POINTER; |
| |
| long& m = *pm; // out |
| m = 0; |
| |
| if (n <= 0) |
| return S_OK; // weird |
| |
| if (pSamples == 0) |
| return E_INVALIDARG; |
| |
| for (long i = 0; i < n; ++i) { |
| IMediaSample* const pSample = pSamples[i]; |
| assert(pSample); |
| |
| const HRESULT hr = Receive(pSample); |
| |
| if (hr != S_OK) |
| return hr; |
| |
| ++m; |
| } |
| |
| return S_OK; |
| } |
| |
| void Inpin::CopyToPlanar(const vpx_image_t* f, IMediaSample* pOutSample, |
| const GUID& subtype_out, |
| const BITMAPINFOHEADER& bmih_out) { |
| // Y |
| |
| const BYTE* pInY = f->planes[VPX_PLANE_Y]; |
| assert(pInY); |
| |
| unsigned int width_in = f->d_w; |
| unsigned int height_in = f->d_h; |
| |
| BYTE* pOutBuf; |
| |
| HRESULT hr = pOutSample->GetPointer(&pOutBuf); |
| assert(SUCCEEDED(hr)); |
| assert(pOutBuf); |
| |
| BYTE* pOut = pOutBuf; |
| |
| const int strideInY = f->stride[VPX_PLANE_Y]; |
| |
| LONG strideOut = bmih_out.biWidth; |
| assert(strideOut); |
| assert((strideOut % 2) == 0); |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| memcpy(pOut, pInY, width_in); |
| pInY += strideInY; |
| pOut += strideOut; |
| } |
| |
| width_in = (width_in + 1) / 2; |
| height_in = (height_in + 1) / 2; |
| |
| const BYTE* pInV = f->planes[VPX_PLANE_V]; |
| assert(pInV); |
| |
| const int strideInV = f->stride[VPX_PLANE_V]; |
| |
| const BYTE* pInU = f->planes[VPX_PLANE_U]; |
| assert(pInU); |
| |
| const int strideInU = f->stride[VPX_PLANE_U]; |
| |
| if (subtype_out == MEDIASUBTYPE_NV12) { |
| // Note that while NV12 is considered a planar format, |
| // the chroma plane packs the UV samples. |
| |
| // UV |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| const BYTE* u = pInU; |
| const BYTE* v = pInV; |
| BYTE* uv = pOut; |
| |
| for (unsigned int idx = 0; idx < width_in; ++idx) { |
| *uv++ = *u++; |
| *uv++ = *v++; |
| } |
| |
| pInU += strideInU; |
| pInV += strideInV; |
| pOut += strideOut; |
| } |
| } else if (subtype_out == MEDIASUBTYPE_YV12) { |
| strideOut /= 2; |
| |
| // V |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| memcpy(pOut, pInV, width_in); |
| pInV += strideInV; |
| pOut += strideOut; |
| } |
| |
| // U |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| memcpy(pOut, pInU, width_in); |
| pInU += strideInU; |
| pOut += strideOut; |
| } |
| } else { |
| assert(subtype_out == WebmTypes::MEDIASUBTYPE_I420); |
| strideOut /= 2; |
| |
| // U |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| memcpy(pOut, pInU, width_in); |
| pInU += strideInU; |
| pOut += strideOut; |
| } |
| |
| // V |
| |
| for (unsigned int y = 0; y < height_in; ++y) { |
| memcpy(pOut, pInV, width_in); |
| pInV += strideInV; |
| pOut += strideOut; |
| } |
| } |
| |
| const ptrdiff_t lenOut_ = pOut - pOutBuf; |
| const long lenOut = static_cast<long>(lenOut_); |
| |
| hr = pOutSample->SetActualDataLength(lenOut); |
| assert(SUCCEEDED(hr)); |
| } |
| |
| void Inpin::CopyToPacked(const vpx_image_t* f, IMediaSample* pOutSample, |
| const GUID& subtype_out, const RECT& rc_out, |
| const BITMAPINFOHEADER& bmih_out) { |
| const LONG rect_width_out = rc_out.right - rc_out.left; |
| assert(rect_width_out >= 0); |
| |
| const LONG width_out = |
| (rect_width_out > 0) ? rect_width_out : bmih_out.biWidth; |
| assert(width_out > 0); |
| |
| const LONG rect_height_out = rc_out.bottom - rc_out.top; |
| assert(rect_height_out >= 0); |
| |
| const LONG height_out = |
| (rect_height_out > 0) ? rect_height_out : labs(bmih_out.biHeight); |
| |
| const BYTE* pInY_base = f->planes[VPX_PLANE_Y]; |
| assert(pInY_base); |
| |
| const int strideInY = f->stride[VPX_PLANE_Y]; |
| |
| const BYTE* pInV_base = f->planes[VPX_PLANE_V]; |
| assert(pInV_base); |
| |
| const int strideInV = f->stride[VPX_PLANE_V]; |
| |
| const BYTE* pInU_base = f->planes[VPX_PLANE_U]; |
| assert(pInU_base); |
| |
| const int strideInU = f->stride[VPX_PLANE_U]; |
| |
| const unsigned int width_in = f->d_w; |
| assert(LONG(width_in) == width_out); |
| |
| const unsigned int height_in = f->d_h; |
| assert(LONG(height_in) == height_out); |
| |
| BYTE* pOutBuf; |
| |
| HRESULT hr = pOutSample->GetPointer(&pOutBuf); |
| assert(SUCCEEDED(hr)); |
| assert(pOutBuf); |
| |
| const LONG strideOut_ = 2 * width_in; |
| LONG strideOut; |
| |
| if (bmih_out.biWidth < strideOut_) |
| strideOut = strideOut_; |
| else |
| strideOut = bmih_out.biWidth; |
| |
| const LONG uv_width = width_in / 2; |
| const LONG uv_height = height_in / 2; |
| |
| int u_off, v_off, y_off; |
| |
| if (subtype_out == MEDIASUBTYPE_UYVY) { |
| u_off = 0; |
| v_off = 2; |
| y_off = 1; |
| } else if ((subtype_out == MEDIASUBTYPE_YUY2) || |
| (subtype_out == MEDIASUBTYPE_YUYV)) { |
| u_off = 1; |
| v_off = 3; |
| y_off = 0; |
| } else { |
| assert(subtype_out == MEDIASUBTYPE_YVYU); |
| |
| u_off = 3; |
| v_off = 1; |
| y_off = 0; |
| } |
| |
| BYTE* pOut = pOutBuf; |
| |
| for (LONG hdx = 0; hdx < uv_height; ++hdx) { |
| BYTE* const pOut0 = pOut; |
| pOut += strideOut; |
| |
| BYTE* const pOut1 = pOut; |
| pOut += strideOut; |
| |
| BYTE* pOutU0 = pOut0 + u_off; |
| BYTE* pOutU1 = pOut1 + u_off; |
| |
| BYTE* pOutV0 = pOut0 + v_off; |
| BYTE* pOutV1 = pOut1 + v_off; |
| |
| BYTE* pOutY0 = pOut0 + y_off; |
| BYTE* pOutY1 = pOut1 + y_off; |
| |
| const BYTE* pInU = pInU_base; |
| pInU_base += strideInU; |
| |
| const BYTE* pInV = pInV_base; |
| pInV_base += strideInV; |
| |
| const BYTE* pInY0 = pInY_base; |
| pInY_base += strideInY; |
| |
| const BYTE* pInY1 = pInY_base; |
| pInY_base += strideInY; |
| |
| for (LONG wdx = 0; wdx < uv_width; ++wdx) { |
| *pOutU0 = *pInU; |
| *pOutU1 = *pInU; |
| |
| pOutU0 += 4; |
| pOutU1 += 4; |
| |
| ++pInU; |
| |
| *pOutV0 = *pInV; |
| *pOutV1 = *pInV; |
| |
| pOutV0 += 4; |
| pOutV1 += 4; |
| |
| ++pInV; |
| |
| *pOutY0 = *pInY0; |
| *pOutY1 = *pInY1; |
| |
| pOutY0 += 2; |
| pOutY1 += 2; |
| |
| ++pInY0; |
| ++pInY1; |
| |
| *pOutY0 = *pInY0; |
| *pOutY1 = *pInY1; |
| |
| pOutY0 += 2; |
| pOutY1 += 2; |
| |
| ++pInY0; |
| ++pInY1; |
| } |
| } |
| |
| const ptrdiff_t lenOut_ = pOut - pOutBuf; |
| const long lenOut = static_cast<long>(lenOut_); |
| |
| hr = pOutSample->SetActualDataLength(lenOut); |
| assert(SUCCEEDED(hr)); |
| } |
| |
| HRESULT Inpin::ReceiveCanBlock() { |
| Filter::Lock lock; |
| |
| const HRESULT hr = lock.Seize(m_pFilter); |
| |
| if (FAILED(hr)) |
| return S_OK; //? |
| |
| if (IMemInputPin* pPin = m_pFilter->m_outpin.m_pInputPin) { |
| lock.Release(); |
| return pPin->ReceiveCanBlock(); |
| } |
| |
| return S_FALSE; |
| } |
| |
| HRESULT Inpin::OnDisconnect() { |
| return m_pFilter->m_outpin.OnInpinDisconnect(); |
| } |
| |
| HRESULT Inpin::GetName(PIN_INFO& info) const { |
| const wchar_t name[] = L"VP80"; |
| |
| #if _MSC_VER >= 1400 |
| enum { namelen = sizeof(info.achName) / sizeof(WCHAR) }; |
| const errno_t e = wcscpy_s(info.achName, namelen, name); |
| e; |
| assert(e == 0); |
| #else |
| wcscpy(info.achName, name); |
| #endif |
| |
| return S_OK; |
| } |
| |
| HRESULT Inpin::Start() { |
| m_bEndOfStream = false; |
| m_bFlush = false; |
| |
| vpx_codec_iface_t& vp8 = vpx_codec_vp8_dx_algo; |
| |
| const int flags = VPX_CODEC_USE_POSTPROC; |
| |
| const vpx_codec_err_t err = vpx_codec_dec_init(&m_ctx, &vp8, 0, flags); |
| |
| if (err == VPX_CODEC_MEM_ERROR) |
| return E_OUTOFMEMORY; |
| |
| if (err != VPX_CODEC_OK) |
| return E_FAIL; |
| |
| const HRESULT hr = OnApplyPostProcessing(); |
| |
| if (FAILED(hr)) { |
| Stop(); |
| return hr; |
| } |
| |
| return S_OK; |
| } |
| |
| void Inpin::Stop() { |
| const vpx_codec_err_t err = vpx_codec_destroy(&m_ctx); |
| err; |
| assert(err == VPX_CODEC_OK); |
| } |
| |
| HRESULT Inpin::OnApplyPostProcessing() { |
| const Filter::Config& src = m_pFilter->m_cfg; |
| vp8_postproc_cfg_t tgt; |
| |
| tgt.post_proc_flag = src.flags; |
| tgt.deblocking_level = src.deblock; |
| tgt.noise_level = src.noise; |
| |
| const vpx_codec_err_t err = vpx_codec_control(&m_ctx, VP8_SET_POSTPROC, &tgt); |
| |
| return (err == VPX_CODEC_OK) ? S_OK : E_FAIL; |
| } |
| |
| } // namespace VP8DecoderLib |