【发布时间】:2014-08-05 10:30:20
【问题描述】:
我正在创建一个 AS3 AIR 应用程序的扩展,它将以 ARGB32 形式接收像素数据,我想使用 H264 编解码器将其渲染到 .mov 容器,然后将其发送到用户的相机胶卷。我目前正在运行一个测试工具,该工具从本地路径(在我的桌面上)获取图像作为 .jpeg,获取像素数据并将其渲染 50 次成短片。我遇到的问题是,当我在 400x200 上渲染它时,它工作正常,但是当我使用任何其他尺寸的图像时,帧出来非常奇怪(图像对角展开,有时右下角有一个小黑条如果有缺失的像素)。
我感觉它与高度和宽度有关,因为这些是我所看到的唯一改变的东西。下面是我的代码:
- (void)initFunction:(NSString *)context width:(int)widthData height:(int)heightData fps:(int)fpsData
{
NSLog(@"Initializing...");
error = nil;
frameCount = 0; //Used for itterating
fps = fpsData; //FPS from AS3
width = widthData; //Width from AS3
height = heightData; //Height from AS3
numberOfSecondsPerFrame = 1.0f/(float)fps; //Seconds per frame
frameDuration = fps * numberOfSecondsPerFrame; //Frame showing time
imageSize = CGSizeMake(width, height); //imageSize from AS3
// Setup and remove pre-existing "render.mov"
fileMgr = [NSFileManager defaultManager];
documentsDirectory = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
videoOutputPath = [documentsDirectory stringByAppendingPathComponent:@"render.mov"];
if ([fileMgr removeItemAtPath:videoOutputPath error:&error] != YES)
{
NSLog(@"This is the first VS render on this device: %@", [error localizedDescription]);
}
else
{
NSLog(@"Removed previous render file in save path.");
}
NSLog(@"Starting render.");
videoWriter = [[AVAssetWriter alloc] initWithURL:[NSURL fileURLWithPath:videoOutputPath] fileType:AVFileTypeQuickTimeMovie error:&error];
NSParameterAssert(videoWriter);
videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
AVVideoCodecH264, AVVideoCodecKey,
[NSNumber numberWithInt:width], AVVideoWidthKey,
[NSNumber numberWithInt:height], AVVideoHeightKey,
nil];
videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
NSDictionary *bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys
[NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];
adaptor = [AVAssetWriterInputPixelBufferAdaptor
assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput
sourcePixelBufferAttributes:nil];
NSParameterAssert(videoWriterInput);
NSParameterAssert([videoWriter canAddInput:videoWriterInput]);
videoWriterInput.expectsMediaDataInRealTime = YES;
[videoWriter addInput:videoWriterInput];
//Start a session:
[videoWriter startWriting];
[videoWriter startSessionAtSourceTime:kCMTimeZero];
buffer = NULL;
NSLog(@"**************************************************");
}
-(void) sendToCameraRoll:(NSString *)videoPath {
UISaveVideoAtPathToSavedPhotosAlbum(videoPath, nil, NULL, nil);
}
//Take in URL from AIR
- (void)addFrameFromBytes:(CGImageRef *)FrameBytes {
UIImage *image = [UIImage imageWithCGImage:*FrameBytes];
CGImageRef cgImage = [image CGImage];
buffer = [self pixelBufferFromCGImage:cgImage];
append_ok = NO;
int j = 0;
while (!append_ok) {
if (adaptor.assetWriterInput.readyForMoreMediaData) {
frameCount = frameCount+1;
NSLog(@"Processing video frame %d",frameCount);
frameTime = CMTimeMake(frameCount*frameDuration,(int32_t) fps);
append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
[NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
if(!append_ok){
error = videoWriter.error;
if(error!=nil) {
NSLog(@"Unresolved error %@,%@.", error, [error userInfo]);
}
}
} else {
printf("Adaptor not ready %d, %d\n", frameCount, j);
[NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
}
j++;
}
if (!append_ok) {
printf("Error appending image %d times %d\n, with error.", frameCount, j);
}
}
//Take in Path from AIR
- (void)addFrameFromURL:(NSString *)FramePath {
UIImage *image = [UIImage imageWithContentsOfFile:FramePath];
CGImageRef cgImage = [image CGImage];
buffer = [self pixelBufferFromCGImage:cgImage];
append_ok = NO;
int j = 0;
while (!append_ok) {
if (adaptor.assetWriterInput.readyForMoreMediaData) {
frameCount = frameCount+1;
NSLog(@"Processing video frame %d",frameCount);
frameTime = CMTimeMake(frameCount*frameDuration,(int32_t) fps);
append_ok = [adaptor appendPixelBuffer:buffer withPresentationTime:frameTime];
[NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
if(!append_ok){
error = videoWriter.error;
if(error!=nil) {
NSLog(@"Unresolved error %@,%@.", error, [error userInfo]);
}
}
} else {
printf("Adaptor not ready %d, %d\n", frameCount, j);
[NSThread sleepForTimeInterval:0.1]; //Prevents system overload but causes lag.
}
j++;
}
if (!append_ok) {
printf("Error appending image %d times %d\n, with error.", frameCount, j);
}
}
- (void)saveVideoWithTrack:(NSString *)AudioTrackPath {
NSLog(@"**************************************************");
//Finish the session:
[videoWriterInput markAsFinished];
[videoWriter finishWritingWithCompletionHandler:^{
NSLog(@"Render complete."); ///Used to satisfy 64-bit devices
}];
sleep(1); //Required to avoid crash due to overload
NSLog(@"Render complete.");
//Audio File Addition
NSLog(@"Singing Tracks...");
AVMutableComposition *mixComposition = [AVMutableComposition composition];
//NSString *bundleDirectory = [[NSBundle mainBundle] bundlePath];
//NSString *audio_inputFilePath = [bundleDirectory stringByAppendingPathComponent:@"30secs.mp3"];
NSURL *audio_inputFileUrl = [NSURL fileURLWithPath:AudioTrackPath];
//Get the final video path
NSURL *video_inputFileUrl = [NSURL fileURLWithPath:videoOutputPath];
//Create the final video amd export as MOV
NSString *outputFilePath = [documentsDirectory stringByAppendingPathComponent:@"final_output.mov"];
NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outputFilePath]) {
[[NSFileManager defaultManager] removeItemAtPath:outputFilePath error:nil];
}
CMTime nextClipStartTime = kCMTimeZero;
AVURLAsset *videoAsset = [[AVURLAsset alloc] initWithURL:video_inputFileUrl options:nil];
CMTimeRange video_timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration);
AVMutableCompositionTrack *a_compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
[a_compositionVideoTrack insertTimeRange:video_timeRange ofTrack:[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] atTime:nextClipStartTime error:nil];
//nextClipStartTime = CMTimeAdd(nextClipStartTime, a_timeRange.duration);
AVURLAsset *audioAsset = [[AVURLAsset alloc]initWithURL:audio_inputFileUrl options:nil];
CMTimeRange audio_timeRange = CMTimeRangeMake(kCMTimeZero, audioAsset.duration);
AVMutableCompositionTrack *b_compositionAudioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
[b_compositionAudioTrack insertTimeRange:audio_timeRange ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:nextClipStartTime error:nil];
AVAssetExportSession *_assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetHighestQuality];
//Supported file types include...
//NSLog(@"Supported file types are %@", [_assetExport supportedFileTypes]);
_assetExport.outputFileType = @"com.apple.quicktime-movie";
_assetExport.outputURL = outputFileUrl;
[_assetExport exportAsynchronouslyWithCompletionHandler:^(){
NSString *exported = [[NSString alloc] initWithString:[outputFileUrl path]];
[self sendToCameraRoll:exported];
}];
NSLog(@"Saved to camera roll. %@", outputFilePath);
}
- (void)saveVideo {
NSLog(@"**************************************************");
//Finish the session:
[videoWriterInput markAsFinished];
[videoWriter finishWritingWithCompletionHandler:^{
NSLog(@"Finished"); //Used to satisfy 64-bit systems.
}];
sleep(1); //Required to avoid system crash
NSLog(@"Render complete.");
[self sendToCameraRoll:videoOutputPath];
NSLog(@"Saved to camera roll. %@", videoOutputPath);
}
- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image {
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
[NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
nil];
CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault,
width,
height,
kCVPixelFormatType_32ARGB,
( CFDictionaryRef) options,
&pxbuffer);
if (status != kCVReturnSuccess){
NSLog(@"Failed to create pixel buffer");
}
CVPixelBufferLockBaseAddress(pxbuffer, 0);
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
size_t bytesPerRow = 4*width; //BytesPerRow 4 bytes each pixel.
CGContextRef imageContext = CGBitmapContextCreate(pxdata, width,
height, 8, bytesPerRow, rgbColorSpace,
kCGBitmapByteOrderDefault | kCGImageAlphaNoneSkipFirst);
CGContextDrawImage(imageContext, CGRectMake(0, 0, width, height), image);
CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(imageContext);
CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
NSLog(@"bPR: %zd", pxbuffer);
return pxbuffer;
}
- (void)viewDidLoad {
[super viewDidLoad];
//[self initFunction:nil width:1138 height:640 fps:25];
//[self initFunction:nil width:910 height:512 fps:25];
//[self initFunction:nil width:400 height:200 fps:25];
[self initFunction:nil width:50 height:50 fps:25];
//find url...
//get pixel data...
//loop through writing it from the pixel data
for (int i = 1; i <= 50; i++) {
//NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/sequence/%06d.jpg", i];
//NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/ThisIsATest/ThisIsATest/image1.jpg"];
//NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/ThisIsATest/ThisIsATest/image2.jpg"];
//NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/mini.jpg"];
NSString *string = [NSString stringWithFormat:@"/Users/Lewis/Desktop/50x50.jpg"];
NSLog(@"%@", string);
[self addFrameFromURL:string];
}
[self saveVideo];
}
编辑:如果图像为 400 x 200,则所有图像都可以使用。 所有这些都适用于 800 x 400、1600 x 800。 600 x 300 不起作用。 可能是纵横比?
【问题讨论】:
标签: ios objective-c xcode codec movie