public MobiEncoder(int Quantizer, int Width, int Height) { if (Width % 16 != 0 || Height % 16 != 0) throw new Exception(); if (Quantizer < 0xC) Quantizer = 0xC; if (Quantizer > 0x34) Quantizer = 0x34; this.Quantizer = Quantizer; this.Width = Width; this.Height = Height; Stride = 1024; if (Width <= 256) Stride = 256; else if (Width <= 512) Stride = 512; YDec = new byte[Stride * Height]; UVDec = new byte[Stride * Height / 2]; MacroBlocks = new MacroBlock[Height / 16][]; for (int i = 0; i < Height / 16; i++) { MacroBlocks[i] = new MacroBlock[Width / 16]; } LastFrameType = FrameType.None; SetupQuantizationTables(); }
private unsafe byte[] EncodePrediction(Bitmap Frame) { //We'll just look if there are blocks that are 100% the same first BitmapData d = Frame.LockBits(new Rectangle(0, 0, Frame.Width, Frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); BitmapData prev = PastFrames[1].LockBits(new Rectangle(0, 0, Frame.Width, Frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); for (int y = 0; y < Height; y += 16) { for (int x = 0; x < Width; x += 16) { MacroBlocks[y / 16][x / 16] = new MacroBlock(d, x, y); bool same = true; for (int y2 = 0; y2 < 16; y2++) { ulong* pA = (ulong*)(((byte*)d.Scan0) + (y + y2) * d.Stride + x * 4); ulong* pB = (ulong*)(((byte*)prev.Scan0) + (y + y2) * prev.Stride + x * 4); for (int x2 = 0; x2 < 16; x2+=2) { ulong color = *pA++; ulong color2 = *pB++; if (color != color2) { same = false; break; } } if (!same) break; } if (same) MacroBlocks[y / 16][x / 16].Predict = true; else { Analyzer.ConfigureBlockY(this, MacroBlocks[y / 16][x / 16]); int blocktype2 = Analyzer.AnalyzeBlockUV(this, MacroBlocks[y / 16][x / 16]); MacroBlocks[y / 16][x / 16].UVPredictionMode = blocktype2; MacroBlocks[y / 16][x / 16].UVUseComplex8x8[0] = true; MacroBlocks[y / 16][x / 16].UVUseComplex8x8[1] = true; MacroBlocks[y / 16][x / 16].SetupDCTs(this); } } } Frame.UnlockBits(d); PastFrames[1].UnlockBits(prev); BitWriter b = new BitWriter(); b.WriteBits(0, 1);//Interframe b.WriteVarIntSigned(0); for (int y = 0; y < Height; y += 16) { for (int x = 0; x < Width; x += 16) { MacroBlock curblock = MacroBlocks[y / 16][x / 16]; if (curblock.Predict) { b.WriteBits(1, 1); b.WriteVarIntUnsigned(0); } else { b.WriteBits(0xE >> 1, 5); EncodeBlockIntra(curblock, b); } } } b.WriteBits(0, 16); b.Flush(); byte[] result = b.ToArray(); return result; }
private void EncodeBlockIntra(MacroBlock Block, BitWriter b) { uint dctmask = (Block.YUseComplex8x8[0] ? 1u : 0) | ((Block.YUseComplex8x8[1] ? 1u : 0) << 1) | ((Block.YUseComplex8x8[2] ? 1u : 0) << 2) | ((Block.YUseComplex8x8[3] ? 1u : 0) << 3) | ((Block.UVUseComplex8x8[0] ? 1u : 0) << 4) | ((Block.UVUseComplex8x8[1] ? 1u : 0) << 5); b.WriteVarIntUnsigned(REV_byte_115FC4[dctmask]); b.WriteBits((uint)Block.YPredictionMode, 3);//Block type for (int y2 = 0; y2 < 2; y2++) { for (int x2 = 0; x2 < 2; x2++) { if (Block.YUseComplex8x8[x2 + y2 * 2] && !Block.YUse4x4[x2 + y2 * 2]) { b.WriteBits(1, 1);//Don't use 4x4 blocks EncodeDCT(Block.YDCT8x8[x2 + y2 * 2], 0, b); } else if (Block.YUseComplex8x8[x2 + y2 * 2] && Block.YUse4x4[x2 + y2 * 2]) { uint dctmask2 = (Block.YUseDCT4x4[x2 + y2 * 2][0] ? 1u : 0) | ((Block.YUseDCT4x4[x2 + y2 * 2][1] ? 1u : 0) << 1) | ((Block.YUseDCT4x4[x2 + y2 * 2][2] ? 1u : 0) << 2) | ((Block.YUseDCT4x4[x2 + y2 * 2][3] ? 1u : 0) << 3); b.WriteVarIntUnsigned(REV_byte_1164F4[dctmask2]); for (int y3 = 0; y3 < 2; y3++) { for (int x3 = 0; x3 < 2; x3++) { if (Block.YUseDCT4x4[x2 + y2 * 2][x3 + y3 * 2]) EncodeDCT(Block.YDCT4x4[x2 + y2 * 2][x3 + y3 * 2], 0, b); } } } } } b.WriteBits((uint)Block.UVPredictionMode, 3);//Block type for (int q = 0; q < 2; q++) { if (Block.UVUseComplex8x8[q] && !Block.UVUse4x4[q]) { b.WriteBits(1, 1);//Don't use 4x4 blocks EncodeDCT(Block.UVDCT8x8[q], 0, b); } } }
private byte[] EncodeIntra(Bitmap Frame) { BitmapData d = Frame.LockBits(new Rectangle(0, 0, Frame.Width, Frame.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); //Setup the macroblocks for (int y = 0; y < Height; y += 16) { for (int x = 0; x < Width; x += 16) { MacroBlocks[y / 16][x / 16] = new MacroBlock(d, x, y); Analyzer.ConfigureBlockY(this, MacroBlocks[y / 16][x / 16]); int blocktype2 = Analyzer.AnalyzeBlockUV(this, MacroBlocks[y / 16][x / 16]); MacroBlocks[y / 16][x / 16].UVPredictionMode = blocktype2; MacroBlocks[y / 16][x / 16].UVUseComplex8x8[0] = true; MacroBlocks[y / 16][x / 16].UVUseComplex8x8[1] = true; MacroBlocks[y / 16][x / 16].SetupDCTs(this); } } Frame.UnlockBits(d); BitWriter b = new BitWriter(); b.WriteBits(1, 1);//Interframe b.WriteBits(1, 1);//YUV format //TODO: determine table (when we actually use it...) b.WriteBits(0, 1);//Table b.WriteBits((uint)Quantizer, 6);//Quantizer for (int y = 0; y < Height; y += 16) { for (int x = 0; x < Width; x += 16) { MacroBlock curblock = MacroBlocks[y / 16][x / 16]; //Todo use predicted prediction mode /*if (x > 0 && y > 0) { int r12 = MacroBlocks[y / 16 - 1][x / 16].YPredictionMode; int r6 = MacroBlocks[y / 16][x / 16 - 1].YPredictionMode; if (r12 > r6) r12 = r6; if (r12 == 9) r12 = 3; r6 = r3 >> 28; if (r6 >= r12) r6++; int r7; if (r6 < 9) { r12 = r6; r7 = 4; } else r7 = 1; r3 <<= r7; nrBitsRemaining -= r7; }*/ b.WriteBits(0, 1);//Func EncodeBlockIntra(curblock, b); } } b.WriteBits(0, 16); b.Flush(); byte[] result = b.ToArray(); return result; }
//This analyzer should determine the best block configuration for a macro block (mb) public static void ConfigureBlockY(MobiEncoder Encoder, MacroBlock Block) { List<BlockScore> scores = new List<BlockScore>(); //8x8 if (Block.Y >= 8) { int score = 0; score += GetScore8x8(Block.YData8x8[0], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[0], Encoder.YDec, Block.X, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 0)); score += GetScore8x8(Block.YData8x8[1], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[1], Encoder.YDec, Block.X + 8, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 0)); score += GetScore8x8(Block.YData8x8[2], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[2], Encoder.YDec, Block.X, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 0)); score += GetScore8x8(Block.YData8x8[3], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[3], Encoder.YDec, Block.X + 8, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 0)); scores.Add(new BlockScore() { BlockConfigId = 0, Score = score }); score = 0; int b = 0; for (int y = 0; y < 16; y += 8) { for (int x = 0; x < 16; x += 8) { int b2 = 0; for (int y2 = 0; y2 < 8; y2 += 4) { for (int x2 = 0; x2 < 8; x2 += 4) { score += GetScore4x4(Block.YData4x4[b][b2], MacroBlock.EncodeDecode4x4Block(Block.YData4x4[b][b2], Encoder.YDec, Block.X + x + x2, Block.Y + y + y2, Encoder.Stride, 0, Encoder.QTable4x4, 10 + 0)); b2++; } } b++; } } scores.Add(new BlockScore() { BlockConfigId = 0 | (1 << 5), Score = score }); } if (Block.X >= 8) { int score = 0; score += GetScore8x8(Block.YData8x8[0], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[0], Encoder.YDec, Block.X, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 1)); score += GetScore8x8(Block.YData8x8[1], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[1], Encoder.YDec, Block.X + 8, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 1)); score += GetScore8x8(Block.YData8x8[2], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[2], Encoder.YDec, Block.X, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 1)); score += GetScore8x8(Block.YData8x8[3], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[3], Encoder.YDec, Block.X + 8, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 1)); scores.Add(new BlockScore() { BlockConfigId = 1, Score = score }); score = 0; int b = 0; for (int y = 0; y < 16; y += 8) { for (int x = 0; x < 16; x += 8) { int b2 = 0; for (int y2 = 0; y2 < 8; y2 += 4) { for (int x2 = 0; x2 < 8; x2 += 4) { score += GetScore4x4(Block.YData4x4[b][b2], MacroBlock.EncodeDecode4x4Block(Block.YData4x4[b][b2], Encoder.YDec, Block.X + x + x2, Block.Y + y + y2, Encoder.Stride, 0, Encoder.QTable4x4, 10 + 1)); b2++; } } b++; } } scores.Add(new BlockScore() { BlockConfigId = 1 | (1 << 5), Score = score }); } //TODO: block type2 //Block type 3 { int score = 0; score += GetScore8x8(Block.YData8x8[0], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[0], Encoder.YDec, Block.X, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 3)); score += GetScore8x8(Block.YData8x8[1], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[1], Encoder.YDec, Block.X + 8, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, 3)); score += GetScore8x8(Block.YData8x8[2], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[2], Encoder.YDec, Block.X, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 3)); score += GetScore8x8(Block.YData8x8[3], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[3], Encoder.YDec, Block.X + 8, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, 3)); scores.Add(new BlockScore() { BlockConfigId = 3, Score = score }); score = 0; int b = 0; for (int y = 0; y < 16; y += 8) { for (int x = 0; x < 16; x += 8) { int b2 = 0; for (int y2 = 0; y2 < 8; y2 += 4) { for (int x2 = 0; x2 < 8; x2 += 4) { score += GetScore4x4(Block.YData4x4[b][b2], MacroBlock.EncodeDecode4x4Block(Block.YData4x4[b][b2], Encoder.YDec, Block.X + x + x2, Block.Y + y + y2, Encoder.Stride, 0, Encoder.QTable4x4, 10 + 3)); b2++; } } b++; } } scores.Add(new BlockScore() { BlockConfigId = 3 | (1 << 5), Score = score }); } if (Block.X >= 8 && Block.Y >= 8) { for (int i = 4; i <= 7; i++) { int score = 0; score += GetScore8x8(Block.YData8x8[0], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[0], Encoder.YDec, Block.X, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, i)); score += GetScore8x8(Block.YData8x8[1], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[1], Encoder.YDec, Block.X + 8, Block.Y, Encoder.Stride, 0, Encoder.QTable8x8, i)); score += GetScore8x8(Block.YData8x8[2], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[2], Encoder.YDec, Block.X, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, i)); score += GetScore8x8(Block.YData8x8[3], MacroBlock.EncodeDecode8x8Block(Block.YData8x8[3], Encoder.YDec, Block.X + 8, Block.Y + 8, Encoder.Stride, 0, Encoder.QTable8x8, i)); scores.Add(new BlockScore() { BlockConfigId = i, Score = score }); score = 0; int b = 0; for (int y = 0; y < 16; y += 8) { for (int x = 0; x < 16; x += 8) { int b2 = 0; for (int y2 = 0; y2 < 8; y2 += 4) { for (int x2 = 0; x2 < 8; x2 += 4) { score += GetScore4x4(Block.YData4x4[b][b2], MacroBlock.EncodeDecode4x4Block(Block.YData4x4[b][b2], Encoder.YDec, Block.X + x + x2, Block.Y + y + y2, Encoder.Stride, 0, Encoder.QTable4x4, 10 + i)); b2++; } } b++; } } scores.Add(new BlockScore() { BlockConfigId = i | (1 << 5), Score = score }); } } BlockScore best = null; foreach (BlockScore s in scores) { if (best == null || s.Score < best.Score || (s.Score == best.Score && (s.BlockConfigId & 0x20) == 0 && (best.BlockConfigId & 0x20) != 0)) best = s; } Block.YPredictionMode = best.BlockConfigId & 0x1F; Block.YUseComplex8x8[0] = true; Block.YUseComplex8x8[1] = true; Block.YUseComplex8x8[2] = true; Block.YUseComplex8x8[3] = true; if (((best.BlockConfigId >> 5) & 1) == 1) { Block.YUse4x4[0] = true; Block.YUse4x4[1] = true; Block.YUse4x4[2] = true; Block.YUse4x4[3] = true; for (int i = 0; i < 4; i++) { for (int j = 0; j < 4; j++) { Block.YUseDCT4x4[i][j] = true; } } } }
public static int AnalyzeBlockUV(MobiEncoder Encoder, MacroBlock Block) { List<BlockScore> scores = new List<BlockScore>(); //8x8 if (Block.Y >= 8) { int score = 0; score += GetScore8x8(Block.UData, MacroBlock.EncodeDecode8x8Block(Block.UData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, 0, Encoder.QTable8x8, 0)); score += GetScore8x8(Block.VData, MacroBlock.EncodeDecode8x8Block(Block.VData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, Encoder.Stride / 2, Encoder.QTable8x8, 0)); scores.Add(new BlockScore() { BlockConfigId = 0, Score = score }); } if (Block.X >= 8) { int score = 0; score += GetScore8x8(Block.UData, MacroBlock.EncodeDecode8x8Block(Block.UData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, 0, Encoder.QTable8x8, 1)); score += GetScore8x8(Block.VData, MacroBlock.EncodeDecode8x8Block(Block.VData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, Encoder.Stride / 2, Encoder.QTable8x8, 1)); scores.Add(new BlockScore() { BlockConfigId = 1, Score = score }); } //TODO: block type2 //Block type 3 { int score = 0; score += GetScore8x8(Block.UData, MacroBlock.EncodeDecode8x8Block(Block.UData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, 0, Encoder.QTable8x8, 3)); score += GetScore8x8(Block.VData, MacroBlock.EncodeDecode8x8Block(Block.VData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, Encoder.Stride / 2, Encoder.QTable8x8, 3)); scores.Add(new BlockScore() { BlockConfigId = 3, Score = score }); } if (Block.X >= 8 && Block.Y >= 8) { for (int i = 4; i <= 7; i++) { int score = 0; score += GetScore8x8(Block.UData, MacroBlock.EncodeDecode8x8Block(Block.UData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, 0, Encoder.QTable8x8, i)); score += GetScore8x8(Block.VData, MacroBlock.EncodeDecode8x8Block(Block.VData, Encoder.UVDec, Block.X / 2, Block.Y / 2, Encoder.Stride, Encoder.Stride / 2, Encoder.QTable8x8, i)); scores.Add(new BlockScore() { BlockConfigId = i, Score = score }); } } BlockScore best = null; foreach (BlockScore s in scores) { if (best == null || s.Score < best.Score) best = s; } return best.BlockConfigId; }