/*
* $Id$
*
* (C) 2006-2010 see AUTHORS
*
* This file is part of mplayerc.
*
* Mplayerc is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Mplayerc is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "stdafx.h"
#include "../../../DSUtil/DSUtil.h"
#include "DXVADecoderH264.h"
#include "MPCVideoDecFilter.h"
#include "VideoDecDXVAAllocator.h"
#include "PODtypes.h"
#include "avcodec.h"
extern "C"
{
#include "FfmpegContext.h"
}
CDXVADecoderH264::CDXVADecoderH264 (CMPCVideoDecFilter* pFilter, IAMVideoAccelerator* pAMVideoAccelerator, DXVAMode nMode, int nPicEntryNumber)
: CDXVADecoder (pFilter, pAMVideoAccelerator, nMode, nPicEntryNumber)
{
m_bUseLongSlice = (GetDXVA1Config()->bConfigBitstreamRaw != 2);
Init();
}
CDXVADecoderH264::CDXVADecoderH264 (CMPCVideoDecFilter* pFilter, IDirectXVideoDecoder* pDirectXVideoDec, DXVAMode nMode, int nPicEntryNumber, DXVA2_ConfigPictureDecode* pDXVA2Config)
: CDXVADecoder (pFilter, pDirectXVideoDec, nMode, nPicEntryNumber, pDXVA2Config)
{
m_bUseLongSlice = (m_pFilter->GetDXVA2Config()->ConfigBitstreamRaw != 2);
Init();
}
CDXVADecoderH264::~CDXVADecoderH264()
{
}
void CDXVADecoderH264::Init()
{
memset (&m_DXVAPicParams, 0, sizeof (m_DXVAPicParams));
memset (&m_DXVAPicParams, 0, sizeof (DXVA_PicParams_H264));
memset (&m_pSliceLong, 0, sizeof (DXVA_Slice_H264_Long) *MAX_SLICES);
memset (&m_pSliceShort, 0, sizeof (DXVA_Slice_H264_Short)*MAX_SLICES);
m_DXVAPicParams.MbsConsecutiveFlag = 1;
if(m_pFilter->GetPCIVendor() == PCIV_Intel)
m_DXVAPicParams.Reserved16Bits = 0x534c;
else
m_DXVAPicParams.Reserved16Bits = 0;
m_DXVAPicParams.ContinuationFlag = 1;
m_DXVAPicParams.Reserved8BitsA = 0;
m_DXVAPicParams.Reserved8BitsB = 0;
m_DXVAPicParams.MinLumaBipredSize8x8Flag = 1; // Improve accelerator performances
m_DXVAPicParams.StatusReportFeedbackNumber = 0; // Use to report status
for (int i =0; i<16; i++)
{
m_DXVAPicParams.RefFrameList[i].AssociatedFlag = 1;
m_DXVAPicParams.RefFrameList[i].bPicEntry = 255;
m_DXVAPicParams.RefFrameList[i].Index7Bits = 127;
}
m_nNALLength = 4;
m_nMaxSlices = 0;
switch (GetMode())
{
case H264_VLD :
AllocExecuteParams (3);
break;
default :
ASSERT(FALSE);
}
}
void CDXVADecoderH264::CopyBitstream(BYTE* pDXVABuffer, BYTE* pBuffer, UINT& nSize)
{
CH264Nalu Nalu;
int nDummy;
int nSlices = 0;
int nDxvaNalLength;
Nalu.SetBuffer (pBuffer, nSize, m_nNALLength);
nSize = 0;
#if 0
// Test to place Nal on multiple of 128 bytes (seems to be not necessary)
if(!m_bUseLongSlice)
{
while (Nalu.ReadNext())
{
switch (Nalu.GetType())
{
case NALU_TYPE_SLICE:
case NALU_TYPE_IDR:
// For AVC1, put startcode 0x000001
pDXVABuffer[0]=pDXVABuffer[1]=0;
pDXVABuffer[2]=1;
// Copy NALU
memcpy (pDXVABuffer+3, Nalu.GetDataBuffer(), Nalu.GetDataLength());
// Complete with zero padding (buffer size should be a multiple of 128)
nDummy = 128 - ((Nalu.GetDataLength()+3) %128);
pDXVABuffer += Nalu.GetDataLength() + 3;
memset (pDXVABuffer, 0, nDummy);
pDXVABuffer += nDummy;
// Update slice control buffer
nDxvaNalLength = Nalu.GetDataLength()+3+nDummy;
m_pSliceShort[nSlices].BSNALunitDataLocation = nSize;
m_pSliceShort[nSlices].SliceBytesInBuffer = nDxvaNalLength;
nSize += nDxvaNalLength;
nSlices++;
break;
}
}
}
else
#endif
{
while (Nalu.ReadNext())
{
switch (Nalu.GetType())
{
case NALU_TYPE_SLICE:
case NALU_TYPE_IDR:
// For AVC1, put startcode 0x000001
pDXVABuffer[0]=pDXVABuffer[1]=0;
pDXVABuffer[2]=1;
// Copy NALU
memcpy (pDXVABuffer+3, Nalu.GetDataBuffer(), Nalu.GetDataLength());
// Update slice control buffer
nDxvaNalLength = Nalu.GetDataLength()+3;
m_pSliceShort[nSlices].BSNALunitDataLocation = nSize;
m_pSliceShort[nSlices].SliceBytesInBuffer = nDxvaNalLength;
nSize += nDxvaNalLength;
pDXVABuffer += nDxvaNalLength;
nSlices++;
break;
}
}
// Complete with zero padding (buffer size should be a multiple of 128)
nDummy = 128 - (nSize %128);
memset (pDXVABuffer, 0, nDummy);
m_pSliceShort[nSlices-1].SliceBytesInBuffer += nDummy;
nSize += nDummy;
}
}
void CDXVADecoderH264::Flush()
{
ClearRefFramesList();
m_DXVAPicParams.UsedForReferenceFlags = 0;
m_nOutPOC = -1;
m_rtLastFrameDisplayed = 0;
__super::Flush();
}
HRESULT CDXVADecoderH264::DecodeFrame (BYTE* pDataIn, UINT nSize, REFERENCE_TIME rtStart, REFERENCE_TIME rtStop)
{
HRESULT hr = S_FALSE;
CH264Nalu Nalu;
UINT nSlices = 0;
int nSurfaceIndex;
int nFieldType;
int nSliceType;
int nFramePOC;
CComPtr pSampleToDeliver;
CComQIPtr pDXVA2Sample;
UINT nNalOffset = 0;
int nOutPOC;
REFERENCE_TIME rtOutStart;
Nalu.SetBuffer (pDataIn, nSize, m_nNALLength);
FFH264DecodeBuffer (m_pFilter->GetAVCtx(), pDataIn, nSize, &nFramePOC, &nOutPOC, &rtOutStart);
while (Nalu.ReadNext())
{
switch (Nalu.GetType())
{
case NALU_TYPE_SLICE:
case NALU_TYPE_IDR:
if(m_bUseLongSlice)
{
m_pSliceLong[nSlices].BSNALunitDataLocation = nNalOffset;
m_pSliceLong[nSlices].SliceBytesInBuffer = Nalu.GetDataLength()+3; //.GetRoundedDataLength();
m_pSliceLong[nSlices].slice_id = nSlices;
FF264UpdateRefFrameSliceLong(&m_DXVAPicParams, &m_pSliceLong[nSlices], m_pFilter->GetAVCtx());
if (nSlices>0)
m_pSliceLong[nSlices-1].NumMbsForSlice = m_pSliceLong[nSlices].NumMbsForSlice = m_pSliceLong[nSlices].first_mb_in_slice - m_pSliceLong[nSlices-1].first_mb_in_slice;
}
nSlices++;
nNalOffset += (UINT)(Nalu.GetDataLength() + 3);
if (nSlices > MAX_SLICES) break;
break;
}
}
if (nSlices == 0) return S_FALSE;
m_nMaxWaiting = min (max (m_DXVAPicParams.num_ref_frames, 3), 8);
// If parsing fail (probably no PPS/SPS), continue anyway it may arrived later (happen on truncated streams)
if (FAILED (FFH264BuildPicParams (&m_DXVAPicParams, &m_DXVAScalingMatrix, &nFieldType, &nSliceType, m_pFilter->GetAVCtx(), m_pFilter->GetPCIVendor())))
return S_FALSE;
// Wait I frame after a flush
if (m_bFlushed && !m_DXVAPicParams.IntraPicFlag)
return S_FALSE;
CHECK_HR (GetFreeSurfaceIndex (nSurfaceIndex, &pSampleToDeliver, rtStart, rtStop));
FFH264SetCurrentPicture (nSurfaceIndex, &m_DXVAPicParams, m_pFilter->GetAVCtx());
CHECK_HR (BeginFrame(nSurfaceIndex, pSampleToDeliver));
m_DXVAPicParams.StatusReportFeedbackNumber++;
// TRACE("CDXVADecoderH264 : Decode frame %u\n", m_DXVAPicParams.StatusReportFeedbackNumber);
// Send picture parameters
CHECK_HR (AddExecuteBuffer (DXVA2_PictureParametersBufferType, sizeof(m_DXVAPicParams), &m_DXVAPicParams));
CHECK_HR (Execute());
// Add bitstream, slice control and quantization matrix
CHECK_HR (AddExecuteBuffer (DXVA2_BitStreamDateBufferType, nSize, pDataIn, &nSize));
if (m_bUseLongSlice)
{
CHECK_HR(AddExecuteBuffer(DXVA2_SliceControlBufferType, sizeof(DXVA_Slice_H264_Long)*nSlices, m_pSliceLong));
}
else
{
CHECK_HR (AddExecuteBuffer (DXVA2_SliceControlBufferType, sizeof (DXVA_Slice_H264_Short)*nSlices, m_pSliceShort));
}
CHECK_HR (AddExecuteBuffer (DXVA2_InverseQuantizationMatrixBufferType, sizeof (DXVA_Qmatrix_H264), (void*)&m_DXVAScalingMatrix));
// Decode bitstream
CHECK_HR (Execute());
CHECK_HR (EndFrame(nSurfaceIndex));
#ifdef _DEBUG
// DisplayStatus();
#endif
bool bAdded = AddToStore (nSurfaceIndex, pSampleToDeliver, m_DXVAPicParams.RefPicFlag, rtStart, rtStop,
m_DXVAPicParams.field_pic_flag, (FF_FIELD_TYPE)nFieldType,
(FF_SLICE_TYPE)nSliceType, nFramePOC);
FFH264UpdateRefFramesList (&m_DXVAPicParams, m_pFilter->GetAVCtx());
ClearUnusedRefFrames();
if (bAdded)
{
hr = DisplayNextFrame();
if (nOutPOC != INT_MIN)
{
m_nOutPOC = nOutPOC;
m_rtOutStart = rtOutStart;
}
}
m_bFlushed = false;
return hr;
}
void CDXVADecoderH264::RemoveUndisplayedFrame(int nPOC)
{
// Find frame with given POC, and free the slot
for (int i=0; iGetAVCtx()))
RemoveRefFrame (i);
}
}
void CDXVADecoderH264::SetExtraData (BYTE* pDataIn, UINT nSize)
{
AVCodecContext* pAVCtx = m_pFilter->GetAVCtx();
m_nNALLength = pAVCtx->nal_length_size;
FFH264DecodeBuffer (pAVCtx, pDataIn, nSize, NULL, NULL, NULL);
FFH264SetDxvaSliceLong (pAVCtx, m_pSliceLong);
}
void CDXVADecoderH264::ClearRefFramesList()
{
int i;
for (int i=0; iGetAvrTimePerFrame();
m_rtLastFrameDisplayed = m_rtOutStart + m_pFilter->GetAvrTimePerFrame();
m_pFilter->ReorderBFrames (m_pPictureStore[nPos].rtStart, m_pPictureStore[nPos].rtStop);
}
return nPos;
}