Skip to content

Commit 5922939

Browse files
Isaac Connorclaude
andcommitted
feat: add sprop-parameter-sets support to H264/H265 SDP
Add SetSPS/SetPPS methods to H264Source and SetVPS/SetSPS/SetPPS to H265Source. When these parameters are set, GetAttribute() includes them as base64-encoded sprop-parameter-sets in the SDP, allowing clients to properly initialize decoders with correct video dimensions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ec9035e commit 5922939

4 files changed

Lines changed: 140 additions & 11 deletions

File tree

src/xop/H264Source.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,28 @@ H264Source::~H264Source()
3535

3636
}
3737

38+
std::string H264Source::Base64Encode(const uint8_t* data, size_t size)
39+
{
40+
static const char base64_chars[] =
41+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42+
43+
std::string result;
44+
result.reserve(((size + 2) / 3) * 4);
45+
46+
for (size_t i = 0; i < size; i += 3) {
47+
uint32_t n = (data[i] << 16);
48+
if (i + 1 < size) n |= (data[i + 1] << 8);
49+
if (i + 2 < size) n |= data[i + 2];
50+
51+
result += base64_chars[(n >> 18) & 0x3F];
52+
result += base64_chars[(n >> 12) & 0x3F];
53+
result += (i + 1 < size) ? base64_chars[(n >> 6) & 0x3F] : '=';
54+
result += (i + 2 < size) ? base64_chars[n & 0x3F] : '=';
55+
}
56+
57+
return result;
58+
}
59+
3860
string H264Source::GetMediaDescription(uint16_t port)
3961
{
4062
char buf[100] = {0};
@@ -44,7 +66,31 @@ string H264Source::GetMediaDescription(uint16_t port)
4466

4567
string H264Source::GetAttribute()
4668
{
47-
return string("a=rtpmap:96 H264/90000");
69+
string attr = "a=rtpmap:96 H264/90000";
70+
71+
// Add fmtp line with profile-level-id and sprop-parameter-sets if SPS/PPS available
72+
if (!sps_.empty() && !pps_.empty()) {
73+
// profile-level-id is bytes 1,2,3 of SPS (after NAL header)
74+
uint32_t profile_level_id = 0;
75+
if (sps_.size() >= 4) {
76+
profile_level_id = (sps_[1] << 16) | (sps_[2] << 8) | sps_[3];
77+
}
78+
79+
std::string sps_b64 = Base64Encode(sps_.data(), sps_.size());
80+
std::string pps_b64 = Base64Encode(pps_.data(), pps_.size());
81+
82+
char buf[512];
83+
sprintf(buf, "\r\na=fmtp:96 packetization-mode=1;profile-level-id=%06X;sprop-parameter-sets=%s,%s",
84+
profile_level_id, sps_b64.c_str(), pps_b64.c_str());
85+
attr += buf;
86+
}
87+
88+
if (width_ > 0 && height_ > 0) {
89+
char buf[64];
90+
sprintf(buf, "\r\na=x-dimensions:%u,%u", width_, height_);
91+
attr += buf;
92+
}
93+
return attr;
4894
}
4995

5096
bool H264Source::HandleFrame(MediaChannelId channel_id, AVFrame frame)

src/xop/H264Source.h

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "MediaSource.h"
88
#include "rtp.h"
9+
#include <vector>
910

1011
namespace xop
1112
{
@@ -19,21 +20,39 @@ class H264Source : public MediaSource
1920
void SetFramerate(uint32_t framerate)
2021
{ framerate_ = framerate; }
2122

22-
uint32_t GetFramerate() const
23+
uint32_t GetFramerate() const
2324
{ return framerate_; }
2425

25-
virtual std::string GetMediaDescription(uint16_t port);
26+
void SetResolution(uint32_t width, uint32_t height)
27+
{ width_ = width; height_ = height; }
2628

27-
virtual std::string GetAttribute();
29+
uint32_t GetWidth() const { return width_; }
30+
uint32_t GetHeight() const { return height_; }
31+
32+
// Set SPS/PPS for sprop-parameter-sets in SDP
33+
void SetSPS(const uint8_t* data, size_t size)
34+
{ sps_.assign(data, data + size); }
35+
36+
void SetPPS(const uint8_t* data, size_t size)
37+
{ pps_.assign(data, data + size); }
38+
39+
virtual std::string GetMediaDescription(uint16_t port);
40+
41+
virtual std::string GetAttribute();
2842

2943
virtual bool HandleFrame(MediaChannelId channel_id, AVFrame frame);
3044

3145
static int64_t GetTimestamp();
32-
46+
3347
private:
3448
H264Source(uint32_t framerate);
49+
static std::string Base64Encode(const uint8_t* data, size_t size);
3550

3651
uint32_t framerate_ = 25;
52+
uint32_t width_ = 0;
53+
uint32_t height_ = 0;
54+
std::vector<uint8_t> sps_;
55+
std::vector<uint8_t> pps_;
3756
};
3857

3958
}

src/xop/H265Source.cpp

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,29 @@ H265Source* H265Source::CreateNew(uint32_t framerate)
3232

3333
H265Source::~H265Source()
3434
{
35-
35+
36+
}
37+
38+
std::string H265Source::Base64Encode(const uint8_t* data, size_t size)
39+
{
40+
static const char base64_chars[] =
41+
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42+
43+
std::string result;
44+
result.reserve(((size + 2) / 3) * 4);
45+
46+
for (size_t i = 0; i < size; i += 3) {
47+
uint32_t n = (data[i] << 16);
48+
if (i + 1 < size) n |= (data[i + 1] << 8);
49+
if (i + 2 < size) n |= data[i + 2];
50+
51+
result += base64_chars[(n >> 18) & 0x3F];
52+
result += base64_chars[(n >> 12) & 0x3F];
53+
result += (i + 1 < size) ? base64_chars[(n >> 6) & 0x3F] : '=';
54+
result += (i + 2 < size) ? base64_chars[n & 0x3F] : '=';
55+
}
56+
57+
return result;
3658
}
3759

3860
string H265Source::GetMediaDescription(uint16_t port)
@@ -44,7 +66,26 @@ string H265Source::GetMediaDescription(uint16_t port)
4466

4567
string H265Source::GetAttribute()
4668
{
47-
return string("a=rtpmap:96 H265/90000");
69+
string attr = "a=rtpmap:96 H265/90000";
70+
71+
// Add fmtp line with sprop-vps, sprop-sps, sprop-pps if available
72+
if (!vps_.empty() && !sps_.empty() && !pps_.empty()) {
73+
std::string vps_b64 = Base64Encode(vps_.data(), vps_.size());
74+
std::string sps_b64 = Base64Encode(sps_.data(), sps_.size());
75+
std::string pps_b64 = Base64Encode(pps_.data(), pps_.size());
76+
77+
char buf[1024];
78+
sprintf(buf, "\r\na=fmtp:96 sprop-vps=%s;sprop-sps=%s;sprop-pps=%s",
79+
vps_b64.c_str(), sps_b64.c_str(), pps_b64.c_str());
80+
attr += buf;
81+
}
82+
83+
if (width_ > 0 && height_ > 0) {
84+
char buf[64];
85+
sprintf(buf, "\r\na=x-dimensions:%u,%u", width_, height_);
86+
attr += buf;
87+
}
88+
return attr;
4889
}
4990

5091
bool H265Source::HandleFrame(MediaChannelId channelId, AVFrame frame)

src/xop/H265Source.h

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "MediaSource.h"
88
#include "rtp.h"
9+
#include <vector>
910

1011
namespace xop
1112
{
@@ -19,21 +20,43 @@ class H265Source : public MediaSource
1920
void Setframerate(uint32_t framerate)
2021
{ framerate_ = framerate; }
2122

22-
uint32_t GetFramerate() const
23+
uint32_t GetFramerate() const
2324
{ return framerate_; }
2425

25-
virtual std::string GetMediaDescription(uint16_t port=0);
26+
void SetResolution(uint32_t width, uint32_t height)
27+
{ width_ = width; height_ = height; }
2628

27-
virtual std::string GetAttribute();
29+
uint32_t GetWidth() const { return width_; }
30+
uint32_t GetHeight() const { return height_; }
31+
32+
// Set VPS/SPS/PPS for sprop parameters in SDP
33+
void SetVPS(const uint8_t* data, size_t size)
34+
{ vps_.assign(data, data + size); }
35+
36+
void SetSPS(const uint8_t* data, size_t size)
37+
{ sps_.assign(data, data + size); }
38+
39+
void SetPPS(const uint8_t* data, size_t size)
40+
{ pps_.assign(data, data + size); }
41+
42+
virtual std::string GetMediaDescription(uint16_t port=0);
43+
44+
virtual std::string GetAttribute();
2845

2946
virtual bool HandleFrame(MediaChannelId channelId, AVFrame frame);
3047

3148
static int64_t GetTimestamp();
32-
49+
3350
private:
3451
H265Source(uint32_t framerate);
52+
static std::string Base64Encode(const uint8_t* data, size_t size);
3553

3654
uint32_t framerate_ = 25;
55+
uint32_t width_ = 0;
56+
uint32_t height_ = 0;
57+
std::vector<uint8_t> vps_;
58+
std::vector<uint8_t> sps_;
59+
std::vector<uint8_t> pps_;
3760
};
3861

3962
}

0 commit comments

Comments
 (0)