|
8 | 8 | #include "Utils.h"
|
9 | 9 | #include "Settings.h"
|
10 | 10 |
|
11 |
| -static const uint8_t NAL_TYPE_SPS = 7; |
| 11 | +static const uint8_t H264_NAL_TYPE_SPS = 7; |
12 | 12 | static const uint8_t H265_NAL_TYPE_VPS = 32;
|
13 | 13 |
|
14 |
| -ClientConnection::ClientConnection() { |
15 |
| - m_Statistics = std::make_shared<Statistics>(); |
| 14 | +static const uint8_t H264_NAL_TYPE_AUD = 9; |
| 15 | +static const uint8_t H265_NAL_TYPE_AUD = 35; |
| 16 | + |
| 17 | +ClientConnection::ClientConnection() { |
| 18 | + m_Statistics = std::make_shared<Statistics>(); |
16 | 19 | }
|
17 | 20 |
|
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; |
| 21 | +/* |
| 22 | + Extracts the (VPS + )SPS + PPS video configuration headers from H.264 or H.265 stream as a sequence of NALs. |
| 23 | + (VPS + )SPS + PPS have short size (8bytes + 28bytes in some environment), so we can |
| 24 | + assume SPS + PPS is contained in first fragment. |
| 25 | +*/ |
| 26 | +void extractHeaders(uint8_t **buf, int *len, int nalNum) { |
| 27 | + static const char NAL_HEADER[] = {0x00, 0x00, 0x00, 0x01}; |
| 28 | + |
| 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[0] == 0 && 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 (headersLen == 0 || foundHeaders != nalNum) { |
| 48 | + return; |
| 49 | + } |
| 50 | + InitializeDecoder((const unsigned char *)b, 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) { |
| 63 | + b += 6; |
| 64 | + l -= 6; |
| 65 | + nalType = b[4] & 0x1F; |
| 66 | + } |
| 67 | + if (nalType == H264_NAL_TYPE_SPS) { |
| 68 | + extractHeaders(buf, len, 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) { |
| 80 | + b += 7; |
| 81 | + l -= 7; |
| 82 | + nalType = (b[4] >> 1) & 0x3F; |
| 83 | + } |
| 84 | + if (nalType == H265_NAL_TYPE_VPS) { |
| 85 | + extractHeaders(buf, len, 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); |
66 |
| - |
67 |
| - // move the cursor forward excluding config NALs |
68 |
| - buf = &buf[end]; |
69 |
| - len = len - end; |
| 95 | + int codec = Settings::Instance().m_codec; |
| 96 | + if (codec == ALVR_CODEC_H264) { |
| 97 | + processH264Nals(&buf, &len); |
| 98 | + } else if (codec == ALVR_CODEC_H265) { |
| 99 | + processH265Nals(&buf, &len); |
70 | 100 | }
|
71 | 101 |
|
72 | 102 | VideoSend(targetTimestampNs, buf, len);
|
|
0 commit comments