美文网首页ffmpeg
libyuv jpg图片转换为i420,旋转270度。

libyuv jpg图片转换为i420,旋转270度。

作者: Leoeoo | 来源:发表于2019-11-25 14:06 被阅读0次

iOS开发中,需要把jpg图片转换成i420,然后进行旋转,再转换为CVPixelBufferRef,最后转换为CMSampleBufferRef。特作此记录。以下代码 filePath 是图片存储路径。
需要注意的是,图片的宽高需要是偶数,奇数会转换错误。

代码里使用到了libyuv库,同时libyuv依赖libjpeg库,我已经编译好了libjpeg-8.0 和9.0的库,需要的可以自行下载:https://github.com/ayangcool/leo_libjpeg

+ (CVPixelBufferRef)jpgConvertToI420:(NSString *)filePath rotate:(BOOL)rotate {
    if (!filePath || filePath.length <= 0) {
        return nil;
    }
    NSData *imgData = [NSData dataWithContentsOfFile:filePath];
    if (!imgData) {
        return nil;
    }
    UIImage *image = [UIImage imageWithData:imgData];
    int img_width = image.size.width;
    int img_height = image.size.height;
    if (img_width % 2 != 0) {
        img_width++;
    }
    if (img_height % 2 != 0) {
        img_height++;
    }
    
    // 1.转换为I420数据
    int dst_plane1_stride = img_width;
    // 步长必须是16的倍数,因为涉及到字节对齐,而且iOS13和之前的版本处理方式不一样,要注意
    int stride_length = 16;
    if ([UIDevice currentDevice].systemVersion.floatValue >= 13.0) {
        stride_length = 64;
    } else {
        stride_length = 16;
    }
    if ((img_width % stride_length) != 0) {
        dst_plane1_stride = (img_width / stride_length + 1) * stride_length;
    }
    
    
    int dst_plane2_stride = dst_plane1_stride;
    int dst_plane1_height = img_height;
    int dst_plane2_height = img_height / 2;
    int dst_plane1_size = dst_plane1_stride * dst_plane1_height;
    int dst_plane2_size = dst_plane2_stride * dst_plane2_height;
    int dst_frame_size = dst_plane1_size + dst_plane2_size;
    
    uint8* dst_buffer_y = (unsigned char *)malloc(dst_frame_size);
    uint8* dst_buffer_u = dst_buffer_y + dst_plane1_size;
    uint8* dst_buffer_v = dst_buffer_u + dst_plane1_size / 4;
    // 转换为const char*
    const uint8 *src_img_data = (const uint8 *)[imgData bytes];
    size_t src_img_size = [imgData length];

    libyuv::MJPGToI420(/*const uint8* sample*/ src_img_data,
                       /*size_t sample_size*/ src_img_size,
                       /*uint8* dst_y*/ dst_buffer_y,
                       /*int dst_stride_y*/ dst_plane1_stride,
                       /*uint8* dst_u*/ dst_buffer_u,
                       /*int dst_stride_u*/ (int)dst_plane1_stride >> 1,
                       /*uint8* dst_v*/ dst_buffer_v,
                       /*int dst_stride_v*/ (int)dst_plane1_stride >> 1,
                       /*int src_width*/ img_width,
                       /*int src_height*/ img_height,
                       /*int dst_width*/ img_width,
                       /*int dst_height*/ img_height);
    
//    static NSInteger count = 0;
//    count++;
//    if (count == 1) {
//        NSData *dstData = [NSData dataWithBytes:dst_buffer_y length:dst_frame_size];
//        NSString *dstPath = [NSString stringWithFormat:@"%@%@", NSHomeDirectory(), @"/Documents/png2i420.yuv"];
//        if ([[NSFileManager defaultManager] fileExistsAtPath:dstPath]) {
//            [[NSFileManager defaultManager] removeItemAtPath:dstPath error:nil];
//        }
//        [dstData writeToFile:dstPath atomically:NO];
//    }
    
    // 2.逆时针旋转90度
    if (rotate) {
        int rotate_plane1_stride = img_height;
        if ((img_height % stride_length) != 0) {
            rotate_plane1_stride = (img_height / stride_length + 1) * stride_length;
        }
        
        int rotate_plane2_stride = rotate_plane1_stride;
        int rotate_plane1_height = img_width;
        int rotate_plane2_height = img_width >> 1;
        int rotate_plane1_size = rotate_plane1_stride * rotate_plane1_height;
        int rotate_plane2_size = rotate_plane2_stride * rotate_plane2_height;
        int rotate_frame_size = rotate_plane1_size + rotate_plane2_size;

        uint8* rotate_buffer_y = (unsigned char *)malloc(rotate_frame_size);
        uint8* rotate_buffer_u = rotate_buffer_y + rotate_plane1_size;
        uint8* rotate_buffer_v = rotate_buffer_u + rotate_plane1_size / 4;
        
        
        libyuv::I420Rotate(/*const uint8 *src_y*/ dst_buffer_y,
                           /*int src_stride_y*/ dst_plane1_stride,
                           /*const uint8 *src_u*/ dst_buffer_u,
                           /*int src_stride_u*/ dst_plane1_stride >> 1,
                           /*const uint8 *src_v*/ dst_buffer_v,
                           /*int src_stride_v*/ dst_plane1_stride >> 1,
                           /*uint8 *dst_y*/ rotate_buffer_y,
                           /*int dst_stride_y*/ rotate_plane1_stride,
                           /*uint8 *dst_u*/ rotate_buffer_u,
                           /*int dst_stride_u*/ rotate_plane1_stride >> 1,
                           /*uint8 *dst_v*/ rotate_buffer_v,
                           /*int dst_stride_v*/ rotate_plane1_stride >> 1,
                           /*int src_width*/ img_width,
                           /*int src_height*/ img_height,
                           /*enum RotationMode mode*/ libyuv::kRotate270
                           );
        free(dst_buffer_y);
        
//        static NSInteger count = 0;
//        count++;
//        if (count == 1) {
//            NSData *dstData = [NSData dataWithBytes:scale_buffer_y length:scale_frame_size];
//            NSString *dstPath = [NSString stringWithFormat:@"%@%@", NSHomeDirectory(), @"/Documents/i420_111.yuv"];
//            if ([[NSFileManager defaultManager] fileExistsAtPath:dstPath]) {
//                [[NSFileManager defaultManager] removeItemAtPath:dstPath error:nil];
//            }
//            [dstData writeToFile:dstPath atomically:NO];
//        }
        
        // 3.把旋转后的I420数据转换为NV12
        int nv12_plane1_stride = rotate_plane1_stride;
        int nv12_width = img_height;
        int nv12_hight = img_width;
        int nv12_frame_size = rotate_frame_size;
        
        uint8 *nv12_dst_y = (uint8 *)malloc(nv12_frame_size);
        uint8 *nv12_dst_uv = nv12_dst_y + nv12_plane1_stride * nv12_hight;
        
        libyuv::I420ToNV12(/*const uint8 *src_y*/ rotate_buffer_y,
                           /*int src_stride_y*/ rotate_plane1_stride,
                           /*const uint8 *src_u*/ rotate_buffer_u,
                           /*int src_stride_u*/ rotate_plane1_stride >> 1,
                           /*const uint8 *src_v*/ rotate_buffer_v,
                           /*int src_stride_v*/ rotate_plane1_stride >> 1,
                           /*uint8 *dst_y*/ nv12_dst_y,
                           /*int dst_stride_y*/ nv12_plane1_stride,
                           /*uint8 *dst_uv*/ nv12_dst_uv,
                           /*int dst_stride_uv*/ nv12_plane1_stride,
                           /*int width*/ nv12_width,
                           /*int height*/ nv12_hight);
        free(rotate_buffer_y);
        
        // 4.NV12转换为CVPixelBufferRef
        NSDictionary *pixelAttributes = @{(id)kCVPixelBufferIOSurfacePropertiesKey : @{}};
        CVPixelBufferRef dstPixelBuffer = NULL;
        CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,
                                              nv12_width, nv12_hight, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
                                              (__bridge CFDictionaryRef)pixelAttributes, &dstPixelBuffer);

        CVPixelBufferLockBaseAddress(dstPixelBuffer, 0);
        uint8_t *yDstPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0);
        memcpy(yDstPlane, nv12_dst_y, nv12_plane1_stride * nv12_hight);
        uint8_t *uvDstPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1);
        memcpy(uvDstPlane, nv12_dst_uv, nv12_plane1_stride * nv12_hight / 2);
        if (result != kCVReturnSuccess) {
            NSLog(@"Unable to create cvpixelbuffer %d", result);
        }
        CVPixelBufferUnlockBaseAddress(dstPixelBuffer, 0);

        free(nv12_dst_y);

        return dstPixelBuffer;
    } else {
        // 3.把I420数据转换为NV12
        int nv12_plane1_stride = dst_plane1_stride;
        int nv12_width = img_width;
        int nv12_hight = img_height;
        int nv12_frame_size = dst_frame_size;
        
        uint8 *nv12_dst_y = (uint8 *)malloc(nv12_frame_size);
        uint8 *nv12_dst_uv = nv12_dst_y + nv12_plane1_stride * nv12_hight;
        
        libyuv::I420ToNV12(/*const uint8 *src_y*/ dst_buffer_y,
                           /*int src_stride_y*/ dst_plane1_stride,
                           /*const uint8 *src_u*/ dst_buffer_u,
                           /*int src_stride_u*/ dst_plane1_stride >> 1,
                           /*const uint8 *src_v*/ dst_buffer_v,
                           /*int src_stride_v*/ dst_plane1_stride >> 1,
                           /*uint8 *dst_y*/ nv12_dst_y,
                           /*int dst_stride_y*/ nv12_plane1_stride,
                           /*uint8 *dst_uv*/ nv12_dst_uv,
                           /*int dst_stride_uv*/ nv12_plane1_stride,
                           /*int width*/ nv12_width,
                           /*int height*/ nv12_hight);
        
        free(dst_buffer_y);
        
        // 4.NV12转换为CVPixelBufferRef
        NSDictionary *pixelAttributes = @{(id)kCVPixelBufferIOSurfacePropertiesKey : @{}};
        CVPixelBufferRef dstPixelBuffer = NULL;
        CVReturn result = CVPixelBufferCreate(kCFAllocatorDefault,
                                              nv12_width, nv12_hight, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
                                              (__bridge CFDictionaryRef)pixelAttributes, &dstPixelBuffer);

        CVPixelBufferLockBaseAddress(dstPixelBuffer, 0);
        uint8_t *yDstPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0);
        memcpy(yDstPlane, nv12_dst_y, nv12_plane1_stride * nv12_hight);
        uint8_t *uvDstPlane = (uint8*)CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1);
        memcpy(uvDstPlane, nv12_dst_uv, nv12_plane1_stride * nv12_hight / 2);
        if (result != kCVReturnSuccess) {
            NSLog(@"Unable to create cvpixelbuffer %d", result);
        }
        CVPixelBufferUnlockBaseAddress(dstPixelBuffer, 0);

        free(nv12_dst_y);

        return dstPixelBuffer;
    }
}

相关文章

网友评论

    本文标题:libyuv jpg图片转换为i420,旋转270度。

    本文链接:https://www.haomeiwen.com/subject/goodwctx.html