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