ios 音视频处理优化总结

做了那么多年C底层,现在做iOS上层,感觉还是有很多优势的。做底层的时候就做过音视频的东西,上层有很多类似,相通的东西。

公司做了个视频软件,实时监控。程序丢包严重,卡顿,还会黑屏。我接手之前就是这个样子。经过一天的摸索,结合之前看了一天的经验,把丢包问题,黑屏问题跟卡顿问题通通解决了。

由于代码不是我写的,我想了个很初级的办法,在所有音视频关键函数打上断点(对xcode来说很方便,体力活),在半个小时内,把视频播放的流程走了一遍。

把音视频路线纪录了下来,把重复进入的无关紧要的函数进行严查,严查程序里用到的所有定时器,特别是耗时的定时器,间隔时间又特别短,发现很多不合理的地方。这些定时器都跟UI没有直接关系,却都跑在主线程,非常占用主线程CPU资源。导致卡顿。分别做了线程runloop处理。

然后是发现一个奇葩的做法,就是用定时器刷新缓冲区,这个是数据接收的缓冲区。间隔时间还特别短。在网络传输高分辨率的图片时,必然造成来不及读取缓冲区,就发现图片已经丢失的情况,造成反复丢帧。我毫不犹豫就把定时器干掉了。至于缓冲区是否会越界,溢出的问题,后面再看。后面发现不会,每次接收都是覆盖缓冲区的。还需要时间检验跟调试工具进一步内存跟踪调试。

再一个发现在跳帧处理的时候,时间逻辑有问题,没有实时更新缓存时间。然后就是frame还在进行插入操作的时候就把frame提前释放的问题。

还有就是OPENGL 转换出RGB数据的时候,数据格式的处理,位数跟深度的处理有误,造成像素没那么高的问题。保存*帧图片的时候也有这个问题。

selenium模块

-selenium模块

文章目录
selenium模块
selenium基本概念
基本使用
代码
基于浏览器自动化的操作代码
代码
selenium处理iframe:
代码
selenium模拟登陆QQ空间
代码
无头浏览器和规避检测
代码
selenium模块
selenium基本概念
selenium优势

便捷的获取网站中动态加载的数据
便捷实现模拟登陆
selenium使用流程:

1.环境安装:pip install selenium

2.下载一个浏览器的驱动程序(谷歌浏览器)

3.实例化一个浏览器对象

基本使用
代码
from selenium import webdriver
from lxml import etree
from time import sleep

if __name__ == ‘__main__’:

bro = webdriver.Chrome(r”E:\google\Chrome\Application\chromedriver.exe”)
bro.get(url=’http://scxk.nmpa.gov.cn:81/xk/’)

page_text = bro.page_source
tree = etree.HTML(page_text)
li_list = tree.xpath(‘//*[@id=”gzlist”]/li’)
for li in li_list:
name = li.xpath(‘./dl/@title’)[0]
print(name)
sleep(5)
bro.quit()

基于浏览器自动化的操作代码
#编写基于浏览器自动化的操作代码

– 发起请求: get(url)

– 标签定位: find系列的方法

– 标签交互: send_ keys( ‘xxx’ )

– 执行js程序: excute_script(‘jsCod’)

– 前进,后退: back(),forward( )

– 关闭浏览器: quit()

代码
https://www.taobao.com/

from selenium import webdriver
from time import sleep

bro = webdriver.Chrome(executable_path=r”E:\google\Chrome\Application\chromedriver.exe”)

bro.get(url=’https://www.taobao.com/’)

#标签定位
search_input = bro.find_element_by_id(‘q’)
sleep(2)
#执行一组js代码,使得滚轮向下滑动
bro.execute_script(‘window.scrollTo(0,document.body.scrollHeight)’)
sleep(2)
#标签交互
search_input.send_keys(‘女装’)
button = bro.find_element_by_class_name(‘btn-search’)
button.click()

bro.get(‘https://www.baidu.com’)
sleep(2)
bro.back()
sleep(2)
bro.forward()
sleep(5)
bro.quit()

selenium处理iframe:
– 如果定位的标签存在于iframe标签之中,则必须使用switch_to.frame(id)

– 动作链(拖动) : from selenium. webdriver import ActionChains
– 实例化一个动作链对象: action = ActionChains (bro)
– click_and_hold(div) :长按且点击操作
– move_by_offset(x,y)
– perform( )让动作链立即执行
– action.release( )释放动作链对象

代码
https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable

from selenium import webdriver
from time import sleep
from selenium.webdriver import ActionChains
bro = webdriver.Chrome(executable_path=r”E:\google\Chrome\Application\chromedriver.exe”)

bro.get(‘https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable’)

bro.switch_to.frame(‘iframeResult’)

div = bro.find_element_by_id(‘draggable’)

#动作链
action = ActionChains(bro)
action.click_and_hold(div)

for i in range(5):
action.move_by_offset(17,0).perform()
sleep(0.3)

#释放动作链
action.release()

bro.quit()

selenium模拟登陆QQ空间
代码
https://qzone.qq.com/

from selenium import webdriver
from time import sleep

bro = webdriver.Chrome(executable_path=r”E:\google\Chrome\Application\chromedriver.exe”)
bro.get(‘https://qzone.qq.com/’)
bro.switch_to.frame(“login_frame”)

switcher = bro.find_element_by_id(‘switcher_plogin’)
switcher.click()

user_tag = bro.find_element_by_id(‘u’)
password_tag = bro.find_element_by_id(‘p’)
user_tag.send_keys(‘1234455’)
password_tag.send_keys(‘qwer123’)
sleep(1)

but = bro.find_element_by_id(‘login_button’)
but.click()

无头浏览器和规避检测
代码
from selenium import webdriver
from time import sleep
#实现无可视化界面
from selenium.webdriver.chrome.options import Options
#实现规避检测
from selenium.webdriver import ChromeOptions

#实现无可视化界面
chrome_options = Options()
chrome_options.add_argument(‘–headless’)
chrome_options.add_argument(‘–disable-gpu’)
#实现规避检测
option = ChromeOptions()
option.add_experimental_option(‘excludeSwitches’,[‘enable-automation’])

bro = webdriver.Chrome(executable_path=r”E:\google\Chrome\Application\chromedriver.exe”,chrome_options=chrome_options,options=option)

bro.get(‘https://www.baidu.com’)
print(bro.page_source)
sleep(2)
bro.quit()

 

iOS高级编程 runtime

代码示例:

1 #import <objc/runtime.h>
2 #import <objc/message.h>
3 #import <stdio.h>
4
5 extern int MyUIApplicationMain(int argc, char *argv[], void *principalClassName, void *delegateClassName);
6
7 struct MyRect {
8     float x;
9     float y;
10     float width;
11     float height;
12 };
13 typedef struct MyRect MyRect;
14
15 void *navController;
16 static int numberOfRows = 100;
17
18 int tableView_numberOfRowInSection(void *receiver, struct objc_selector *selector, void *tblView, int section) {
19     return numberOfRows;
20 }
21
22 void *tableView_cellForRowAtIndexPath(void *receiver, struct objc_selector *selector, void *tblView, void *indexPath) {
23     Class TableViewCell = (Class)objc_getClass(“UITableViewCell”);
24     void *cell = class_createInstance(TableViewCell, 0);
25     objc_msgSend(cell, sel_registerName(“init”));
26     char buffer[7];
27
28     int row = (int)objc_msgSend(indexPath, sel_registerName(“row”));
29     sprintf(buffer, “Row %d”, row);
30     void *label = objc_msgSend(objc_getClass(“NSString”), sel_registerName(“stringWithUTF8String:”), buffer);
31     objc_msgSend(cell, sel_registerName(“setText:”), label);
32
33     return cell;
34 }
35
36 void tableView_didSelectRowAtIndexPath(void *receiver, struct objc_selector *seletor, void *tblView, void *indexPath) {
37     Class ViewController = (Class)objc_getClass(“UIViewController”);
38     void *vc = class_createInstance(ViewController, 0);
39     objc_msgSend(vc, sel_registerName(“init”));
40     char buffer[8];
41     int row = (int)objc_msgSend(indexPath, sel_registerName(“row”));
42     sprintf(buffer, “Item %d”, row);
43     void *label = objc_msgSend(objc_getClass(“NSString”), sel_registerName(“stringWithUTF8String”), buffer);
44     objc_msgSend(vc, sel_registerName(“setTitle”), label);
45
46     objc_msgSend(navController, sel_registerName(“pushViewController:animated:”), vc, 1);
47 }
48
49 void *createDataSource() {
50     Class superclass = (Class)objc_getClass(“NSObject”);
51     Class DataSource = objc_allocateClassPair(superclass, “DataSource”, 0);
52     class_addMethod(DataSource, sel_registerName(“tableView:numberOfRowsInSection:”), (void (*))tableView_numberOfRowInSection, nil);
53     class_addMethod(DataSource, sel_registerName(“tableView:cellForRowAtIndexPath:”), (void (*))tableView_cellForRowAtIndexPath, nil);
54
55     objc_registerClassPair(DataSource);
56     return class_createInstance(DataSource, 0);
57 }
58
59 void *createDelegate() {
60     Class superClass = (Class)object_getClass(@”NSObject”);
61     Class DataSource = objc_allocateClassPair(superClass, “Delegate”, 0);
62     class_addMethod(DataSource, sel_registerName(“tableView:didSelectRowAtIndexPath:”), (void (*))tableView_didSelectRowAtIndexPath, nil);
63
64     objc_registerClassPair(DataSource);
65     return class_createInstance(DataSource, 0);
66 }
67
68 //自定义主函数
69 void applicationdidFinishLaunching(void *receiver, struct objc_selector *selector, void *application) {
70     Class windowClass = (Class)object_getClass(@”UIWindow”);
71     void *windowInstance = class_createInstance(windowClass, 0);
72
73     objc_msgSend(windowClass, sel_registerName(“initWithFrame:”), (MyRect){0, 0, 320, 480});
74
75     //make key and visiable
76     objc_msgSend(windowInstance, sel_registerName(“makeKeyAndVisible”));
77
78     //创建表
79     Class TableViewController = (Class)object_getClass(@”UITableViewController”);
80     void *tableViewController = class_createInstance(TableViewController, 0);
81     objc_msgSend(TableViewController, sel_registerName(“init”));
82     void *tableView = objc_msgSend(TableViewController, sel_registerName(“tableView”));
83     objc_msgSend(tableView, sel_registerName(“setDataSource”), createDataSource());
84     objc_msgSend(tableView, sel_registerName(“setDelegate”), createDelegate());
85
86     Class NavController = (Class)object_getClass(@”UINavigationController”);
87     navController = class_createInstance(NavController, 0);
88     objc_msgSend(navController, sel_registerName(“initWithRootViewCOntroller:”), tableViewController);
89     void *view = objc_msgSend(navController, sel_registerName(“view”));
90
91     //add TableView to window
92     objc_msgSend(windowInstance, sel_registerName(“addSubview:”), view);
93
94 }
95
96 //create an class named “AppDelegate”, and return its name as an instance of class NSString
97 void *createAppDelegate() {
98     Class mySubclass = objc_allocateClassPair((Class)object_getClass(@”NSObject”), “AppDelegate”, 0);
99     struct objc_selector *selName = sel_registerName(“application:didFinishLaunchingWithOptions:”);
100     class_addMethod(mySubclass, selName, (void (*))applicationdidFinishLaunching, nil);
101     objc_registerClassPair(mySubclass);
102     return objc_msgSend(object_getClass(@”NNString”), sel_registerName(“stringWithUTF8String:”), “AppDelegate”);
103 }
104
105 int main(int argc, char *argv[]) {
106     return MyUIApplicationMain(argc, argv, 0, createAppDelegate());

给Web中的网页添加Loading进度条形式

前段时间客户提了一个需求,要求给网站中某些功能添加进度条形式,因为某些功能查询的数据量太大,经常会出现点击Search按钮,但是没有任何反应的情况,会让用户以为网站挂掉了,导致投诉的事情发生,所以客户要求必须要添加。

其实不论在网站中,或者是在APP中,添加动画Loading进度条,这都是必须要去做的。我们在浏览网页、玩游戏、或者在玩手机的时候,不可避免会遇到因为网络差或者硬件差等等的原因,而要等待,而对这样的等待,大多数人都是没有耐心去等的,这时如何可以出现一个动画Loading进度条,就可以大大缓解等待时的烦躁情绪,让用户可以明确、直观的看到Loading的进度。

常见的Loading样式有很多,比如:直线进度条、圆形进度条、旋转进度条、Logo描边形式的进度条、奔跑的形式等等。

我此次添加的Loading样式,是比较常见的旋转Loading样式,效果如下:

%title插图%num%title插图%num

 

(图1:是我自己做的,网上的图片颜色不太符合我的需求

图2:在网站上实际显示的效果,除了Loading样式,还有一个遮盖的效果)

 

具体实现的方式:

前台代码:

 1 //遮盖层
 2 <div id="divbase" class="ui-widget-overlay" style="z-index: 997; display: none;">
 3     <iframe class="ui-widget-overlay" runat="server" id="iFameBase" style="z-index: 998;
 4         display: none;">
 5     </iframe>
 6 </div>
 7 //Loading部分
 8 <div runat="server" id="divSK" style="position:absolute; left:36%; top:30%; width:30%; height:30%; z-index:999;display:none;">
 9     <img runat="server" id="imgLoading" src="../Images/gif/CirclePoint.gif" />
10 </div>

 

CSS样式:

 1 /*
 2 弹出小视窗下层的div,作遮盖showdialog的效果,设定背景色,给透明度,可以看到主画面的内容.
 3 */
 4 .ui-widget-overlay
 5 {
 6     position: absolute;
 7     top: 0;
 8     left: 0;
 9     width: 100%;
10     height: 100%;
11     background-color: Gray;
12     opacity: .30;
13     filter: Alpha(Opacity=30);
14 }

 

后台处理(包含JS部分):

 1 //当点击按钮的时候(JS实现)
 2 $(function() {  
 3      $("#btnQuery").click(function() { 
 4         $("#base").show();
 5         document.getElementById("divSK").style.display = "";
 6     });
 7 });
 8 
 9 //当按钮事件处理完毕后(后台处理)
10 this.Page.ClientScript.RegisterStartupScript(this.GetType(), "DisplayDiv", "funDisplayDiv();", true);
11 
12 //funDisplayDiv()(JS实现)
13 function funDisplayDiv(){
14     $("#base").hide();
15     document.getElementById("divSK").style.display = "none"; 
16 }

 

以上是我本次添加实现的Loading样式,当然还会有其它更多的实现样式,后续有时间再继续整理。

iOS 实时录音和播放

需求:*近公司需要做一个楼宇对讲的功能:门口机(连接WIFI)拨号对室内机(对应的WIFI)的设备进行呼叫,室内机收到呼叫之后将对收到的数据进行UDP广播的转发,手机(连接对应的WIFI)收到视频流之后,实时的展示视频数据(手机可以接听,挂断,手机接听之后,室内机不展示视频,只是进行转发。)

简单点说就是手机客户端需要做一个类似于直播平台的软件,可以实时的展示视频,实时的播放接收到的声音数据,并且实时将手机麦克风收到的声音回传给室内机,室内机负责转发给门口机。

 

这篇文章介绍iOS怎么进行实时的录音和播放收到的声音数据

 

想要使用系统的框架实时播放声音和录音数据,就得知道音频队列服务,

在AudioToolbox框架中的音频队列服务,它完全可以做到音频播放和录制,

一个音频服务队列有三个部分组成:

1.三个缓冲器Buffers:每个缓冲器都是一个存储音频数据的临时仓库。
2.一个缓冲队列Buffer Queue:一个包含音频缓冲器的有序队列。
3.一个回调CallBack:一个自定义的队列回调函数。

具体怎么运转的还是百度吧!

我的简单理解:

对于播放:系统会自动从缓冲队列中循环取出每个缓冲器中的数据进行播放,我们需要做的就是将接收到的数据循环的放到缓冲器中,剩下的就交给系统去实现了。

对于录音:  系统会自动将录的声音放入队列中的每个缓冲器中,我们需要做的就是从回调函数中将数据转化我们自己的数据就OK了。

 

#pragma mark–实时播放

1. 导入系统框架AudioToolbox.framework  AVFoundation.framework

2. 获取麦克风权限,在工程的Info.plist文件中加入Privacy – Microphone Usage Description 这个key 描述:App想要访问您的麦克风

3. 创建播放声音的类 EYAudio

 

EYAudio.h

#import <Foundation/Foundation.h>

@interface EYAudio : NSObject

// 播放的数据流数据
– (void)playWithData:(NSData *)data;

// 声音播放出现问题的时候可以重置一下
– (void)resetPlay;

// 停止播放
– (void)stop;

@end

 

EYAudio.m

 

#import “EYAudio.h”
#import <AVFoundation/AVFoundation.h>
#import <AudioToolbox/AudioToolbox.h>

#define MIN_SIZE_PER_FRAME 1920 //每个包的大小,室内机要求为960,具体看下面的配置信息
#define QUEUE_BUFFER_SIZE 3 //缓冲器个数
#define SAMPLE_RATE 16000 //采样频率

@interface EYAudio(){
AudioQueueRef audioQueue; //音频播放队列
AudioStreamBasicDescription _audioDescription;
AudioQueueBufferRef audioQueueBuffers[QUEUE_BUFFER_SIZE]; //音频缓存
BOOL audioQueueBufferUsed[QUEUE_BUFFER_SIZE]; //判断音频缓存是否在使用
NSLock *sysnLock;
NSMutableData *tempData;
OSStatus osState;
}
@end

@implementation EYAudio

#pragma mark – 提前设置AVAudioSessionCategoryMultiRoute 播放和录音
+ (void)initialize
{
NSError *error = nil;
//只想要播放:AVAudioSessionCategoryPlayback
//只想要录音:AVAudioSessionCategoryRecord
//想要”播放和录音”同时进行 必须设置为:AVAudioSessionCategoryMultiRoute 而不是AVAudioSessionCategoryPlayAndRecord(设置这个不好使)
BOOL ret = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryMultiRoute error:&error];
if (!ret) {
NSLog(@”设置声音环境失败”);
return;
}
//启用audio session
ret = [[AVAudioSession sharedInstance] setActive:YES error:&error];
if (!ret)
{
NSLog(@”启动失败”);
return;
}
}

– (void)resetPlay
{
if (audioQueue != nil) {
AudioQueueReset(audioQueue);
}
}

– (void)stop
{
if (audioQueue != nil) {
AudioQueueStop(audioQueue,true);
}

audioQueue = nil;
sysnLock = nil;
}

– (instancetype)init
{
self = [super init];
if (self) {
sysnLock = [[NSLock alloc]init];

//设置音频参数 具体的信息需要问后台
_audioDescription.mSampleRate = SAMPLE_RATE;
_audioDescription.mFormatID = kAudioFormatLinearPCM;
_audioDescription.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
//1单声道
_audioDescription.mChannelsPerFrame = 1;
//每一个packet一侦数据,每个数据包下的桢数,即每个数据包里面有多少桢
_audioDescription.mFramesPerPacket = 1;
//每个采样点16bit量化 语音每采样点占用位数
_audioDescription.mBitsPerChannel = 16;
_audioDescription.mBytesPerFrame = (_audioDescription.mBitsPerChannel / 8) * _audioDescription.mChannelsPerFrame;
//每个数据包的bytes总数,每桢的bytes数*每个数据包的桢数
_audioDescription.mBytesPerPacket = _audioDescription.mBytesPerFrame * _audioDescription.mFramesPerPacket;

// 使用player的内部线程播放 新建输出
AudioQueueNewOutput(&_audioDescription, AudioPlayerAQInputCallback, (__bridge void * _Nullable)(self), nil, 0, 0, &audioQueue);

// 设置音量
AudioQueueSetParameter(audioQueue, kAudioQueueParam_Volume, 1.0);

// 初始化需要的缓冲区
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
audioQueueBufferUsed[i] = false;
osState = AudioQueueAllocateBuffer(audioQueue, MIN_SIZE_PER_FRAME, &audioQueueBuffers[i]);
}

osState = AudioQueueStart(audioQueue, NULL);
if (osState != noErr) {
NSLog(@”AudioQueueStart Error”);
}
}
return self;
}

// 播放数据
-(void)playWithData:(NSData *)data
{
[sysnLock lock];

tempData = [NSMutableData new];
[tempData appendData: data];
NSUInteger len = tempData.length;
Byte *bytes = (Byte*)malloc(len);
[tempData getBytes:bytes length: len];

int i = 0;
while (true) {
if (!audioQueueBufferUsed[i]) {
audioQueueBufferUsed[i] = true;
break;
}else {
i++;
if (i >= QUEUE_BUFFER_SIZE) {
i = 0;
}
}
}

audioQueueBuffers[i] -> mAudioDataByteSize = (unsigned int)len;
// 把bytes的头地址开始的len字节给mAudioData,向第i个缓冲器
memcpy(audioQueueBuffers[i] -> mAudioData, bytes, len);

// 释放对象
free(bytes);

//将第i个缓冲器放到队列中,剩下的都交给系统了
AudioQueueEnqueueBuffer(audioQueue, audioQueueBuffers[i], 0, NULL);

[sysnLock unlock];
}

// ************************** 回调 **********************************
// 回调回来把buffer状态设为未使用
static void AudioPlayerAQInputCallback(void* inUserData,AudioQueueRef audioQueueRef, AudioQueueBufferRef audioQueueBufferRef) {

EYAudio* audio = (__bridge EYAudio*)inUserData;

;
}

– (void)resetBufferState:(AudioQueueRef)audioQueueRef and:(AudioQueueBufferRef)audioQueueBufferRef {
// 防止空数据让audioqueue后续都不播放,为了安全防护一下
if (tempData.length == 0) {
audioQueueBufferRef->mAudioDataByteSize = 1;
Byte* byte = audioQueueBufferRef->mAudioData;
byte = 0;
AudioQueueEnqueueBuffer(audioQueueRef, audioQueueBufferRef, 0, NULL);
}

for (int i = 0; i < QUEUE_BUFFER_SIZE; i++) {
// 将这个buffer设为未使用
if (audioQueueBufferRef == audioQueueBuffers[i]) {
audioQueueBufferUsed[i] = false;
}
}
}

@end

 

 

外界使用: 不断调用下面的方法将NSData传递进来

– (void)playWithData:(NSData *)data;

 

#pragma mark–实时录音

 

1. 导入系统框架AudioToolbox.framework  AVFoundation.framework

2. 创建录音的类 EYRecord

 

EYRecord.h

 

#import <Foundation/Foundation.h>

@interface ESARecord : NSObject

//开始录音
– (void)startRecording;

//停止录音
– (void)stopRecording;

@end

 

EYRecord.m

 

#import “ESARecord.h”
#import <AudioToolbox/AudioToolbox.h>

#define QUEUE_BUFFER_SIZE 3 // 输出音频队列缓冲个数
#define kDefaultBufferDurationSeconds 0.03//调整这个值使得录音的缓冲区大小为960,实际会小于或等于960,需要处理小于960的情况
#define kDefaultSampleRate 16000 //定义采样率为16000

extern NSString * const ESAIntercomNotifationRecordString;

static BOOL isRecording = NO;

@interface ESARecord(){
AudioQueueRef _audioQueue; //输出音频播放队列
AudioStreamBasicDescription _recordFormat;
AudioQueueBufferRef _audioBuffers[QUEUE_BUFFER_SIZE]; //输出音频缓存
}
@property (nonatomic, assign) BOOL isRecording;

@end

@implementation ESARecord

– (instancetype)init
{
self = [super init];
if (self) {
//重置下
memset(&_recordFormat, 0, sizeof(_recordFormat));
_recordFormat.mSampleRate = kDefaultSampleRate;
_recordFormat.mChannelsPerFrame = 1;
_recordFormat.mFormatID = kAudioFormatLinearPCM;

_recordFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
_recordFormat.mBitsPerChannel = 16;
_recordFormat.mBytesPerPacket = _recordFormat.mBytesPerFrame = (_recordFormat.mBitsPerChannel / 8) * _recordFormat.mChannelsPerFrame;
_recordFormat.mFramesPerPacket = 1;

//初始化音频输入队列
AudioQueueNewInput(&_recordFormat, inputBufferHandler, (__bridge void *)(self), NULL, NULL, 0, &_audioQueue);

//计算估算的缓存区大小
int frames = (int)ceil(kDefaultBufferDurationSeconds * _recordFormat.mSampleRate);
int bufferByteSize = frames * _recordFormat.mBytesPerFrame;

NSLog(@”缓存区大小%d”,bufferByteSize);

//创建缓冲器
for (int i = 0; i < QUEUE_BUFFER_SIZE; i++){
AudioQueueAllocateBuffer(_audioQueue, bufferByteSize, &_audioBuffers[i]);
AudioQueueEnqueueBuffer(_audioQueue, _audioBuffers[i], 0, NULL);
}
}
return self;
}

-(void)startRecording
{
// 开始录音
AudioQueueStart(_audioQueue, NULL);
isRecording = YES;
}

void inputBufferHandler(void *inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer, const AudioTimeStamp *inStartTime,UInt32 inNumPackets, const AudioStreamPacketDescription *inPacketDesc)
{
if (inNumPackets > 0) {
ESARecord *recorder = (__bridge ESARecord*)inUserData;
[recorder processAudioBuffer:inBuffer withQueue:inAQ];
}

if (isRecording) {
AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
}
}

– (void)processAudioBuffer:(AudioQueueBufferRef )audioQueueBufferRef withQueue:(AudioQueueRef )audioQueueRef
{
NSMutableData * dataM = [NSMutableData dataWithBytes:audioQueueBufferRef->mAudioData length:audioQueueBufferRef->mAudioDataByteSize];

if (dataM.length < 960) { //处理长度小于960的情况,此处是补00
Byte byte[] = {0x00};
NSData * zeroData = [[NSData alloc] initWithBytes:byte length:1];
for (NSUInteger i = dataM.length; i < 960; i++) {
[dataM appendData:zeroData];
}
}

// NSLog(@”实时录音的数据–%@”, dataM);
//此处是发通知将dataM 传递出去
[[NSNotificationCenter defaultCenter] postNotificationName:@”EYRecordNotifacation” object:@{@”data” : dataM}];
}

-(void)stopRecording
{
if (isRecording)
{
isRecording = NO;

//停止录音队列和移除缓冲区,以及关闭session,这里无需考虑成功与否
AudioQueueStop(_audioQueue, true);
//移除缓冲区,true代表立即结束录制,false代表将缓冲区处理完再结束
AudioQueueDispose(_audioQueue, true);
}
NSLog(@”停止录音”);
}

@end

python3整数反转

python3整数反转

python3整数反转
给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。

如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0。

假设环境不允许存储 64 位整数(有符号或无符号)。

示例 1:

输入:x = 123
输出:321
示例 2:

输入:x = -123
输出:-321
示例 3:

输入:x = 120
输出:21
示例 4:

输入:x = 0
输出:0

思路1:将其转为字符串进行翻转,并进行正负的判断。*后,题目要求如果反转后整数超过 32 位的有符号整数的范围 [−2^31, 2^31 − 1] ,就返回 0

class Solution:
def reverse(self, x: int) -> int:
str1 = str(x)

if str1[0] == ‘-‘:
str1 = str1[0] + str1[:0:-1]
else:
str1 = str1[::-1]
return int(str1) if -2147483648<int(str1)<2147483648 else 0

思路2:不使用字符串。当翻转后的数字大于条件就返回0

class Solution:
def reverse(self, x: int) -> int:
y, res = abs(x), 0
# 则其数值范围为 [−2^31, 2^31 − 1]
boundry = (1<<31) -1 if x>0 else 1<<31
while y != 0:
res = res*10 +y%10
if res > boundry :
return 0
y //=10
return res if x >0 else -res

改进:

class Solution:
def reverse(self, x: int) -> int:
str1 = str(x)

if str1[0] == ‘-‘:
str1 = str1[0] + str1[:0:-1]
a=int(str1)
if (1<<31)<abs(a):
return 0
else:
str1 = str1[::-1]
a= int(str1)
if a>(1<<31) -1:
return 0
return a

python3合并两个有序列表 and 加一

python3合并两个有序列表 and 加一

21.合并两个有序列表点此做题
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

%title插图%num

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:

输入:l1 = [], l2 = []
输出:[]
示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

常规思路:

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
new_list = ListNode(-1,None)
head = new_list
while l1 and l2:
if l1.val<l2.val:
new_list.next = l1
new_list=new_list.next
l1=l1.next
else:
new_list.next = l2
new_list=new_list.next
l2=l2.next
if l1:
new_list.next = l1
if l2:
new_list.next = l2
return head.next

递归思路

if not l1: return l2 # 终止条件,直到两个链表都空
if not l2: return l1
if l1.val <= l2.val: # 递归调用
l1.next = self.mergeTwoLists(l1.next,l2)
return l1
else:
l2.next = self.mergeTwoLists(l1,l2.next)
return l2

加一
给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
*高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入:digits = [1,2,3]
输出:[1,2,4]
解释:输入数组表示数字 123。
示例 2:

输入:digits = [4,3,2,1]
输出:[4,3,2,2]
解释:输入数组表示数字 4321。
示例 3:

输入:digits = [0]
输出:[1]

思路1:字符串和整型相互转然后+1,思路简单,但是耗用内存

%title插图%num

class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
str1 = ”
lst = []
for i in digits:
str1 += str(i)
str1 = str(int(str1)+1)
for i in str1:
lst.append(int(i))
return lst

思路2:直接操作;将*后一位加一,然后判断是否需要进位,以及考虑*坏情况

class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
addone = 0
digits[-1] += 1
for i in range(len(digits)-1,-1,-1):
if addone == 1:
digits[i] += addone
addone = 0
if digits[i] == 10:
digits[i] = 0
addone = 1
else:
return digits
if digits[0] == 0:
digits.insert(0, 1)
return digits

思路3:

newlst = []
while digits and digits[-1] == 9:
digits.pop()
newlst.append(0)
if not digits:
return [1] + newlst
else:
digits[-1] += 1
return digits + newlst

%title插图%num

基于iOS的网络音视频实时传输系统(一)- 前言

演示图

(由于GIF大小限制,图做得比较模糊和不太流畅,实际的效果是不错的,可以自己下下来实际跑一下)

client端:

%title插图%num

%title插图%num
server端:

%title插图%num

%title插图%num

下载

GitHub:

client 端:https://github.com/AmoAmoAmo/Smart_Device_Client

server端:https://github.com/AmoAmoAmo/Smart_Device_Server

另还写了一份macOS版的server,但是目前还有一些问题,有兴趣的去看看吧:https://github.com/AmoAmoAmo/Server_Mac

简介

之前在做类似的网络协议的时候,突发奇想,想写一个网络视频监控,基于局域网的情况下,将MacBook摄像头捕获到的视频,在手机端显示,但是由于对macOS不是很熟悉,*终导致该计划流产。所以后来干脆使用手机捕获视频数据。

为了简化项目工作量,socket协议也只用到了一些必要的功能,其他细节如client端退出监控视频时,server端会crash,各位有需要可以自行去添加一些如设置select()函数,或者设置signal()函数忽略这个断开的信号。等等

项目中没有写录制设备视频的功能,所以没有用到MP4封装
更多其他的细节已经搭建过程,有兴趣的可以去我的GitHub上回退到各个版本看循序渐进的过程。关于音视频我也是初学者,欢迎各位斧正。

主要功能

client端:

1. udp局域网搜索设备(模拟设备的代码在这里),或者手动添加其他设备(并没有功能) 到plist
2. 点击已添加的监控设备,开始TCP音视频数据传输
3. 接收到音视频数据,进行解码,并用OpenGL es渲染显示到界面上 或openAL播放音频
4. 横竖屏功能

server端(摄像头):

1. 点击“reset”,进入“配对模式”,即开始UDP监听AP
2. 连接成功后,将自己的设备信息发送给client
3. 开始捕获音视频,并进行硬编码,发送给client

使用方法

*次使用时,先点击“server”端的“reset”按钮,表示开始处于配对模式,

“client”端点击“+”按钮,开始去局域网里搜索(UDP)设备,就会得到设备的IP地址等信息,后面音视频传输就是用这个IP地址来建立TCP通信的。这个过程模拟实际应用中

这里音视频传输用TCP还是UDP没有什么讲究,主要是根据嵌入式工程师给的音视频协议文档里用到的是哪个。

这里就是用“server”端来模拟一种型号的监控摄像头。

找到设备后,信息会自动被填写到textField上,点击“添加”,设备就被写入plist文件里。

点击已添加的监控视频,即可开始音视频传输

阿里云正式推出相册和网盘

xuanbg · 181 天前 · 7805 次点击
这是一个创建于 181 天前的主题,其中的信息可能已经有所发展或是发生改变。
还提供接口,开发者可以按自己的需求来定制自己的网盘

网盘 相册 阿里云 开发者43 条回复 • 2020-10-12 16:16:28 +08:00
iConnect 1
iConnect 181 天前 via Android
这个主要是企业内部文件共享用途,做 2c 服务,流量 5 毛 1g,这那个用户会付款
SenLief 2
SenLief 181 天前
这不就是整合了 oos 啊,流量很贵。
mangogeek 3
mangogeek 181 天前
1G5 毛,这是想钱想疯了
xuecan 4
xuecan 181 天前
链接在哪
xlogcc 5
xlogcc 181 天前
还没开通啊…….
xinyana 6
xinyana 181 天前 via Android ❤️ 6
我的[zhao.pp.ua]每天 4T 流量,算了下,大概每年 70 万 rmb
whileFalse 7
whileFalse 181 天前 via iPhone
@xinyana 所以哥们你这个现在是怎么扛 4t 流量的
user8341 8
user8341 181 天前
@whileFalse 谷歌网盘免费?
geekvcn 9
geekvcn 181 天前 ❤️ 8
阿里的网盘服务也敢用?百度虽然流氓但是非小姐姐正常数据还是有保证的,UC 网盘就是阿里旗下的说关就关…….国内免费网盘靠谱的只有微云和百度,纵使你们再不喜欢,也不要用更坑爹的阿里系网盘
AkideLiu 10
AkideLiu 181 天前 via iPhone
@xinyana 同问怎么抗住

matrix67 11
matrix67 181 天前
@whileFalse
@AkideLiu

原理在这儿 https://github.com/gdtool/zhaopp/blob/master/README.md#%E8%B4%A1%E7%8C%AE%E8%B5%84%E6%BA%90

试了下,还这能下,厉害了。
x86 12
x86 181 天前 via iPhone
吃过酷盘的亏懂得都懂
gricn 13
gricn 181 天前 via Android
@geekvcn 微云是新浪旗下的吗,好像以前也关过。百度云限速但的确比较稳。现在的天翼云如何?
cmdOptionKana 14
cmdOptionKana 181 天前
@xinyana 人才啊
sagaxu 15
sagaxu 181 天前 via Android
@xinyana 每天有 4T 流量,每周都不止赚 70 万了
witfun 16
witfun 181 天前 via Android
@gricn #13
微云是腾讯旗下的,https://www.weiyun.com/
较之百度好一点点

天翼云依旧爽的很,基本不限速,日流量基本够用(我开了黄金会员)
![Screenshot]( https://kyun.ltyuanfang.cn/tc/2020/10/11/be5ef15a3fbb5.jpg)
JavaIO 17
JavaIO 181 天前
目前只用免费的
Oysmart 18
Oysmart 181 天前 ❤️ 2
阿里商人本质的尿性,我就看看,你们用吧。
weitch 19
weitch 181 天前 ❤️ 4
@geekvcn #9
嘿嘿,阿哩价值观是很正的,当它觉得你不符合价值观时,就会主动帮你纠正(比如帮你删除软件什么的,避免你受到侵害),当它觉得产品不符合价值观时,不管你有多少人在用,它都会直接关闭。
shm7 20
shm7 181 天前
google photo 不是免费吗?
xinyana 21
xinyana 181 天前 via Android
@whileFalse re:所以哥们你这个现在是怎么扛 4t 流量的
用爱,快扛不住了
myqoo 22
myqoo 181 天前
轻量云不是可以优化到 8 块多 1TB 吗
binxin 23
binxin 181 天前
@geekvcn
微云毁约过~
wtks1 24
wtks1 180 天前 via Android
@gricn 天翼云看着不错,但同属运营商的沃家云盘已经关闭主页离死不远了….很难相信能一直运营下去
love 25
love 180 天前
@xinyana 卧槽牛 b 。不过搜索功能一泡污,搜个番号出来的全是不相关的,自己不用的吗
jousca 26
jousca 180 天前
@geekvcn 腾讯系有时候比阿里还更靠谱。你看 QQ 空间多少年了,我十几年前的照片都在里面。哈哈
jousca 27
jousca 180 天前
@wtks1 中移动和彩云网盘你了解一下。中移动不垮它就不垮
Jianrry 28
Jianrry 180 天前 via Android
115 网盘值得拥有,懂的都懂
annielong 29
annielong 180 天前
阿里系真不敢用,死在它手里几个网盘了?
GuangXiN 30
GuangXiN 180 天前 via Android
@shm7 Google 的服务得爬墙
xunco 31
xunco 180 天前 via Android
@GuangXiN 但它值得
mahaonan1994 32
mahaonan1994 180 天前
@whileFalse G suit 要涨价的原因找到了
irytu 33
irytu 180 天前
@xinyana 很厉害 https://zhao.pp.ua/?q=fc2
xingzw 34
xingzw 180 天前
不缺大客户,钱挣的嗷嗷的。
seakingii 35
seakingii 180 天前
不喜欢阿里系的互联网服务,以及该公司的开源项目。
xuanbg 36
xuanbg 180 天前
toB 啊。。。兄弟们,这个市场空间相当的大啊。有很多做加密网盘的企业,现在你不需要有自己的服务器也能做了。
lankaka 37
lankaka 180 天前
@gricn 新浪旗下的是微盘
Felix2Yu 38
Felix2Yu 180 天前
这个是面向企业 /开发者的相册,一半人上来就喷来类比网盘……
tony1890 39
tony1890 180 天前
把数据放别人的篮子里,怎么都不放心。云有云的好处,但安全性(非机器故障)是无法保障的。
boluo 40
boluo 180 天前
我已经开始用和彩云了,20 元 1 年会员+免流,备份手机挺合适
Rioad 41
Rioad 180 天前
地址呢 只知道阿里出网盘了,还没内测资格
hushao 42
hushao 180 天前
看到了 LZ 说的,应该是这个:
相册与网盘服务
相册与网盘服务( Photo and Drive Service )是为开发者提供的面向企业与个人数据管理,内容识别与协作的开放平台。
<https://help.aliyun.com/product/175790.html>)

并不是之前说的正在内测的阿里云网盘(<https://pan.aliyun.com>)
dianxinyonghu 43
dianxinyonghu 179 天前
流量 0.5 元 /GB,不便宜啊

iOS开发——APP回退到历史版本

1.软件准备

  • 【必备】Charles

2.正式开始

2.1打开Charles青花瓷
%title插图%num

2.2安装证书以便能够拦截解析HTTPS请求数据
%title插图%num

2.2.1如果是选择本地安装证书的话,记住保存格式选择.cer
%title插图%num

2.2.2双击下载好的证书
%title插图%num

2.2.3右键点击证书
%title插图%num

2.2.4信任证书
%title插图%num

2.2.5 ssl代理证书配置完成
%title插图%num

2.3打开iTunes
%title插图%num

2.4单击编辑左上角以便能够出现应用这个选项
%title插图%num

2.5在下拉选项中选择应用
%title插图%num

2.6单击正上方的App Store
%title插图%num

2.7搜索需要下载历史版本的App,这里我以IT之家为例
%title插图%num

2.8点击获取按钮,就会变成正在下载
%title插图%num

2.9这时候查看Charles能够看到一大堆请求,看关键字p28-buy,这个是Itunes的下载请求,而且是HTTPS加密的,链接旁边有一把小锁,不能查看里面的内容
%title插图%num

2.10打开ssl代理并打断点拦截该请求
%title插图%num

2.11进入iTunes删除刚才下载的App,因为刚才只为为了获取下载请求
%title插图%num

2.12删除好App后刷新一下,重启Charles,再次点击iTunes的获取按钮
%title插图%num

2.13可以看到这时候已经能够查看HTTPS请求里面的内容了,点击图中相应位置
%title插图%num

2.14往下翻,可以查看到一大串数字,这些全都是该应用的历史版本编号,我们只需要把想下载的历史版本的编号复制下来就好,后面会用到,这里我复制*个11577427,为该应用程序的*个版本
%title插图%num

2.15再次点击获取按钮,下载请求会被拦截,跳转到Charles
%title插图%num

2.16这时候把刚才我们复制好的历史版本号
%title插图%num

2.17点击Excute执行按钮,可能会来断点2次,除了*次,后面的都直接点击Excute执行按钮
%title插图%num

2.18大功告成!!!
%title插图%num

2.19查看下载好的App的版本号
%title插图%num

%title插图%num

%title插图%num

3.安装旧版本应用程序

旧版本的应用程序下载好了,接下来我们要安装到手机上才是王道,或者进行其他用途,这里介绍下2种安装到iPhone上的方法。

  • 1.利用iTunes的安装
    手机链接电脑,iTunes会自动弹出,然后会进行同步,刚才电脑上下载好的应用会自动安装到iPhone上。
  • 2.利用iTools的安装
%title插图%num
友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速