Skip to content

Commit

Permalink
🐛 Fix issues in motion compensation decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
pleonex committed Nov 4, 2023
1 parent c7296ac commit 5c8755f
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 17 deletions.
33 changes: 17 additions & 16 deletions src/PlayMobic/Video/Mobiclip/MotionCompensationDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,9 @@ private void DecodeBlockPartioning(YuvBlock block, int mode, int macroBlockIndex
int newLumaHeight = partitions[0].Luma.Height;
Huffman huffmanTable = huffmanTables[(newLumaWidth, newLumaHeight)];

// Read the partitions mode encoded with huffman
int partitionsMode = huffmanTable.ReadCodeword(reader);

// Re-run motion compensation algorithm on each partition.
foreach (YuvBlock partition in partitions) {
int partitionsMode = huffmanTable.ReadCodeword(reader);
DecodeBlock(partition, partitionsMode, macroBlockIndex);
}
}
Expand All @@ -118,38 +116,41 @@ private void DecodeBlockMotion(YuvBlock block, int mode, int macroBlockIdx)
CopyMovingYuvBlock(block, src, vector);
}

private void CopyMovingYuvBlock(YuvBlock dstBlock, YuvBlock srcFrame, Vector2D vector)
private void CopyMovingYuvBlock(YuvBlock dstBlock, YuvBlock srcFrame, Vector2D delta)
{
ComponentBlock dstLuma = dstBlock.Luma;
ComponentBlock dstChromaU = dstBlock.ChromaU;
ComponentBlock dstChromaV = dstBlock.ChromaV;

for (int y = 0; y < dstBlock.Luma.Height; y++) {
for (int x = 0; x < dstBlock.Luma.Width; x++) {
int srcLumaX = dstLuma.X + x + vector.X;
int srcLumaY = dstLuma.Y + y + vector.Y;
dstLuma[x, y] = GetHalfPelComponent(srcFrame.Luma, srcLumaX, srcLumaY);
int srcLumaX = dstLuma.X + x;
int srcLumaY = dstLuma.Y + y;
dstLuma[x, y] = GetHalfPelComponent(srcFrame.Luma, srcLumaX, srcLumaY, delta);

// Because of YUV 4:2:0 downsampling we divide by 2 and do it half of time
if ((x % 2) == 0 && (y % 2) == 0) {
int dstChromaX = dstChromaU.X + (x / 2) + (vector.X / 2);
int dstChromaY = dstChromaU.Y + (y / 2) + (vector.Y / 2);
dstChromaU[x / 2, y / 2] = GetHalfPelComponent(srcFrame.ChromaU, dstChromaX, dstChromaY);
dstChromaV[x / 2, y / 2] = GetHalfPelComponent(srcFrame.ChromaV, dstChromaX, dstChromaY);
var chromaDelta = new Vector2D(delta.X / 2, delta.Y / 2);
int dstChromaX = dstChromaU.X + (x / 2);
int dstChromaY = dstChromaU.Y + (y / 2);
dstChromaU[x / 2, y / 2] = GetHalfPelComponent(srcFrame.ChromaU, dstChromaX, dstChromaY, chromaDelta);
dstChromaV[x / 2, y / 2] = GetHalfPelComponent(srcFrame.ChromaV, dstChromaX, dstChromaY, chromaDelta);
}
}
}
}

private byte GetHalfPelComponent(ComponentBlock block, int x, int y)
private byte GetHalfPelComponent(ComponentBlock block, int x, int y, Vector2D delta)
{
// Actual positions are multiplied by two, so we can have half-pixel positions
// without storing decimals.
bool isExactX = (x % 2) == 0;
bool isExactY = (y % 2) == 0;
bool isExactX = (delta.X % 2) == 0;
bool isExactY = (delta.Y % 2) == 0;

x /= 2;
y /= 2;
// We divide with bitwise shifting (same as /2) because when it's -1
// shifting 1, gives still -1 rather than 0 (it propages sign so it's still negative).
x += delta.X >> 1;
y += delta.Y >> 1;

if (isExactX && isExactY) {
return block[x, y];
Expand Down
2 changes: 1 addition & 1 deletion src/PlayMobic/Video/YuvBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public readonly YuvBlock[] PartitionBy(int widthDiv, int heightDiv)
{
ComponentBlock[] lumas = Luma.Partition(Luma.Width / widthDiv, Luma.Height / heightDiv);
ComponentBlock[] chromaUs = ChromaU.Partition(ChromaU.Width / widthDiv, ChromaU.Height / heightDiv);
ComponentBlock[] chromaVs = ChromaU.Partition(ChromaV.Width / widthDiv, ChromaV.Height / heightDiv);
ComponentBlock[] chromaVs = ChromaV.Partition(ChromaV.Width / widthDiv, ChromaV.Height / heightDiv);

var partitions = new YuvBlock[lumas.Length];
for (int i = 0; i < lumas.Length; i++) {
Expand Down

0 comments on commit 5c8755f

Please sign in to comment.