I need to write a simple encoder that will add an SEI unit after each encoded frame.
I looked at the BSAnalyzer and it looks like what I need
SEI comes after each frame. But when I play a file in ffmpeg I get errors:
[NULL @ 000001cd901df1c0] SEI type 5 size 179843 truncated at 179776
[h264 @ 000001cd96eae780] SEI type 5 size 179843 truncated at 179774
[NULL @ 000001cd901df1c0] SEI type 5 size 179843 truncated at 179776
[h264 @ 000001cd970789c0] SEI type 5 size 179843 truncated at 179774
...and more
My code of write SEI:
static const uint8_t START_MARKER[4] = {0x0, 0x0, 0x0, 0x1};
static const uint8_t UUID[16] = {
0x81, 0x39, 0xe9, 0xbd, 0xa6, 0x09, 0x48, 0xb7,
0x76, 0x2c, 0xc8, 0x20, 0xd9, 0x68, 0xcc, 0xcf};
static const uint8_t SEI_TYPE[1] = {0x06};
static const uint8_t PAYLOAD_TYPE[1] = {0x05};
int calc_sei_size(size_t user_data_size)
{
int new_buffer_size = 0;
new_buffer_size += sizeof(START_MARKER); // NAL marker
new_buffer_size += sizeof(SEI_TYPE); //+NAL type = 0x06
new_buffer_size += sizeof(PAYLOAD_TYPE); //+PayloadType = 0x05
int payload_size = user_data_size + sizeof(UUID); //user_data + uuid
while (payload_size > 0xFF)
{
payload_size -= 0xFF;
new_buffer_size++; // PayloadSize how FF while is more 255
}
new_buffer_size += 0xFF - payload_size; //+PayloadSize diff
new_buffer_size += sizeof(UUID); //+Uuid
new_buffer_size += user_data_size; //+userData
return new_buffer_size;
}
void sei_write(uint8_t *user_data, size_t user_data_size)
{
int newSize = calc_sei_size(user_data_size);
uint8_t *buffer = (uint8_t *)calloc(sizeof(uint8_t), newSize);
int offset = 0;
memcpy(&buffer[offset], START_MARKER, sizeof(START_MARKER)); // copy start marker
offset += sizeof(START_MARKER);
memcpy(&buffer[offset], SEI_TYPE, sizeof(SEI_TYPE)); // copy SEI_TYPE
offset += sizeof(SEI_TYPE);
memcpy(&buffer[offset], PAYLOAD_TYPE, sizeof(PAYLOAD_TYPE)); // copy SEI_TYPE
offset += sizeof(PAYLOAD_TYPE);
int payload_size = user_data_size + sizeof(UUID);
while (payload_size > 0xFF)
{
payload_size -= 0xFF;
buffer[offset++] = 0xFF;
}
buffer[offset++] = 0xFF - payload_size;
memcpy(&buffer[offset], UUID, sizeof(UUID)); // copy UUID
offset += sizeof(UUID);
memcpy(&buffer[offset], user_data, user_data_size); // copy user_data
DoSomeWithBuffer(buffer, newSize)
}
Okay, I think I figured out what the problem was. There are several features in packaging user_date in the SEI format:
00 00 00
00 00 01
00 00 02
00 00 03
this can be perceived as NAL markers, so it is necessary to insert 03 after the 2nd byte 00, i.e. convert them to this form:
00 00 03 00
00 00 03 01
00 00 03 02
00 00 03 03
this transformation is called rbsp https://docs.rs/h264-reader/latest/h264_reader/rbsp/
Since in the first paragraph we changed the user_data and size, you should in any case write down the payloadsize of the original size, not the size after the RBSP conversion
The last byte should not be 00; it is recommended to use some endbyte as an end of data marker. I insert 0x80 at the end of the data
PayloadSize actually means LengthSizeMinusOne, and since I always insert an endbyte (0x80) at the end, I do not take into account the length of this byte https://membrane.stream/learn/h264/3
If the last byte is PayloadSize = FF then we must add 00, otherwise decoders will perceive the next byte as still payodsize, and there will be a user date
As a result, I wrote the following code:
static const uint8_t START_MARKER[4] = {0x0, 0x0, 0x0, 0x1};
static const uint8_t UUID[16] = {
0x81, 0x39, 0xe9, 0xbd, 0xa6, 0x09, 0x48, 0xb7,
0x76, 0x2c, 0xc8, 0x20, 0xd9, 0x68, 0xcc, 0xcf};
static const uint8_t SEI_TYPE[1] = {0x06};
static const uint8_t PAYLOAD_TYPE[1] = {0x05};
int calc_sei_size(size_t user_data_size) {
int new_buffer_size = 0;
new_buffer_size += sizeof(START_MARKER); // NAL marker
new_buffer_size += sizeof(SEI_TYPE); //+NAL type = 0x06
new_buffer_size += sizeof(PAYLOAD_TYPE); //+PayloadType = 0x05
int payload_size = user_data_size + sizeof(UUID); // user_data + uuid
while (payload_size > 0xFF) {
payload_size -= 0xFF;
new_buffer_size++; // PayloadSize how FF while is more 255
}
if (payload_size == 0xFF)
new_buffer_size+= 1;
new_buffer_size += 0xFF - payload_size; //+PayloadSize diff
new_buffer_size += sizeof(UUID); //+Uuid
new_buffer_size += user_data_size; //+userData
return new_buffer_size;
}
void sei_write(uint8_t *user_data, size_t user_data_size) {
// allocate a larger buffer because the data may be enlarged after rbsp conversion
int new_size = calc_sei_size(user_data_size) * 2;
uint8_t *buffer = (uint8_t *)calloc(sizeof(uint8_t), new_size);
int offset = 0;
memcpy(&buffer[offset], START_MARKER, sizeof(START_MARKER)); // copy start marker
offset += sizeof(START_MARKER);
memcpy(&buffer[offset], SEI_TYPE, sizeof(SEI_TYPE)); // copy SEI_TYPE
offset += sizeof(SEI_TYPE);
memcpy(&buffer[offset], PAYLOAD_TYPE, sizeof(PAYLOAD_TYPE)); // copy SEI_TYPE
offset += sizeof(PAYLOAD_TYPE);
// write payload size
int payload_size = user_data_size + sizeof(UUID);
while (payload_size > 0xFF) {
payload_size -= 0xFF;
buffer[offset++] = 0xFF;
}
buffer[offset++] = payload_size; // Correctly write the remaining size
if (payload_size == 0xFF) // if last byte is FF add 00
buffer[offset++] = 0x00;
buffer[offset++] = 0xFF - payload_size;
memcpy(&buffer[offset], UUID, sizeof(UUID)); // copy UUID
offset += sizeof(UUID);
// convert to rbsp
uint8_t *new_buffer = NULL;
int new_buffer_size = 0;
rbsp_encode(user_data, user_data_size, new_buffer, &new_buffer_size);
memcpy(&buffer[offset], new_buffer, new_buffer_size); // copy user_data
offset += new_buffer_size;
buffer[offset++] = 0x80; // add EndByte
write_buffer(buffer, offset);
free(new_buffer);
free(buffer);
}
void rbsp_encode(uint8_t *data, int data_size, uint8_t *&new_buffer, int *new_size) {
// 0x00 00 00 => 0x00 00 03 00
// 0x00 00 01 => 0x00 00 03 01
// 0x00 00 02 => 0x00 00 03 02
// 0x00 00 03 => 0x00 00 03 03
new_buffer = (uint8_t *)calloc(sizeof(uint8_t), data_size * 2);
int offset = 0;
*new_size = 0;
int value = *new_size;
while (offset < data_size) {
if (offset + 2 < data_size &&
data[offset] == 0 && data[offset + 1] == 0 &&
(data[offset + 2] == 0 || data[offset + 2] == 1 || data[offset + 2] == 2 || data[offset + 2] == 3)) {
new_buffer[ *new_size += 1] = data[offset++];
new_buffer[ *new_size += 1] = data[offset++];
new_buffer[ *new_size += 1] = 0x03;
} else {
new_buffer[ *new_size += 1] = data[offset++];
}
}
}
//just for sample
void write_buffer(uint8_t *buffer_data, size_t buffer_size) {
FILE *ptrFile = fopen("C:\\Temp\\data_conv", "wb");
fwrite(buffer_data, 1, buffer_size, ptrFile); // записать в файл содержимое буфера
fclose(ptrFile);
}