CC_VideoToolBoxLearning_h264

有部分是看视频写的。。。。。



//
//  ViewController.m
//  CC_VideoToolBoxLearning_1
//
//  Created by CC老师 on 2017/6/26.
//  Copyright © 2017年 Miss CC. All rights reserved.
//

#import "ViewController.h"
#import <AVFoundation/AVFoundation.h>
#import <VideoToolbox/VideoToolbox.h>

@interface ViewController ()<AVCaptureVideoDataOutputSampleBufferDelegate>

@property (nonatomic,strong)UILabel *cLabel;
@property (nonatomic, strong)AVCaptureSession *cCapturesession;// 捕捉会话
@property (nonatomic, strong)AVCaptureDeviceInput *cCaptureDeviceInput; // 捕捉输入
@property (nonatomic, strong)AVCaptureDeviceInput *activeVideoInput; // 捕捉输入

@property (nonatomic, strong)AVCaptureVideoDataOutput *cCaptureDataOutput; // 捕捉输出
@property (nonatomic, strong)AVCaptureVideoPreviewLayer *cPreviewLayer;

@end

@implementation ViewController
{
int frameID;//帧id,每张图片id
dispatch_queue_t cCaptureQueue; //捕捉队列
dispatch_queue_t cEncodeQueue;//编码队列
VTCompressionSessionRef cEncodeingSession;
CMFormatDescriptionRef format;//编码格式
NSFileHandle *fileHandele; //文件句柄 追加 删除
// NSFileManager // 文件创建,管理,移动,复制,删除

}

- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.

//基础UI实现
_cLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 0, 200, 100)];
_cLabel.text = @"H.264硬编码";
_cLabel.textColor = [UIColor redColor];
[self.view addSubview:_cLabel];

UIButton *cButton = [[UIButton alloc]initWithFrame:CGRectMake(0, 20, 100, 100)];
[cButton setTitle:@"play" forState:UIControlStateNormal];
[cButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
[cButton setBackgroundColor:[UIColor orangeColor]];
[cButton addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:cButton];



}


-(void)buttonClick:(UIButton *)button
{

if (!_cCapturesession || !_cCapturesession.isRunning)
{
[button setTitle:@"Stop" forState:UIControlStateNormal];
[self startCapture];
}else{
[button setTitle:@"Play" forState:UIControlStateNormal];
[self stopCapture];
}

}

//开始捕捉
- (void)startCapture
{
//配置捕捉会话
self.cCapturesession = [[AVCaptureSession alloc] init];

//设置捕捉分辨率
self.cCapturesession.sessionPreset = AVCaptureSessionPreset640x480;


cCaptureQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
cEncodeQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);


AVCaptureDevice *inputCamera = nil;
// 拿到默认的捕捉设备
NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
for (AVCaptureDevice *device in devices)
{
if ([device position] == AVCaptureDevicePositionBack)
{
inputCamera = device;
}
}
self.cCaptureDeviceInput = [[AVCaptureDeviceInput alloc] initWithDevice:inputCamera error:nil];
if ([self.cCapturesession canAddInput:self.cCaptureDeviceInput])
{
[self.cCapturesession addInput:self.cCaptureDeviceInput];
self.activeVideoInput = self.cCaptureDeviceInput;

}


//    AVCaptureDevice *audioDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];



self.cCaptureDataOutput = [[AVCaptureVideoDataOutput alloc] init];
[self.cCaptureDataOutput setAlwaysDiscardsLateVideoFrames:NO];
[self.cCaptureDataOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];

[self.cCaptureDataOutput setSampleBufferDelegate:self queue:cCaptureQueue];

if ([self.cCapturesession canAddOutput:self.cCaptureDataOutput])
{
[self.cCapturesession addOutput:self.cCaptureDataOutput];
}


AVCaptureConnection *connection = [self.cCaptureDataOutput connectionWithMediaType:AVMediaTypeVideo];
[connection setVideoOrientation:AVCaptureVideoOrientationPortrait];

self.cPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:self.cCapturesession];
[self.cPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];
[self.cPreviewLayer setFrame:CGRectMake(100, 80, 200, 200)];

[self.view.layer addSublayer:self.cPreviewLayer];



NSString *filePath = [NSHomeDirectory() stringByAppendingString:@"/Documents/cc_video.h264"];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
BOOL createFile = [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];


if (!createFile)
{
NSLog(@"create file failed");
}else{
NSLog(@"create file success");
}


NSLog(@"filePaht = %@",filePath);

fileHandele = [NSFileHandle fileHandleForWritingAtPath:filePath];

[self initVideoToolBox];

[self.cCapturesession startRunning];
}


//停止捕捉
- (void)stopCapture
{


[self.cCapturesession stopRunning];
[self.cPreviewLayer removeFromSuperlayer];

[self endVideoToolBox];

[fileHandele closeFile];

fileHandele = NULL;

}

#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate  //音频 视频 捕捉数据
-(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
dispatch_sync(cEncodeQueue, ^{
[self encode:sampleBuffer];
});

}



//初始化videoToolBox
-(void)initVideoToolBox
{
dispatch_sync(cEncodeQueue, ^{
frameID = 0;
int width = 480,height = 640;

OSStatus status = VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self), &cEncodeingSession);
NSLog(@"h264:VTCompressionSessionCreate:%d",(int)status);

if (status != 0)
{
NSLog(@"h264:unable to create a h264 session");
return;
}

//设置实时编码输出
VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);

VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_AutoLevel);

//设置关键帧

int frameInterval = 10;
CFNumberRef frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);

VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_MaxKeyFrameInterval, frameIntervalRef);



//设置期望帧率 不是实际帧率
int fps = 10;
CFNumberRef fpsref = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &fps);

VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_ExpectedFrameRate, fpsref);

//设置码率,上限,单位是bps
int bitrate = width*height*3*4*8;

CFNumberRef bitRateRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitrate);

VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_AverageBitRate, bitRateRef);

//设置码率,均值,单位是byte
int bigRateLimit = width*height*3*4;
CFNumberRef bitRateLimitRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bigRateLimit);
VTSessionSetProperty(cEncodeingSession, kVTCompressionPropertyKey_DataRateLimits, bitRateLimitRef);

// 开始编码

VTCompressionSessionPrepareToEncodeFrames(cEncodeingSession);

});

}




//编码
- (void) encode:(CMSampleBufferRef )sampleBuffer
{
//拿到第一帧未编码数据
CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
CMTime pTime = CMTimeMake(frameID++, 1000);

VTEncodeInfoFlags flags = 0;
OSStatus statusCode = VTCompressionSessionEncodeFrame(cEncodeingSession, imageBuffer, pTime, kCMTimeInvalid, NULL, NULL, &flags);

if (statusCode != noErr)
{
VTCompressionSessionInvalidate(cEncodeingSession);
CFRelease(cEncodeingSession);
cEncodeingSession = NULL;
return;

}


}


//编码完成回调
void didCompressH264(void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef sampleBuffer)
{

NSLog(@"didCompressH264 called with status %d infoFlags %d",(int)status,(int)(infoFlags));

//状态错误
if (status != 0)
{
return;
}

//没准备好
if (!CMSampleBufferDataIsReady(sampleBuffer))
{
NSLog(@"didCompressh264 data is not ready");
return;
}

ViewController *encoder = (__bridge ViewController *)outputCallbackRefCon;

//判断当前帧是否关键帧

bool keyFrame = !CFDictionaryContainsKey((CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true),0)), kCMSampleAttachmentKey_NotSync);

if (keyFrame)
{
CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);

size_t spsSize,spsCount;
const uint8_t *spsSet;
OSStatus status = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &spsSet, &spsSize, &spsCount, 0);
if (status == noErr)
{
size_t ppsSize,ppsCount;
const uint8_t *ppsSet;
OSStatus ppsstatus = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &ppsSet, &ppsSize, &ppsCount, 0);
if (ppsstatus == noErr)
{
NSData *sps = [NSData dataWithBytes:spsSet length:spsSize];
NSData *pps = [NSData dataWithBytes:ppsSet length:ppsSize];

if (encoder)
{
[encoder gotSpsPps:sps pps:pps];
}
}
}

}

CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length,totoalLength ;
char *dataPointer;
OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totoalLength, &dataPointer);


if (statusCodeRet == noErr) {
size_t bufferOffset = 0;
static const int AVL = 4;
while (bufferOffset < totoalLength - AVL) {
uint32_t NAL = 0;
memcpy(&NAL, dataPointer + bufferOffset, AVL);
NAL = CFSwapInt32BigToHost(NAL);
NSData *data = [[NSData alloc] initWithBytes:dataPointer + bufferOffset +AVL length:NAL];
[encoder gotEncodedData:data isKeyFrame:keyFrame];

bufferOffset += AVL +NAL;
}
}

}

- (void)gotSpsPps:(NSData*)sps pps:(NSData*)pps
{

const char bytes[] = "\x00\x00\x00\x01";

//长度
size_t length = (sizeof bytes) -1;
//头字节
NSData *byteHeader = [NSData dataWithBytes:bytes length:length];

//写入文件
[fileHandele writeData:byteHeader];

//写入h264数据
[fileHandele writeData:sps];

//写入文件
[fileHandele writeData:byteHeader];

//写入h264数据
[fileHandele writeData:pps];



}


- (void)gotEncodedData:(NSData*)data isKeyFrame:(BOOL)isKeyFrame
{
NSLog(@"gotEncodeData:%d",(int)[data length]);
if (fileHandele != NULL)
{
const char bytes[] = "\x00\x00\x00\x01";

//长度
size_t length = (sizeof bytes) -1;
//头字节
NSData *byteHeader = [NSData dataWithBytes:bytes length:length];

//写入文件
[fileHandele writeData:byteHeader];

//写入h264数据
[fileHandele writeData:data];
}
}

//结束VideoToolBox
-(void)endVideoToolBox
{
VTCompressionSessionCompleteFrames(cEncodeingSession, kCMTimeInvalid);
VTCompressionSessionInvalidate(cEncodeingSession);
CFRelease(cEncodeingSession);
cEncodeingSession = NULL;
}


- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}


@end




Loading Disqus comments...
Table of Contents