ios h264解码播放 相机数据和后台数据的区别

IOS摄像头采集YUV420数据
这里简单说下IOS摄像头采集YUV420数据。
1.初始采集数据需要的对象
2.设置采集回调的代理并开始
3.在回调中获取到采集的数据(注意:这里采集到的数据格式NV12并不是YUV420格式数据)
4.将NV12转成我们需要的YUV420
//摄像头采集
capturSession=[[AVCaptureSessionalloc]init];
capturSession.sessionPreset=AVCaptureSessionPreset;//设置采集的分辨率
captureDevice=[AVCaptureDevicedefaultDeviceWithMediaType:AVMediaTypeVideo];
NSError *error=nil;
captureDeviceInput=[AVCaptureDeviceInputdeviceInputWithDevice:captureDeviceerror:&error];
if (captureDeviceInput) {
[capturSessionaddInput:captureDeviceInput];
NSLog(@"%@",error);
//初始化流输出对象
captureVideoDataOutput=[[AVCaptureVideoDataOutputalloc]init];
[captureVideoDataOutputsetAlwaysDiscardsLateVideoFrames:YES];
//设置输出参数
NSDictionary *settingsDic = [[NSDictionaryalloc]
initWithObjectsAndKeys:
[NSNumbernumberWithUnsignedInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange],
kCVPixelBufferPixelFormatTypeKey,
nil];// X264_CSP_NV12
captureVideoDataOutput.videoSettings = settingsD
dispatch_queue_t queue =dispatch_queue_create("myQueue",NULL);
[captureVideoDataOutputsetSampleBufferDelegate:selfqueue:queue];
[capturSessionaddOutput:captureVideoDataOutput];
/* ---------------------------------------------------------------------- */
previewLayer = [[AVCaptureVideoPreviewLayeralloc]
initWithSession:capturSession];
//设置显示的视图位置
previewLayer.frame =CGRectMake(50,330,
[self.view.layeraddSublayer:previewLayer];
//开始采集
[capturSessionstartRunning];
//回调函数
#pragma mark --
AVCaptureVideo(Audio)DataOutputSampleBufferDelegate method
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection
*)connection
if (captureOutput == captureVideoDataOutput) {
CVImageBufferRef imageBuffer =CMSampleBufferGetImageBuffer(sampleBuffer);
// 获取采集的数据
CVPixelBufferLockBaseAddress(imageBuffer,0);
CMTime pts =CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
CMTime duration =CMSampleBufferGetDuration(sampleBuffer);
void *imageAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);//YYYYYYYY
size_t row0=CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,0);
void *imageAddress1=CVPixelBufferGetBaseAddressOfPlane(imageBuffer,1);//UVUVUVUV
size_t row1=CVPixelBufferGetBytesPerRowOfPlane(imageBuffer,1);
size_t width =CVPixelBufferGetWidth(imageBuffer);
size_t height =CVPixelBufferGetHeight(imageBuffer);
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
size_t a=width*
//开始将NV12转换成YUV420
uint8_t *yuv420Data=(uint8_t*)malloc(a*1.5);
for (int i=0; i& ++i) {
memcpy(yuv420Data+i*width, imageAddress+i*row0, width);
uint8_t *UV=imageAddress1;
uint8_t *U=yuv420Data+a;
uint8_t *V=U+a/4;
for (int i=0; i&0.5* i++) {
for (int j=0; j&0.5* j++) {
printf("%d\n",j&&1);
*(U++)=UV[j&&1];
*(V++)=UV[(j&&1)+1];
//这里根据自己的情况对YUV420数据进行处理
//...........
//最后记得释放哦
free(yuv420Data);
没有更多推荐了,IOS 如何播放流数据
G711格式 data类型
一帧一帧的数据
[问题点数:40分,结帖人qq_]
本版专家分:0
结帖率 88.24%
CSDN今日推荐
本版专家分:0
本版专家分:180
本版专家分:0
本版专家分:40
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
本版专家分:0
匿名用户不能发表回复!|
其他相关推荐iOS8系统H264视频硬件编解码说明 - 简书
iOS8系统H264视频硬件编解码说明
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码,简称为硬编解码。苹果在iOS 8.0系统之前,没有开放系统的硬件编码解码功能,不过Mac OS系统一直有,被称为Video ToolBox的框架来处理硬件的编码和解码,终于在iOS 8.0后,苹果将该框架引入iOS系统。由此,开发者便可以在iOS里面,调用Video Toolbox框架提供的接口,来对视频进行硬件编解码的工作,为VOIP视频通话,视频流播放等应用的视频编解码提供了便利。(PS:按照苹果WWDC《direct access to media encoding and decoding》的描述,苹果之前提供的AVFoundation框架也使用硬件对视频进行硬编码和解码,但是编码后直接写入文件,解码后直接显示。Video Toolbox框架可以得到编码后的帧结构,也可以得到解码后的原始图像,因此具有更大的灵活性做一些视频图像处理。)一,VideoToolbox基本数据结构。Video Toolbox视频编解码前后需要应用的数据结构进行说明。(1)CVPixelBuffer:编码前和解码后的图像数据结构。(2)CMTime、CMClock和CMTimebase:时间戳相关。时间以64-bit/32-bit的形式出现。(3)CMBlockBuffer:编码后,结果图像的数据结构。(4)CMVideoFormatDescription:图像存储方式,编解码器等格式描述。(5)CMSampleBuffer:存放编解码前后的视频图像的容器数据结构。
图1.1视频H264编解码前后数据结构示意图如图1.1所示,编解码前后的视频图像均封装在CMSampleBuffer中,如果是编码后的图像,以CMBlockBuffe方式存储;解码后的图像,以CVPixelBuffer存储。CMSampleBuffer里面还有另外的时间信息CMTime和视频描述信息CMVideoFormatDesc。二,硬解码使用方法。通过如图2.1所示的一个典型应用,来说明如何使用硬件解码接口。该应用场景是从网络处传来H264编码后的视频码流,最后显示在手机屏幕上。
图2.1 H264典型应用场景1,将H264码流转换成解码前的CMSampleBuffer。由图1.1所示,解码前的CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer。需要从H264的码流里面提取出以上的三个信息。最后组合成CMSampleBuffer,提供给硬解码接口来进行解码工作。H264的码流由NALU单元组成,NALU单元包含视频图像数据和H264的参数信息。其中视频图像数据就是CMBlockBuffer,而H264的参数信息则可以组合成FormatDesc。具体来说参数信息包含SPS(Sequence Parameter Set)和PPS(Picture Parameter Set)。图2.2显示一个H264码流的结构。
图2.2 H264码流结构(1)提取sps和pps生成format description。a,每个NALU的开始码是0x00 00 01,按照开始码定位NALU。b,通过类型信息找到sps和pps并提取,开始码后第一个byte的后5位,7代表sps,8代表pps。c,CMVideoFormatDescriptionCreateFromH264ParameterSets函数来构建CMVideoFormatDescriptionRef。具体代码可以见demo。(2)提取视频图像数据生成CMBlockBuffer。a,通过开始码,定位到NALU。b,确定类型为数据后,将开始码替换成NALU的长度信息(4 Bytes)。c,CMBlockBufferCreateWithMemoryBlock接口构造CMBlockBufferRef。具体代码可以见demo。(3)根据需要,生成CMTime信息。(实际测试时,加入time信息后,有不稳定的图像,不加入time信息反而没有,需要进一步研究,这里建议不加入time信息)根据上述得到CMVideoFormatDescriptionRef、CMBlockBufferRef和可选的时间信息,使用CMSampleBufferCreate接口得到CMSampleBuffer数据这个待解码的原始的数据。见图2.3的H264数据转换示意图。
图2.3 H264码流转换CMSampleBuffer示意图2,硬件解码图像显示。硬件解码显示的方式有两种:(1)通过系统提供的AVSampleBufferDisplayLayer来解码并显示。AVSampleBufferDisplayLayer是苹果提供的一个专门显示编码后的H264数据的显示层,它是CALayer的子类,因此使用方式和其它CALayer类似。该层内置了硬件解码功能,将原始的CMSampleBuffer解码后的图像直接显示在屏幕上面,非常的简单方便。图2.4显示了这一解码过程。
图2.4 AVSampleBufferDisplayLayer硬解压后显示图像显示的接口为[_avslayer enqueueSampleBuffer:sampleBuffer];(2)通过VTDecompression接口来,将CMSampleBuffer解码成图像,将图像通过UIImageView或者OpenGL上显示。a,初始化VTDecompressionSession,设置解码器的相关信息。初始化信息需要CMSampleBuffer里面的FormatDescription,以及设置解码后图像的存储方式。demo里面设置的CGBitmap模式,使用RGB方式存放。编码后的图像经过解码后,会调用一个回调函数,将解码后的图像交个这个回调函数来进一步处理。我们就在这个回调里面,将解码后的图像发给control来显示,初始化的时候要将回调指针作为参数传给create接口函数。最后使用create接口对session来进行初始化。b,a中所述的回调函数可以完成CGBitmap图像转换成UIImage图像的处理,将图像通过队列发送到Control来进行显示处理。c,调用VTDecompresSessionDecodeFrame接口进行解码操作。解码后的图像会交由a,b步骤设置的回调函数,来进一步的处理。图2.5显示来硬解码的过程步骤。
图2.5 VTDecompression硬解码过程示意图三,硬编码使用方法。硬编码的使用也通过一个典型的应用场景来描述。首先,通过摄像头来采集图像,然后将采集到的图像,通过硬编码的方式进行编码,最后编码后的数据将其组合成H264的码流通过网络传播。1,摄像头采集数据。摄像头采集,iOS系统提供了AVCaptureSession来采集摄像头的图像数据。设定好session的采集解析度。再设定好input和output即可。output设定的时候,需要设置delegate和输出队列。在delegate方法,处理采集好的图像。注意,需要说明的是,图像输出的格式,是未编码的CMSampleBuffer形式。2,使用VTCompressionSession进行硬编码。(1)初始化VTCompressionSession。VTCompressionSession初始化的时候,一般需要给出width宽,height长,编码器类型kCMVideoCodecType_H264等。然后通过调用VTSessionSetProperty接口设置帧率等属性,demo里面提供了一些设置参考,测试的时候发现几乎没有什么影响,可能需要进一步调试。最后需要设定一个回调函数,这个回调是视频图像编码成功后调用。全部准备好后,使用VTCompressionSessionCreate创建session。(2)提取摄像头采集的原始图像数据给VTCompressionSession来硬编码。摄像头采集后的图像是未编码的CMSampleBuffer形式,利用给定的接口函数CMSampleBufferGetImageBuffer从中提取出CVPixelBufferRef,使用硬编码接口VTCompressionSessionEncodeFrame来对该帧进行硬编码,编码成功后,会自动调用session初始化时设置的回调函数。(3)利用回调函数,将因编码成功的CMSampleBuffer转换成H264码流,通过网络传播。基本上是硬解码的一个逆过程。解析出参数集SPS和PPS,加上开始码后组装成NALU。提取出视频数据,将长度码转换成开始码,组长成NALU。将NALU发送出去。图2.6显示了整个硬编码的处理逻辑。
图2.6硬编码处理流程示意图四,硬编解码的一些编码说明。由于Video Toolbox是基础的core Foundation库函数,C语言写成,和使用core Foundation所有的其它功能一样需要适应,记得Github有个同志,将其改成了OC语言能方便调用的模式,但是地址忘了,以后有缘找到,就会提供下链接。Demo : https://github.com/manishganvir/iOS-h264Hw-Toolbox
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码,简称为...
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码,简称为...
文章转自:小武的技术渔场-iOS8系统H264视频硬件编解码说明 1,将H264码流转换成解码前的CMSampleBuffer。由图1.1所示,解码前的CMSampleBuffer = CMTime + FormatDesc + CMBlockBuffer。需要从H264的...
公司项目原因,接触了一下视频流H264的编解码知识,之前项目使用的是FFMpeg多媒体库,利用CPU做视频的编码和解码,俗称为软编软解。该方法比较通用,但是占用CPU资源,编解码效率不高。一般系统都会提供GPU或者专用处理器来对视频流进行编解码,也就是硬件编码和解码,简称为...
硬件编码相关知识(H264,H265) 阅读人群:研究硬件编码器应用于iOS开发中,从0研究关于硬件编解码,码流中解析数据结构 内容概述:关于H264,H265的背景,数据结构,在iOS开发中编解码的应用 博客地址:
硬件编码相关知识 简书地址:
硬件编码相关知识 一....
心灵鸡汤,就是“充满知识与感情的话语”,柔软、温暖,充满正能量。如,“是金子总会发光”、“什么时候都不算晚”……但在2014年5月,网络刮起一阵“反心灵鸡汤”旋风。为什么要反?我想是因为大家都被“鸡汤”骗了,或是太相信它的力量了。到最后,不得不面对现实。 “反心灵鸡汤”语录...
第四期核心词汇解析来了!!! 点击这里听音频课件【考研英语词汇】词根词缀记忆法第四期(abundant/abundance/abound) 【课前感慨】越来越觉得时间紧张起来了,从现在开始到2018年考研,一年多点时间,但是碰到春节和各种小长假,又得减掉两月时间,就按天天来...
阳江的海边还是不错的,上下川岛的照片找不到,先不放上来了。 海陵岛: 沙扒湾: 上洋镇白沙村边: 蓝袍湾: 台山黑沙滩:
验证器是一个可调用的函数,它接受一个值,并在不符合验证规则时抛出ValidationError 异常。 现在我们定义一个验证器,让它只接受大于4的整数: 我们先在 shell 中测试下这个验证器: 可以看到我们自定义的验证错误信息已经生效。 我们继续让验证器的错误信息在前端...
很多小白会有烘焙的各种各样的问题,其实烘焙没有我们想像的那么难。不要害怕做不好,一定要勇往直前,你会慢慢发现其实也就那么回事。多点尝试、总结,找到一些小窍门,就像这个原味牛油蛋糕,将黄油和蛋黄打发到呈轻盈羽毛状,再用烤箱烤出来就很成功,很美味。今天欧品皇室蛋糕培训学校分享原...&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
iOS相机开发的踩坑篇
摘要:相机的设置,这个demo用GPUImageview为基准,做了一个基础的demo,处理了供底层OpenGL的方向处理,其他功能参考最后的参考链接可以实现。ps:2016年整年基本上都是做avfoundation的开发,自我感觉对苹果这套库应该算是蛮了解了,网上的例子也很多,但是深入了解后发现很多其实坑很多,这篇分享主要是和大家介绍一下坑和难点,基本的用法可以看一下参考链接和下面的图。一.整体架构这里有一个很大的问题,就是屏幕旋转。现在大部分视频或者直播软件我观察了一下基本上是
相机的设置,这个demo用GPUImageview为基准,做了一个基础的demo,处理了供底层OpenGL的方向处理,其他功能参考 最后的参考链接可以实现。
ps:2016年整年基本上都是做avfoundation的开发,自我感觉对苹果这套库应该算是蛮了解了,网上的例子也很多,但是深入了解后发现很多其实坑很多,这篇分享主要是和大家介绍一下坑和难点,基本的用法可以看一下参考链接和下面的图。
一. 整体架构
这里有一个很大的问题,就是屏幕旋转。现在大部分视频或者直播软件我观察了一下基本上是不支持转动手机方向的。这个的选择会影响整套架构。相应难度也会降低很多。很悲催地我们一开始就要求四个方向。
录制架构,之前我使用的都是AVFoundation的AVWritter自己写的,后来发现有一个很大的问题。就是做美颜的时候,录制又会有问题。后来干脆用了GPUMovieWritter去录制。这里自带了音频录制。
GPUImageVideoCamera这个大家用的时候建议是继承一个自己写,最起码要看看源码,这个定制的化的东西太多了。并且之后设置慢动作等问题不方便。所以这个我没有用。用了AVFoundation自己的裤,然后模仿这里面的旋转等做了一下openGL的处理。
根据上诉所说,我们来整理一下流程:
二. 视频录制
1. 视频参数设置
这里是很核心的配置参数,要配合 StreamEye 以及相机捕捉去看自己的录制和系统录制的区别,主要是qa,清晰度等。
码率的观看可以用苹果自带的相机软件,然后用Alt+I快捷键弹出一个信息看码率。
千万不要用airdrop传文件到电脑,会导致重新编码。用苹果应用程序里面的相机捕捉工具就可以很好看的去看了。
以上几点都是无数实践的踩坑。大家一定要注意,相应参数的设置错误,会导致如果同样是4k的录制,那么你录制的达不到30fps,或者要比系统的录制大小占用空间大很多。下面我提供了一个参考设置,都是经过和系统对比还有实践出来的一些参数。如果有错误,请大家在评论下方告知我,或者加入qq群,不甚感激。 NSDictionary *videoCompressionP
NSDictionary *videoS
switch (cameraModel.videoResolution) {
case AVCaptureSessionPreset:
videoCompressionProps = @{
AVVideoAverageBitRateKey:@(50*4),
AVVideoH264EntropyModeKey:AVVideoH264EntropyModeCABAC,
AVVideoMaxKeyFrameIntervalKey:@(30),
AVVideoAllowFrameReorderingKey:@NO,
AVVideoExpectedSourceFrameRateKey:@30,
case AVCaptureSessionPreset:
videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:18*4], AVVideoAverageBitRateKey,
AVVideoH264EntropyModeCABAC,AVVideoH264EntropyModeKey,
case AVCaptureSessionPreset:
videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:8*4], AVVideoAverageBitRateKey,
AVVideoH264EntropyModeCABAC,AVVideoH264EntropyModeKey,
videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264,AVVideoCodecKey,
videoCompressionProps, AVVideoCompressionPropertiesKey,
AVVideoScalingModeResizeAspectFill,AVVideoScalingModeKey,
[NSNumber numberWithInteger:videoSize.width],AVVideoWidthKey,
[NSNumber numberWithInteger:videoSize.height],AVVideoHeightKey,
nil];self.writerInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];if(cameraModel.devicePosition == DJIIPhone_DevicePositionFront){
self.writerInput.transform = [OSMOMediaUtils getAngleTransformFromScreenOritationFront];
self.writerInput.transform = [OSMOMediaUtils getAngleTransformFromScreenOritationBack];
}self.writerInput.expectsMediaDataInRealTime = YES;
2. 录制方向
给AVWritter设置一个初始化的Transform,从摄像头过来的CMSampleBuffer直接进入录制流,不要去做屏幕流程。因为不这样的化会出现一个问题。
你点击录制视频,这过程中去旋转屏幕,当你支持四个方向的时候,旋转的时候就会出现闪烁。具体可以用苹果自己的相机试一下就一清二楚了。
四. GPU美颜处理后用CVPixBuffer编码直播 GPU美颜处理
错误处理方法: CGSize outputSize = {720, 1280};
GPUImageRawDataOutput *rawDataOutput = [[GPUImageRawDataOutput alloc] initWithImageSize:CGSizeMake(outputSize.width, outputSize.height) resultsInBGRAFormat:YES];
[self.beautifyFilter addTarget:rawDataOutput];
将rawDataoutput作为Target加入,然后获取pixbuffer给videotoolbox处理 __weak GPUImageRawDataOutput *weakOutput = rawDataO
__weak typeof(self) weakSelf =
[rawDataOutput setNewFrameAvailableBlock:^{
__strong GPUImageRawDataOutput *strongOutput = weakO
[strongOutput lockFramebufferForReading];
// 这里就可以获取到添加滤镜的数据了
GLubyte *outputBytes = [strongOutput rawBytesForImage];
NSInteger bytesPerRow = [strongOutput bytesPerRowInOutput];
CVPixelBufferRef pixelBuffer = NULL;
CVPixelBufferCreateWithBytes(kCFAllocatorDefault, outputSize.width, outputSize.height, kCVPixelFormatType_32BGRA, outputBytes, bytesPerRow, nil, nil, nil, πxelBuffer);
// 之后可以利用VideoToolBox进行硬编码再结合rtmp协议传输视频流了
[weakSelf encodeWithCVPixelBufferRef:pixelBuffer];
[strongOutput unlockFramebufferAfterReading];
CFRelease(pixelBuffer);
大家可以看一下为什么我不推荐这种方法,CVPixelBufferCreateWithBytes这里是很耗时的,要重新创建。
**正确处理方法:**
正确的方法建议大家看一下GPUFrameBuffer,里面有一个renderTarget,加一个类别去获取renderTarget(CVPixBuffer),我们取出来的时候lock一下,用完unlock一下就好了。## 五. 慢动作
[慢动作设置](//https://developer.apple.com/library/mac/documentation/AVFoundation/Reference/AVCaptureDevice_Class/index.html),这里的技术原理实则以指定超过60fps去执行一个录制,比如240fps,然后将240fps按照30fps去写入数据填充时间段,所以就有了慢动作效果。下面的代码是设置最大速度。
-(void)configureCameraForHighestFrameRate:(AVCaptureDevice*) device
AVCaptureDeviceFormat *bestFormat =
AVFrameRateRange *bestFrameRateRange =
for(AVCaptureDeviceFormat *format in [device formats] ) {
for ( AVFrameRateRange *range in format.videoSupportedFrameRateRanges ) {
if ( range.maxFrameRate & bestFrameRateRange.maxFrameRate ) {
bestFormat =
bestFrameRateRange =
}if ( bestFormat ) {
if ( [device lockForConfiguration:NULL] == YES ) {
device.activeFormat = bestF
device.activeVideoMinFrameDuration = bestFrameRateRange.minFrameD
device.activeVideoMaxFrameDuration = bestFrameRateRange.minFrameD
[device unlockForConfiguration];
}六. 延时摄影
由于系统是每秒回调30次,那么现在我们自己定时器每秒取一张图,然后利用 AVAssetWriterInputPixelBufferAdaptor 和 AVAssetWriterInput ,还有 AVAssetWriter 去用图片组帧视频。
七. 星轨拍摄
利用GPUBlenderFilter的双输入通道即可,叠加加上。生成照片之前记得把滤镜useNextCapture.
六. 参考链接
用来拍照和录像的官方demo https://developer.apple.com/library/prerelease/content/samplecode/AVCam/Introduction/Intro.html
苹果官方的关于拍照录像的一个综合性的demo,我也是参照这个做的 https://developer.apple.com/library/prerelease/content/samplecode/AVCamManual/Introduction/Intro.html
GPU直播 iOS中为直播APP集成美颜功能
录制视频,分段录制 github地址
扩展GPU支持视频录制暂停和恢复.支持闪关灯开启和关闭. GPUImageExtend
使用 AVAssetWriter 录制小视频,声音的录制也在里面 使用 AVAssetWriter 录制小视频,
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
新用户大礼包!
现在注册,免费体验40+云产品,及域名优惠!
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
iOS相机开发的踩坑篇相关信息,包括
的信息,所有iOS相机开发的踩坑篇相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
InternationalPages: 1/2
主题 : iOS 实时硬编码camera的数据为h264的问题?
级别: 新手上路
可可豆: 99 CB
威望: 99 点
在线时间: 464(时)
发自: Web Page
来源于&&分类
iOS 实时硬编码camera的数据为h264的问题?&&&
iOS 使用videotoolbox实时硬编码h264,从中得到pps和sps,可是sps以27开头,pps28开头,虽说nalu type也是正确的,但是一般情况下应当是67和68啊,而且这个数据的长度有点短。而且从samplebuffer众获取nalu的数据,发现绝大多数情况下,一个samplebuffer中就一个nalu啊,这个nalu非常大。我们需要使用rtp打包发送出去,所以,每个nalu都要分包。现在发送出去之后,使用vlc播放,vlc是绿屏的,根本看不清。是不是iOSvideotoolbox不能呢这么用啊?
级别: 新手上路
可可豆: 42 CB
威望: 53 点
在线时间: 616(时)
发自: Web Page
正好做了硬编码H264. 判断pps和sps不是看67 68 而是int nalu_type = (frame[4] & 0x1F)   所以28也是对的。默认设置一个nalu就是一帧。。。I和B都挺大的。这个是正常的。此外。有3个地方也会影响帧数据大小1:视频等级设置 VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_4_1);2:bitRate   这个值越高。图像越清晰。对于的数据就越大。 SInt32 bitRate = width*height*50;        CFNumberRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);        VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_AverageBitRate, ref);        CFRelease(ref);3:       int frameInterval = 10; 这个是关键帧间隔     值越小,压缩率越低。值越大。压缩率越高。但是掉帧后果也越明显        CFNumberRef  frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);        VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_MaxKeyFrameInterval,frameIntervalRef);        CFRelease(frameIntervalRef);     绿屏是分包掉了。数据不完整。你可以考虑一些简单的算法。比如主包号+子包号,发现子包不连续了就丢弃数据。这样画面会卡顿但是不会花屏。实际上,正式运营环境。丢包不可避免。不是简单的丢包就行,得有前向纠错或者重传等算法。[ 此帖被srddrs在 00:03重新编辑 ]
级别: 新手上路
可可豆: 99 CB
威望: 99 点
在线时间: 464(时)
发自: Web Page
回 1楼(srddrs) 的帖子
嗯。现在视频使用vlc可以播放了。主体的部分是使用了13年老外写的一个demo,那个时候videotoolbox还没有在iOS上开放,使用硬编码只能是使用avassetwriter。他那个demo是通过avassetwriter将camero的视频文件写成mp4到本地,然后从mp4中读取数据,打包rtp发送出去。所以现在使用videotoolbox改成了实时硬编码然后直接打包发送出去。自己写的rtp打包发送的过程可能有问题,所以vlc播放是绿萍的。现在通过使用demo中的rtp打包发送过程,实现了这个功能,但是画面运动时有时候还是会花一下。不知道是不是网络原因。他那里边的一些过程看的还不是太明白。现在先不管了,先开始做pcm硬编码aac,然后rtp打包。大神能不嫩给个方向呢?
级别: 新手上路
可可豆: 42 CB
威望: 53 点
在线时间: 616(时)
发自: Web Page
大神不敢当,一起学习进步。我没有存文件。直接发的流。这样吧。我发些实现的关键代码。1 采集的摄像头数据-(void) captureOutput:(AVCaptureOutput*)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection*)connection{&&&&if (connection==connectionVideo)&&&&{&&&&&&&&[h264Encoder encode:sampleBuffer];&&&&}&&&&else//connectionAudio在你编码264里 有一个编码回调函数。大概是这样申明的 dispatch_sync(aQueue, ^{&&&&&&&&OSStatus status = VTCompressionSessionCreate(NULL, width, height, kCMVideoCodecType_H264, NULL, NULL, NULL, didCompressH264, (__bridge void *)(self),&&&EncodingSession);&&&&&&&&if (status != 0)&&&&&&&&{&&&&&&&&&&&&NSLog(@&H264: Unable to create a H264 session&);&&&&&&&&&&&&&&&&&&&&}&&&&&&&&&&&&&&&&VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_RealTime, kCFBooleanTrue);&&&&&&&&VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_ProfileLevel, kVTProfileLevel_H264_Baseline_4_1);&&&&&&&&&&&&&&&&SInt32 bitRate = width*height*50;&&&&&&&&CFNumberRef ref = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &bitRate);&&&&&&&&VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_AverageBitRate, ref);&&&&&&&&CFRelease(ref);&&&&&&&&int frameInterval = 10; //关键帧间隔&&&&&&&&CFNumberRef&&frameIntervalRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &frameInterval);&&&&&&&&VTSessionSetProperty(EncodingSession, kVTCompressionPropertyKey_MaxKeyFrameInterval,frameIntervalRef);&&&&&&&&CFRelease(frameIntervalRef);&&&&&&&&// Tell the encoder to start encoding&&&&&&&&VTCompressionSessionPrepareToEncodeFrames(EncodingSession);&&&&});我的回调函数是didCompressH264void 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)&&&&&&&&if (!CMSampleBufferDataIsReady(sampleBuffer))&&&&{&&&&&&&&NSLog(@&didCompressH264 data is not ready &);&&&&&&&&&&&&}&&&&H264HwEncoderImpl* encoder = (__bridge H264HwEncoderImpl*)outputCallbackRefC&&&&&&&&// Check if we have got a key frame first&&&&bool keyframe = !CFDictionaryContainsKey( (CFArrayGetValueAtIndex(CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, true), 0)), kCMSampleAttachmentKey_NotSync);&&&&&&&&if (keyframe)&&&&{&&&&&&&&CMFormatDescriptionRef format = CMSampleBufferGetFormatDescription(sampleBuffer);&&&&&&&&&&&&&&&&size_t sparameterSetSize, sparameterSetC&&&&&&&&const uint8_t *sparameterS&&&&&&&&OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 0, &sparameterSet, &sparameterSetSize, &sparameterSetCount, 0 );&&&&&&&&if (statusCode == noErr)&&&&&&&&{&&&&&&&&&&&&// Found sps and now check for pps&&&&&&&&&&&&size_t pparameterSetSize, pparameterSetC&&&&&&&&&&&&const uint8_t *pparameterS&&&&&&&&&&&&OSStatus statusCode = CMVideoFormatDescriptionGetH264ParameterSetAtIndex(format, 1, &pparameterSet, &pparameterSetSize, &pparameterSetCount, 0 );&&&&&&&&&&&&if (statusCode == noErr)&&&&&&&&&&&&{&&&&&&&&&&&&&&&&// Found pps&&&&&&&&&&&&&&&&encoder-&sps = [NSData dataWithBytes:sparameterSet length:sparameterSetSize];&&&&&&&&&&&&&&&&encoder-&pps = [NSData dataWithBytes:pparameterSet length:pparameterSetSize];&&&&&&&&&&&&&&&&if (encoder-&_delegate)&&&&&&&&&&&&&&&&{&&&&&&&&&&&&&&&&&&&&[encoder-&_delegate gotSpsPps:encoder-&sps pps:encoder-&pps];&&&&&&&&&&&&&&&&}&&&&&&&&&&&&}&&&&&&&&}&&&&}&&&&&&&&CMBlockBufferRef dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);&&&&size_t length, totalL&&&&char *dataP&&&&OSStatus statusCodeRet = CMBlockBufferGetDataPointer(dataBuffer, 0, &length, &totalLength, &dataPointer);&&&&if (statusCodeRet == noErr) {&&&&&&&&&&&&&&&&size_t bufferOffset = 0;&&&&&&&&static const int AVCCHeaderLength = 4;&&&&&&&&while (bufferOffset & totalLength - AVCCHeaderLength) {&&&&&&&&&&&&&&&&&&&&&&&&// Read the NAL unit length&&&&&&&&&&&&uint32_t NALUnitLength = 0;&&&&&&&&&&&&memcpy(&NALUnitLength, dataPointer + bufferOffset, AVCCHeaderLength);&&&&&&&&&&&&&&&&&&&&&&&&// Convert the length value from Big-endian to Little-endian&&&&&&&&&&&&NALUnitLength = CFSwapInt32BigToHost(NALUnitLength);&&&&&&&&&&&&&&&&&&&&&&&&NSData* data = [[NSData alloc] initWithBytes:(dataPointer + bufferOffset + AVCCHeaderLength) length:NALUnitLength];&&&&&&&&&&&&[encoder-&_delegate gotEncodedData:data isKeyFrame:keyframe];&&&&&&&&&&&&&&&&&&&&&&&&// Move to the next NAL unit in the block buffer&&&&&&&&&&&&bufferOffset += AVCCHeaderLength + NALUnitL&&&&&&&&}&&&&&&&&&&&&}&&&&}在这里可以得到sps pps I帧&&B/P帧&&我用代理传到了视图控制器&&[encoder-&_delegate gotSpsPps:encoder-&sps pps:encoder-&pps]; [encoder-&_delegate gotEncodedData:data isKeyFrame:keyframe];这两行就是想必你也是在这里存的文件。需要注意的是。硬编码得到的帧是没有0000001这样的头的。需要你自己加下面是视图控制器代码- (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];&&&&//发sps&&&&NSMutableData *h264Data = [[NSMutableData alloc] init];&&&&[h264Data appendData:ByteHeader];&&&&[h264Data appendData:sps];&&&&if (sps!=nil)&&&&{&&&&&&&&if (++mKeyFrameNo & 64)&&&&&&&&{&&&&&&&&&&&&mKeyFrameNo = 1;&&&&&&&&}&&&&&&&&mFrameIndex = 0;&&&&&&&&H264_header h264H&&&&&&&&h264Head.m_bKeyNo = mKeyFrameNo;&&&&&&&&h264Head.m_btIndexNum = mFrameIndex++;&&&&&&&&h264Head.bSize = BSIZE;&&&&&&&&h264Head.bEncoder = 0;&&&&&&&&h264Head.bKeyFrame = 1;&&&&&&&&h264Head.bFlag = 1;&&&&&&&&&&&&&&&&unsigned char spsBuf[h264Data.length+3];&&&&&&&&memset(spsBuf, 0, h264Data.length+3);&&&&&&&&memcpy(spsBuf,&h264Head,sizeof(H264_header));&&&&&&&&memcpy(spsBuf+sizeof(H264_header),[h264Data bytes],h264Data.length);&&&&&&&&NSData *newSPS = [NSData dataWithBytes:spsBuf length:h264Data.length+3];&&&&&&&&&&&&&&&& [self sendH264ToServer:newSPS];//&&&&&&&&[h264Decoder receivedRawVideoFrame:[h264Data bytes] withSize:h264Data.length isIFrame:1];&&&&}&& &&&&//发pps&&&&[h264Data resetBytesInRange:NSMakeRange(0, [h264Data length])];&&&&[h264Data setLength:0];&&&&[h264Data appendData:ByteHeader];&&&&[h264Data appendData:pps];&&&&&&&&if (sps!=nil)&&&&{&&&&&&&&if (++mKeyFrameNo & 64)&&&&&&&&{&&&&&&&&&&&&mKeyFrameNo = 1;&&&&&&&&}&&&&&&&&mFrameIndex = 0;&&&&&&&&&&&&&&&&H264_header h264H&&&&&&&&h264Head.m_bKeyNo = mKeyFrameNo;&&&&&&&&h264Head.m_btIndexNum = 0;&&&&&&&&h264Head.bSize = BSIZE;&&&&&&&&h264Head.bEncoder = 0;&&&&&&&&h264Head.bKeyFrame = 1;&&&&&&&&h264Head.bFlag = 1;&&&&&&&&&&&&&&&&unsigned char ppsBuf[h264Data.length+3];&&&&&&&&memset(ppsBuf, 0, h264Data.length+3);&&&&&&&&memcpy(ppsBuf,&h264Head,sizeof(H264_header));&&&&&&&&memcpy(ppsBuf+sizeof(H264_header),[h264Data bytes],h264Data.length);&&&&&&&&NSData *newPPS = [NSData dataWithBytes:ppsBuf length:h264Data.length+3];&&&&&&&&&&&&&&&&[self sendH264ToServer:newPPS];//&&&&&&&&[h264Decoder receivedRawVideoFrame:[h264Data bytes] withSize:h264Data.length isIFrame:1];&&&&&&&&&&&&}}- (void)gotEncodedData:(NSData*)data isKeyFrame:(BOOL)isKeyFrame{&&&&const char bytes[] = &\x00\x00\x00\x01&;&&&&size_t length = (sizeof bytes) - 1; //string literals have implicit trailing '\0'&&&&NSData *ByteHeader = [NSData dataWithBytes:bytes length:length];&&&&NSMutableData *h264Data = [[NSMutableData alloc] init];&&&&[h264Data appendData:ByteHeader];&&&&[h264Data appendData:data];&&&&&&&&&&&&if (data!=nil)&&&&{&&&&&&&&&&&&&&&&&&&&&&H264_header h264H&&&&&&&&if (isKeyFrame==YES)&&&&&&&&{&&&&&&&&&&&&h264Head.bKeyFrame = 1;&&&&&&&&&&&&if (++mKeyFrameNo & 64)&&&&&&&&&&&&{&&&&&&&&&&&&&&&&mKeyFrameNo = 1;&&&&&&&&&&&&}&&&&&&&&&&&&mFrameIndex = 0;&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&h264Head.bKeyFrame = 0;&&&&&&&&}&&&&&&&&&&&&&&&&h264Head.m_bKeyNo = mKeyFrameNo;&&&&&&&&h264Head.m_btIndexNum = mFrameIndex++;&&&&&&&&h264Head.bSize = BSIZE;&&&&&&&&h264Head.bEncoder = 0;&&&&&&&&&&&&&&&&h264Head.bFlag = 1;&&&&&&&&&&&&&&&&unsigned char dataBuf[h264Data.length+3];&&&&&&&&memset(dataBuf, 0, h264Data.length+3);&&&&&&&&memcpy(dataBuf,&h264Head,sizeof(H264_header));&&&&&&&&memcpy(dataBuf+sizeof(H264_header),[h264Data bytes],h264Data.length);&&&&&&&&NSData *newData = [NSData dataWithBytes:dataBuf length:h264Data.length+3];&&&&&&&&if (isKeyFrame==YES)&&&&&&&&{&&&&&&&&&&&&[self sendH264ToServer:newData];&&&&&&&&}&&&&&&&&else&&&&&&&&{&&&&&&&&&&&&[self sendH264ToServer:newData];&&&&&&&&}&&&&&&&&//&&&&&&&&[h264Decoder receivedRawVideoFrame:[h264Data bytes] withSize:h264Data.length isIFrame:1];&&&&&&&&&&&&&&&&&&&&}&&&&&&&&}这里的顺序。必然是先有sps pps&&然后是i帧。接下来是几个b/p帧。 sendH264ToServer方法是拆包发socket&& 你的项目就拆包加rtp头发,应该是没有问题的
级别: 新手上路
可可豆: 99 CB
威望: 99 点
在线时间: 464(时)
发自: Web Page
回 3楼(srddrs) 的帖子
嗯嗯,在编码后的回调函数中,打印了下while循环中的nalutype,发现,nalutype 有3种可能,等于1、5、6,其中绝大多数都是1,也就说nalu的类型为“非IDR分片”、“IDR分片”、“补充增强信息的分片”,其中主要的部分都是“非IDR分片”。现在有个概念不是很明确,按照camera的回调函数获取到的samplebuffer应该是一帧,那么是不是说nalu 1、5、6都代表着一帧呢。我是觉得一帧应该是可以分成多个nalu的,而不是一帧一个nalu。还有就是,在camera的回调函数中,开发文档里说,一定不要再这里面进行太多的操作,否则会导致回调函数停止运行,现在都摸不准做多少工作不会导致camera停止运行了。是不是需要另外开个线程
级别: 新手上路
可可豆: 42 CB
威望: 53 点
在线时间: 616(时)
发自: Web Page
写了个demo 你看看吧。
级别: 新手上路
可可豆: 5 CB
威望: 5 点
在线时间: 230(时)
发自: Web Page
回 3楼(srddrs) 的帖子
你好,我想请问一下我挂断四次之后为什么VTCompressionSession是为空呢?然后直接走return了呢?- (void)encode:(CMSampleBufferRef )sampleBuffer{&&&&if (EncodingSession==nil||EncodingSession==NULL)&&&&{&&&&&&&&&&&&}&&&&dispatch_sync(aQueue, ^{&&&&&&&&frameCount++;&&&&&&&&CVImageBufferRef imageBuffer = (CVImageBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);&&&&&&&&CMTime presentationTimeStamp = CMTimeMake(frameCount, 1000);&&&&&&&&VTEncodeInfoF&&&&&&&&OSStatus statusCode = VTCompressionSessionEncodeFrame(EncodingSession,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&imageBuffer,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&presentationTimeStamp,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&kCMTimeInvalid,&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&NULL, NULL, &flags);&&&&&&&&if (statusCode != noErr)&&&&&&&&{&&&&&&&&&&&&if (EncodingSession!=nil||EncodingSession!=NULL)&&&&&&&&&&&&{&&&&&&&&&&&&&&&&VTCompressionSessionInvalidate(EncodingSession);&&&&&&&&&&&&&&&&CFRelease(EncodingSession);&&&&&&&&&&&&&&&&EncodingSession = NULL;&&&&&&&&&&&&&&&&&&&&&&&&&&&&}&&&&&&&&}&&&&});}
级别: 新手上路
可可豆: 28 CB
威望: 28 点
在线时间: 36(时)
发自: Web Page
问一下&&使用硬编码打包RTP 后,组包H264 ,使用 VLC 可以播放,但是硬解码 不行 , 大家转换了 avcc 的头吗,0x00 00 00 01 不行
级别: 版主
UID: 123750
发帖: 2246
可可豆: 3653 CB
威望: 3574 点
在线时间: 1798(时)
发自: Web Page
回 4楼(加班的菜鸟) 的帖子
baseline 级别的视频只支持i/p 帧,另外 好多东西做了简化,数据段直接写入非idr片的不分区组,真对你这种情况出现为1 的时候可以断定就是p帧了,当然5就是i 帧
级别: 新手上路
UID: 553043
可可豆: 51 CB
威望: 36 点
在线时间: 37(时)
发自: Web Page
硬编的编码,帧率是怎么控制的呢?试了很多方法,结果编出的视频一直是30帧每秒的。
Pages: 1/2
关注本帖(如果有新回复会站内信通知您)
发帖、回帖都会得到可观的积分奖励。
按"Ctrl+Enter"直接提交
关注CocoaChina
关注微信 每日推荐
扫一扫 关注CVP公众号
扫一扫 浏览移动版}

我要回帖

更多关于 ios h264硬编码 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信