@@ -3279,6 +3279,11 @@ std::string SrsRtcRecvTrack::get_track_id()
32793279 return track_desc_->id_ ;
32803280}
32813281
3282+ SrsRtcTrackDescription *SrsRtcRecvTrack::get_track_desc ()
3283+ {
3284+ return track_desc_;
3285+ }
3286+
32823287srs_error_t SrsRtcRecvTrack::on_nack (SrsRtpPacket **ppkt)
32833288{
32843289 srs_error_t err = srs_success;
@@ -3774,3 +3779,147 @@ uint32_t SrsRtcSSRCGenerator::generate_ssrc()
37743779
37753780 return ++ssrc_num_;
37763781}
3782+
3783+ ISrsRtcFormat::ISrsRtcFormat ()
3784+ {
3785+ }
3786+
3787+ ISrsRtcFormat::~ISrsRtcFormat ()
3788+ {
3789+ }
3790+
3791+ SrsRtcFormat::SrsRtcFormat ()
3792+ {
3793+ req_ = NULL ;
3794+ video_codec_reported_ = false ;
3795+ audio_codec_reported_ = false ;
3796+
3797+ stat_ = _srs_stat;
3798+ }
3799+
3800+ SrsRtcFormat::~SrsRtcFormat ()
3801+ {
3802+ req_ = NULL ;
3803+
3804+ stat_ = NULL ;
3805+ }
3806+
3807+ srs_error_t SrsRtcFormat::initialize (ISrsRequest *req)
3808+ {
3809+ req_ = req;
3810+ return srs_success;
3811+ }
3812+
3813+ srs_error_t SrsRtcFormat::on_rtp_packet (SrsRtcRecvTrack *track, bool is_audio)
3814+ {
3815+ srs_error_t err = srs_success;
3816+
3817+ SrsRtcTrackDescription *track_desc = track ? track->get_track_desc () : NULL ;
3818+ if (!req_ || !track_desc || !track_desc->media_ ) {
3819+ return err;
3820+ }
3821+
3822+ if (is_audio) {
3823+ // Only report once
3824+ if (audio_codec_reported_) {
3825+ return err;
3826+ }
3827+ audio_codec_reported_ = true ;
3828+
3829+ SrsCodecPayload *media = track_desc->media_ ;
3830+ SrsAudioCodecId codec_id = (SrsAudioCodecId)media->codec (false );
3831+
3832+ // Parse channels and sample rate from track description
3833+ if (codec_id == SrsAudioCodecIdOpus) {
3834+ SrsAudioPayload *audio_media = dynamic_cast <SrsAudioPayload *>(media);
3835+ if (!audio_media) {
3836+ return err;
3837+ }
3838+
3839+ // Get sample rate from media payload
3840+ SrsAudioSampleRate sample_rate = (SrsAudioSampleRate)audio_media->sample_ ;
3841+
3842+ // Get channels from audio payload
3843+ SrsAudioChannels channels = (SrsAudioChannels)audio_media->channel_ ;
3844+
3845+ if ((err = stat_->on_audio_info (req_, codec_id, sample_rate, channels,
3846+ SrsAacObjectTypeReserved)) != srs_success) {
3847+ return srs_error_wrap (err, " stat audio info" );
3848+ }
3849+ srs_trace (" RTC: parsed %s codec, sample_rate=%dHz, channels=%d" ,
3850+ srs_audio_codec_id2str (codec_id).c_str (), sample_rate, channels);
3851+ }
3852+ } else {
3853+ // Only report once
3854+ if (video_codec_reported_) {
3855+ return err;
3856+ }
3857+ video_codec_reported_ = true ;
3858+
3859+ SrsCodecPayload *media = track_desc->media_ ;
3860+ SrsVideoCodecId codec_id = (SrsVideoCodecId)media->codec (true );
3861+
3862+ // Parse profile and level from track description
3863+ if (codec_id == SrsVideoCodecIdAVC) {
3864+ SrsVideoPayload *video_media = dynamic_cast <SrsVideoPayload *>(media);
3865+ if (!video_media || video_media->h264_param_ .profile_level_id_ .empty ()) {
3866+ return err;
3867+ }
3868+
3869+ // Parse profile_level_id hex string (e.g., "42e01f")
3870+ // Format: PPCCLL where PP=profile_idc, CC=constraint_set, LL=level_idc
3871+ std::string profile_level_id = video_media->h264_param_ .profile_level_id_ ;
3872+ if (profile_level_id.length () != 6 ) {
3873+ return err;
3874+ }
3875+
3876+ // Decode hex string to bytes
3877+ // srs_hex_decode_string expects size to be the hex string length (6 chars = 3 bytes)
3878+ uint8_t bytes[3 ];
3879+ int hex_len = (int )profile_level_id.length ();
3880+ int r0 = srs_hex_decode_string (bytes, profile_level_id.c_str (), hex_len);
3881+ if (r0 != (int )sizeof (bytes)) {
3882+ srs_trace (" RTC: failed to decode profile_level_id hex string: %s, r0=%d" , profile_level_id.c_str (), r0);
3883+ video_codec_reported_ = true ;
3884+ return err;
3885+ }
3886+
3887+ // Extract profile and level from the decoded bytes
3888+ SrsAvcProfile profile = (SrsAvcProfile)bytes[0 ];
3889+ SrsAvcLevel level = (SrsAvcLevel)bytes[2 ];
3890+
3891+ if ((err = stat_->on_video_info (req_, codec_id, profile, level, 0 , 0 )) != srs_success) {
3892+ return srs_error_wrap (err, " stat video info" );
3893+ }
3894+ srs_trace (" RTC: parsed %s codec, profile=%s, level=%s" ,
3895+ srs_video_codec_id2str (codec_id).c_str (),
3896+ srs_avc_profile2str (profile).c_str (),
3897+ srs_avc_level2str (level).c_str ());
3898+ } else if (codec_id == SrsVideoCodecIdHEVC) {
3899+ SrsVideoPayload *video_media = dynamic_cast <SrsVideoPayload *>(media);
3900+ if (!video_media || video_media->h265_param_ .profile_id_ .empty () ||
3901+ video_media->h265_param_ .level_id_ .empty ()) {
3902+ return err;
3903+ }
3904+
3905+ // Parse HEVC profile_id and level_id from SDP parameters
3906+ // profile_id is a decimal string (e.g., "1" for Main profile)
3907+ // level_id is a decimal string (e.g., "93" for Level 3.1)
3908+ int profile_id = atoi (video_media->h265_param_ .profile_id_ .c_str ());
3909+ int level_id = atoi (video_media->h265_param_ .level_id_ .c_str ());
3910+
3911+ SrsHevcProfile profile = (SrsHevcProfile)profile_id;
3912+ SrsHevcLevel level = (SrsHevcLevel)level_id;
3913+
3914+ if ((err = stat_->on_video_info (req_, codec_id, profile, level, 0 , 0 )) != srs_success) {
3915+ return srs_error_wrap (err, " stat video info" );
3916+ }
3917+ srs_trace (" RTC: parsed %s codec, profile=%s, level=%d" ,
3918+ srs_video_codec_id2str (codec_id).c_str (),
3919+ srs_hevc_profile2str (profile).c_str (),
3920+ level_id);
3921+ }
3922+ }
3923+
3924+ return err;
3925+ }
0 commit comments