WebRTC 오디오 및 비디오 통화 - WebRTC 로컬 라이브 고해상도에 화면 문제가 표시되지 않습니다.

iOS 개발 - WebRTC 로컬 라이브 고해상도에 화면 문제가 표시되지 않습니다.

이전에 ossrs와 결합된 WebRTC를 사용하여 스트림을 푸시할 때 ossrs 플레이어에서는 고해상도 이미지의 문제를 볼 수 없었습니다. 이 질문에 따르면 해결책이 발견되었습니다.

1. WebRTC란 무엇인가

WebRTC란 무엇입니까?

WebRTC(Web Real-Time Communications)는 네트워크 애플리케이션이나 사이트가 중개자 없이 브라우저 간에 피어 투 피어(Peer-to-Peer) 연결을 설정하여 비디오 스트리밍 및/또는 전송을 달성할 수 있도록 하는 실시간 통신 기술입니다. 오디오 스트림 또는 기타 임의의 데이터.

2. OSSRS란 무엇인가요?

ossrs는 무엇입니까?
SRS(Simple Realtime Server)는 RTMP, WebRTC, HLS, HTTP-FLV, SRT 등 다양한 실시간 스트리밍 미디어 프로토콜을 지원하는 간단하고 효율적인 실시간 비디오 서버입니다.

공식 홈페이지 주소: https://ossrs.net/lts/zh-cn/

여기서는 RTC 라이브 방송을 달성하기 위한 iOS Google WebRTC 및 ossrs에 대해 쓰지 않겠습니다. 당분간은 고해상도 로컬 WebRTC 라이브 방송에서 사진이 표시되지 않는 문제를 기록해 둘 가치가 있습니다.

3. 고해상도에서 화면이 나오지 않는 문제 해결

로컬 WebRTC 라이브 고해상도 라이브 방송에서 사진이 표시되지 않는 문제는 SDP의 profile-level-id와 관련이 있습니다.

profile-level-id는 정확히 SPS에서 두 번째부터 4바이트까지의 base16 인코딩입니다. 이 3바이트의 구체적인 의미는 다음과 같습니다.

sps[1] AVCProfileIndication
sps[2] profile_compatibility
sps[3] AVCLevlIndication

실제로 http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC#Levels
를 설정하면 레벨 값에 10을 곱한 값, 예를 들어 레벨 1.0이면 설정 값은 0x0A가 됩니다. 레벨 3.0의 경우 설정값은 0x1E입니다. 예외는 레벨 1b이며 설정 값은 0x09입니다.

이미지 출처 네트워크 (주소를 잊어버려서 죄송합니다. 블로그 이미지를 인용하실 경우 메시지를 남겨주세요)
여기에 이미지 설명을 삽입하세요

세 번째는 레벨을 나타내며, 예를 들어 1f의 값은 31이며, 그림에서 볼 수 있듯이 3.1이고 해상도는 720 480 720 576 1280*720 입니다.

고해상도 1920 1080 2560 1920 3840*2160이 필요한 경우 레벨 5.1 16진수 값을 33으로 설정해야 하며 여기서 설정한 값은 42e033입니다.

아래는 SDP의 예입니다.

"코드": 0,
"서버": "vid-415v5lz",
“sdp”: “v=0\r\no=SRS/4.0.268(Leo) 94003279212192 2 IN IP4 0.0.0.0\r\ns=SRSPublishSession\r\nt=0 0\r\na=ice-lite\ r\na=그룹:BUNDLE 0 1\r\na=msid-의미: WMS 라이브/라이브스트림\r\nm=오디오 9 UDP/TLS/RTP/SAVPF 111\r\nc=IN IP4 0.0.0.0\r\ na=ice-ufrag:q01184s8\r\na=ice-pwd:o25158210twbb093o342910094v0wo5k\r\na=지문:sha-256 6A:66:81:7C:68:91:79:18:05:2C:EE:5F :BF:1B:4B:F4:78:C4:01:06:CC:CC:9E:F0:32:5B:72:21:4A:C2:A1:AA\r\na=설정:수동\r \na=mid:0\r\na=recvonly\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:111 opus/48000/2\r\na=fmtp:111 minptime =10;useinbandfec=1\r\na=candidate:0 1 udp 2130706431 10.0.80.128 8000 일반 호스트 세대 0\r\na=candidate:1 1 1 udp 2130706431 112.124.157.141 8000 일반 호스트 세대 0\r\nm=video 9 UDP/TLS/RTP/SAVPF 96 127\r\nc=IN IP4 0.0.0.0\r\na=ice-ufrag:q01184s8\r\na=ice-pwd:o25158210twbb093o342910094v0wo5k\r\na=지문:샤-256 6A:66:81:7C:68:91:79:18:05:2C:EE:5F:BF:1B:4B:F4:78:C4:01:06:CC:CC:9E:F0 :32:5B:72:21:4A:C2:A1:AA\r\na=setup:passive\r\na=mid:1\r\na=extmap:5 http://www.ietf.org/ id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=recvonly\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 H264/90000 \r\na=rtcp-fb:96 Transport-cc\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=fmtp:96 level-asymmetry-allowed= 1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:127 red/90000\r\na=candidate:0 1 udp 2130706431 10.0.80.128 8000 일반 호스트 생성 0\r\na= 후보:1 1 udp 2130706431 112.124.157.141 8000 일반 호스트 세대 0\r\n",org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=recvonly\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 H264 /90000\r\na=rtcp-fb:96 Transport-cc\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=fmtp:96 레벨-비대칭- allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:127 red/90000\r\na=candidate:0 1 udp 2130706431 10.0.80.128 8000 일반 호스트 생성 0\r\ na=후보:1 1 udp 2130706431 112.124.157.141 8000 일반 호스트 세대 0\r\n”,org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\r\na=recvonly\r\na=rtcp-mux\r\na=rtcp-rsize\r\na=rtpmap:96 H264 /90000\r\na=rtcp-fb:96 Transport-cc\r\na=rtcp-fb:96 nack\r\na=rtcp-fb:96 nack pli\r\na=fmtp:96 레벨-비대칭- allowed=1;packetization-mode=1;profile-level-id=42e01f\r\na=rtpmap:127 red/90000\r\na=candidate:0 1 udp 2130706431 10.0.80.128 8000 일반 호스트 생성 0\r\ na=후보:1 1 udp 2130706431 112.124.157.141 8000 일반 호스트 세대 0\r\n”,141 8000 일반 호스트 세대 0\r\n",141 8000 일반 호스트 세대 0\r\n",
"세션 ID": "q01184s8:oPvh"
}

여기서 profile-level-id=42e01f를 볼 수 있습니다.

WebRTC에서 로컬 SDP의 profile-level-id를 42e033으로 바꾸는 경우

[weakSelf.webRTCClient offer:^(RTCSessionDescription *sdp) {
    
    
        DebugLog(@"changeSDP2Server offer sdp:%@", sdp);
        NSString *offerSDPString = [SDWebRTCSDPUtil setMediaBitrate:sdp.sdp media:@"video" bitrate:(6*1024*1024)];
        DebugLog(@"changeSDP2Server offerSDPString:%@", offerSDPString);
        [weakSelf changeSDP2Server:offerSDPString];
    }];

호출하기 전에 rtc/v1/publish/ 인터페이스를 호출하여 얻은 RemoteSDPString의 profile-level-id를 42e033으로 변경합니다.

- (void)setRemoteDescription:(RTC_OBJC_TYPE(RTCSessionDescription) *)sdp
           completionHandler:(nullable void (^)(NSError *_Nullable error))completionHandler;

테스트 후 고해상도 이미지 전환이 정상적으로 표시될 수 있습니다.

NSString *resultRemoteSDPString = [SDWebRTCSDPUtil setMediaBitrate:remoteSDPString media:@"video" bitrate:(6*1024*1024)];
               DebugLog(@"changeSDP2Server resultRemoteSDPString:%@", resultRemoteSDPString);

               RTCSessionDescription *remoteSDP = [[RTCSessionDescription alloc] initWithType:RTCSdpTypeAnswer sdp:resultRemoteSDPString];
               [weakSelf.webRTCClient setRemoteSdp:remoteSDP completion:^(NSError * error) {
    
    
                   DebugLog(@"changeSDP2Server setRemoteDescription error:%@", error);
               }];

4. RTCVideoEncoderFactory를 통해 고해상도 화면이 표시되지 않는 문제를 해결합니다.

SDP에서 profile-level-id를 변경하는 것이 최종 해결책은 아닙니다. 마지막으로 RTCVideoEncoder의 RTCVideoCodecInfo에 있는 constrainedHighParams의 profile-level-id 값을 사용하여 42e033을 지정합니다.

코덱의 ConstrainedHighInfo 및 ConstrainedBaselineInfo 요소를 설정합니다.

NSDictionary<NSString *, NSString *> *constrainedHighParams = @{
    
    
    @"profile-level-id" : kLevelHighConstrainedHigh,
    @"level-asymmetry-allowed" : @"1",
    @"packetization-mode" : @"1",
  };
  RTCVideoCodecInfo *constrainedHighInfo =
      [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name parameters:constrainedHighParams];
  [codecs addObject:constrainedHighInfo];

  NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{
    
    
    @"profile-level-id" : kLevelHighConstrainedBaseline,
    @"level-asymmetry-allowed" : @"1",
    @"packetization-mode" : @"1",
  };
  RTCVideoCodecInfo *constrainedBaselineInfo =
      [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name parameters:constrainedBaselineParams];
  [codecs addObject:constrainedBaselineInfo];

전체 코드는 다음과 같습니다

SDRTCVideoEncoderFactory.h

#import <Foundation/Foundation.h>
#import <WebRTC/WebRTC.h>

/**
 + (NSArray<RTCVideoCodecInfo *> *)supportedCodecs {
   NSDictionary<NSString *, NSString *> *constrainedHighParams = @{
     @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedHigh,
     @"level-asymmetry-allowed" : @"1",
     @"packetization-mode" : @"1",
   };
   RTCVideoCodecInfo *constrainedHighInfo =
       [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name
                                    parameters:constrainedHighParams];
   NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{
     @"profile-level-id" : kRTCMaxSupportedH264ProfileLevelConstrainedBaseline,
     @"level-asymmetry-allowed" : @"1",
     @"packetization-mode" : @"1",
   };
   RTCVideoCodecInfo *constrainedBaselineInfo =
       [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name
                                    parameters:constrainedBaselineParams];
   RTCVideoCodecInfo *vp8Info = [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecVp8Name];
 #if defined(RTC_ENABLE_VP9)
   RTCVideoCodecInfo *vp9Info = [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecVp9Name];
 #endif
   return @[
     constrainedHighInfo,
     constrainedBaselineInfo,
     vp8Info,
 #if defined(RTC_ENABLE_VP9)
     vp9Info,
 #endif
   ];
 }
 */

@interface SDRTCVideoEncoderFactory : NSObject<RTCVideoEncoderFactory>

@end

SDRTCVideoEncoderFactory.m

#import "SDRTCVideoEncoderFactory.h"

static NSString *kLevelHighConstrainedHigh = @"640c33";
static NSString *kLevelHighConstrainedBaseline = @"42e033";

@implementation SDRTCVideoEncoderFactory

- (id<RTCVideoEncoder>)createEncoder:(RTCVideoCodecInfo *)info {
    
    
  if ([info.name isEqualToString:kRTCVideoCodecH264Name]) {
    
    
    return [[RTCVideoEncoderH264 alloc] initWithCodecInfo:info];
  } else if ([info.name isEqualToString:kRTCVideoCodecVp8Name]) {
    
    
    return [RTCVideoEncoderVP8 vp8Encoder];
  } else if ([info.name isEqualToString:kRTCVideoCodecVp9Name]) {
    
    
    return [RTCVideoEncoderVP9 vp9Encoder];
  }

  return nil;
}

- (NSArray<RTCVideoCodecInfo *> *)supportedCodecs {
    
    
  NSMutableArray<RTCVideoCodecInfo *> *codecs = [NSMutableArray array];

  NSDictionary<NSString *, NSString *> *constrainedHighParams = @{
    
    
    @"profile-level-id" : kLevelHighConstrainedHigh,
    @"level-asymmetry-allowed" : @"1",
    @"packetization-mode" : @"1",
  };
  RTCVideoCodecInfo *constrainedHighInfo =
      [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name parameters:constrainedHighParams];
  [codecs addObject:constrainedHighInfo];

  NSDictionary<NSString *, NSString *> *constrainedBaselineParams = @{
    
    
    @"profile-level-id" : kLevelHighConstrainedBaseline,
    @"level-asymmetry-allowed" : @"1",
    @"packetization-mode" : @"1",
  };
  RTCVideoCodecInfo *constrainedBaselineInfo =
      [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecH264Name parameters:constrainedBaselineParams];
  [codecs addObject:constrainedBaselineInfo];

  RTCVideoCodecInfo *vp8Info = [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecVp8Name parameters:nil];
  [codecs addObject:vp8Info];

#if defined(RTC_ENABLE_VP9)
    RTCVideoCodecInfo *vp9Info = [[RTCVideoCodecInfo alloc] initWithName:kRTCVideoCodecVp9Name];
    [codecs addObject:vp9Info];
#endif
  
  return [codecs copy];
}

@end

마지막으로 RTCPeerConnectionFactory가 초기화될 때 몇 가지 설정을 수행합니다.

#pragma mark - Lazy
- (RTCPeerConnectionFactory *)factory {
    
    
    if (!_factory) {
    
    
        RTCInitializeSSL();
        
        SDRTCVideoDecoderFactory *decoderFactory = [[SDRTCVideoDecoderFactory alloc] init];
        SDRTCVideoEncoderFactory *encoderFactory = [[SDRTCVideoEncoderFactory alloc] init];
        
//        for (RTCVideoCodecInfo *codec in encoderFactory.supportedCodecs) {
    
    
//            DebugLog(@"RTCVideoCodecInfo codec.parameters:%@", codec.parameters);
//        }
        _factory = [[RTCPeerConnectionFactory alloc] initWithEncoderFactory:encoderFactory decoderFactory:decoderFactory];
    }
    return _factory;
}

테스트 결과, 사진이 고해상도로 표시되지 않는 문제가 해결되었습니다.

V. 요약

iOS 개발 - 고해상도 WebRTC 로컬 라이브 방송은 문제를 표시하지 않습니다.WebRTC를 ossrs와 결합하여 사용하여 스트림을 푸시하면 ossrs 플레이어가 고해상도 화면 문제를 볼 수 없습니다. 마지막으로 RTCPeerConnectionFactory의 initWithEncoderFactory를 설정하여 문제를 해결했습니다.

https://blog.csdn.net/gloryFlow/article/details/132240952

학습 기록, 매일 계속 향상됩니다.

추천

출처blog.csdn.net/gloryFlow/article/details/132240952