Skip to content

Commit

Permalink
H265: Support HEVC over SRT.(ossrs#465)
Browse files Browse the repository at this point in the history
  • Loading branch information
chundonglinlin committed Jan 6, 2023
1 parent 3c6ade8 commit 4de28bc
Show file tree
Hide file tree
Showing 5 changed files with 853 additions and 2 deletions.
198 changes: 197 additions & 1 deletion trunk/src/app/srs_app_srt_source.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@ srs_error_t SrsRtmpFromSrtBridge::on_ts_message(SrsTsMessage* msg)
}

// check supported codec
if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamAudioAAC) {
if (msg->channel->stream != SrsTsStreamVideoH264 && msg->channel->stream != SrsTsStreamVideoHEVC && msg->channel->stream != SrsTsStreamAudioAAC) {
return srs_error_new(ERROR_STREAM_CASTER_TS_CODEC, "ts: unsupported stream codec=%d", msg->channel->stream);
}

Expand All @@ -358,6 +358,14 @@ srs_error_t SrsRtmpFromSrtBridge::on_ts_message(SrsTsMessage* msg)
}

// TODO: FIXME: implements other codec?
#ifdef SRS_H265
if (msg->channel->stream == SrsTsStreamVideoHEVC) {
if ((err = on_ts_hevc(msg, &avs)) != srs_success) {
return srs_error_wrap(err, "ts: consume hevc video");
}
}
#endif

return err;
}

Expand Down Expand Up @@ -525,6 +533,194 @@ srs_error_t SrsRtmpFromSrtBridge::on_h264_frame(SrsTsMessage* msg, vector<pair<c
return err;
}

#ifdef SRS_H265
srs_error_t SrsRtmpFromSrtBridge::on_ts_hevc(SrsTsMessage *msg, SrsBuffer *avs)
{
srs_error_t err = srs_success;

vector<pair<char*, int> > ipb_frames;

SrsRawHEVCStream *hevc = new SrsRawHEVCStream();
SrsAutoFree(SrsRawHEVCStream, hevc);

// send each frame.
while (!avs->empty()) {
char* frame = NULL;
int frame_size = 0;
if ((err = hevc->annexb_demux(avs, &frame, &frame_size)) != srs_success) {
return srs_error_wrap(err, "demux hevc annexb");
}

if (frame == NULL || frame_size == 0) {
continue;
}

// for vps
if (hevc->is_vps(frame, frame_size)) {
std::string vps;
if ((err = hevc->sps_demux(frame, frame_size, vps)) != srs_success) {
return srs_error_wrap(err, "demux vps");
}

if (!vps.empty() && hevc_vps_ != vps) {
vps_sps_pps_change_ = true;
}

hevc_vps_ = vps;
continue;
}

// for sps
if (hevc->is_sps(frame, frame_size)) {
std::string sps;
if ((err = hevc->sps_demux(frame, frame_size, sps)) != srs_success) {
return srs_error_wrap(err, "demux sps");
}

if (! sps.empty() && hevc_sps_ != sps) {
vps_sps_pps_change_ = true;
}

hevc_sps_ = sps;
continue;
}

// for pps
if (hevc->is_pps(frame, frame_size)) {
std::string pps;
if ((err = hevc->pps_demux(frame, frame_size, pps)) != srs_success) {
return srs_error_wrap(err, "demux pps");
}

if (! pps.empty() && hevc_pps_ != pps) {
vps_sps_pps_change_ = true;
}

hevc_pps_ = pps;
continue;
}

ipb_frames.push_back(make_pair(frame, frame_size));
}

if ((err = check_vps_sps_pps_change(msg)) != srs_success) {
return srs_error_wrap(err, "check vps sps pps");
}

return on_hevc_frame(msg, ipb_frames);
}

srs_error_t SrsRtmpFromSrtBridge::check_vps_sps_pps_change(SrsTsMessage* msg)
{
srs_error_t err = srs_success;

if (!vps_sps_pps_change_) {
return err;
}

if (hevc_vps_.empty() || hevc_sps_.empty() || hevc_pps_.empty()) {
return srs_error_new(ERROR_SRT_TO_RTMP_EMPTY_SPS_PPS, "vps or sps or pps empty");
}

// vps/sps/pps changed, generate new video sh frame and dispatch it.
vps_sps_pps_change_ = false;

// ts tbn to flv tbn.
uint32_t dts = (uint32_t)(msg->dts / 90);

std::string sh;
SrsRawHEVCStream* hevc = new SrsRawHEVCStream();
SrsAutoFree(SrsRawHEVCStream, hevc);

if ((err = hevc->mux_sequence_header(hevc_vps_, hevc_sps_, hevc_pps_, sh)) != srs_success) {
return srs_error_wrap(err, "mux sequence header");
}

// h264 packet to flv packet.
char* flv = NULL;
int nb_flv = 0;
if ((err = hevc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, dts, dts, &flv, &nb_flv)) != srs_success) {
return srs_error_wrap(err, "avc to flv");
}

SrsMessageHeader header;
header.initialize_video(nb_flv, dts, video_streamid_);
SrsCommonMessage rtmp;
if ((err = rtmp.create(&header, flv, nb_flv)) != srs_success) {
return srs_error_wrap(err, "create rtmp");
}

if ((err = live_source_->on_video(&rtmp)) != srs_success) {
return srs_error_wrap(err, "srt to rtmp sps/pps");
}

return err;
}

srs_error_t SrsRtmpFromSrtBridge::on_hevc_frame(SrsTsMessage* msg, vector<pair<char*, int> >& ipb_frames)
{
srs_error_t err = srs_success;

if (ipb_frames.empty()) {
return srs_error_new(ERROR_SRT_CONN, "empty frame");
}

// ts tbn to flv tbn.
uint32_t dts = (uint32_t)(msg->dts / 90);
uint32_t pts = (uint32_t)(msg->pts / 90);
int32_t cts = pts - dts;

// for IDR frame, the frame is keyframe.
SrsVideoAvcFrameType frame_type = SrsVideoAvcFrameTypeInterFrame;

// 5bytes video tag header
int frame_size = 5;
for (size_t i = 0; i != ipb_frames.size(); ++i) {
// 4 bytes for nalu length.
frame_size += 4 + ipb_frames[i].second;
SrsHevcNaluType nal_unit_type = (SrsHevcNaluType)((ipb_frames[i].first[0] & 0x7e) >> 1);
if ((nal_unit_type >= SrsHevcNaluType_CODED_SLICE_BLA) && (nal_unit_type <= SrsHevcNaluType_RESERVED_23)) {
frame_type = SrsVideoAvcFrameTypeKeyFrame;
}
}

SrsCommonMessage rtmp;
rtmp.header.initialize_video(frame_size, dts, video_streamid_);
rtmp.create_payload(frame_size);
rtmp.size = frame_size;
SrsBuffer payload(rtmp.payload, rtmp.size);

// Write 5bytes video tag header.

// @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
// Frame Type, Type of video frame.
// CodecID, Codec Identifier.
// set the rtmp header
payload.write_1bytes((frame_type << 4) | SrsVideoCodecIdHEVC);
// hevc_type: nalu
payload.write_1bytes(0x01);
// composition time
payload.write_3bytes(cts);

// Write video nalus.
for (size_t i = 0; i != ipb_frames.size(); ++i) {
char* nal = ipb_frames[i].first;
int nal_size = ipb_frames[i].second;

// write 4 bytes of nalu length.
payload.write_4bytes(nal_size);
// write nalu
payload.write_bytes(nal, nal_size);
}

if ((err = live_source_->on_video(&rtmp)) != srs_success) {
return srs_error_wrap(err ,"srt ts hevc video to rtmp");
}

return err;
}
#endif

srs_error_t SrsRtmpFromSrtBridge::on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs)
{
srs_error_t err = srs_success;
Expand Down
14 changes: 14 additions & 0 deletions trunk/src/app/srs_app_srt_source.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,13 @@ class SrsRtmpFromSrtBridge : public ISrsSrtSourceBridge, public ISrsTsHandler
srs_error_t on_h264_frame(SrsTsMessage* msg, std::vector<std::pair<char*, int> >& ipb_frames);
srs_error_t check_audio_sh_change(SrsTsMessage* msg, uint32_t pts);
srs_error_t on_aac_frame(SrsTsMessage* msg, uint32_t pts, char* frame, int frame_size);

#ifdef SRS_H265
srs_error_t on_ts_hevc(SrsTsMessage *msg, SrsBuffer *avs);
srs_error_t check_vps_sps_pps_change(SrsTsMessage *msg);
srs_error_t on_hevc_frame(SrsTsMessage *msg, std::vector<std::pair<char *, int>> &ipb_frames);
#endif

private:
SrsTsContext* ts_ctx_;

Expand All @@ -131,6 +138,13 @@ class SrsRtmpFromSrtBridge : public ISrsSrtSourceBridge, public ISrsTsHandler
std::string sps_;
std::string pps_;

#ifdef SRS_H265
bool vps_sps_pps_change_;
std::string hevc_vps_;
std::string hevc_sps_;
std::string hevc_pps_;
#endif

// Record audio sepcific config had changed, if change, need to generate new audio sh frame.
bool audio_sh_change_;
std::string audio_sh_;
Expand Down
7 changes: 6 additions & 1 deletion trunk/src/kernel/srs_kernel_error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,9 @@
XX(ERROR_HTTP_URL_UNESCAPE , 3096, "HttpUrlUnescape", "Failed to unescape URL for HTTP") \
XX(ERROR_HTTP_WITH_BODY , 3097, "HttpWithBody", "Failed for HTTP body") \
XX(ERROR_HEVC_DISABLED , 3098, "HevcDisabled", "HEVC is disabled") \
XX(ERROR_HEVC_DECODE_ERROR , 3099, "HevcDecode", "HEVC decode av stream failed")
XX(ERROR_HEVC_DECODE_ERROR , 3099, "HevcDecode", "HEVC decode av stream failed") \
XX(ERROR_HEVC_DROP_BEFORE_SPS_PPS , 4050, "HevcDropBeforeSequence", "HEVC Drop frames before get sps and pps") \
XX(ERROR_HEVC_API_NO_PREFIXED , 4051, "HevcAnnexbPrefix", "No annexb prefix for HEVC decoder")

/**************************************************/
/* HTTP/StreamConverter protocol error. */
Expand Down Expand Up @@ -322,6 +324,9 @@
XX(ERROR_GB_SSRC_GENERATE , 4051, "GbSsrcGenerate", "Failed to generate SSRC for GB28181") \
XX(ERROR_GB_CONFIG , 4052, "GbConfig", "Invalid configuration for GB28181") \
XX(ERROR_GB_TIMEOUT , 4053, "GbTimeout", "SIP or media connection timeout for GB28181") \
XX(ERROR_STREAM_CASTER_HEVC_VPS , 4060, "CasterTsHevcVps", "Invalid ts HEVC VPS for stream caster") \
XX(ERROR_STREAM_CASTER_HEVC_SPS , 4061, "CasterTsHevcSps", "Invalid ts HEVC SPS for stream caster") \
XX(ERROR_STREAM_CASTER_HEVC_PPS , 4062, "CasterTsHevcPps", "Invalid ts HEVC PPS for stream caster")

/**************************************************/
/* RTC protocol error. */
Expand Down
Loading

0 comments on commit 4de28bc

Please sign in to comment.