FFmpeg - 播放YUV,視頻幀格式轉(zhuǎn)換

播放YUV

  • 定時(shí)讀取YUV的視頻幀
- (void)play {
    //
    NSTimeInterval interval = 1.0 / _yuv.fps * 1.0;
    __weak typeof(self)weakSelf = self;
    self.timer = [NSTimer timerWithTimeInterval:interval repeats:true block:^(NSTimer * _Nonnull timer) {
        [weakSelf timerAction];
    }];
    [self.timer fire];
    [[NSRunLoop mainRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}
  • 將YUV轉(zhuǎn)換為RGB數(shù)據(jù)
  • 用RGB數(shù)據(jù)生成CGimage
  • 在view上繪制CGImage

- (void)play {
    //
    NSTimeInterval interval = 1.0 / _yuv.fps * 1.0;
    __weak typeof(self)weakSelf = self;
    self.timer = [NSTimer timerWithTimeInterval:interval repeats:true block:^(NSTimer * _Nonnull timer) {
        [weakSelf timerAction];
    }];
    [self.timer fire];
    [[NSRunLoop mainRunLoop]addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)setYUV:(YuvParam*)yuv {
    _yuv = yuv;
    NSInteger format = yuv.pixelFomat;
    self.file = [NSFileHandle fileHandleForReadingAtPath: _yuv.filename];
    // 一幀圖片的大小
    imageSize = av_image_get_buffer_size((AVPixelFormat)_yuv.pixelFomat, _yuv.width, _yuv.height, 1);
    // 當(dāng)前控件的大小
    CGFloat width = self.bounds.size.width;
    CGFloat height = self.bounds.size.height;
    // 計(jì)算Rect
    CGFloat dx = 0;
    CGFloat dy = 0;
    CGFloat dw = _yuv.width;
    CGFloat dh = _yuv.height;
    // 計(jì)算目標(biāo)尺寸
    if (dw > width || dh > height) {
        if (dw * height > width * dh) { // 視頻的寬高比 > 播放器的寬高比
            dh = width * dh / dw;
            dw = width;
        } else {
            dw = height * dw / dh;
            dh = height;
        }
    }
    dx = (width - dw) * 0.5;
    dy = (height - dh) * 0.5;
    playerRect = CGRectMake(dx, dy, dw, dh);
}

- (CGImageRef)generateImage:(const RawVideoFrame &)output {
    int width = output.width;
    int height = output.height;
    size_t bufferLength = width * height * 3;
    char * buffer = output.pixels;
    CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer, bufferLength, NULL);
    size_t bitsPerComponent = 8;
    size_t bitsPerPixel = 24;
    size_t bytesPerRow = 3 * width;
    
    CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
    if(colorSpaceRef == NULL) {
        CGDataProviderRelease(provider);
    }
    
    CGBitmapInfo bitmapInfo = kCGImageAlphaNone;
    CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault;
    
    CGImageRef iref = CGImageCreate(width,
                                    height,
                                    bitsPerComponent,
                                    bitsPerPixel,
                                    bytesPerRow,
                                    colorSpaceRef,
                                    bitmapInfo,
                                    provider,   // data provider
                                    NULL,      // decode
                                    NO,          // should interpolate
                                    renderingIntent);
    return iref;
}

- (void)timerAction {
    NSData *imageData = [self.file readDataOfLength:imageSize];
    if (imageData.length > 0) {
        RawVideoFrame input = {
            (char*)imageData.bytes,
            static_cast<int>(_yuv.width),
            static_cast<int>(_yuv.height),
            (AVPixelFormat)_yuv.pixelFomat,
        };
        RawVideoFrame output = {
            nullptr,
            static_cast<int>(_yuv.width),
            static_cast<int>(_yuv.height),
            AV_PIX_FMT_RGB24
        };
        [FFMpegs convertRawVideo:&input output:&output];
        CGImageRef iref = [self generateImage:output];
        self.playLayer.contents = (__bridge id)iref;
        self.playLayer.frame = playerRect;
    } else {
        [self.timer invalidate];
        self.timer = nil;
    }
    
}

視頻幀格式轉(zhuǎn)換

  • 創(chuàng)建轉(zhuǎn)換上下文
  • 設(shè)置輸入輸出緩沖區(qū)
  • 計(jì)算輸出輸出幀的大小
  • 開始轉(zhuǎn)換
  • 獲取轉(zhuǎn)換后的數(shù)據(jù)

+ (void)convertRawVideo:(RawVideoFrame*)input
                  output:(RawVideoFrame*)output {
    // 上下文
    SwsContext *ctx = nullptr;
    // 輸入,輸出緩沖區(qū)(指向每一個(gè)平面的數(shù)據(jù))(Y U V  apha)
    uint8_t *inData[4], *outData[4];
    // 每一個(gè)平面的一行大小
    int inStrides[4], ouStrides[4];
    // 每一幀圖片的大小
    int inFrameSize, outFrameSize;
    int ret = 0;
    // 創(chuàng)建上下文
    ctx = sws_getContext(input->width, input->height, input->format,
                         output->width, output->height, output->format,
                         SWS_BILINEAR, nullptr, nullptr, nullptr);
    if(!ctx) {
        NSLog(@"sws_getContext error");
        goto end;
    }
    // 輸入緩沖區(qū)
    ret = av_image_alloc(inData, inStrides,
                         input->width,
                         input->height,
                         input->format, 1);
    END(av_image_alloc);
    // 輸出緩沖區(qū)
    ret = av_image_alloc(outData, ouStrides,
                         output->width,
                         output->height,
                         output->format, 1);
    END(av_image_alloc);
    
    // 計(jì)算每一幀的大小
    inFrameSize = av_image_get_buffer_size(input->format, input->width, input->height, 1);
    outFrameSize = av_image_get_buffer_size(output->format, output->width, output->height, 1);
    
    // 拷貝輸入數(shù)據(jù)
    memcpy(inData[0], input->pixels, inFrameSize);
    
    // 轉(zhuǎn)換
    sws_scale(ctx,
              inData, inStrides, 0, input->height,
              outData, ouStrides);
    // 寫到輸出文件去
    output->frameSize = outFrameSize;
    output->pixels = (char*)malloc(outFrameSize);
    memcpy(output->pixels, outData[0], outFrameSize);

end:
    NSLog(@"end");
    av_freep(&inData[0]);
    av_freep(&outData[0]);
    sws_freeContext(ctx);
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容