【问题标题】:Add bit padding (bit shifting?) to 10bit values stored in a byte array将位填充(位移?)添加到存储在字节数组中的 10 位值
【发布时间】:2021-06-14 23:56:31
【问题描述】:

我正在寻找一种有效的方法来左移 (<<) 使用 C++/Win32 存储在字节数组中的 10 位值。

我通过 UDP 接收未压缩的 4:2:2 10 位视频流,由于位的打包,数据存储在无符号字符数组中。

始终发送数据,以便像素组在字节边界上完成(在这种情况下,以 10 位深度采样的 4 个像素使用 5 个字节):

我使用的渲染器 (Media Foundation Enhanced Video Renderer) 要求将 10 位值放入 16 bit WORD with 6 padding bits to the right,虽然这很烦人,但我认为这是为了帮助他们确保 1 字节内存对齐:

将每个 10 位值左移 6 次(并在需要时移动到新数组)的有效方法是什么?虽然我会收到不同长度的数据,但它们总是由这 40 位块组成。

我确信一个粗略的循环使用一些位掩码(?)就足够了,但这对我来说听起来很昂贵,而且我必须每秒处理 1500 个数据包,每个数据包有大约 1200 字节的有效负载。

为清晰起见进行编辑

示例输入:

unsigned char byteArray[5] = {0b01110101, 0b01111010, 0b00001010, 0b11111010, 0b00000110}

期望的输出:

WORD wordArray[4] = {0b0111010101000000, 0b1110100000000000, 0b1010111110000000, 0b1000000110000000}

(或字节数组中的相同结果数据)

【问题讨论】:

  • 从获取功能性和可读性的代码开始。稍后进行优化,但仅在需要时进行。速度分析您的代码以查看它的瓶颈程度。但是,这需要一些代码。因此,首先要获取功能性和可读性的代码。稍后优化...
  • 我不认为我理解这个问题。如果用外行术语表示,您是否需要从字符流中提取 10 位,打包成 2 个字节(在低位上填充)并发送过来,还是还有更多?
  • @GabrielStaples 4:2:2 是对像素进行二次采样的方法,但每个样本的深度为 10 位。
  • @GabrielStaples 是的,可以一次处理 4 个像素(40 位)的数据,因为每个数据包的整个有效负载都是由这些块组成的(在视频世界中也称为 pGroups )。
  • 如果有人能给我一个起点(也许如何使用掩码等来获取字节数组的前 10 位),我会很高兴!

标签: c++ winapi bit bit-shift ms-media-foundation


【解决方案1】:

这样就可以了:

void ProcessPGroup(const uint8_t byteArrayIn[5], uint16_t twoByteArrayOut[4])
{
    twoByteArrayOut[0] = (((uint16_t)byteArrayIn[0] & 0b11111111u) << (0 + 8)) | (((uint16_t)byteArrayIn[1] & 0b11000000u) << 0);
    twoByteArrayOut[1] = (((uint16_t)byteArrayIn[1] & 0b00111111u) << (2 + 8)) | (((uint16_t)byteArrayIn[2] & 0b11110000u) << 2);
    twoByteArrayOut[2] = (((uint16_t)byteArrayIn[2] & 0b00001111u) << (4 + 8)) | (((uint16_t)byteArrayIn[3] & 0b11111100u) << 4);
    twoByteArrayOut[3] = (((uint16_t)byteArrayIn[3] & 0b00000011u) << (6 + 8)) | (((uint16_t)byteArrayIn[4] & 0b11111111u) << 6);
}

不要被上面函数签名中的[5][4] 值混淆。他们什么都不做,只是告诉你,用户,这是每个数组中强制性的、预期的元素数量。在这里查看我的答案:Passing an array as an argument to a function in C。传递更短的数组会导致未定义的行为,这是一个错误!

完整的测试代码(在我的eRCaGuy_hello_world repo 中下载:cpp/process_10_bit_video_data.cpp):

test.cpp

/*

GS
17 Mar. 2021

To compile and run:
    mkdir -p bin && g++ -Wall -Wextra -Werror -ggdb -std=c++17 -o bin/test \
    test.cpp && bin/test

*/

#include <bitset>
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <iostream>

// Get the number of elements in any C array
// - Usage example: [my own answer]:
//   https://arduino.stackexchange.com/questions/80236/initializing-array-of-structs/80289#80289
#define ARRAY_LEN(array) (sizeof(array)/sizeof(array[0]))

/// \brief      Process a packed video P group, which is 4 pixels of 10 bits each (exactly 5 uint8_t
///             bytes) into a uint16_t 4-element array (1 element per pixel).
/// \details    Each group of 10-bits for a pixel will be placed into a 16-bit word, with all 10
///             bits left-shifted to the far left edge, leaving 6 empty (zero) bits in the right
///             side of the word.
/// \param[in]  byteArrayIn  5 bytes of 10-bit pixel data for exactly 4 pixels; any array size < 5
///                        will result in undefined behavior! So, ensure you pass the proper array
///                        size in!
/// \param[out] twoByteArrayOut  The output array into which the 4 pixels will be packed, 10 bits per
///                        16-bit word, all 10 bits shifted to the left edge; any array size < 4
///                        will result in undefined behavior!
/// \return     None
void ProcessPGroup(const uint8_t byteArrayIn[5], uint16_t twoByteArrayOut[4])
{
    twoByteArrayOut[0] = (((uint16_t)byteArrayIn[0] & 0b11111111u) << (0 + 8)) | (((uint16_t)byteArrayIn[1] & 0b11000000u) << 0);
    twoByteArrayOut[1] = (((uint16_t)byteArrayIn[1] & 0b00111111u) << (2 + 8)) | (((uint16_t)byteArrayIn[2] & 0b11110000u) << 2);
    twoByteArrayOut[2] = (((uint16_t)byteArrayIn[2] & 0b00001111u) << (4 + 8)) | (((uint16_t)byteArrayIn[3] & 0b11111100u) << 4);
    twoByteArrayOut[3] = (((uint16_t)byteArrayIn[3] & 0b00000011u) << (6 + 8)) | (((uint16_t)byteArrayIn[4] & 0b11111111u) << 6);
}

// Reference: https://stackoverflow.com/questions/7349689/how-to-print-using-cout-a-number-in-binary-form/7349767
void PrintArrayAsBinary(const uint16_t* twoByteArray, size_t len)
{
    std::cout << "{\n";
    for (size_t i = 0; i < len; i++)
    {
        std::cout << std::bitset<16>(twoByteArray[i]);
        if (i < len - 1)
        {
            std::cout << ",";
        }
        std::cout << std::endl;
    }
    std::cout << "}\n";
}

int main()
{
    printf("Processing 10-bit video data example\n");

    constexpr uint8_t TEST_BYTE_ARRAY_INPUT[5] = {0b01110101, 0b01111010, 0b00001010, 0b11111010, 0b00000110};
    constexpr uint16_t TEST_TWO_BYTE_ARRAY_OUTPUT[4] = {
        0b0111010101000000, 0b1110100000000000, 0b1010111110000000, 0b1000000110000000};

    uint16_t twoByteArrayOut[4];
    ProcessPGroup(TEST_BYTE_ARRAY_INPUT, twoByteArrayOut);

    if (std::memcmp(twoByteArrayOut, TEST_TWO_BYTE_ARRAY_OUTPUT, sizeof(TEST_TWO_BYTE_ARRAY_OUTPUT)) == 0)
    {
        printf("TEST PASSED!\n");
    }
    else
    {
        printf("TEST ==FAILED!==\n");

        std::cout << "expected = \n";
        PrintArrayAsBinary(TEST_TWO_BYTE_ARRAY_OUTPUT, ARRAY_LEN(TEST_TWO_BYTE_ARRAY_OUTPUT));

        std::cout << "actual = \n";
        PrintArrayAsBinary(twoByteArrayOut, ARRAY_LEN(twoByteArrayOut));
    }

    return 0;
}

示例运行和输出:

$ mkdir -p bin && g++ -Wall -Wextra -Werror -ggdb -std=c++17 \
-o bin/test test.cpp && bin/test
Processing 10-bit video data example
TEST PASSED!

我现在也将此代码放入我的eRCaGuy_hello_world 存储库:cpp/process_10_bit_video_data.cpp

参考资料:

  1. How to print (using cout) a number in binary form?
  2. [我的回答]Passing an array as an argument to a function in C
  3. [我的eRCaGuy_hello_world repo]ARRAY_LEN() 宏:见utilities.h
  4. https://en.cppreference.com/w/cpp/string/byte/memcmp

关键字:c 和 c++ 位掩码和位移位、位打包; bit-masking 位掩码、bitshifting 位移位、bitpacking 位打包、字节打包、无损数据压缩

【讨论】:

    猜你喜欢
    • 2010-09-29
    • 2010-11-19
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多