Skip to content

Commit da29230

Browse files
committed
1 同步对讲采样+播放参数
2 优化对讲流程 3 支持8k 16k重采样 4 添加设置对讲音频参数接口 5 video页面添加p2p版本号 6 添加接口回调error透传 7 优化版本号匹配UI提示 8 添加通过播放器拉流,保存原始数据到Document目录 9 添加播放面板开关 Change-Id: I4de509b6de132c2a1fd2ce535a2e2039e84ec39b
2 parents d6f03f2 + 54819c0 commit da29230

File tree

14 files changed

+411
-37
lines changed

14 files changed

+411
-37
lines changed

Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ PODS:
113113
- nanopb/decode (1.30906.0)
114114
- nanopb/encode (1.30906.0)
115115
- PromisesObjC (1.2.12)
116-
- Protobuf (3.18.0)
116+
- Protobuf (3.19.0)
117117
- QCloudCore (5.5.2)
118118
- QCloudCOSXML/Transfer (5.5.2):
119119
- QCloudCore (= 5.5.2)
@@ -251,7 +251,7 @@ SPEC CHECKSUMS:
251251
MJRefresh: ed450d6eb9d3346a2cb033ab7eb6de090aeef437
252252
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
253253
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
254-
Protobuf: 1a37ebea1338949e9ac35a3f06e80b3f536eec8d
254+
Protobuf: e9b99dd5da72f665ec503b19191f245a22188b4d
255255
QCloudCore: b91a762d280f0a80cc6f537fd186ac43a4a3025e
256256
QCloudCOSXML: a30ab17f45b0cbbcbb218a3677298498e8748a22
257257
SDWebImage: 624d6e296c69b244bcede364c72ae0430ac14681

Source/LinkSDKDemo/Video/P2P/Controller/TIoTDemoPreviewDeviceVC.m

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ @interface TIoTDemoPreviewDeviceVC ()<UITableViewDelegate,UITableViewDataSource>
7777
@property (nonatomic, assign) CFTimeInterval endPlayer;
7878
@property (nonatomic, assign) CFTimeInterval startIpcP2P;
7979
@property (nonatomic, assign) CFTimeInterval endIpcP2P;
80-
80+
@property (nonatomic, assign) BOOL is_init_alert;
8181
@property (nonatomic, assign) BOOL is_ijkPlayer_stream; //通过播放器 还是 通过裸流拉取数据
8282
@end
8383

@@ -86,7 +86,7 @@ @implementation TIoTDemoPreviewDeviceVC
8686
- (void)viewDidLoad {
8787
[super viewDidLoad];
8888
// Do any additional setup after loading the view.
89-
89+
_is_init_alert = NO;
9090
_is_ijkPlayer_stream = YES;
9191
//关闭日志
9292
// [TIoTCoreXP2PBridge sharedInstance].logEnable = NO;
@@ -208,7 +208,22 @@ - (void)getDeviceStatusWithType:(NSString *)singleType qualityType:(NSString *)q
208208
NSString *channelNum = self.selectedModel.Channel?:@"0";
209209
channel = [NSString stringWithFormat:@"channel=%d",channelNum.intValue];
210210
}
211-
[[TIoTCoreXP2PBridge sharedInstance] sendVoiceToServer:self.deviceName?:@"" channel:channel];
211+
212+
AWAudioConfig *config = [[AWAudioConfig alloc] init];
213+
config.bitrate = 32000;
214+
config.channelCount = 1;
215+
config.sampleSize = 16;
216+
config.sampleRate = 8000;
217+
218+
// config.bitrate = 100000;
219+
// config.channelCount = 1;
220+
// config.sampleSize = 16;
221+
// config.sampleRate = 44100;
222+
223+
//设置会话 withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionAllowBluetooth
224+
// [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord mode:AVAudioSessionModeVoiceChat options:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionAllowBluetooth error:nil ];
225+
// [[AVAudioSession sharedInstance] setActive:YES error:nil];
226+
[[TIoTCoreXP2PBridge sharedInstance] sendVoiceToServer:self.deviceName?:@"" channel:channel audioConfig:config];
212227
}
213228

214229
}else {
@@ -563,6 +578,8 @@ - (void)clickTalkback:(UIButton *)button {
563578
}else {
564579
self.talkbackIcon.image = [UIImage imageNamed:@"talkback_unselect"];
565580
[[TIoTCoreXP2PBridge sharedInstance] stopVoiceToServer];
581+
582+
[self.player play];
566583
}
567584

568585
button.selected = !button.selected;
@@ -984,14 +1001,22 @@ - (void)moviePlayBackStateDidChange:(NSNotification*)notification
9841001
}
9851002

9861003
//弹框
987-
NSString *messageString = [NSString stringWithFormat: @"P2P连接时间: %ld(ms)\n画面显示时间: %ld(ms)",(long)p2pConnectTime,(NSInteger)((self.endPlayer - self.startPlayer)*1000)];
988-
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"设备名:%@",self.deviceName?:@""] message:messageString preferredStyle:(UIAlertControllerStyleAlert)];
989-
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
1004+
if (!_is_init_alert) {
1005+
NSString *messageString = [NSString stringWithFormat: @"P2P连接时间: %ld(ms)\n画面显示时间: %ld(ms)",(long)p2pConnectTime,(NSInteger)((self.endPlayer - self.startPlayer)*1000)];
1006+
UIAlertController *alertC = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:@"设备名:%@",self.deviceName?:@""] message:messageString preferredStyle:(UIAlertControllerStyleAlert)];
1007+
UIAlertAction *alertA = [UIAlertAction actionWithTitle:@"确定" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
1008+
1009+
}];
9901010

991-
}];
992-
993-
[alertC addAction:alertA];
994-
[self presentViewController:alertC animated:YES completion:nil];
1011+
[alertC addAction:alertA];
1012+
[self presentViewController:alertC animated:YES completion:nil];
1013+
1014+
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
1015+
[alertC dismissViewControllerAnimated:YES completion:NULL];
1016+
});
1017+
1018+
_is_init_alert = YES;
1019+
}
9951020

9961021
break;
9971022
}

Source/LinkSDKDemo/Video/PlayBack/CloudStorage/Controller/TIoTCloudStorageVC.m

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1345,7 +1345,6 @@ - (void)configVideo {
13451345

13461346
IJKFFOptions *options = [IJKFFOptions optionsByDefault];
13471347

1348-
13491348
self.cloudPlayer = [[IJKFFMoviePlayerController alloc] initWithContentURL:[NSURL URLWithString:self.videoUrl] withOptions:options];
13501349
self.cloudPlayer.view.autoresizingMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight;
13511350
self.cloudPlayer.view.frame = self.cloudImageView.bounds;

Source/LinkSDKDemo/Video/PlayBack/LocalPlayBack/Controller/TIoTDemoLocalRecordVC.m

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,8 +1512,7 @@ - (void)configVideo {
15121512
[self.imageView addSubview:self.player.view];
15131513
[self.player resetHubFrame:self.player.view.frame];
15141514

1515-
[self.player setOptionIntValue:10 * 1000 forKey:@"analyzeduration" ofCategory:kIJKFFOptionCategoryFormat];
1516-
[self.player setOptionIntValue:10 * 1024 forKey:@"probesize" ofCategory:kIJKFFOptionCategoryFormat];
1515+
[self.player setOptionIntValue:25 * 1024 forKey:@"probesize" ofCategory:kIJKFFOptionCategoryFormat];
15171516
[self.player setOptionIntValue:0 forKey:@"packet-buffering" ofCategory:kIJKFFOptionCategoryPlayer];
15181517
[self.player setOptionIntValue:1 forKey:@"start-on-prepared" ofCategory:kIJKFFOptionCategoryPlayer];
15191518
[self.player setOptionIntValue:1 forKey:@"threads" ofCategory:kIJKFFOptionCategoryCodec];
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
3+
#import <Foundation/Foundation.h>
4+
#import <AVFoundation/AVFoundation.h>
5+
#import <AudioToolbox/AudioToolbox.h>
6+
7+
@interface AWAACEncoder : NSObject
8+
@property (nonatomic,assign) NSInteger sample_rate;
9+
10+
@property (nonatomic) dispatch_queue_t encoderQueue;
11+
@property (nonatomic) dispatch_queue_t callbackQueue;
12+
13+
- (void) encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer completionBlock:(void (^)(NSData *encodedData, NSError* error))completionBlock;
14+
15+
@end
Lines changed: 243 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,243 @@
1+
2+
3+
#import "AWAACEncoder.h"
4+
@interface AWAACEncoder()
5+
@property (nonatomic) AudioConverterRef audioConverter;
6+
@property (nonatomic) uint8_t *aacBuffer;
7+
@property (nonatomic) NSUInteger aacBufferSize;
8+
@property (nonatomic) char *pcmBuffer;
9+
@property (nonatomic) size_t pcmBufferSize;
10+
11+
@end
12+
13+
@implementation AWAACEncoder
14+
15+
- (void) dealloc {
16+
AudioConverterDispose(_audioConverter);
17+
free(_aacBuffer);
18+
}
19+
20+
- (id) init {
21+
if (self = [super init]) {
22+
_encoderQueue = dispatch_queue_create("AAC Encoder Queue", DISPATCH_QUEUE_SERIAL);
23+
_callbackQueue = dispatch_queue_create("AAC Encoder Callback Queue", DISPATCH_QUEUE_SERIAL);
24+
_audioConverter = NULL;
25+
_pcmBufferSize = 0;
26+
_pcmBuffer = NULL;
27+
_aacBufferSize = 1024;
28+
_aacBuffer = malloc(_aacBufferSize * sizeof(uint8_t));
29+
memset(_aacBuffer, 0, _aacBufferSize);
30+
}
31+
return self;
32+
}
33+
34+
/**
35+
* 设置编码参数
36+
*
37+
* @param sampleBuffer 音频
38+
*/
39+
- (void) setupEncoderFromSampleBuffer:(CMSampleBufferRef)sampleBuffer {
40+
AudioStreamBasicDescription inAudioStreamBasicDescription = *CMAudioFormatDescriptionGetStreamBasicDescription((CMAudioFormatDescriptionRef)CMSampleBufferGetFormatDescription(sampleBuffer));
41+
42+
AudioStreamBasicDescription outAudioStreamBasicDescription = {0}; // 初始化输出流的结构体描述为0. 很重要。
43+
outAudioStreamBasicDescription.mSampleRate = self.sample_rate;//inAudioStreamBasicDescription.mSampleRate; // 音频流,在正常播放情况下的帧率。如果是压缩的格式,这个属性表示解压缩后的帧率。帧率不能为0。
44+
outAudioStreamBasicDescription.mFormatID = kAudioFormatMPEG4AAC; // 设置编码格式
45+
outAudioStreamBasicDescription.mFormatFlags = kMPEG4Object_AAC_LC; // 无损编码 ,0表示没有
46+
outAudioStreamBasicDescription.mBytesPerPacket = 0; // 每一个packet的音频数据大小。如果的动态大小,设置为0。动态大小的格式,需要用AudioStreamPacketDescription 来确定每个packet的大小。
47+
outAudioStreamBasicDescription.mFramesPerPacket = 1024; // 每个packet的帧数。如果是未压缩的音频数据,值是1。动态码率格式,这个值是一个较大的固定数字,比如说AAC的1024。如果是动态大小帧数(比如Ogg格式)设置为0。
48+
outAudioStreamBasicDescription.mBytesPerFrame = 0; // 每帧的大小。每一帧的起始点到下一帧的起始点。如果是压缩格式,设置为0 。
49+
outAudioStreamBasicDescription.mChannelsPerFrame = 1; // 声道数
50+
outAudioStreamBasicDescription.mBitsPerChannel = 0; // 压缩格式设置为0
51+
outAudioStreamBasicDescription.mReserved = 0; // 8字节对齐,填0.
52+
AudioClassDescription *description = [self getAudioClassDescriptionWithType:kAudioFormatMPEG4AAC fromManufacturer:kAppleSoftwareAudioCodecManufacturer]; //软编
53+
54+
OSStatus status = AudioConverterNewSpecific(&inAudioStreamBasicDescription, &outAudioStreamBasicDescription, 1, description, &_audioConverter); // 创建转换器
55+
if (status != 0) {
56+
NSLog(@"setup converter: %d", (int)status);
57+
}
58+
}
59+
60+
/**
61+
* 获取编解码器
62+
*
63+
* @param type 编码格式
64+
* @param manufacturer 软/硬编
65+
*
66+
编解码器(codec)指的是一个能够对一个信号或者一个数据流进行变换的设备或者程序。这里指的变换既包括将 信号或者数据流进行编码(通常是为了传输、存储或者加密)或者提取得到一个编码流的操作,也包括为了观察或者处理从这个编码流中恢复适合观察或操作的形式的操作。编解码器经常用在视频会议和流媒体等应用中。
67+
* @return 指定编码器
68+
*/
69+
- (AudioClassDescription *)getAudioClassDescriptionWithType:(UInt32)type
70+
fromManufacturer:(UInt32)manufacturer
71+
{
72+
static AudioClassDescription desc;
73+
74+
UInt32 encoderSpecifier = type;
75+
OSStatus st;
76+
77+
UInt32 size;
78+
st = AudioFormatGetPropertyInfo(kAudioFormatProperty_Encoders,
79+
sizeof(encoderSpecifier),
80+
&encoderSpecifier,
81+
&size);
82+
if (st) {
83+
NSLog(@"error getting audio format propery info: %d", (int)(st));
84+
return nil;
85+
}
86+
87+
unsigned int count = size / sizeof(AudioClassDescription);
88+
AudioClassDescription descriptions[count];
89+
st = AudioFormatGetProperty(kAudioFormatProperty_Encoders,
90+
sizeof(encoderSpecifier),
91+
&encoderSpecifier,
92+
&size,
93+
descriptions);
94+
if (st) {
95+
NSLog(@"error getting audio format propery: %d", (int)(st));
96+
return nil;
97+
}
98+
99+
for (unsigned int i = 0; i < count; i++) {
100+
if ((type == descriptions[i].mSubType) &&
101+
(manufacturer == descriptions[i].mManufacturer)) {
102+
memcpy(&desc, &(descriptions[i]), sizeof(desc));
103+
return &desc;
104+
}
105+
}
106+
107+
return nil;
108+
}
109+
110+
111+
/**
112+
* A callback function that supplies audio data to convert. This callback is invoked repeatedly as the converter is ready for new input data.
113+
114+
*/
115+
OSStatus inInputDataProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
116+
{
117+
AWAACEncoder *encoder = (__bridge AWAACEncoder *)(inUserData);
118+
UInt32 requestedPackets = *ioNumberDataPackets;
119+
120+
// *ioNumberDataPackets = (int)encoder.pcmBufferSize/2;
121+
size_t copiedSamples = [encoder copyPCMSamplesIntoBuffer:ioData];
122+
if (copiedSamples < requestedPackets) {
123+
//PCM 缓冲区还没满
124+
*ioNumberDataPackets = 0;
125+
return -1;
126+
}
127+
*ioNumberDataPackets = 1;
128+
return noErr;
129+
}
130+
131+
/**
132+
* 填充PCM到缓冲区
133+
*/
134+
- (size_t) copyPCMSamplesIntoBuffer:(AudioBufferList*)ioData {
135+
size_t originalBufferSize = _pcmBufferSize;
136+
if (!originalBufferSize) {
137+
return 0;
138+
}
139+
ioData->mBuffers[0].mData = _pcmBuffer;
140+
ioData->mBuffers[0].mDataByteSize = (int)_pcmBufferSize;
141+
_pcmBuffer = NULL;
142+
_pcmBufferSize = 0;
143+
return originalBufferSize;
144+
}
145+
146+
147+
- (void) encodeSampleBuffer:(CMSampleBufferRef)sampleBuffer completionBlock:(void (^)(NSData * encodedData, NSError* error))completionBlock {
148+
CFRetain(sampleBuffer);
149+
dispatch_async(_encoderQueue, ^{
150+
if (!_audioConverter) {
151+
[self setupEncoderFromSampleBuffer:sampleBuffer];
152+
}
153+
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
154+
CFRetain(blockBuffer);
155+
OSStatus status = CMBlockBufferGetDataPointer(blockBuffer, 0, NULL, &_pcmBufferSize, &_pcmBuffer);
156+
NSError *error = nil;
157+
if (status != kCMBlockBufferNoErr) {
158+
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
159+
}
160+
memset(_aacBuffer, 0, _aacBufferSize);
161+
162+
AudioBufferList outAudioBufferList = {0};
163+
outAudioBufferList.mNumberBuffers = 1;
164+
outAudioBufferList.mBuffers[0].mNumberChannels = 1;
165+
outAudioBufferList.mBuffers[0].mDataByteSize = (int)_aacBufferSize;
166+
outAudioBufferList.mBuffers[0].mData = _aacBuffer;
167+
AudioStreamPacketDescription *outPacketDescription = NULL;
168+
UInt32 ioOutputDataPacketSize = 1;
169+
// Converts data supplied by an input callback function, supporting non-interleaved and packetized formats.
170+
// Produces a buffer list of output data from an AudioConverter. The supplied input callback function is called whenever necessary.
171+
status = AudioConverterFillComplexBuffer(_audioConverter, inInputDataProc, (__bridge void *)(self), &ioOutputDataPacketSize, &outAudioBufferList, outPacketDescription);
172+
NSData *data = nil;
173+
if (status == 0) {
174+
NSData *rawAAC = [NSData dataWithBytes:outAudioBufferList.mBuffers[0].mData length:outAudioBufferList.mBuffers[0].mDataByteSize];
175+
NSData *adtsHeader = [self adtsDataForPacketLength:rawAAC.length];
176+
NSMutableData *fullData = [NSMutableData dataWithData:adtsHeader];
177+
[fullData appendData:rawAAC];
178+
data = fullData;
179+
// data = rawAAC;
180+
} else {
181+
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:status userInfo:nil];
182+
}
183+
if (completionBlock) {
184+
dispatch_async(_callbackQueue, ^{
185+
completionBlock(data, error);
186+
});
187+
}
188+
CFRelease(sampleBuffer);
189+
CFRelease(blockBuffer);
190+
});
191+
}
192+
193+
194+
195+
#pragma mark - HEADER
196+
- (NSData*) adtsDataForPacketLength:(NSUInteger)packetLength {
197+
int adtsLength = 7;
198+
char *packet = malloc(sizeof(char) * adtsLength);
199+
// Variables Recycled by addADTStoPacket
200+
int profile = 2; //AAC LC
201+
//39=MediaCodecInfo.CodecProfileLevel.AACObjectELD;
202+
int freqIdx = 4; //44.1KHz
203+
if (_sample_rate == 44100) {
204+
freqIdx = 4;
205+
}else if (_sample_rate == 16000) {
206+
freqIdx = 8;
207+
}else if (_sample_rate == 8000) {
208+
freqIdx = 11;
209+
}
210+
/* 其中,samplingFreguencyIndex 对应关系如下:
211+
0 - 96000
212+
1 - 88200
213+
2 - 64000
214+
3 - 48000
215+
4 - 44100
216+
5 - 32000
217+
6 - 24000
218+
7 - 22050
219+
8 - 16000
220+
9 - 12000
221+
10 - 11025
222+
11 - 8000
223+
12 - 7350
224+
13 - Reserved
225+
14 - Reserved
226+
15 - frequency is written explictly
227+
*/
228+
int chanCfg = 1; //MPEG-4 Audio Channel Configuration. 1 Channel front-center
229+
NSUInteger fullLength = adtsLength + packetLength;
230+
// fill in ADTS data
231+
packet[0] = (char)0xFF; // 11111111 = syncword
232+
packet[1] = (char)0xF9; // 1111 1 00 1 = syncword MPEG-2 Layer CRC
233+
packet[2] = (char)(((profile-1)<<6) + (freqIdx<<2) +(chanCfg>>2));
234+
packet[3] = (char)(((chanCfg&3)<<6) + (fullLength>>11));
235+
packet[4] = (char)((fullLength&0x7FF) >> 3);
236+
packet[5] = (char)(((fullLength&7)<<5) + 0x1F);
237+
packet[6] = (char)0xFC;
238+
NSData *data = [NSData dataWithBytesNoCopy:packet length:adtsLength freeWhenDone:YES];
239+
return data;
240+
}
241+
242+
243+
@end

Source/SDK/LinkVideo/AudioFLV/AWAVCapture.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939

4040
-(void) sendAudioSampleBuffer:(CMSampleBufferRef) sampleBuffer;
4141
-(void) sendAudioPcmData:(NSData *)audioData;
42+
-(void) sendAudioAACData:(NSData *)audioData;
4243
-(void) sendFlvAudioTag:(aw_flv_audio_tag *)flvAudioTag;
4344

4445
@end

0 commit comments

Comments
 (0)