VideoCodec/Modules/StreamCoding/HuffmanEntropyDecoder.cpp

#include "HuffmanEntropyDecoder.h" // Macros to get the macroblock type based on the bitmask. #define IS_QUANT(x) (((x) & 0x10) == 0x10) #define IS_MOTION_FORWARD(x) (((x) & 0x8) == 0x8) #define IS_MOTION_BACKWARD(x) (((x) & 0x4) == 0x4) #define IS_PATTERN(x) (((x) & 0x2) == 0x2) #define IS_INTRA(x) (((x) & 0x1) == 0x1) namespace VideoCodec {    static unsigned char reverseZigZagScanIndicesEightByEight[64] =       {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};        HuffmanEntropyDecoder::HuffmanEntropyDecoder() : Module(HUFFMAN_ENTROPY_DECODER_MODULE_ID)    {    }        HuffmanEntropyDecoder::~HuffmanEntropyDecoder()    {    }        ConfigurationStatus HuffmanEntropyDecoder::Configure(ConfigurationElement* configuration)    {       return MODULE_CONFIGURATION_OK;    }        void HuffmanEntropyDecoder::decodeInterFrame(BinaryStreamReader* reader, StreamHeader* streamHeader, FrameHeader* frameHeader, PictureInfo* picture)    {       BlockCount blockRows = streamHeader->blockRows;       BlockCount blockColumns = streamHeader->blockColumns;              SignedShort* blockEncodedValues = new SignedShort[8 * 8];              SignedShort* frameTransformedValues = new SignedShort[8 * 8 * 6 * blockRows * blockColumns];       memset((void*)frameTransformedValues, 0, sizeof(SignedShort) * 8 * 8 * 6 * blockRows * blockColumns);              // Motion vector component prediction values.       SignedByte xPrediction = 0;       SignedByte yPrediction = 0;              // Decoded motion vector components. Zero them so that we don't have to set blank ones manually.       SignedByte* xComponents;       SignedByte* yComponents;       if (streamHeader->mv4)       {          xComponents = new SignedByte[blockRows * blockColumns * 4];          yComponents = new SignedByte[blockRows * blockColumns * 4];          memset((void*)xComponents, 0, sizeof(SignedByte) * blockRows * blockColumns * 4);          memset((void*)yComponents, 0, sizeof(SignedByte) * blockRows * blockColumns * 4);       }       else       {          xComponents = new SignedByte[blockRows * blockColumns];          yComponents = new SignedByte[blockRows * blockColumns];          memset((void*)xComponents, 0, sizeof(SignedByte) * blockRows * blockColumns);          memset((void*)yComponents, 0, sizeof(SignedByte) * blockRows * blockColumns);       }       bool halfPixel = streamHeader->halfPixel;              // There is always an initial increment.       short int mbIndex = -1;              while (mbIndex < blockRows * blockColumns)       {          while (reader->MatchBits(MACROBLOCK_INCREMENT_ESCAPE_LENGTH, MACROBLOCK_INCREMENT_ESCAPE))          {             mbIndex += 33;             xPrediction = yPrediction = 0;          }                    for (unsigned char length = 0; length < 11; length++)             for (unsigned char value = 0; value < 12; value++)             {                DecoderMAI mai = maisByLength[length][value];                                // Skip if we reach the sentinel in this group.                if (mai.code == nullMAI.code)                   break;                                if (reader->MatchBits(length + 1, mai.code))                {                   if (mai.increment > 1)                      xPrediction = yPrediction = 0;                   mbIndex += mai.increment;                   goto addressIncrementMatched;                }             }                    // A MAI should have been matched.          Logger::LogMessage("Warning: No macroblock address increment was matched.");           addressIncrementMatched:          // mbIndex is now at a coded macroblock, or at the end of the stream.          if (mbIndex == blockRows * blockColumns)             break;                    // Get the type of macroblock.          UnsignedByte type = this->decodeMBType(reader, true);                    unsigned short int blockCBP = 0x0000;          if (IS_PATTERN(type))          {             // Get the coded block pattern.             for (unsigned char length = 0; length < 7; length++)                for (unsigned char value = 0; value < 28; value++)                {                   DecoderCBP cbp = cbpsByLength[length][value];                                      // Skip if we reach the sentinel in this group.                   if (cbp.code == nullCBP.code)                      break;                                      if (reader->MatchBits(length + 3, cbp.code))                   {                      blockCBP = cbp.bp;                      goto blockPatternMatched;                   }                }                          // A CBP should have been matched.             Logger::LogMessage("Warning: No coded block pattern was matched."); blockPatternMatched:             if (blockCBP == 0)                Logger::LogMessage("Warning: We should never have a zero CBP.");          }                    Logger::DebugLog("(Inter %d) ", mbIndex);                    if (IS_MOTION_FORWARD(type))          {             SignedByte px = this->decodeMVComponent(reader);             SignedByte py = this->decodeMVComponent(reader);                          // Decode the motion vector components.             xComponents[streamHeader->mv4 ? mbIndex * 4 : mbIndex] = px + xPrediction;             yComponents[streamHeader->mv4 ? mbIndex * 4 : mbIndex] = py + yPrediction;                          if (!halfPixel)             {                xComponents[streamHeader->mv4 ? mbIndex * 4 : mbIndex] *= 2;                yComponents[streamHeader->mv4 ? mbIndex * 4 : mbIndex] *= 2;             }                          if (streamHeader->mv4)             {                for (int mvI = 1; mvI < 4; mvI++)                {                   xComponents[mbIndex * 4 + mvI] = this->decodeMVComponent(reader) + xPrediction;                   yComponents[mbIndex * 4 + mvI] = this->decodeMVComponent(reader) + yPrediction;                                      if (!halfPixel)                   {                      xComponents[mbIndex * 4 + mvI] *= 2;                      yComponents[mbIndex * 4 + mvI] *= 2;                   }                }             }             xPrediction += px; yPrediction += py;                          Logger::DebugLog("MV %d, %d ", xPrediction, yPrediction);          }          else          {             // Whenever a block is not motion compensated, reset the prediction.             xPrediction = yPrediction = 0;          }                    if (IS_INTRA(type) || IS_QUANT(type) || IS_MOTION_BACKWARD(type))             Logger::LogMessage("Warning: Unsupported macroblock type: intra in P-picture.");                    // Is this is not a CBP macroblock, the specification says it's not coded, so skip the decoding stage.          if (!IS_PATTERN(type))             continue;                    for (unsigned char block = 0; block < 6; block++)          {             if (((1 << (5 - block)) & blockCBP) == 0)                continue;                          Logger::DebugLog(" block %d AC ", block);                          // The offset into the coefficient block.             unsigned char acOffset = 0;             // For inter blocks, a special value is used for an initial run/level 0/1, because there is             // no chance of a clash with EOB (as empty blocks are not coded due to CBPs).             if (reader->MatchBits(1, INTER_VLC_ZERO_RUN_ONE_LEVEL_CODE))             {                Logger::DebugLog("{0, 1}* ");                blockEncodedValues[acOffset] = (reader->GetBits(1) == 0x0001 ? -1 : 1);                reader->AdvanceReader(1);                acOffset++;             }                          // The next set of codes are AC coefficients.             // To decode these, we match bits into the AC VLC table to work out a zero run-length and level,             // then use the next bit to set the sign of the AC value. If it is not present in the table, try to match             // with the escape code and decode six bits of zero run-length and twelve bits of level             // Keep looping until we reach the EOB (end of block) marker, which indicates that the remainder of the coefficients are zero.             while (!reader->MatchBits(AC_END_OF_BLOCK_LENGTH, AC_END_OF_BLOCK))             {                short int level = -1;                unsigned char zeroRunLength = 0;                for (int i = 0; i < 15; i++)                {                   DecoderVLCEntry thisEntry;                   for (int j = 0; j < 16; j++)                   {                      thisEntry = patternLengthVLCTable[i][j];                      // This is the sentinel at the end of this group. Try the next index in the table.                      if (thisEntry.code == nullDecoderVLC.code)                         break;                                            if (reader->MatchBits(i + 3 - 1, thisEntry.code))                      {                         level = thisEntry.level;                                                  // A sign bit succeeeds the code.                         if (reader->GetBits(1) == 0x0001)                            level = -level;                         reader->AdvanceReader(1);                                                  zeroRunLength = thisEntry.zeroRunLength;                                                  // Now that the string has been matched, jump to after escape codes.                         goto interBitsMatched;                      }                   }                }                                // Try matching with the escape code.                if (reader->MatchBits(AC_ESCAPE_LENGTH, AC_ESCAPE))                {                   // There are now six bits of zero count and twelve bits of level value.                   zeroRunLength = reader->GetBits(6);                   reader->AdvanceReader(6);                   level = reader->GetBits(11);                   reader->AdvanceReader(11);                   if ((reader->GetBits(1) & 0x0001) == 0x0001)                      level = -level;                   reader->AdvanceReader(1);                }                else                {                   Logger::LogMessage("Warning: Unrecognized bit sequence in AC coefficients block.");                }                                // We jump to here if there was a match in the VLC table.                // `level' and `zeroRunLength' have been set to their appropriate values here. interBitsMatched:                // Add in the appropriate number of zeros, and then put the coefficient level in.                for (int i = 0; i < zeroRunLength; i++)                   blockEncodedValues[acOffset + i] = 0x0000;                                acOffset += zeroRunLength;                blockEncodedValues[acOffset] = level;                                Logger::DebugLog("{%d, %d} ", zeroRunLength, level);                                acOffset++;             }                          // Fill in the rest with zeros.             while (acOffset < 64)             {                blockEncodedValues[acOffset] = 0x0000;                acOffset++;             }                          Logger::DebugLog("EOB\n");                          // The coefficients need to be reordered, as they are currently in zig-zag scan form.             this->zigZagScan(blockEncodedValues, frameTransformedValues + mbIndex * 64 * 6 + block * 64);          }       }              delete [] blockEncodedValues;              // TODO: Should have macroblock layout in the stream header.       // Populate the picture information with data about this frame and the pixel values.       picture->SetBlockRows(blockRows);       picture->SetBlockColumns(blockColumns);       picture->SetColourFormat(COLOUR_FORMAT_YUV);       picture->SetPixelLayout(PIXEL_LAYOUT_MB_FOUR_TWO_ZERO);       picture->SetPixelsX(blockColumns << 4);       picture->SetPixelsY(blockRows << 4);       picture->SetSignedShortData(frameTransformedValues, blockRows * blockColumns * 6 * 64);       picture->SetMVDisplacementsX(xComponents, blockRows * blockColumns);       picture->SetMVDisplacementsY(yComponents, blockRows * blockColumns);    }        inline UnsignedByte HuffmanEntropyDecoder::decodeMBType(BinaryStreamReader* reader, bool pPicture)    {       if (!pPicture)       {          if (reader->MatchBits(1, 0x1))             return 0x1;          else if (reader->MatchBits(2, 0x1))             return 0x11;       }       else       {          if (reader->MatchBits(1, 0x1))             return 0xA;          else if (reader->MatchBits(2, 0x1))             return 0x2;          else if (reader->MatchBits(3, 0x1))             return 0x8;          else if (reader->MatchBits(5, 0x3))             return 0x1;          else if (reader->MatchBits(5, 0x2))             return 0x1A;          else if (reader->MatchBits(5, 0x1))             return 0x12;          else if (reader->MatchBits(6, 0x1))             return 0x11;       }       Logger::LogMessage("Warning: Invalid macroblock type decoded.");       return 0x00;    }        inline SignedByte HuffmanEntropyDecoder::decodeMVComponent(BinaryStreamReader* reader)    {       unsigned char deltaLength;       SignedByte delta;       if (reader->MatchBits(2, 0x00))       {          delta = 0;          deltaLength = 0;       }       else if (reader->MatchBits(3, 0x2))       {          delta = 1;          deltaLength = 1;       }       else if (reader->MatchBits(3, 0x3))       {          delta = 2;          deltaLength = 2;       }       else if (reader->MatchBits(3, 0x4))       {          delta = 4;          deltaLength = 3;       }       else if (reader->MatchBits(3, 0x5))       {          delta = 8;          deltaLength = 4;       }       else if (reader->MatchBits(3, 0x6))       {          delta = 16;          deltaLength = 5;       }       else if (reader->MatchBits(4, 0xE))       {          delta = 32;          deltaLength = 6;       }       else if (reader->MatchBits(5, 0x1E))       {          delta = 64;          deltaLength = 7;       }       else       {          Logger::LogMessage("Warning: Invalid motion vector code encountered.");          return 0;       }              // Work out the sign.       if (delta != 0)       {          bool negative = ((reader->GetBits(1) & 0x1) == 0);          reader->AdvanceReader(1);          // Now get the actual delta.          unsigned short int readValue = reader->GetBits(deltaLength - 1);          reader->AdvanceReader(deltaLength - 1);          if (negative)             readValue = (~readValue) & ((1 << (deltaLength - 1)) - 1);                    // Add on the extra delta.          delta += readValue;                    // Incorporate the sign.          if (negative)             delta = -delta;                    return delta;       }       else          // The zero vector.          return 0;    }        void HuffmanEntropyDecoder::DecodeFrame(BinaryStreamReader* reader, StreamHeader* streamHeader, FrameHeader* frameHeader, PictureInfo* picture)    {       // Set frame attributes.       picture->SetFrameType(frameHeader->pFrame ? FRAME_TYPE_INTER : FRAME_TYPE_INTRA);              if (picture->GetFrameType() == FRAME_TYPE_INTER)       {          this->decodeInterFrame(reader, streamHeader, frameHeader, picture);          return;       }              // Get the dimensions of this frame.       BlockCount blockRows = streamHeader->blockRows;       BlockCount blockColumns = streamHeader->blockColumns;              // An array to store the values once they are decoded.       SignedShort* blockEncodedValues = new SignedShort[8 * 8];              // An array to store the final values, out of which the picture information data will be constructed.       SignedShort* frameTransformedValues = new SignedShort[8 * 8 * 6 * blockRows * blockColumns];              // Initial values for the DC coefficients in each type of 8 * 8 block within the macroblock.       short int yDC = 0;       short int cbDC = 0;       short int crDC = 0;              // Reusable variable for the current absolute DC value.       short int dc;       unsigned char category;              for (int index = 0, y = 0; y < blockRows; y++)       {          for (int x = 0; x < blockColumns; x++)          {             // Decode the macroblock type.             UnsignedByte type = this->decodeMBType(reader, false);                          if (IS_QUANT(type))                Logger::LogMessage("Warning: Unsupported macroblock type.");             else if (IS_MOTION_FORWARD(type) || IS_MOTION_BACKWARD(type) || IS_PATTERN(type) || !IS_INTRA(type))                Logger::LogMessage("Warning: Illegal macroblock type.");                          for (int block = 0; block < 6; block++, index += 64)             {                // The next code is a DC coefficient delta.                // Decoding proceeds by matching the first bits with the category VLC, which yields a category number and range for the DC delta,                // and then selecting `category number' additional bits to give a sign and precise magnitude to this DC delta value.                if (block < 4)                {                   for (category = 0; category < 9; category++)                      if (reader->MatchBits(dcVLCsY[category].length, dcVLCsY[category].code))                         break;                }                else if (block == 4)                {                   for (category = 0; category < 9; category++)                      if (reader->MatchBits(dcVLCsC[category].length, dcVLCsC[category].code))                         break;                }                else                {                   for (category = 0; category < 9; category++)                      if (reader->MatchBits(dcVLCsC[category].length, dcVLCsC[category].code))                         break;                }                                // Read in `category' additional bits to specify the DC delta exactly.                // If the category is non-zero there is one bit for the sign first.                unsigned short int additional = reader->GetBits(category);                                // Move the reader on past the additional bits.                reader->AdvanceReader(category);                                bool negative = false;                if (category != 0 && (additional & (1 << (category - 1))) == 0x0000)                {                   negative = true;                   additional = ~additional & ((1 << category) - 1);                }                                additional |= (1 << (category - 1));                                if (negative)                   additional = -additional;                                // The DC coefficient delta is now stored in additional.                if (block < 4)                {                   dc = yDC + additional;                   yDC = dc;                }                else if (block == 4)                {                   dc = cbDC + additional;                   cbDC = dc;                }                else                {                   dc = crDC + additional;                   crDC = dc;                }                                // Write the actual DC coefficient `dc' to this block.                blockEncodedValues[0] = dc;                                Logger::DebugLog("(%d, %d, %d) DC %d AC ", x, y, block, dc);                                // The offset past `index' into the coefficient block.                unsigned char acOffset = 1;                // The next set of codes are AC coefficients.                // To decode these, we match bits into the AC VLC table to work out a zero run-length and level,                // then use the next bit to set the sign of the AC value. If it is not present in the table, try to match                // with the escape code and decode six bits of zero run-length and twelve bits of level                // Keep looping until we reach the EOB (end of block) marker, which indicates that the remainder of the coefficients are zero.                while (!reader->MatchBits(AC_END_OF_BLOCK_LENGTH, AC_END_OF_BLOCK))                {                   short int level = -1;                   unsigned char zeroRunLength = 0;                   for (int i = 0; i < 15; i++)                   {                      DecoderVLCEntry thisEntry;                      for (int j = 0; j < 16; j++)                      {                         thisEntry = patternLengthVLCTable[i][j];                         // This is the sentinel at the end of this group. Try the next index in the table.                         if (thisEntry.code == nullDecoderVLC.code)                            break;                         if (reader->MatchBits(i + 3 - 1, thisEntry.code))                         {                            level = thisEntry.level;                                                        // A sign bit succeeeds the code.                            if (reader->GetBits(1) == 0x0001)                               level = -level;                            reader->AdvanceReader(1);                                                        zeroRunLength = thisEntry.zeroRunLength;                                                        // Now that the string has been matched, jump to after escape codes.                            goto bitsMatched;                         }                      }                   }                                      // Try matching with the escape code.                   if (reader->MatchBits(AC_ESCAPE_LENGTH, AC_ESCAPE))                   {                      // There are now six bits of zero count and twelve bits of level value.                      zeroRunLength = reader->GetBits(6);                      reader->AdvanceReader(6);                      level = reader->GetBits(11);                      reader->AdvanceReader(11);                      if ((reader->GetBits(1) & 0x0001) == 0x0001)                         level = -level;                      reader->AdvanceReader(1);                   }                   else                   {                      Logger::LogMessage("Warning: Unrecognized bit sequence in AC coefficients block.");                   }                                      // We jump to here if there was a match in the VLC table.                   // `level' and `zeroRunLength' have been set to their appropriate values here. bitsMatched:                   // Add in the appropriate number of zeros, and then put the coefficient level in.                   for (int i = 0; i < zeroRunLength; i++)                      blockEncodedValues[acOffset + i] = 0x0000;                                      acOffset += zeroRunLength;                                      blockEncodedValues[acOffset] = level;                                      Logger::DebugLog("{%d, %d} ", zeroRunLength, level);                                      acOffset++;                }                                // Fill in the rest with zeros.                while (acOffset < 64)                {                   blockEncodedValues[acOffset] = 0x0000;                   acOffset++;                }                                Logger::DebugLog("EOB\n");                                // The coefficients need to be reordered, as they are currently in zig-zag scan form.                this->zigZagScan(blockEncodedValues, frameTransformedValues + index);             }          }       }              delete [] blockEncodedValues;              // TODO: Should have macroblock layout in the stream header.       // Populate the picture information with data about this frame and the pixel values.       picture->SetBlockRows(blockRows);       picture->SetBlockColumns(blockColumns);       picture->SetColourFormat(COLOUR_FORMAT_YUV);       picture->SetPixelLayout(PIXEL_LAYOUT_MB_FOUR_TWO_ZERO);       picture->SetPixelsX(blockColumns << 4);       picture->SetPixelsY(blockRows << 4);       picture->SetSignedShortData(frameTransformedValues, blockRows * blockColumns * 6 * 64);    }        void HuffmanEntropyDecoder::zigZagScan(short int* blockData, short int* output)    {       for (int i = 0; i < 64; i++)          output[reverseZigZagScanIndicesEightByEight[i]] = blockData[i];    } }