硬件加速h.264解码到纹理,覆盖或类似的iOS

是否有可能并支持使用iOS硬件加速h.264解码API来解码本地(非stream式)video文件,然后在其上编写其他对象?

我想制作一个应用程序,它涉及在video前面绘制graphics对象,并使用播放定时器将我在上面绘制的内容与video上正在播放的内容同步。 然后,根据用户的操作,改变我正在绘制的顶部(而不是video)

来自DirectX,OpenGL和Android的OpenGL ES,我想像的是将video渲染为纹理,并使用该纹理绘制全屏幕四边形,然后使用其他精灵来绘制其余的对象; 或者可能在渲染器之前写一个中间filter,这样我就可以操纵单个输出框架并绘制我的东西; 或者可以在video顶部绘制一个2D图层。

AV基金会或核心媒体似乎可以帮助我做我正在做的事情,但在深入细节之前,我想知道是否有可能做我想做的事情,我的主要路线来解决问题。

请不要“这太先进了,先试试世界先声”的答案吧。 我知道我的东西,只是想知道我想做什么是可能的(最重要的是,支持,所以应用程序不会最终被拒绝),我自己研究的细节之前。

编辑:

我对iOS开发不熟悉,但专业地为Android做DirectX,OpenGL和OpenGL ES。 我正在考虑制作一个我现在拥有的Android应用程序的iOS版本,我只想知道这是否可能。 如果是这样,我有足够的时间从头开始iOS开发,直到做我想做的事情。 如果不可能,那么我现在就不会花时间研究整个平台。

所以这是一个技术上的可行性问题。 我不是在请求代码。 我正在寻找types为“是的,你可以这样做的答案,只需使用A和B,使用C渲染到D并用E画出你的东西”,或者“不,你不能,硬件加速解码是不适用于第三方应用程序“(这是朋友告诉我的)。 就这样,我会在路上。

我已阅读ios技术概述第32页中的video技术概述 。 它几乎说,我可以使用媒体播放器的最简单的播放function(不是我所期待的),UIKitembeddedvideo多一点控制embedded,但不是在实际播放(而不是我的'寻找),AVFoundation更多的控制回放(也许我需要的,但我在网上find的大部分资源谈论如何使用相机),或核心媒体有完整的低级别的video控制(可能是我需要,但是logging极其糟糕 ,甚至比AVFoundation更缺乏播放资源)。

我担心我可能会在接下来的六个月中全职学习iOS编程,但最终却发现相关API不适用于第三方开发人员,我想做的是iTunes Store部署无法接受。 这就是我的朋友告诉我的,但我似乎无法在应用程序开发指南中find任何相关的东西。 因此,我来​​这里问问在这方面有多less经验的人,不pipe我想做什么都是可能的。 不再。

我认为这是一个有效的高层次的问题,可以被误解为一个我没有做我的家庭作业的PLZ给我这个codez的问题。 如果我在这里的判断是错误的,可以随意删除,或者低估这个问题到你的内心的蔑视。

是的,你可以做到这一点,我认为你的问题是足够具体的属于这里。 你不是唯一一个想要这样做的人,而且要花一点时间来弄清楚你能做什么,不能做什么。

AV Foundation可让您使用AVAssetReader对H.264video进行硬件加速解码,此时您将BGRA格式的video原始解码帧交给您。 可以使用glTexImage2D()或iOS 5.0中更高效的纹理caching将其上传到纹理。 从那里,您可以处理显示或从OpenGL ES中检索帧,并使用AVAssetWriter对结果执行硬件加速的H.264编码。 所有这些都使用公共API,所以你从来没有得到任何可能导致App Store拒绝的东西。

但是,您不必自行执行此操作。 我的BSD许可的开源框架GPUImage封装了这些操作,并为您处理所有这些。 您可以为input的H.264电影创build一个GPUImageMovie实例,将滤镜附加到其上(例如叠加混合或色度键控操作),然后将这些滤镜附加到GPUImageView以供显示和/或GPUImageMovieWriter重新编码H. 264video中的电影。

我现在面临的一个问题是,我不遵守video中的时间戳进行播放,因此帧的处理速度与其从电影中解码一样快。 对于video的过滤和重新编码,这不是一个问题,因为时间戳传递到logging器,但是直接显示到屏幕上,这意味着video可以加速2-4倍。 我欢迎任何可以让您将播放速率与实际video时间戳同步的贡献。

我现在可以在iPhone 4和720pvideo下以20-25 FPS的速度以30 FPS以上的速度播放,过滤和重新编码640x480video,iPhone 4S能够以高于30 FPS的速度进行1080p过滤和编码。 一些更昂贵的filter可以使GPU减税并使其稍微降低一些,但大多数filter都在这些帧率范围内运行。

如果你愿意,你可以检查GPUImageMovie类,看看它是如何上传到OpenGL ES的,但是相关的代码如下:

 - (void)startProcessing; { NSDictionary *inputOptions = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:AVURLAssetPreferPreciseDurationAndTimingKey]; AVURLAsset *inputAsset = [[AVURLAsset alloc] initWithURL:self.url options:inputOptions]; [inputAsset loadValuesAsynchronouslyForKeys:[NSArray arrayWithObject:@"tracks"] completionHandler: ^{ NSError *error = nil; AVKeyValueStatus tracksStatus = [inputAsset statusOfValueForKey:@"tracks" error:&error]; if (!tracksStatus == AVKeyValueStatusLoaded) { return; } reader = [AVAssetReader assetReaderWithAsset:inputAsset error:&error]; NSMutableDictionary *outputSettings = [NSMutableDictionary dictionary]; [outputSettings setObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey: (NSString*)kCVPixelBufferPixelFormatTypeKey]; // Maybe set alwaysCopiesSampleData to NO on iOS 5.0 for faster video decoding AVAssetReaderTrackOutput *readerVideoTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:[[inputAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] outputSettings:outputSettings]; [reader addOutput:readerVideoTrackOutput]; NSArray *audioTracks = [inputAsset tracksWithMediaType:AVMediaTypeAudio]; BOOL shouldRecordAudioTrack = (([audioTracks count] > 0) && (self.audioEncodingTarget != nil) ); AVAssetReaderTrackOutput *readerAudioTrackOutput = nil; if (shouldRecordAudioTrack) { audioEncodingIsFinished = NO; // This might need to be extended to handle movies with more than one audio track AVAssetTrack* audioTrack = [audioTracks objectAtIndex:0]; readerAudioTrackOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:nil]; [reader addOutput:readerAudioTrackOutput]; } if ([reader startReading] == NO) { NSLog(@"Error reading from file at URL: %@", self.url); return; } if (synchronizedMovieWriter != nil) { __unsafe_unretained GPUImageMovie *weakSelf = self; [synchronizedMovieWriter setVideoInputReadyCallback:^{ [weakSelf readNextVideoFrameFromOutput:readerVideoTrackOutput]; }]; [synchronizedMovieWriter setAudioInputReadyCallback:^{ [weakSelf readNextAudioSampleFromOutput:readerAudioTrackOutput]; }]; [synchronizedMovieWriter enableSynchronizationCallbacks]; } else { while (reader.status == AVAssetReaderStatusReading) { [self readNextVideoFrameFromOutput:readerVideoTrackOutput]; if ( (shouldRecordAudioTrack) && (!audioEncodingIsFinished) ) { [self readNextAudioSampleFromOutput:readerAudioTrackOutput]; } } if (reader.status == AVAssetWriterStatusCompleted) { [self endProcessing]; } } }]; } - (void)readNextVideoFrameFromOutput:(AVAssetReaderTrackOutput *)readerVideoTrackOutput; { if (reader.status == AVAssetReaderStatusReading) { CMSampleBufferRef sampleBufferRef = [readerVideoTrackOutput copyNextSampleBuffer]; if (sampleBufferRef) { runOnMainQueueWithoutDeadlocking(^{ [self processMovieFrame:sampleBufferRef]; }); CMSampleBufferInvalidate(sampleBufferRef); CFRelease(sampleBufferRef); } else { videoEncodingIsFinished = YES; [self endProcessing]; } } else if (synchronizedMovieWriter != nil) { if (reader.status == AVAssetWriterStatusCompleted) { [self endProcessing]; } } } - (void)processMovieFrame:(CMSampleBufferRef)movieSampleBuffer; { CMTime currentSampleTime = CMSampleBufferGetOutputPresentationTimeStamp(movieSampleBuffer); CVImageBufferRef movieFrame = CMSampleBufferGetImageBuffer(movieSampleBuffer); int bufferHeight = CVPixelBufferGetHeight(movieFrame); int bufferWidth = CVPixelBufferGetWidth(movieFrame); CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent(); if ([GPUImageOpenGLESContext supportsFastTextureUpload]) { CVPixelBufferLockBaseAddress(movieFrame, 0); [GPUImageOpenGLESContext useImageProcessingContext]; CVOpenGLESTextureRef texture = NULL; CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, coreVideoTextureCache, movieFrame, NULL, GL_TEXTURE_2D, GL_RGBA, bufferWidth, bufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture); if (!texture || err) { NSLog(@"Movie CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err); return; } outputTexture = CVOpenGLESTextureGetName(texture); // glBindTexture(CVOpenGLESTextureGetTarget(texture), outputTexture); glBindTexture(GL_TEXTURE_2D, outputTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); for (id<GPUImageInput> currentTarget in targets) { NSInteger indexOfObject = [targets indexOfObject:currentTarget]; NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue]; [currentTarget setInputSize:CGSizeMake(bufferWidth, bufferHeight) atIndex:targetTextureIndex]; [currentTarget setInputTexture:outputTexture atIndex:targetTextureIndex]; [currentTarget newFrameReadyAtTime:currentSampleTime]; } CVPixelBufferUnlockBaseAddress(movieFrame, 0); // Flush the CVOpenGLESTexture cache and release the texture CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0); CFRelease(texture); outputTexture = 0; } else { // Upload to texture CVPixelBufferLockBaseAddress(movieFrame, 0); glBindTexture(GL_TEXTURE_2D, outputTexture); // Using BGRA extension to pull in video frame data directly glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferWidth, bufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(movieFrame)); CGSize currentSize = CGSizeMake(bufferWidth, bufferHeight); for (id<GPUImageInput> currentTarget in targets) { NSInteger indexOfObject = [targets indexOfObject:currentTarget]; NSInteger targetTextureIndex = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue]; [currentTarget setInputSize:currentSize atIndex:targetTextureIndex]; [currentTarget newFrameReadyAtTime:currentSampleTime]; } CVPixelBufferUnlockBaseAddress(movieFrame, 0); } if (_runBenchmark) { CFAbsoluteTime currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime); NSLog(@"Current frame time : %f ms", 1000.0 * currentFrameTime); } }