blob: 70ff004a697e9e15288eb1c2d8f814420a331525 [file] [log] [blame]
/*
* Copyright (C) 2019 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "VideoCommon.h"
#import <video/renderer.h>
#import <QSize>
extern "C" {
#import <libavutil/frame.h>
}
@implementation RendererConnectionsHolder
@end
@implementation VideoCommon
+ (void)copyLineByLineSrc:(uint8_t*)src
toDest:(uint8_t*)dest
srcLinesize:(size_t)srcLinesize
destLinesize:(size_t)destLinesize
height:(size_t)height {
for (size_t i = 0; i < height ; i++) {
memcpy(dest, src, srcLinesize);
dest = dest + destLinesize;
src = src + srcLinesize;
}
}
+ (void) fillPixelBuffr:(CVPixelBufferRef &)pixelBuffer
fromFrame:(const AVFrame*)frame
bufferPool:(CVPixelBufferPoolRef &)pixelBufferPool {
if(!frame || !frame->data[0] || !frame->data[1]) {
return;
}
CVReturn theError;
bool createPool = false;
if (!pixelBufferPool) {
createPool = true;
} else {
NSDictionary* atributes = (__bridge NSDictionary*)CVPixelBufferPoolGetAttributes(pixelBufferPool);
if(!atributes)
atributes = (__bridge NSDictionary*)CVPixelBufferPoolGetPixelBufferAttributes(pixelBufferPool);
int width = [[atributes objectForKey:(NSString*)kCVPixelBufferWidthKey] intValue];
int height = [[atributes objectForKey:(NSString*)kCVPixelBufferHeightKey] intValue];
if (width != frame->width || height != frame->height) {
createPool = true;
}
}
if (createPool) {
CVPixelBufferPoolRelease(pixelBufferPool);
CVPixelBufferRelease(pixelBuffer);
pixelBuffer = nil;
pixelBufferPool = nil;
NSMutableDictionary* attributes = [NSMutableDictionary dictionary];
[attributes setObject:[NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey];
[attributes setObject:[NSNumber numberWithInt:frame->width] forKey: (NSString*)kCVPixelBufferWidthKey];
[attributes setObject:[NSNumber numberWithInt:frame->height] forKey: (NSString*)kCVPixelBufferHeightKey];
[attributes setObject:@(frame->linesize[0]) forKey:(NSString*)kCVPixelBufferBytesPerRowAlignmentKey];
[attributes setObject:[NSDictionary dictionary] forKey:(NSString*)kCVPixelBufferIOSurfacePropertiesKey];
theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPool);
if (theError != kCVReturnSuccess) {
NSLog(@"CVPixelBufferPoolCreate Failed");
return;
}
}
if(!pixelBuffer) {
theError = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPool, &pixelBuffer);
if(theError != kCVReturnSuccess) {
NSLog(@"CVPixelBufferPoolCreatePixelBuffer Failed");
return;
}
}
theError = CVPixelBufferLockBaseAddress(pixelBuffer, 0);
if (theError != kCVReturnSuccess) {
NSLog(@"lock error");
return;
}
size_t bytePerRowY = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
size_t bytesPerRowUV = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
uint8_t* base = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
if (bytePerRowY == frame->linesize[0]) {
memcpy(base, frame->data[0], bytePerRowY * frame->height);
} else {
[VideoCommon copyLineByLineSrc: frame->data[0]
toDest: base
srcLinesize: frame->linesize[0]
destLinesize: bytePerRowY
height: frame->height];
}
if ((AVPixelFormat)frame->format == AV_PIX_FMT_NV12) {
base = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1));
if (bytesPerRowUV == frame->linesize[0]) {
memcpy(base, frame->data[1], bytesPerRowUV * frame->height/2);
} else {
[VideoCommon copyLineByLineSrc: frame->data[1]
toDest: base
srcLinesize: frame->linesize[0]
destLinesize: bytesPerRowUV
height: frame->height/2];
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
return;
}
base = static_cast<uint8_t*>(CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1));
if (bytesPerRowUV == frame->linesize[1] * 2) {
for(size_t i = 0; i < frame->height / 2 * bytesPerRowUV / 2; i++ ) {
*base++ = frame->data[1][i];
*base++ = frame->data[2][i];
}
} else {
uint32_t size = frame->linesize[1] * frame->height / 2;
uint8_t* dstData = new uint8_t[2 * size];
for (int i = 0; i < 2 * size; i++){
if (i % 2 == 0){
dstData[i] = frame->data[1][i/2];
}else {
dstData[i] = frame->data[2][i/2];
}
}
[VideoCommon copyLineByLineSrc: dstData
toDest: base
srcLinesize: frame->linesize[1] * 2
destLinesize: bytesPerRowUV
height: frame->height/2];
}
CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}
+ (CGSize) fillPixelBuffr:(CVPixelBufferRef &)pixelBuffer
fromRenderer:(const lrc::api::video::Renderer*)renderer
bufferPool:(CVPixelBufferPoolRef &)pixelBufferPool{
auto framePtr = renderer->currentAVFrame();
auto frame = framePtr.get();
if(!frame || !frame->width || !frame->height) {
return CGSizeZero;
}
auto frameSize = CGSizeMake(frame->width, frame->height);
if (frame->data[3] != NULL && (CVPixelBufferRef)frame->data[3]) {
pixelBuffer = (CVPixelBufferRef)frame->data[3];
return frameSize;
}
[VideoCommon fillPixelBuffr:&pixelBuffer fromFrame:frame bufferPool:&pixelBufferPool];
return frameSize;
}
@end