将位填充(位移位?)添加到存储在字节数组中的 10 位值
Add bit padding (bit shifting?) to 10bit values stored in a byte array
我正在寻找一种使用 C++/Win32 将存储在字节数组中的 10 位值左移 (<<
) 的有效方法。
我正在通过 UDP 接收未压缩的 4:2:2 10 位视频流,由于位的打包,数据存储在无符号字符数组中。
始终发送数据,以便像素组在字节边界上结束(在这种情况下,以 10 的位深度采样的 4 个像素使用 5 个字节):
我正在使用的渲染器(Media Foundation Enhanced Video Renderer) requires that 10 bit values are placed into a 16 bit WORD with 6 padding bits to the right,虽然这很烦人,但我认为这是为了帮助他们确保 1 字节内存对齐:
将每个 10 位值左移 6 次(并在需要时移动到新数组)的有效方法是什么?虽然我会收到不同长度的数据,但它们总是由这 40 位块组成。
我确定一个粗略的循环就足够了一些位掩码(?)但是这对我来说听起来很昂贵而且我必须处理 1500 packets/second,每个都有大约 1200 字节的有效负载。
编辑清楚
示例输入:
unsigned char byteArray[5] = {0b01110101, 0b01111010, 0b00001010, 0b11111010, 0b00000110}
期望的输出:
WORD wordArray[4] = {0b0111010101000000, 0b1110100000000000, 0b1010111110000000, 0b1000000110000000}
(或字节数组中的相同结果数据)
这样就可以了:
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 here: 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:
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 repo here: cpp/process_10_bit_video_data.cpp。
参考文献:
- How to print (using cout) a number in binary form?
- [我的回答]Passing an array as an argument to a function in C
- [我的eRCaGuy_hello_world repo]
ARRAY_LEN()
macro: see utilities.h
- https://en.cppreference.com/w/cpp/string/byte/memcmp
关键字:c 和 c++ 位掩码和位移位、位打包; bit-masking 位掩码,bitshifting bit shifting,bitpacking 位打包,字节打包,无损数据压缩
我正在寻找一种使用 C++/Win32 将存储在字节数组中的 10 位值左移 (<<
) 的有效方法。
我正在通过 UDP 接收未压缩的 4:2:2 10 位视频流,由于位的打包,数据存储在无符号字符数组中。
始终发送数据,以便像素组在字节边界上结束(在这种情况下,以 10 的位深度采样的 4 个像素使用 5 个字节):
我正在使用的渲染器(Media Foundation Enhanced Video Renderer) requires that 10 bit values are placed into a 16 bit WORD with 6 padding bits to the right,虽然这很烦人,但我认为这是为了帮助他们确保 1 字节内存对齐:
将每个 10 位值左移 6 次(并在需要时移动到新数组)的有效方法是什么?虽然我会收到不同长度的数据,但它们总是由这 40 位块组成。
我确定一个粗略的循环就足够了一些位掩码(?)但是这对我来说听起来很昂贵而且我必须处理 1500 packets/second,每个都有大约 1200 字节的有效负载。
编辑清楚
示例输入:
unsigned char byteArray[5] = {0b01110101, 0b01111010, 0b00001010, 0b11111010, 0b00000110}
期望的输出:
WORD wordArray[4] = {0b0111010101000000, 0b1110100000000000, 0b1010111110000000, 0b1000000110000000}
(或字节数组中的相同结果数据)
这样就可以了:
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 here: 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:
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 repo here: cpp/process_10_bit_video_data.cpp。
参考文献:
- How to print (using cout) a number in binary form?
- [我的回答]Passing an array as an argument to a function in C
- [我的eRCaGuy_hello_world repo]
ARRAY_LEN()
macro: see utilities.h - https://en.cppreference.com/w/cpp/string/byte/memcmp
关键字:c 和 c++ 位掩码和位移位、位打包; bit-masking 位掩码,bitshifting bit shifting,bitpacking 位打包,字节打包,无损数据压缩