| /* |
| * 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 |