|
8 | 8 | #include "Utils.h" |
9 | 9 | #include "Settings.h" |
10 | 10 |
|
11 | | -static const uint8_t NAL_TYPE_SPS = 7; |
| 11 | +static const char NAL_HEADER[] = {0x00, 0x00, 0x00, 0x01}; |
| 12 | + |
| 13 | +static const uint8_t H264_NAL_TYPE_SPS = 7; |
12 | 14 | static const uint8_t H265_NAL_TYPE_VPS = 32; |
13 | 15 |
|
14 | | -ClientConnection::ClientConnection() { |
15 | | - m_Statistics = std::make_shared<Statistics>(); |
| 16 | +static const uint8_t H264_NAL_TYPE_AUD = 9; |
| 17 | +static const uint8_t H265_NAL_TYPE_AUD = 35; |
| 18 | + |
| 19 | +ClientConnection::ClientConnection() { |
| 20 | + m_Statistics = std::make_shared<Statistics>(); |
16 | 21 | } |
17 | 22 |
|
18 | | -int findVPSSPS(const uint8_t *frameBuffer, int frameByteSize) { |
19 | | - int zeroes = 0; |
20 | | - int foundNals = 0; |
21 | | - for (int i = 0; i < frameByteSize; i++) { |
22 | | - if (frameBuffer[i] == 0) { |
23 | | - zeroes++; |
24 | | - } else if (frameBuffer[i] == 1) { |
25 | | - if (zeroes >= 2) { |
26 | | - foundNals++; |
27 | | - if (Settings::Instance().m_codec == ALVR_CODEC_H264 && foundNals >= 3) { |
28 | | - // Find end of SPS+PPS on H.264. |
29 | | - return i - 3; |
30 | | - } else if (Settings::Instance().m_codec == ALVR_CODEC_H265 && foundNals >= 4) { |
31 | | - // Find end of VPS+SPS+PPS on H.264. |
32 | | - return i - 3; |
33 | | - } |
34 | | - } |
35 | | - zeroes = 0; |
36 | | - } else { |
37 | | - zeroes = 0; |
38 | | - } |
39 | | - } |
40 | | - return -1; |
| 23 | +/* |
| 24 | + Sends the (VPS + )SPS + PPS video configuration headers from H.264 or H.265 stream as a sequence of NALs. |
| 25 | + (VPS + )SPS + PPS have short size (8bytes + 28bytes in some environment), so we can |
| 26 | + assume SPS + PPS is contained in first fragment. |
| 27 | +*/ |
| 28 | +void sendHeaders(uint8_t **buf, int *len, int nalNum) { |
| 29 | + uint8_t *b = *buf; |
| 30 | + uint8_t *end = b + *len; |
| 31 | + |
| 32 | + int headersLen = 0; |
| 33 | + int foundHeaders = -1; // Offset by 1 header to find the length until the next header |
| 34 | + while (b != end) { |
| 35 | + if (b + sizeof(NAL_HEADER) <= end && memcmp(b, NAL_HEADER, sizeof(NAL_HEADER)) == 0) { |
| 36 | + foundHeaders++; |
| 37 | + if (foundHeaders == nalNum) { |
| 38 | + break; |
| 39 | + } |
| 40 | + b += sizeof(NAL_HEADER); |
| 41 | + headersLen += sizeof(NAL_HEADER); |
| 42 | + } |
| 43 | + |
| 44 | + b++; |
| 45 | + headersLen++; |
| 46 | + } |
| 47 | + if (foundHeaders != nalNum) { |
| 48 | + return; |
| 49 | + } |
| 50 | + InitializeDecoder((const unsigned char *)*buf, headersLen); |
| 51 | + |
| 52 | + // move the cursor forward excluding config NALs |
| 53 | + *buf = b; |
| 54 | + *len -= headersLen; |
| 55 | +} |
| 56 | + |
| 57 | +void processH264Nals(uint8_t **buf, int *len) { |
| 58 | + uint8_t *b = *buf; |
| 59 | + int l = *len; |
| 60 | + uint8_t nalType = b[4] & 0x1F; |
| 61 | + |
| 62 | + if (nalType == H264_NAL_TYPE_AUD && l > sizeof(NAL_HEADER) * 2 + 2) { |
| 63 | + b += sizeof(NAL_HEADER) + 2; |
| 64 | + l -= sizeof(NAL_HEADER) + 2; |
| 65 | + nalType = b[4] & 0x1F; |
| 66 | + } |
| 67 | + if (nalType == H264_NAL_TYPE_SPS) { |
| 68 | + sendHeaders(&b, &l, 2); // 2 headers SPS and PPS |
| 69 | + } |
| 70 | + *buf = b; |
| 71 | + *len = l; |
| 72 | +} |
| 73 | + |
| 74 | +void processH265Nals(uint8_t **buf, int *len) { |
| 75 | + uint8_t *b = *buf; |
| 76 | + int l = *len; |
| 77 | + uint8_t nalType = (b[4] >> 1) & 0x3F; |
| 78 | + |
| 79 | + if (nalType == H265_NAL_TYPE_AUD && l > sizeof(NAL_HEADER) * 2 + 3) { |
| 80 | + b += sizeof(NAL_HEADER) + 3; |
| 81 | + l -= sizeof(NAL_HEADER) + 3; |
| 82 | + nalType = (b[4] >> 1) & 0x3F; |
| 83 | + } |
| 84 | + if (nalType == H265_NAL_TYPE_VPS) { |
| 85 | + sendHeaders(&b, &l, 3); // 3 headers VPS, SPS and PPS |
| 86 | + } |
| 87 | + *buf = b; |
| 88 | + *len = l; |
41 | 89 | } |
42 | 90 |
|
43 | 91 | void ClientConnection::SendVideo(uint8_t *buf, int len, uint64_t targetTimestampNs) { |
44 | 92 | // Report before the frame is packetized |
45 | 93 | ReportEncoded(targetTimestampNs); |
46 | 94 |
|
47 | | - uint8_t NALType; |
48 | | - if (Settings::Instance().m_codec == ALVR_CODEC_H264) |
49 | | - NALType = buf[4] & 0x1F; |
50 | | - else |
51 | | - NALType = (buf[4] >> 1) & 0x3F; |
52 | | - |
53 | | - if ((Settings::Instance().m_codec == ALVR_CODEC_H264 && NALType == NAL_TYPE_SPS) || |
54 | | - (Settings::Instance().m_codec == ALVR_CODEC_H265 && NALType == H265_NAL_TYPE_VPS)) { |
55 | | - // This frame contains (VPS + )SPS + PPS + IDR on NVENC H.264 (H.265) stream. |
56 | | - // (VPS + )SPS + PPS has short size (8bytes + 28bytes in some environment), so we can |
57 | | - // assume SPS + PPS is contained in first fragment. |
58 | | - |
59 | | - int end = findVPSSPS(buf, len); |
60 | | - if (end == -1) { |
61 | | - // Invalid frame. |
62 | | - return; |
63 | | - } |
64 | | - |
65 | | - InitializeDecoder((const unsigned char *)buf, end); |
| 95 | + if (len < sizeof(NAL_HEADER)) { |
| 96 | + return; |
| 97 | + } |
66 | 98 |
|
67 | | - // move the cursor forward excluding config NALs |
68 | | - buf = &buf[end]; |
69 | | - len = len - end; |
| 99 | + int codec = Settings::Instance().m_codec; |
| 100 | + if (codec == ALVR_CODEC_H264) { |
| 101 | + processH264Nals(&buf, &len); |
| 102 | + } else if (codec == ALVR_CODEC_H265) { |
| 103 | + processH265Nals(&buf, &len); |
70 | 104 | } |
71 | 105 |
|
72 | 106 | VideoSend(targetTimestampNs, buf, len); |
|
0 commit comments