最近開發(fā)中遇到一個(gè)需求刨摩,就是想微信那樣錄制一個(gè)小視頻寺晌,然后在錄制視頻的圖層上播放,然后發(fā)布到朋友圈码邻,無聲播放折剃,但有滾動(dòng)起來不影響性能。一開始接到這個(gè)需求的時(shí)候我是很興奮的像屋,可以好好研究一番 AVFoundation 的東西了。但是在研究中不斷的高潮迭起边篮,也是讓我心力交瘁呀己莺。但是奏甫,做程序猿的成長(zhǎng)就是這樣的嘛。題外話了凌受,好了阵子,今天我們就說一下怎么用AVCaptureSession+AVCaptureMovieFileOutput來錄制視頻,并通過AVAssetExportSeeion手段來壓縮視頻并轉(zhuǎn)換為 MP4 格式
一開始我們要了解一下 AVFoundation 做視頻的類應(yīng)該有那些胜蛉,并且他們有什么用呢挠进?
AVCaptureSession
AVCaptureSession:媒體(音、視頻)捕獲會(huì)話誊册,負(fù)責(zé)把捕獲的音視頻數(shù)據(jù)輸出到輸出設(shè)備中领突。一個(gè)AVCaptureSession可以有多個(gè)輸入輸出。AVCaptureDevice:輸入設(shè)備案怯,包括麥克風(fēng)君旦、攝像頭,通過該對(duì)象可以設(shè)置物理設(shè)備的一些屬性(例如相機(jī)聚焦嘲碱、白平衡等)金砍。AVCaptureDeviceInput:設(shè)備輸入數(shù)據(jù)管理對(duì)象,可以根據(jù)AVCaptureDevice創(chuàng)建對(duì)應(yīng)的AVCaptureDeviceInput對(duì)象麦锯,該對(duì)象將會(huì)被添加到AVCaptureSession中管理恕稠。AVCaptureVideoPreviewLayer:相機(jī)拍攝預(yù)覽圖層,是CALayer的子類扶欣,使用該對(duì)象可以實(shí)時(shí)查看拍照或視頻錄制效果谱俭,創(chuàng)建該對(duì)象需要指定對(duì)應(yīng)的AVCaptureSession對(duì)象。AVCaptureOutput:輸出數(shù)據(jù)管理對(duì)象宵蛀,用于接收各類輸出數(shù)據(jù)昆著,通常使用對(duì)應(yīng)的子類AVCaptureAudioDataOutput、AVCaptureStillImageOutput术陶、AVCaptureVideoDataOutput凑懂、AVCaptureFileOutput, 該對(duì)象將會(huì)被添加到AVCaptureSession中管理。 注意:前面幾個(gè)對(duì)象的輸出數(shù)據(jù)都是NSData類型梧宫,而AVCaptureFileOutput代表數(shù)據(jù)以文件形式輸出接谨,類似的,AVCcaptureFileOutput也不會(huì)直接創(chuàng)建使用塘匣,通常會(huì)使用其子類:AVCaptureAudioFileOutput脓豪、AVCaptureMovieFileOutput。當(dāng)把一個(gè)輸入或者輸出添加到AVCaptureSession之后AVCaptureSession就會(huì)在所有相符的輸入忌卤、輸出設(shè)備之間 建立連接(AVCaptionConnection)扫夜。
那么建立視頻拍攝的步驟如下 :
1.創(chuàng)建AVCaptureSession對(duì)象。
// 創(chuàng)建會(huì)話 (AVCaptureSession) 對(duì)象。_captureSession = [[AVCaptureSessionalloc] init];if([_captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {// 設(shè)置會(huì)話的 sessionPreset 屬性, 這個(gè)屬性影響視頻的分辨率[_captureSession setSessionPreset:AVCaptureSessionPreset640x480];}
2.使用AVCaptureDevice的靜態(tài)方法獲得需要使用的設(shè)備笤闯,例如拍照和錄像就需要獲得攝像頭設(shè)備堕阔,錄音就要獲得麥克風(fēng)設(shè)備。
// 獲取攝像頭輸入設(shè)備颗味, 創(chuàng)建 AVCaptureDeviceInput 對(duì)象// 在獲取攝像頭的時(shí)候超陆,攝像頭分為前后攝像頭,我們創(chuàng)建了一個(gè)方法通過用攝像頭的位置來獲取攝像頭AVCaptureDevice*videoCaptureDevice = [selfgetCameraDeviceWithPosition:AVCaptureDevicePositionBack];if(!captureDevice) {NSLog(@"---- 取得后置攝像頭時(shí)出現(xiàn)問題---- ");return;}// 添加一個(gè)音頻輸入設(shè)備// 直接可以拿數(shù)組中的數(shù)組中的第一個(gè)AVCaptureDevice*audioCaptureDevice = [[AVCaptureDevicedevicesWithMediaType:AVMediaTypeAudio] firstObject];
3.利用輸入設(shè)備AVCaptureDevice初始化AVCaptureDeviceInput對(duì)象浦马。
// 視頻輸入對(duì)象// 根據(jù)輸入設(shè)備初始化輸入對(duì)象时呀,用戶獲取輸入數(shù)據(jù)_videoCaptureDeviceInput = [[AVCaptureDeviceInputalloc] initWithDevice:captureDevice error:&error];if(error) {NSLog(@"---- 取得設(shè)備輸入對(duì)象時(shí)出錯(cuò) ------ %@",error);return;}//? 音頻輸入對(duì)象//根據(jù)輸入設(shè)備初始化設(shè)備輸入對(duì)象,用于獲得輸入數(shù)據(jù)_audioCaptureDeviceInput = [[AVCaptureDeviceInputalloc] initWithDevice:audioCaptureDevice error:&error];if(error) {NSLog(@"取得設(shè)備輸入對(duì)象時(shí)出錯(cuò) ------ %@",error);return;}
4.初始化輸出數(shù)據(jù)管理對(duì)象晶默,如果要拍照就初始化AVCaptureStillImageOutput對(duì)象;如果拍攝視頻就初始化AVCaptureMovieFileOutput對(duì)象荤胁。
// 拍攝視頻輸出對(duì)象// 初始化輸出設(shè)備對(duì)象瞧预,用戶獲取輸出數(shù)據(jù)_caputureMovieFileOutput = [[AVCaptureMovieFileOutputalloc] init];
5.將數(shù)據(jù)輸入對(duì)象AVCaptureDeviceInput、數(shù)據(jù)輸出對(duì)象AVCaptureOutput添加到媒體會(huì)話管理對(duì)象AVCaptureSession中仅政。
// 將視頻輸入對(duì)象添加到會(huì)話 (AVCaptureSession) 中if([_captureSession canAddInput:_videoCaptureDeviceInput]) {? ? [_captureSession addInput:_videoCaptureDeviceInput];}// 將音頻輸入對(duì)象添加到會(huì)話 (AVCaptureSession) 中if([_captureSession canAddInput:_captureDeviceInput]) {? ? [_captureSession addInput:audioCaptureDeviceInput];AVCaptureConnection*captureConnection = [_caputureMovieFileOutput connectionWithMediaType:AVMediaTypeVideo];// 標(biāo)識(shí)視頻錄入時(shí)穩(wěn)定音頻流的接受垢油,我們這里設(shè)置為自動(dòng)if([captureConnection isVideoStabilizationSupported]) {? ? ? ? captureConnection.preferredVideoStabilizationMode=AVCaptureVideoStabilizationModeAuto;? ? }}
6.創(chuàng)建視頻預(yù)覽圖層AVCaptureVideoPreviewLayer并指定媒體會(huì)話,添加圖層到顯示容器中圆丹,調(diào)用AVCaptureSession的startRuning方法開始捕獲滩愁。
// 通過會(huì)話 (AVCaptureSession) 創(chuàng)建預(yù)覽層_captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayeralloc] initWithSession:_captureSession];// 顯示在視圖表面的圖層CALayer*layer =self.viewContrain.layer;layer.masksToBounds=true;_captureVideoPreviewLayer.frame= layer.bounds;_captureVideoPreviewLayer.masksToBounds=true;_captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式[layer addSublayer:_captureVideoPreviewLayer];// 讓會(huì)話(AVCaptureSession)勾搭好輸入輸出,然后把視圖渲染到預(yù)覽層上[_captureSession startRunning];
7.將捕獲的音頻或視頻數(shù)據(jù)輸出到指定文件辫封。
創(chuàng)建一個(gè)拍攝的按鈕硝枉,當(dāng)我們點(diǎn)擊這個(gè)按鈕就會(huì)觸發(fā)視頻錄制,并將這個(gè)錄制的視頻放到 temp 文件夾中- (IBAction)takeMovie:(id)sender {[(UIButton*)sender setSelected:![(UIButton*)sender isSelected]];if([(UIButton*)sender isSelected]) {AVCaptureConnection*captureConnection=[self.caputureMovieFileOutputconnectionWithMediaType:AVMediaTypeVideo];// 開啟視頻防抖模式AVCaptureVideoStabilizationModestabilizationMode =AVCaptureVideoStabilizationModeCinematic;if([self.captureDeviceInput.device.activeFormatisVideoStabilizationModeSupported:stabilizationMode]) {? ? ? ? [captureConnection setPreferredVideoStabilizationMode:stabilizationMode];? ? }//如果支持多任務(wù)則則開始多任務(wù)if([[UIDevicecurrentDevice] isMultitaskingSupported]) {self.backgroundTaskIdentifier= [[UIApplicationsharedApplication] beginBackgroundTaskWithExpirationHandler:nil];? ? }// 預(yù)覽圖層和視頻方向保持一致,這個(gè)屬性設(shè)置很重要倦微,如果不設(shè)置妻味,那么出來的視頻圖像可以是倒向左邊的。captureConnection.videoOrientation=[self.captureVideoPreviewLayerconnection].videoOrientation;// 設(shè)置視頻輸出的文件路徑欣福,這里設(shè)置為 temp 文件NSString*outputFielPath=[NSTemporaryDirectory() stringByAppendingString:MOVIEPATH];// 路徑轉(zhuǎn)換成 URL 要用這個(gè)方法责球,用 NSBundle 方法轉(zhuǎn)換成 URL 的話可能會(huì)出現(xiàn)讀取不到路徑的錯(cuò)誤NSURL*fileUrl=[NSURLfileURLWithPath:outputFielPath];// 往路徑的 URL 開始寫入錄像 Buffer ,邊錄邊寫[self.caputureMovieFileOutputstartRecordingToOutputFileURL:fileUrl recordingDelegate:self];}else{// 取消視頻拍攝[self.caputureMovieFileOutputstopRecording];? ? [self.captureSessionstopRunning];? ? [selfcompleteHandle];}}
當(dāng)然我們錄制的開始與結(jié)束都是有監(jiān)聽方法的,AVCaptureFileOutputRecordingDelegate這個(gè)代理里面就有我們想要做的
- (void)captureOutput:(AVCaptureFileOutput*)captureOutput didStartRecordingToOutputFileAtURL:(NSURL*)fileURL fromConnections:(NSArray*)connections{NSLog(@"---- 開始錄制 ----");}- (void)captureOutput:(AVCaptureFileOutput*)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL*)outputFileURL fromConnections:(NSArray*)connections error:(NSError*)error{NSLog(@"---- 錄制結(jié)束 ----");}
到此拓劝,我們錄制視頻就結(jié)束了雏逾,那么是不是我們錄制好了視頻,就可以馬上把這個(gè)視頻上傳給服務(wù)器分享給你的小伙伴們看了呢郑临?
我們可以用如下方法測(cè)試一下我們錄制出來的視頻有多大 (m)
- (CGFloat)getfileSize:(NSString*)path{NSDictionary*outputFileAttributes = [[NSFileManagerdefaultManager] attributesOfItemAtPath:path error:nil];NSLog(@"file size: %f", (unsignedlonglong)[outputFileAttributes fileSize]/1024.00/1024.00);return(CGFloat)[outputFileAttributes fileSize]/1024.00/1024.00;}
個(gè)人在這里做過測(cè)試栖博,錄制了 10s 的小視頻得到的文件大小為 4.1M 左右,而且我用的分辨率還是640x480。厢洞。仇让。很無語(yǔ)了是不是典奉?
如果我們錄制的視頻,錄制完成后要與服務(wù)器進(jìn)行必要的上傳妹孙,那么秋柄,我們肯定不能把這個(gè)剛剛錄制出來的視頻上傳給服務(wù)器的获枝,我們有必要對(duì)這個(gè)視頻進(jìn)行壓縮了蠢正。那么我們的壓縮方法,就要用到AVAssetExportSeeion這個(gè)類了省店。
// 這里我們創(chuàng)建一個(gè)按鈕嚣崭,當(dāng)點(diǎn)擊這個(gè)按鈕,我們就會(huì)調(diào)用壓縮視頻的方法懦傍,然后再去重新計(jì)算大小雹舀,這樣就會(huì)跟未被壓縮前的大小有個(gè)明顯的對(duì)比了// 壓縮視頻- (IBAction)compressVideo:(id)sender{NSString*cachePath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) lastObject];NSString*savePath=[cachePath stringByAppendingPathComponent:MOVIEPATH];NSURL*saveUrl=[NSURLfileURLWithPath:savePath];// 通過文件的 url 獲取到這個(gè)文件的資源AVURLAsset*avAsset = [[AVURLAssetalloc] initWithURL:saveUrl options:nil];// 用 AVAssetExportSession 這個(gè)類來導(dǎo)出資源中的屬性NSArray*compatiblePresets = [AVAssetExportSessionexportPresetsCompatibleWithAsset:avAsset];// 壓縮視頻if([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {// 導(dǎo)出屬性是否包含低分辨率// 通過資源(AVURLAsset)來定義 AVAssetExportSession,得到資源屬性來重新打包資源 (AVURLAsset, 將某一些屬性重新定義AVAssetExportSession*exportSession = [[AVAssetExportSessionalloc] initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];// 設(shè)置導(dǎo)出文件的存放路徑NSDateFormatter*formatter = [[NSDateFormatteralloc] init];? ? [formatter setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];NSDate*date = [[NSDatealloc] init];NSString*outPutPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,true) lastObject] stringByAppendingPathComponent:[NSStringstringWithFormat:@"output-%@.mp4",[formatter stringFromDate:date]]];? ? exportSession.outputURL= [NSURLfileURLWithPath:outPutPath];// 是否對(duì)網(wǎng)絡(luò)進(jìn)行優(yōu)化exportSession.shouldOptimizeForNetworkUse=true;// 轉(zhuǎn)換成MP4格式exportSession.outputFileType=AVFileTypeMPEG4;// 開始導(dǎo)出,導(dǎo)出后執(zhí)行完成的block[exportSession exportAsynchronouslyWithCompletionHandler:^{// 如果導(dǎo)出的狀態(tài)為完成if([exportSession status] ==AVAssetExportSessionStatusCompleted) {dispatch_async(dispatch_get_main_queue(), ^{// 更新一下顯示包的大小self.videoSize.text= [NSStringstringWithFormat:@"%f MB",[selfgetfileSize:outPutPath]];? ? ? ? ? ? });? ? ? ? }? ? }];}}
經(jīng)過我們的壓縮粗俱,這個(gè)時(shí)候10s 的 4M 視頻就只剩下不夠 1M 了说榆。
以下是一些擴(kuò)展
自動(dòng)閃光燈開啟
- (IBAction)flashAutoClick:(UIButton*)sender {? ? [selfsetFlashMode:AVCaptureFlashModeAuto];? ? [selfsetFlashModeButtonStatus];}
打開閃光燈
- (IBAction)flashOnClick:(UIButton*)sender {? ? [selfsetFlashMode:AVCaptureFlashModeOn];? ? [selfsetFlashModeButtonStatus];}
關(guān)閉閃光燈
- (IBAction)flashOffClick:(UIButton*)sender {? ? [selfsetFlashMode:AVCaptureFlashModeOff];? ? [selfsetFlashModeButtonStatus];}
通知
/**
*? 給輸入設(shè)備添加通知
*/-(void)addNotificationToCaptureDevice:(AVCaptureDevice*)captureDevice{//注意添加區(qū)域改變捕獲通知必須首先設(shè)置設(shè)備允許捕獲[selfchangeDeviceProperty:^(AVCaptureDevice*captureDevice) {? ? captureDevice.subjectAreaChangeMonitoringEnabled=YES;}];NSNotificationCenter*notificationCenter= [NSNotificationCenterdefaultCenter];//捕獲區(qū)域發(fā)生改變[notificationCenter addObserver:selfselector:@selector(areaChange:) name:AVCaptureDeviceSubjectAreaDidChangeNotificationobject:captureDevice];}-(void)removeNotificationFromCaptureDevice:(AVCaptureDevice*)captureDevice{NSNotificationCenter*notificationCenter= [NSNotificationCenterdefaultCenter];[notificationCenter removeObserver:selfname:AVCaptureDeviceSubjectAreaDidChangeNotificationobject:captureDevice];}/**
*? 移除所有通知
*/-(void)removeNotification{NSNotificationCenter*notificationCenter= [NSNotificationCenterdefaultCenter];? ? [notificationCenter removeObserver:self];}-(void)addNotificationToCaptureSession:(AVCaptureSession*)captureSession{NSNotificationCenter*notificationCenter= [NSNotificationCenterdefaultCenter];//會(huì)話出錯(cuò)[notificationCenter addObserver:selfselector:@selector(sessionRuntimeError:) name:AVCaptureSessionRuntimeErrorNotificationobject:captureSession];}/**
*? 設(shè)備連接成功
*
*? @param notification 通知對(duì)象
*/-(void)deviceConnected:(NSNotification*)notification{NSLog(@"設(shè)備已連接...");}/**
*? 設(shè)備連接斷開
*
*? @param notification 通知對(duì)象
*/-(void)deviceDisconnected:(NSNotification*)notification{NSLog(@"設(shè)備已斷開.");}/**
*? 捕獲區(qū)域改變
*
*? @param notification 通知對(duì)象
*/-(void)areaChange:(NSNotification*)notification{NSLog(@"捕獲區(qū)域改變...");}/**
*? 會(huì)話出錯(cuò)
*
*? @param notification 通知對(duì)象
*/-(void)sessionRuntimeError:(NSNotification*)notification{NSLog(@"會(huì)話發(fā)生錯(cuò)誤.");
}
私有方法
/**
*? 取得指定位置的攝像頭
*
*? @param position 攝像頭位置
*
*? @return 攝像頭設(shè)備
*/-(AVCaptureDevice*)getCameraDeviceWithPosition:(AVCaptureDevicePosition)position{NSArray*cameras= [AVCaptureDevicedevicesWithMediaType:AVMediaTypeVideo];for(AVCaptureDevice*cameraincameras) {if([camera position]==position) {returncamera;? ? ? ? }? ? }returnnil;}/**
*? 改變?cè)O(shè)備屬性的統(tǒng)一操作方法
*
*? @param propertyChange 屬性改變操作
*/-(void)changeDeviceProperty:(PropertyChangeBlock)propertyChange{AVCaptureDevice*captureDevice= [self.captureDeviceInputdevice];NSError*error;//注意改變?cè)O(shè)備屬性前一定要首先調(diào)用lockForConfiguration:調(diào)用完之后使用unlockForConfiguration方法解鎖if([captureDevice lockForConfiguration:&error]) {? ? ? ? propertyChange(captureDevice);? ? ? ? [captureDevice unlockForConfiguration];? ? }else{NSLog(@"設(shè)置設(shè)備屬性過程發(fā)生錯(cuò)誤,錯(cuò)誤信息:%@",error.localizedDescription);? ? }}/**
*? 設(shè)置閃光燈模式
*
*? @param flashMode 閃光燈模式
*/-(void)setFlashMode:(AVCaptureFlashMode)flashMode{? ? [selfchangeDeviceProperty:^(AVCaptureDevice*captureDevice) {if([captureDevice isFlashModeSupported:flashMode]) {? ? ? ? ? ? [captureDevice setFlashMode:flashMode];? ? ? ? }? ? }];}/**
*? 設(shè)置聚焦模式
*
*? @param focusMode 聚焦模式
*/-(void)setFocusMode:(AVCaptureFocusMode)focusMode{? ? [selfchangeDeviceProperty:^(AVCaptureDevice*captureDevice) {if([captureDevice isFocusModeSupported:focusMode]) {? ? ? ? ? ? [captureDevice setFocusMode:focusMode];? ? ? ? }? ? }];}/**
*? 設(shè)置曝光模式
*
*? @param exposureMode 曝光模式
*/-(void)setExposureMode:(AVCaptureExposureMode)exposureMode{? ? [selfchangeDeviceProperty:^(AVCaptureDevice*captureDevice) {if([captureDevice isExposureModeSupported:exposureMode]) {? ? ? ? ? ? [captureDevice setExposureMode:exposureMode];? ? ? ? }? ? }];}/**
*? 設(shè)置聚焦點(diǎn)
*
*? @param point 聚焦點(diǎn)
*/-(void)focusWithMode:(AVCaptureFocusMode)focusMode exposureMode:(AVCaptureExposureMode)exposureMode atPoint:(CGPoint)point{? ? [selfchangeDeviceProperty:^(AVCaptureDevice*captureDevice) {if([captureDevice isFocusModeSupported:focusMode]) {? ? ? ? ? ? [captureDevice setFocusMode:AVCaptureFocusModeAutoFocus];? ? ? ? }if([captureDevice isFocusPointOfInterestSupported]) {? ? ? ? ? ? [captureDevice setFocusPointOfInterest:point];? ? ? ? }if([captureDevice isExposureModeSupported:exposureMode]) {? ? ? ? ? ? [captureDevice setExposureMode:AVCaptureExposureModeAutoExpose];? ? ? ? }if([captureDevice isExposurePointOfInterestSupported]) {? ? ? ? ? ? [captureDevice setExposurePointOfInterest:point];? ? ? ? }? ? }];}/**
*? 添加點(diǎn)按手勢(shì)寸认,點(diǎn)按時(shí)聚焦
*/-(void)addGenstureRecognizer{UITapGestureRecognizer*tapGesture=[[UITapGestureRecognizeralloc]initWithTarget:selfaction:@selector(tapScreen:)];? ? [self.viewContaineraddGestureRecognizer:tapGesture];}-(void)tapScreen:(UITapGestureRecognizer*)tapGesture{CGPointpoint= [tapGesture locationInView:self.viewContainer];//將UI坐標(biāo)轉(zhuǎn)化為攝像頭坐標(biāo)CGPointcameraPoint= [self.captureVideoPreviewLayercaptureDevicePointOfInterestForPoint:point];? ? [selfsetFocusCursorWithPoint:point];? ? [selffocusWithMode:AVCaptureFocusModeAutoFocusexposureMode:AVCaptureExposureModeAutoExposeatPoint:cameraPoint];}/**
*? 設(shè)置閃光燈按鈕狀態(tài)
*/-(void)setFlashModeButtonStatus{AVCaptureDevice*captureDevice=[self.captureDeviceInputdevice];AVCaptureFlashModeflashMode=captureDevice.flashMode;if([captureDevice isFlashAvailable]){self.flashAutoButton.hidden=NO;self.flashOnButton.hidden=NO;self.flashOffButton.hidden=NO;self.flashAutoButton.enabled=YES;self.flashOnButton.enabled=YES;self.flashOffButton.enabled=YES;switch(flashMode) {caseAVCaptureFlashModeAuto:self.flashAutoButton.enabled=NO;break;caseAVCaptureFlashModeOn:self.flashOnButton.enabled=NO;break;caseAVCaptureFlashModeOff:self.flashOffButton.enabled=NO;break;default:break;? ? }}else{self.flashAutoButton.hidden=YES;self.flashOnButton.hidden=YES;self.flashOffButton.hidden=YES;}}/**
*? 設(shè)置聚焦光標(biāo)位置
*
*? @param point 光標(biāo)位置
*/-(void)setFocusCursorWithPoint:(CGPoint)point{self.focusCursor.center=point;self.focusCursor.transform=CGAffineTransformMakeScale(1.5,1.5);self.focusCursor.alpha=1.0;? ? [UIViewanimateWithDuration:1.0animations:^{self.focusCursor.transform=CGAffineTransformIdentity;? ? } completion:^(BOOLfinished) {self.focusCursor.alpha=0;? ? }];}
@end
文/止于浮水(簡(jiǎn)書作者)
原文鏈接:http://www.reibang.com/p/7c57c58c253d/comments/1184468
著作權(quán)歸作者所有签财,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書作者”偏塞。