1

我有一个实现openGL 绘图的uiview。我正在尝试实现一个基本的录制功能,如此处所示。

所有设置都可以正常工作,openGL 绘图也是如此。但是我在 AVAdaptor 尝试附加的第三或第四帧出现错误(前几帧正常)。

我认为这是 CVPixelBufferPoolCreatePixelBuffer 调用的问题,但我无法追踪它......

这是代码。录制代码在底部附近:

#import "ochrDrawingView.h"

#define POINT_FREQUENCY_CONSTANT 0.3 // 1 is a point for every pixel of distance. Note: performance drops above 0.5 on long lines.
#define ARBITRARY_BUFFER_SIZE 2048

typedef struct {
    GLfloat Position[2];
    GLfloat TextureCoordinates[2];
} TexturedVertex;

typedef struct {
    TexturedVertex BottomLeft;
    TexturedVertex BottomRight;
    TexturedVertex TopLeft;
    TexturedVertex TopRight;
} TexturedSquare;

#pragma mark - Implementation

@implementation ochrDrawingView {

    // OpenGL
    EAGLContext *context;
    CAEAGLLayer *eaglLayer;
    GLuint renderBuffer;
    GLuint frameBuffer;
    GLKBaseEffect *effect;
    GLint backingWidth;
    GLint backingHeight;

    // Drawing
    CGSize brushSize;
    float brushScale;
    BOOL isFirstTouch;
    CGPoint origin;
    CGPoint midpoint;
    CGPoint destination;

    // Concurrency
    dispatch_queue_t recordingQueue;

    // Video Recording
    AVAssetWriter *videoWriter;
    AVAssetWriterInput *videoWriterInput;
    AVAssetWriterInputPixelBufferAdaptor *avAdaptor;
    BOOL isRecording;
    BOOL hasFinishedRecording;
    NSDate *recordingBeganAt;
    void* bitmapData;
}

#pragma mark - Setup
- (id)initWithCoder:(NSCoder *)aDecoder {
    NSLog(@"About to initWithCoder...");
    self = [super initWithCoder:aDecoder];
    if (self) {
        isRecording = NO;
        [self setupLayer];
        [self setupContext];
        [self setupRenderBuffer];
        [self setupFrameBuffer];
        [self setViewportParameters];
        [self setupBaseEffect];
        [self eraseScreen];
        recordingQueue = dispatch_queue_create("recordingQueue", NULL);
    }
    return self;
}

+ (Class) layerClass {
    return [CAEAGLLayer class];
}

- (void) setupContext {
    context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    [EAGLContext setCurrentContext:context];
}

- (void) setupLayer {
    eaglLayer = (CAEAGLLayer *) self.layer;
    [eaglLayer setOpaque:YES];
    [eaglLayer setDrawableProperties: [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]];
}

- (void)setupRenderBuffer {
    glGenRenderbuffers(1, &renderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer];
}

- (void) setViewportParameters {
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
    NSLog(@"Width: %d, Height: %d", backingWidth, backingHeight);
    glViewport(0, 0, backingWidth, backingHeight);

}

- (void) setupFrameBuffer {
    glGenFramebuffers(1, &frameBuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);
}

- (void) setupBaseEffect {
    effect = [[GLKBaseEffect alloc] init];

    // Load the texture into the effect
    NSError *error;
    NSDictionary *options = @{ GLKTextureLoaderOriginBottomLeft: @YES };
    NSString *path = [[NSBundle mainBundle] pathForResource:@"brush.png" ofType:nil];
    GLKTextureInfo *texture = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
    if (texture == nil) NSLog(@"Texture failed to load. Error: %@", error.localizedDescription);
    effect.texture2d0.name = texture.name;
    effect.texture2d0.enabled = GL_TRUE;

    // Set the brushSize (used later, in drawing method)
    brushSize = CGSizeMake(texture.width, texture.height);
    brushScale = 0.6;

    // Set up a project and modelview matrix. maybe:
    GLKMatrix4 projectionMatrix = GLKMatrix4MakeOrtho(0, backingWidth, 0, backingHeight, 0, 1.0f);
    effect.transform.projectionMatrix = projectionMatrix;
    [effect prepareToDraw];

}

- (void) eraseScreen {
    [EAGLContext setCurrentContext:context];

    // Clear the buffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];
}


#pragma mark - Touch Response

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    isFirstTouch = YES;
    origin = [[touches anyObject] locationInView:self];
    origin.y = backingHeight - origin.y;
    [self drawPointAtPoint:origin];
}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    if (isFirstTouch) {
        isFirstTouch = NO;
        midpoint = [[touches anyObject] locationInView:self];
        midpoint.y = backingHeight - midpoint.y;
    } else {
        destination = [[touches anyObject] locationInView:self];
        destination.y = backingHeight - destination.y;
        [self drawCubicLineFromPoint:origin  HalfwayToPoint:destination WithControl:midpoint];
    }
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
    [self touchesMoved:touches withEvent:event];
}

#pragma mark - Drawing Algorithms


- (void)drawCubicLineFromPoint:(CGPoint)start HalfwayToPoint:(CGPoint)end WithControl:(CGPoint)control {

    static GLfloat tempVertexBuffer[ARBITRARY_BUFFER_SIZE];
    static GLubyte tempIndexBuffer[ARBITRARY_BUFFER_SIZE];

    int vertexCount = 0;
    int indexCount = 0;
    float pointCount;

    // [EAGLContext setCurrentContext:context];
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    // Get the number of points to be drawn between the two points
    float distance = sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y));
    pointCount = MAX(ceilf(distance * POINT_FREQUENCY_CONSTANT), 1);

    // Adjust the size of the brush based on the (rough) speed of the stroke
    if (distance > 20) {
        brushScale = 0.5;
    } else {
        brushScale = 0.6;
    }

    // Get the adjustment value used to center each texture (the brush image is a square, so here I use only width)
    float positionAdjustmentToCenterTexture = brushScale * brushSize.width / 2.0;

    // Iterate through the points to be drawn, drawing a TexturedSquare (more or less) for each.
    float t = 0.0, x, y;
    for (float i = 0; i < pointCount; i++) {

        x = powf(1 - t, 2) * start.x + 2 * (1 - t) * t * control.x + t * t * end.x;
        y = powf(1 - t, 2) * start.y + 2 * (1 - t) * t * control.y + t * t * end.y;
        t += 0.5 / pointCount;

        // Bottom-left vertex
        tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 0;
        tempVertexBuffer[4 * vertexCount + 3] = 0;
        vertexCount++;

        // Bottom-right vertex
        tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 1;
        tempVertexBuffer[4 * vertexCount + 3] = 0;
        vertexCount++;

        // Top-left vertex
        tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 0;
        tempVertexBuffer[4 * vertexCount + 3] = 1;
        vertexCount++;

        // Top-right vertex
        tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 1;
        tempVertexBuffer[4 * vertexCount + 3] = 1;
        vertexCount++;

        // Add the indices for the triangles
        tempIndexBuffer[indexCount++] = vertexCount - 4;
        tempIndexBuffer[indexCount++] = vertexCount - 3;
        tempIndexBuffer[indexCount++] = vertexCount - 2;
        tempIndexBuffer[indexCount++] = vertexCount - 3;
        tempIndexBuffer[indexCount++] = vertexCount - 2;
        tempIndexBuffer[indexCount++] = vertexCount - 1;
    }

    origin = CGPointMake(x, y); // sets the origin to the last point drawn
    midpoint = end; // sets the midpoint to the previous destination.

    long pointer = (long)&tempVertexBuffer;
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, Position));

    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, TextureCoordinates));

    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_BYTE, tempIndexBuffer);

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];

    // If recording, record this frame
    if (isRecording) {
        dispatch_async(recordingQueue, ^{
            [self writeCurrentFrame];
        });
    }
}


- (void)drawStraightLineFromPoint:(CGPoint)start ToPoint:(CGPoint)end {

    GLfloat tempVertexBuffer[128];
    GLubyte tempIndexBuffer[128];

    int vertexCount = 0;
    int indexCount = 0;
    float pointCount;

    // Dumb setup stuff.
    [EAGLContext setCurrentContext:context];
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    // Get the number of points to be drawn between the two points
    pointCount = MAX(ceilf(sqrtf((end.x - start.x) * (end.x - start.x) + (end.y - start.y) * (end.y - start.y)) * POINT_FREQUENCY_CONSTANT), 1);

    // Get the adjustment value used to center each texture (the brush image is a square, so here I use only width)
    float positionAdjustmentToCenterTexture = brushScale * brushSize.width / 2.0;

    // Iterate through the points to be drawn, drawing a TexturedSquare (more or less) for each.
    float x;
    float y;
    for (float i = 0; i < pointCount; i++) {

        // Set the x and y coordinates for each points, interpolating based on the distance
        x = start.x + ((end.x - start.x) * (i / pointCount));
        y = start.y + (end.y - start.y) * (i / pointCount);

        // Bottom-left vertex
        tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 0;
        tempVertexBuffer[4 * vertexCount + 3] = 0;
        vertexCount++;

        // Bottom-right vertex
        tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 1;
        tempVertexBuffer[4 * vertexCount + 3] = 0;
        vertexCount++;

        // Top-left vertex
        tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 0;
        tempVertexBuffer[4 * vertexCount + 3] = 1;
        vertexCount++;

        // Top-right vertex
        tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
        tempVertexBuffer[4 * vertexCount + 2] = 1;
        tempVertexBuffer[4 * vertexCount + 3] = 1;
        vertexCount++;

        // Add the indices for the triangles
        tempIndexBuffer[indexCount++] = vertexCount - 4;
        tempIndexBuffer[indexCount++] = vertexCount - 3;
        tempIndexBuffer[indexCount++] = vertexCount - 2;
        tempIndexBuffer[indexCount++] = vertexCount - 3;
        tempIndexBuffer[indexCount++] = vertexCount - 2;
        tempIndexBuffer[indexCount++] = vertexCount - 1;
    }

    long pointer = (long)&tempVertexBuffer;
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, Position));

    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, TextureCoordinates));

    // Last parameter may be wrong...
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_BYTE, tempIndexBuffer);

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];
}

- (void)drawPointAtPoint:(CGPoint)start {

    GLfloat tempVertexBuffer[16];
    GLubyte tempIndexBuffer[6];

    int vertexCount = 0;
    int indexCount = 0;

    // Dumb setup stuff.
    // [EAGLContext setCurrentContext:context];
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);

    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);

    // Get the adjustment value used to center each texture (the brush image is a square, so here I use only width)
    float positionAdjustmentToCenterTexture = brushScale * brushSize.width / 2.0;

    // Iterate through the points to be drawn, drawing a TexturedSquare (more or less) for each.
    float x = start.x;
    float y = start.y;

    // Bottom-left vertex
    tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 2] = 0;
    tempVertexBuffer[4 * vertexCount + 3] = 0;
    vertexCount++;

    // Bottom-right vertex
    tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 1] = y - positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 2] = 1;
    tempVertexBuffer[4 * vertexCount + 3] = 0;
    vertexCount++;

    // Top-left vertex
    tempVertexBuffer[4 * vertexCount + 0] = x - positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 2] = 0;
    tempVertexBuffer[4 * vertexCount + 3] = 1;
    vertexCount++;

    // Top-right vertex
    tempVertexBuffer[4 * vertexCount + 0] = x + positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 1] = y + positionAdjustmentToCenterTexture;
    tempVertexBuffer[4 * vertexCount + 2] = 1;
    tempVertexBuffer[4 * vertexCount + 3] = 1;
    vertexCount++;

    // Add the indices for the triangles
    tempIndexBuffer[indexCount++] = vertexCount - 4;
    tempIndexBuffer[indexCount++] = vertexCount - 3;
    tempIndexBuffer[indexCount++] = vertexCount - 2;
    tempIndexBuffer[indexCount++] = vertexCount - 3;
    tempIndexBuffer[indexCount++] = vertexCount - 2;
    tempIndexBuffer[indexCount++] = vertexCount - 1;

    long pointer = (long)&tempVertexBuffer;
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, Position));

    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *)pointer + offsetof(TexturedVertex, TextureCoordinates));

    // Last parameter may be wrong...
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_BYTE, tempIndexBuffer);

    // Display the buffer
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER];
}


#pragma mark - Video Recorder Functions

- (BOOL) setupVideoWriter {
    NSError* error = nil;
    videoWriter = [[AVAssetWriter alloc] initWithURL:[self temporaryFileURL] fileType:AVFileTypeQuickTimeMovie error:&error];
    NSParameterAssert(videoWriter);

    //Configure video
    NSDictionary* videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                           [NSNumber numberWithDouble:1024.0*1024.0], AVVideoAverageBitRateKey,
                                           nil ];

    NSDictionary* videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                   AVVideoCodecH264, AVVideoCodecKey,
                                   [NSNumber numberWithInt:self.frame.size.width], AVVideoWidthKey,
                                   [NSNumber numberWithInt:self.frame.size.height], AVVideoHeightKey,
                                   videoCompressionProps, AVVideoCompressionPropertiesKey,
                                   nil];

    videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings];
    NSParameterAssert(videoWriterInput);

    videoWriterInput.expectsMediaDataInRealTime = YES;
    NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                      [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];

    avAdaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput
                                                                                 sourcePixelBufferAttributes:bufferAttributes];

    //add input
    [videoWriter addInput:videoWriterInput];
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:CMTimeMake(0, 1000)];

    return YES;
}

- (CGContextRef) getContextOfSize:(CGSize)size {
    CGContextRef    tempContext = NULL;
    CGColorSpaceRef colorSpace;
    int             bitmapByteCount;
    int             bitmapBytesPerRow;

    bitmapBytesPerRow   = size.width * 4;
    bitmapByteCount     = bitmapBytesPerRow * size.height;
    colorSpace = CGColorSpaceCreateDeviceRGB();

    if (bitmapData != NULL) {
        free(bitmapData);
    }

    bitmapData = malloc( bitmapByteCount );

    if (bitmapData == NULL) {
        fprintf (stderr, "Memory not allocated!");
        return NULL;
    }

    tempContext = CGBitmapContextCreate (bitmapData,
                                         size.width,
                                         size.height,
                                         8,  
                                         bitmapBytesPerRow,
                                         colorSpace,
                                         kCGImageAlphaNoneSkipFirst);

    CGContextSetAllowsAntialiasing(tempContext, NO);
    if (tempContext == NULL) {
        free (bitmapData);
        fprintf (stderr, "Context not created!");
        return NULL;
    }
    CGColorSpaceRelease(colorSpace);

    return tempContext;
}

- (void) writeCurrentFrame {

    NSLog(@"writeCurrentFrame called");

    // Get a context
    CGContextRef videoContext = [self getContextOfSize:self.frame.size];

    // Render the current screen into that context
    [self.layer renderInContext:videoContext];

    // Get a CGImage from the context
    CGImageRef cgImage = CGBitmapContextCreateImage(videoContext);

    // Check if the AVAssetWriterInput is ready for more data
    if (![videoWriterInput isReadyForMoreMediaData]) {

        NSLog(@"Not ready for video data");

    } else {

        // If it is, convert the CGImage into a CVPixelBufferReference
        CVPixelBufferRef pixelBuffer = NULL;
        CFDataRef cfImage = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
        int status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, avAdaptor.pixelBufferPool, &pixelBuffer);

        if (status != 0) {
            NSLog(@"Error creating pixel buffer. Status: %d", status);
        } else {
            NSLog(@"No error creating the pixel buffer...");
        }

        // Set image data into pixel buffer
        CVPixelBufferLockBaseAddress(pixelBuffer, 0);
        uint8_t* destPixels = CVPixelBufferGetBaseAddress(pixelBuffer);
        CFDataGetBytes(cfImage, CFRangeMake(0, CFDataGetLength(cfImage)), destPixels);

        // If all's well so far, append the pixelbuffer to the adaptor
        if (status == 0) {
            float millisecondsSinceStart = [[NSDate date] timeIntervalSinceDate:recordingBeganAt];
            BOOL success = [avAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:CMTimeMake((int) millisecondsSinceStart, 1000)];
            if (!success) {
                NSLog(@"Warning:  Unable to write buffer to video.");
            } else {
                NSLog(@"Success! Was able to write buffer to video.");
            }
        }

        // Clean up
        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        // CVPixelBufferRelease(pixelBuffer);
        CFRelease(cfImage);
        CGImageRelease(cgImage);
        CGContextRelease(videoContext);
    }
}

- (BOOL) completeRecordingSession {
    return YES;
}

- (NSURL *) temporaryFileURL {
    NSString* outputPath = [[NSString alloc] initWithFormat:@"%@/%@", [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0], @"output.mp4"];
    NSURL* outputURL = [[NSURL alloc] initFileURLWithPath:outputPath];
    NSLog(@"Will try to store the file at %@", outputURL);
    NSFileManager* fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:outputPath]) {
        NSLog(@"There is already a file there - trying to delete it...");
        NSError* error;
        if ([fileManager removeItemAtPath:outputPath error:&error] == NO) {
            NSLog(@"Could not delete old recording file at path:  %@", outputPath);
        } else {
            NSLog(@"Sucessfully deleted file. The new file can be stored at %@", outputURL);
        }
    } else {
        NSLog(@"File can be stored at %@", outputURL);
    }
    return outputURL;
}

- (BOOL) startRecording {
    isRecording = YES;
    return [self setupVideoWriter];;
}

- (BOOL) stopRecording {
    return YES;
}





#pragma mark - Helper Functions

- (void) logLocationOfPoint:(CGPoint)point {
    NSLog(@"point at { %d, %d }", (int) point.x, (int) point.y);
}


@end
4

1 回答 1

2

对于任何好奇的人:

上述方法采用了有缺陷的方法。一般来说,渲染成纹理,然后从纹理进入渲染缓冲区要好得多。额外步骤对性能的影响很小,而且这种方法在流式传输、录制、处理等方面更具可扩展性。

最好的。小号

于 2013-11-10T21:06:40.200 回答