- objective-c - iOS 5 : Can you override UIAppearance customisations in specific classes?
- iphone - 如何将 CGFontRef 转换为 UIFont?
- ios - 以编程方式关闭标记的信息窗口 google maps iOS
- ios - Xcode 5 - 尝试验证存档时出现 "No application records were found"
是否可以并支持使用 iOS 硬件加速 h.264 解码 API 来解码本地(非流式)视频文件,然后在其上合成其他对象?
我想制作一个涉及在视频前面绘制图形对象的应用程序,并使用播放计时器将我在顶部绘制的内容与视频中正在播放的内容同步。然后,根据用户的操作,更改我在上面绘制的内容(但不是视频)
来自 Android 的 DirectX、OpenGL 和 OpenGL ES,我正在想象将视频渲染为纹理,并使用该纹理绘制全屏四边形,然后使用其他 Sprite 绘制其余对象;或者也许在渲染器之前编写一个中间过滤器,这样我就可以操纵各个输出帧并绘制我的东西;或者可能绘制到视频顶部的 2D 图层。
似乎 AV Foundation 或 Core Media 可以帮助我做我正在做的事情,但在我深入细节之前,我想知道是否有可能做我想做什么,我解决问题的主要途径是什么。
请避免使用“这对您来说太高级了,请先尝试 hello world”的回答。我知道我的东西,只是想知道我想做的事情是否可行(最重要的是,支持,所以应用程序最终不会被拒绝),然后再自己研究细节。
编辑:
我对 iOS 开发一无所知,但专业为 Android 开发 DirectX、OpenGL 和 OpenGL ES。我正在考虑制作我目前拥有的 Android 应用程序的 iOS 版本,我只是想知道这是否可能。如果是这样,我有足够的时间从头开始 iOS 开发,直到做我想做的事。如果不可能,那么我现在就不会花时间研究整个平台。
因此,这是一个技术可行性问题。我不是在请求代码。我正在寻找“是的,你可以做到这一点。只需使用 A 和 B,使用 C 渲染到 D 并用 E 绘制你的东西”类型的答案,或者“不,你不能。硬件加速解码是不适用于第三方应用程序”(这是一位 friend 告诉我的)。仅此而已,我会继续前进。
我已阅读 ios technology overview 的第 32 页中的视频技术概述。 .它几乎说我可以使用 Media Player 实现最简单的播放功能(不是我想要的),UIKit 用于嵌入视频,对嵌入有更多控制,但不能控制实际播放(不是我想要的)我正在寻找),AVFoundation 可以更好地控制播放(也许是我需要的,但我在网上找到的大部分资源都在谈论如何使用相机),或者 Core Media 可以对视频进行完全低级控制(可能是我需要的需要,但是 extremely poorly documented ,甚至比 AVFoundation 更缺乏播放资源)。
我担心我可能会在接下来的六个月里全职学习 iOS 编程,最后发现相关 API 对第三方开发者不可用,而我想做的事情对 iTunes 商店来说是 Not Acceptable 部署。这是我 friend 告诉我的,但我似乎在应用程序开发指南中找不到任何相关内容。因此,我来这里是想请教这方面比较有经验的人,我想做的事情是否可行。没有了。
我认为这是一个有效的高级问题,可能会被误解为我没有做我的家庭作业请给我我的代码问题。如果我在这里的判断是错误的,请随意删除,或者将这个问题投给你的内心蔑视。
最佳答案
是的,您可以这样做,我认为您的问题非常具体,适合放在此处。您不是唯一想这样做的人,确实需要深入挖掘才能弄清楚您能做什么和不能做什么。
AV Foundation 允许您使用 AVAssetReader 对 H.264 视频进行硬件加速解码,此时您将获得 BGRA 格式的原始解码视频帧。这些可以使用 glTexImage2D()
或 iOS 5.0 中更高效的纹理缓存上传到纹理。从那里,您可以处理显示或从 OpenGL ES 检索帧,并使用 AVAssetWriter 对结果执行硬件加速的 H.264 编码。所有这些都使用公共(public) API,因此您绝不会接近会导致 App Store 拒绝的东西。
但是,您不必自行实现。我的 BSD 许可开源框架 GPUImage封装了这些操作并为您处理所有这些。您为输入的 H.264 电影创建一个 GPUImageMovie 实例,在其上附加滤镜(例如叠加混合或色度键控操作),然后将这些滤镜附加到 GPUImageView 以进行显示和/或 GPUImageMovieWriter 以重新编码 H. 264 视频中的电影。
我目前遇到的一个问题是我不遵守播放视频中的时间戳,因此帧的处理速度与从电影中解码的速度一样快。对于视频的过滤和重新编码,这不是问题,因为时间戳会传递到记录器,但对于直接显示在屏幕上,这意味着视频可以加速 2-4 倍.我欢迎任何能让您将播放速率与实际视频时间戳同步的贡献。
我目前可以在 iPhone 4 上以超过 30 FPS 的速度播放、过滤和重新编码 640x480 视频,以约 20-25 FPS 的速度播放 720p 视频,而 iPhone 4S 能够以更高的速度进行 1080p 过滤和编码超过 30 FPS。一些更昂贵的过滤器可能会对 GPU 造成负担并稍微降低速度,但大多数过滤器都在这些帧速率范围内运行。
如果你愿意,你可以检查 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);
}
}
关于iphone - 硬件加速 h.264 解码到 iOS 中的纹理、覆盖或类似内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10646657/
我知道 C++ 中的 overriding 是什么。但是,是否存在覆盖?如果有,是什么意思? 谢谢。 最佳答案 在 C++ 术语中,您有 覆盖(与类层次结构中的虚拟方法相关)和 重载(与具有相同名称但
我想捕获位于另一个元素下的元素的鼠标事件。 这是我所拥有的示例:http://jsfiddle.net/KVLkp/13/ 现在我想要的是当鼠标悬停在红色方 block 上时蓝色方 block 有黄色
以下报道 here我尝试创建一个带有重叠散点图的箱线图。 但是当我运行时: In [27]: table1.t_in[table1.duration==6] Out[27]: counter 7
有一个 JS Fiddle here , 你能在不克隆到新对象的情况下替换 e.target 吗? 下面重复了那个 fiddle 的听众; one.addEventListener('click',
首先要解决重复的可能性: 我不是询问 Override 是什么、它的含义或 @Override 在 java 文档注释之外。那是我不是问 /**Some JavaDoc Comment*/ @over
我想要高于定义的数组。它存储点及其坐标。 public static List simpleGraph(List nodes) { int numEdges = nodes.size() *
我在 http://olisan.dk/blog/ 有一个博客- 如您所见,有一个 28 像素的高间隙(边距顶部)...在 style.css 中: margin-top: 0; 也被设置为 marg
Vulkan 句柄是指向 struct 的不透明指针,或者只是无符号的 64 位整数,具体取决于 VK_USE_64_BIT_PTR_DEFINES 的值: #if (VK_USE_64_BI
我正在尝试提供一个行为类似于 DataGridTextColumn 的 DataGrid 列,但在编辑模式下有一个附加按钮。我查看了 DataGridTemplateColumn,但似乎更容易将 Da
使用 Django 1.10 我想在用户名中允许\字符,因为我在使用“django.contrib.auth.middleware.RemoteUserMiddleware”的 Windows 环境中
我正在尝试使用 ffmpeg 将 Logo 放入 rtmp 流中。我的 ffmpeg 版本是 ffmpeg version 4.3.1目前在我的复杂过滤器中,我有: ffmpeg -re -i 'v
是否有用于Firebase 3存储的方法/规则来禁用文件更新或覆盖? 我为数据库找到了data.exists(),但没有为存储找到解决方案。 最佳答案 TL; DR:在Storage Security
我有两个 Docker Compose 文件,docker-compose.yml看起来像这样 version: '2' services: mongo: image: mongo:3.2
我需要覆盖 JPA 中的集合表吗?也许有人有想法 public class nationality{ @Embedded @AttributeOverrides({
嗨,我正在使用 WIX 和下面的代码将文件安装到目录中。 我的应用程序的工作方式是用户可以在该目录中复制他们自己的文件,覆盖他们喜欢的内容
我正在尝试为 Lua 中的字符串实现我自己的长度方法。 我已成功覆盖字符串的 len() 方法,但我不知道如何为 # 运算符执行此操作。 orig_len = string.len function
在Scala 2.10.4中,给出以下类: scala> class Foo { | val x = true | val f = if (x) 100 else 200
我想做上面的事情。 我过去覆盖了许多文件...... block ,模型,助手......但这个让我望而却步。 谁能看到我在这里做错了什么: (我编辑了这段代码......现在包括一些建议......
根据javadoc An instance method in a subclass with the same signature (name, plus the number and the ty
我有一段代码,只要有可用的新数据作为 InputStream 就会生成新数据。每次都覆盖同一个文件。有时文件在写入之前变为 0 kb。 Web 服务会定期读取这些文件。我需要避免文件为 0 字节的情况
我是一名优秀的程序员,十分优秀!