文档章节

YUV数据

S
 SimonXun
发布于 2016/11/09 18:35
字数 1054
阅读 4.3K
收藏 0

精选30+云产品,助力企业轻松上云!>>>

视频是由一帧一帧的数据连接而成,而一帧视频数据其实就是一张图片。

yuv是一种图片储存格式,跟RGB格式类似。

RGB格式的图片很好理解,计算机中的大多数图片,都是以RGB格式存储的。

yuv中,y表示亮度,单独只有y数据就可以形成一张图片,只不过这张图片是灰色的。u和v表示色差(u和v也被称为:Cb-蓝色差,Cr-红色差)。

YUV 字节数

  • 一张yuv格式的图像,占用字节数为 (width height + (width height) / 4 + (width height) / 4) = (width height) 3 / 2
  • 一张RGB格式的图像,占用字节数为(width height) * 3

YUV 格式

  • yuv420也包含不同的数据排列格式:I420,NV12,NV21. 其格式分别如下,

    I420格式:y,u,v 3个部分分别存储:Y0,Y1...Yn,U0,U1...Un/2,V0,V1...Vn/2
    
    NV12格式:y和uv 2个部分分别存储:Y0,Y1...Yn,U0,V0,U1,V1...Un/2,Vn/2
    
    NV21格式:同NV12,只是U和V的顺序相反。
    

综合来说,除了存储顺序不同之外,上述格式对于显示来说没有任何区别。

使用哪种视频的格式,取决于初始化相机时设置的视频输出格式。

设置为kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange时,表示输出的视频格式为NV12;

设置为kCVPixelFormatType_420YpCbCr8Planar时,表示使用I420。

GPUImage设置相机输出数据时,使用的就是NV12.

NSData 转 CVPixelBufferRef

static OSType KVideoPixelFormatType = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;

 
+ (CVPixelBufferRef)yuvPixelBufferWithData:(NSData *)dataFrame
                          presentationTime:(CMTime)presentationTime
                                     width:(size_t)w
                                    heigth:(size_t)h
{
    unsigned char* buffer = (unsigned char*) dataFrame.bytes;
    CVPixelBufferRef getCroppedPixelBuffer = [self copyDataFromBuffer:buffer toYUVPixelBufferWithWidth:w Height:h];
    return getCroppedPixelBuffer;
}

+ (CVPixelBufferRef) copyDataFromBuffer:(const unsigned char*)buffer toYUVPixelBufferWithWidth:(size_t)w Height:(size_t)h
{
    NSDictionary *pixelBufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
//                                           [NSDictionary dictionary],kCVPixelBufferIOSurfacePropertiesKey,
                                           nil];
    
    CVPixelBufferRef pixelBuffer;
    CVPixelBufferCreate(NULL, w, h, KVideoPixelFormatType, (__bridge CFDictionaryRef)(pixelBufferAttributes), &pixelBuffer);
    
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    
    size_t d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
    const unsigned char* src = buffer;
    unsigned char* dst = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    
    for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += d, src += w) {
        memcpy(dst, src, w);
    }
    
    d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
    dst = (unsigned char *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
    h = h >> 1;
    for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += d, src += w) {
        memcpy(dst, src, w);
    }
    
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    
    return pixelBuffer;
 
}

CVPixelBufferRef 转 NSData

+ (NSData *)dataWithYUVPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t height  = CVPixelBufferGetHeight(pixelBuffer);
    unsigned char* buffer = (unsigned char*) malloc(width * height * 1.5);
    // 取视频YUV数据
    [self copyDataFromYUVPixelBuffer:pixelBuffer toBuffer:buffer];
    // 保存到本地
    NSData *retData = [NSData dataWithBytes:buffer length:sizeof(unsigned char)*(width*height*1.5)];
    free(buffer);
    buffer = nil;
    return retData;
}

//the size of buffer has to be width * height * 1.5 (yuv)
+ (void) copyDataFromYUVPixelBuffer:(CVPixelBufferRef)pixelBuffer toBuffer:(unsigned char*)buffer {
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    if (CVPixelBufferIsPlanar(pixelBuffer)) {
        size_t w = CVPixelBufferGetWidth(pixelBuffer);
        size_t h = CVPixelBufferGetHeight(pixelBuffer);
        
        size_t d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
        unsigned char* src = (unsigned char*) CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
        unsigned char* dst = buffer;
        
        for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += w, src += d) {
            memcpy(dst, src, w);
        }
        
        d = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 1);
        src = (unsigned char *) CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 1);
        
        h = h >> 1;
        for (unsigned int rIdx = 0; rIdx < h; ++rIdx, dst += w, src += d) {
            memcpy(dst, src, w);
        }
    }
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
 
}

CVPixelBufferRef转UIImage

+ (UIImage *)makeUIImageWithYUVPixelBuffer:(CVPixelBufferRef)pixelBuffer
{
    //Lock the imagebuffer
    CVPixelBufferLockBaseAddress(pixelBuffer,0);
    size_t width = CVPixelBufferGetWidth(pixelBuffer);
    size_t height  = CVPixelBufferGetHeight(pixelBuffer);
    // Get information about the image
    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddress(pixelBuffer);
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer);
    CVPlanarPixelBufferInfo_YCbCrBiPlanar *bufferInfo = (CVPlanarPixelBufferInfo_YCbCrBiPlanar *)baseAddress;
    // This just moved the pointer past the offset
    baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    UIImage *image = [NSData makeUIImage:baseAddress bufferInfo:bufferInfo width:width height:height bytesPerRow:bytesPerRow];
    return image;
}

// 需要 导入 #include <endian.h>  文件 
+ (UIImage *)makeUIImage:(uint8_t *)inBaseAddress bufferInfo:(CVPlanarPixelBufferInfo_YCbCrBiPlanar *)inBufferInfo width:(size_t)inWidth height:(size_t)inHeight bytesPerRow:(size_t)inBytesPerRow {
    
    NSUInteger yOffset = EndianU32_BtoN(inBufferInfo->componentInfoY.offset);
    NSUInteger yPitch = EndianU32_BtoN(inBufferInfo->componentInfoY.rowBytes);
    
    NSUInteger cbCrOffset = EndianU32_BtoN(inBufferInfo->componentInfoCbCr.offset);
    NSUInteger cbCrPitch = EndianU32_BtoN(inBufferInfo->componentInfoCbCr.rowBytes);
    
    int bytesPerPixel = 4;
    
    uint8_t *yBuffer = inBaseAddress + yOffset;
    uint8_t *cbCrBuffer = inBaseAddress + cbCrOffset;
    uint8_t *rgbBuffer = (uint8_t *)malloc(inWidth * inHeight * bytesPerPixel);
    
    for(int y = 0; y < inHeight; y++)
    {
        uint8_t *rgbBufferLine = &rgbBuffer[y * inWidth * bytesPerPixel];
        uint8_t *yBufferLine = &yBuffer[y * yPitch];
        uint8_t *cbCrBufferLine = &cbCrBuffer[(y >> 1) * cbCrPitch];
        
        for(int x = 0; x < inWidth; x++)
        {
            int16_t y = yBufferLine[x];
            int16_t cb = cbCrBufferLine[x & ~1] - 128;
            int16_t cr = cbCrBufferLine[x | 1] - 128;
            
            uint8_t *rgbOutput = &rgbBufferLine[x*bytesPerPixel];
            
            int16_t r = (int16_t)roundf( y + cr *  1.4 );
            int16_t g = (int16_t)roundf( y + cb * -0.343 + cr * -0.711 );
            int16_t b = (int16_t)roundf( y + cb *  1.765);
            
            //ABGR
            rgbOutput[0] = 0xff;
            rgbOutput[1] = b > 0 ? (b < 255 ? b : 255) : 0;
            rgbOutput[2] = g > 0 ? (g < 255 ? g : 255) : 0;
            rgbOutput[3] = r > 0 ? (r < 255 ? r : 255) : 0;
        }
    }
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(rgbBuffer, yPitch, inHeight, 8,
                                                 yPitch*4, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast);
    
    CGImageRef quartzImage = CGBitmapContextCreateImage(context);
    
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);
    
    UIImage *image = [UIImage imageWithCGImage:quartzImage];
    
    CGImageRelease(quartzImage);
    free(rgbBuffer);
    rgbBuffer = NULL;
    return  image;
 
}
S
粉丝 6
博文 75
码字总数 55764
作品 0
深圳
程序员
私信 提问
加载中
此博客有 2 条评论,请先登录后再查看。
FFmpeg视频解码,保存原始YUV数据(使用最新FFmpeg4.1)

网上文章都太老,本文基于FFmpeg4.1,没有使用任何弃用的API,要运行先配置环境 解码流程关键函数: avformatopeninput() avformatfindstream_info() avreadframe() avcodecsendpacke...

Little__Jerry
2019/01/17
0
0
图文详解YUV420数据格式

完整的链接: http://www.xuebuyuan.com/1541892.html YUV格式有两大类:planar和packed。 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。...

aspirs
2016/01/24
126
0
视音频数据处理入门:RGB、YUV像素数据处理

===================================================== 视音频数据处理入门系列文章: 视音频数据处理入门:RGB、YUV像素数据处理 视音频数据处理入门:PCM音频采样数据处理 视音频数据处理...

leixiaohua1020
2016/01/29
0
0
YUV格式

YUV格式有两大类:planar和packed。 对于planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V。 对于packed的YUV格式,每个像素点的Y,U,V是连续交*存储...

理性编程
2016/06/10
131
0
YUV图解 (YUV444, YUV422, YUV420, YV12, NV12, NV21)

背景:   最近在研究音视频,了解YUV这样的格式对于音视频开发比较重要。   虽然这篇文章大部分是转载别人的,但是经过了校对以后,重新排版并补充了一部分内容 概览: 之所以提出yuv格式...

osc_22zrmzfz
2019/12/25
4
0

没有更多内容

加载失败,请刷新页面

加载更多

使当前提交成为Git存储库中唯一的(初始)提交? - Make the current commit the only (initial) commit in a Git repository?

问题: I currently have a local Git repository, which I push to a Github repository. 我目前有一个本地Git存储库,我将其推送到Github存储库。 The local repository has ~10 commits, ......

javail
12分钟前
14
0
IntelliJ IDEA 默认快捷键大全

Remember these Shortcuts 常用 功能 快捷键 备注 ● Smart code completion Ctrl + Shift + Space - ● Search everywhere Double Shift - ● Show intention actions and quick-fixes Alt......

巨輪
51分钟前
18
0
Hacker News 简讯 2020-07-14

更新时间: 2020-07-14 01:01 I Know What You Download on BitTorrent - (iknowwhatyoudownload.com) 我知道你在BitTorrent上下载了什么 得分:196 | 评论:159 Show HN: Primo – all-in-one......

FalconChen
今天
121
0
绕过移动端系统限制的 dlopen 库 byOpen

byOpen是一个绕过移动端系统限制的增强版dlfunctions库。 支持特性 Android 支持App中加载和使用Android系统库接口(即使maps中还没有被加载也支持)。 Android 7以上dlopen, System.load都是...

shzwork
昨天
31
0
Golang学习系列第二天:变量、常量、数据类型和流程语句

继golang第一天后,今天学习下golang的变量、常量、数据类型和控制流语句。 做过其他编程语言(比如JavaScript,java,python)项目的话,其实很好理解变量、常量、数据类型和控制流。 变量也...

董广明
昨天
48
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部