ryujinx/Ryujinx.Graphics/VDec/H264Decoder.cs
gdkchan c86aacde76
NVDEC implementation using FFmpeg (#443)
* Initial nvdec implementation using FFmpeg

* Fix swapped channels on the video decoder and the G8R8 texture format

* Fix texture samplers not being set properly (regression)

* Rebased

* Remove unused code introduced on the rebase

* Add support for RGBA8 output format on the video image composer

* Correct spacing

* Some fixes for rebase and other tweaks

* Allow size mismatch on frame copy

* Get rid of GetHostAddress calls on VDec
2018-12-03 00:38:47 -02:00

238 lines
9 KiB
C#

using System.IO;
namespace Ryujinx.Graphics.VDec
{
class H264Decoder
{
private int Log2MaxPicOrderCntLsbMinus4;
private bool DeltaPicOrderAlwaysZeroFlag;
private bool FrameMbsOnlyFlag;
private int PicWidthInMbs;
private int PicHeightInMapUnits;
private bool EntropyCodingModeFlag;
private bool BottomFieldPicOrderInFramePresentFlag;
private int NumRefIdxL0DefaultActiveMinus1;
private int NumRefIdxL1DefaultActiveMinus1;
private bool DeblockingFilterControlPresentFlag;
private bool RedundantPicCntPresentFlag;
private bool Transform8x8ModeFlag;
private bool MbAdaptiveFrameFieldFlag;
private bool Direct8x8InferenceFlag;
private bool WeightedPredFlag;
private bool ConstrainedIntraPredFlag;
private bool FieldPicFlag;
private bool BottomFieldFlag;
private int Log2MaxFrameNumMinus4;
private int ChromaFormatIdc;
private int PicOrderCntType;
private int PicInitQpMinus26;
private int ChromaQpIndexOffset;
private int ChromaQpIndexOffset2;
private int WeightedBipredIdc;
private int FrameNumber;
private byte[] ScalingMatrix4;
private byte[] ScalingMatrix8;
public void Decode(H264ParameterSets Params, H264Matrices Matrices, byte[] FrameData)
{
Log2MaxPicOrderCntLsbMinus4 = Params.Log2MaxPicOrderCntLsbMinus4;
DeltaPicOrderAlwaysZeroFlag = Params.DeltaPicOrderAlwaysZeroFlag;
FrameMbsOnlyFlag = Params.FrameMbsOnlyFlag;
PicWidthInMbs = Params.PicWidthInMbs;
PicHeightInMapUnits = Params.PicHeightInMapUnits;
EntropyCodingModeFlag = Params.EntropyCodingModeFlag;
BottomFieldPicOrderInFramePresentFlag = Params.BottomFieldPicOrderInFramePresentFlag;
NumRefIdxL0DefaultActiveMinus1 = Params.NumRefIdxL0DefaultActiveMinus1;
NumRefIdxL1DefaultActiveMinus1 = Params.NumRefIdxL1DefaultActiveMinus1;
DeblockingFilterControlPresentFlag = Params.DeblockingFilterControlPresentFlag;
RedundantPicCntPresentFlag = Params.RedundantPicCntPresentFlag;
Transform8x8ModeFlag = Params.Transform8x8ModeFlag;
MbAdaptiveFrameFieldFlag = ((Params.Flags >> 0) & 1) != 0;
Direct8x8InferenceFlag = ((Params.Flags >> 1) & 1) != 0;
WeightedPredFlag = ((Params.Flags >> 2) & 1) != 0;
ConstrainedIntraPredFlag = ((Params.Flags >> 3) & 1) != 0;
FieldPicFlag = ((Params.Flags >> 5) & 1) != 0;
BottomFieldFlag = ((Params.Flags >> 6) & 1) != 0;
Log2MaxFrameNumMinus4 = (int)(Params.Flags >> 8) & 0xf;
ChromaFormatIdc = (int)(Params.Flags >> 12) & 0x3;
PicOrderCntType = (int)(Params.Flags >> 14) & 0x3;
PicInitQpMinus26 = (int)(Params.Flags >> 16) & 0x3f;
ChromaQpIndexOffset = (int)(Params.Flags >> 22) & 0x1f;
ChromaQpIndexOffset2 = (int)(Params.Flags >> 27) & 0x1f;
WeightedBipredIdc = (int)(Params.Flags >> 32) & 0x3;
FrameNumber = (int)(Params.Flags >> 46) & 0x1ffff;
PicInitQpMinus26 = (PicInitQpMinus26 << 26) >> 26;
ChromaQpIndexOffset = (ChromaQpIndexOffset << 27) >> 27;
ChromaQpIndexOffset2 = (ChromaQpIndexOffset2 << 27) >> 27;
ScalingMatrix4 = Matrices.ScalingMatrix4;
ScalingMatrix8 = Matrices.ScalingMatrix8;
if (FFmpegWrapper.IsInitialized)
{
FFmpegWrapper.DecodeFrame(FrameData);
}
else
{
FFmpegWrapper.H264Initialize();
FFmpegWrapper.DecodeFrame(DecoderHelper.Combine(EncodeHeader(), FrameData));
}
}
private byte[] EncodeHeader()
{
using (MemoryStream Data = new MemoryStream())
{
H264BitStreamWriter Writer = new H264BitStreamWriter(Data);
//Sequence Parameter Set.
Writer.WriteU(1, 24);
Writer.WriteU(0, 1);
Writer.WriteU(3, 2);
Writer.WriteU(7, 5);
Writer.WriteU(100, 8);
Writer.WriteU(0, 8);
Writer.WriteU(31, 8);
Writer.WriteUe(0);
Writer.WriteUe(ChromaFormatIdc);
if (ChromaFormatIdc == 3)
{
Writer.WriteBit(false);
}
Writer.WriteUe(0);
Writer.WriteUe(0);
Writer.WriteBit(false);
Writer.WriteBit(false); //Scaling matrix present flag
Writer.WriteUe(Log2MaxFrameNumMinus4);
Writer.WriteUe(PicOrderCntType);
if (PicOrderCntType == 0)
{
Writer.WriteUe(Log2MaxPicOrderCntLsbMinus4);
}
else if (PicOrderCntType == 1)
{
Writer.WriteBit(DeltaPicOrderAlwaysZeroFlag);
Writer.WriteSe(0);
Writer.WriteSe(0);
Writer.WriteUe(0);
}
int PicHeightInMbs = PicHeightInMapUnits / (FrameMbsOnlyFlag ? 1 : 2);
Writer.WriteUe(16);
Writer.WriteBit(false);
Writer.WriteUe(PicWidthInMbs - 1);
Writer.WriteUe(PicHeightInMbs - 1);
Writer.WriteBit(FrameMbsOnlyFlag);
if (!FrameMbsOnlyFlag)
{
Writer.WriteBit(MbAdaptiveFrameFieldFlag);
}
Writer.WriteBit(Direct8x8InferenceFlag);
Writer.WriteBit(false); //Frame cropping flag
Writer.WriteBit(false); //VUI parameter present flag
Writer.End();
//Picture Parameter Set.
Writer.WriteU(1, 24);
Writer.WriteU(0, 1);
Writer.WriteU(3, 2);
Writer.WriteU(8, 5);
Writer.WriteUe(0);
Writer.WriteUe(0);
Writer.WriteBit(EntropyCodingModeFlag);
Writer.WriteBit(false);
Writer.WriteUe(0);
Writer.WriteUe(NumRefIdxL0DefaultActiveMinus1);
Writer.WriteUe(NumRefIdxL1DefaultActiveMinus1);
Writer.WriteBit(WeightedPredFlag);
Writer.WriteU(WeightedBipredIdc, 2);
Writer.WriteSe(PicInitQpMinus26);
Writer.WriteSe(0);
Writer.WriteSe(ChromaQpIndexOffset);
Writer.WriteBit(DeblockingFilterControlPresentFlag);
Writer.WriteBit(ConstrainedIntraPredFlag);
Writer.WriteBit(RedundantPicCntPresentFlag);
Writer.WriteBit(Transform8x8ModeFlag);
Writer.WriteBit(true);
for (int Index = 0; Index < 6; Index++)
{
Writer.WriteBit(true);
WriteScalingList(Writer, ScalingMatrix4, Index * 16, 16);
}
if (Transform8x8ModeFlag)
{
for (int Index = 0; Index < 2; Index++)
{
Writer.WriteBit(true);
WriteScalingList(Writer, ScalingMatrix8, Index * 64, 64);
}
}
Writer.WriteSe(ChromaQpIndexOffset2);
Writer.End();
return Data.ToArray();
}
}
//ZigZag LUTs from libavcodec.
private static readonly byte[] ZigZagDirect = new byte[]
{
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63
};
private static readonly byte[] ZigZagScan = new byte[]
{
0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4,
1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4,
3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4
};
private static void WriteScalingList(H264BitStreamWriter Writer, byte[] List, int Start, int Count)
{
byte[] Scan = Count == 16 ? ZigZagScan : ZigZagDirect;
int LastScale = 8;
for (int Index = 0; Index < Count; Index++)
{
byte Value = List[Start + Scan[Index]];
int DeltaScale = Value - LastScale;
Writer.WriteSe(DeltaScale);
LastScale = Value;
}
}
}
}