您当前的位置:首页 > 计算机 > 编程开发 > C语言

从文件中读取yuv和h264数据

时间:09-27来源:作者:点击数:

参考ffmpeg avc.c写的从文件中一帧帧读取h.264数据的demo

#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

char* filebuf_;
const char* pbuf_;
int filesize_;
unsigned char is_stop_;


const char* AVCFindStartCodeInternal(const char *p, const char *end)
{
    const char *a = p + 4 - ((ptrdiff_t)p & 3);

    for (end -= 3; p < a && p < end; p++) {
        if (p[0] == 0 && p[1] == 0 && p[2] == 1)
            return p;
    }

    for (end -= 3; p < end; p += 4) {
        unsigned int x = *(const unsigned int*)p;
        //      if ((x - 0x01000100) & (~x) & 0x80008000) // little endian
        //      if ((x - 0x00010001) & (~x) & 0x00800080) // big endian
        if ((x - 0x01010101) & (~x) & 0x80808080) { // generic
            if (p[1] == 0) {
                if (p[0] == 0 && p[2] == 1)
                    return p;
                if (p[2] == 0 && p[3] == 1)
                    return p + 1;
            }
            if (p[3] == 0) {
                if (p[2] == 0 && p[4] == 1)
                    return p + 2;
                if (p[4] == 0 && p[5] == 1)
                    return p + 3;
            }
        }
    }

    for (end += 3; p < end; p++) {
        if (p[0] == 0 && p[1] == 0 && p[2] == 1)
            return p;
    }

    return end + 3;
}

const char* AVCFindStartCode(const char *p, const char *end)
{
    const char *out = AVCFindStartCodeInternal(p, end);
    if (p<out && out<end && !out[-1]) out--;
    return out;
}


H264FrameReader_Init(const char* filename)
{
    FILE* fp = fopen(filename, "rb");
    filebuf_ = 0;
    filesize_ = 0;

    if (fp)
    {
        int retval = 0;
        fseek(fp, 0, SEEK_END);
        filesize_ = ftell(fp);
        fseek(fp, 0, SEEK_SET);

        filebuf_ = (char*)malloc(filesize_);
        retval = fread(filebuf_, 1, filesize_, fp);

        fclose(fp);
    }
    pbuf_ = filebuf_;
}

H264FrameReader_Free()
{
    free(filebuf_);
}

H264FrameReader_ReadFrame(char* outBuf, int* outBufSize)
{
    char* pbufout = 0;
    const char *p = 0;
    const char *end = 0;
    const char *nal_start, *nal_end;


    char startcodebuf[] = { 0x00, 0x00, 0x00, 0x01 };
    if (pbuf_ >= filebuf_ + filesize_)
    {
        return 0;
    }

    pbufout = outBuf;
    p = pbuf_;
    end = filebuf_ + filesize_;

    nal_start = AVCFindStartCode(p, end);
    while (nal_start < end)
    {
        unsigned int nal_size = 0;
        unsigned char nal_type = 0;

        while (!*(nal_start++));

        nal_end = AVCFindStartCode(nal_start, end);

        nal_size = nal_end - nal_start;
        nal_type = nal_start[0] & 0x1f;

        memcpy(pbufout, startcodebuf, 4);
        pbufout += 4;
        memcpy(pbufout, nal_start, nal_size);
        pbufout += nal_size;

        nal_start = nal_end;
        break;
    }

    *outBufSize = pbufout - outBuf;
    pbuf_ = nal_start;

    return 1;
}


int main(int argc, char **argv)
{
    unsigned long max_size = 1280 * 720;
    int tmpbuf_len = 0;
    int current_read_len = 0;
    char* tmpbuf = (char*)malloc(max_size * 10);

    FILE *fp = fopen("out.h264", "wb+");
    if (!fp)
    {
        printf("open file error\n");
        return -1;
    }


    H264FrameReader_Init("test.h264");
    printf("file size = %d\n", filesize_);
    while (current_read_len < filesize_)
    {
        if (H264FrameReader_ReadFrame(tmpbuf, &tmpbuf_len))
        {
            printf("tmpbuf_len = %d\n", tmpbuf_len);
            fwrite(tmpbuf, tmpbuf_len, 1, fp);
            current_read_len += tmpbuf_len;
        }
    }
    fclose(fp);
    H264FrameReader_Free();

    return 0;
}

2.从文件中读取yuv数据

从planar yuv420 文件中读取每一帧数据,从nvenc demo中参考来的,原理如下

1.通过fseek和ftell计算出文件的大小

2.通过yuv的分辨率可以计算出每一帧yuv数据的大小

3.通过上面两步可以计算出文件中包含多少帧的yuv数据,然后通过每一帧数据在文件中的偏移,就可以读出该帧数据

int loadframe(uint8_t *yuvInput[3], FILE *hInputYUVFile, uint32_t frmIdx, uint32_t width, uint32_t height)
{
    uint64_t fileOffset;
    uint32_t result;
    uint32_t dwInFrameSize = 0;
    int anFrameSize[3] = {};

    dwInFrameSize = width * height * 3 / 2;
    anFrameSize[0] = width * height;
    anFrameSize[1] = anFrameSize[2] = width * height / 4;
    
    //当前帧在文件中的偏移量:当前index * 每一帧的大小
    fileOffset = (uint64_t)dwInFrameSize * frmIdx;
    //seek到偏移处
    result = _fseeki64(hInputYUVFile, fileOffset, SEEK_SET);
    if (result == -1)
    {
        return -1;
    }
    //把当前帧的Y、U、V数据分别读取到对应的数组中
    fread(yuvInput[0], anFrameSize[0], 1, hInputYUVFile);
    fread(yuvInput[1], anFrameSize[1], 1, hInputYUVFile);
    fread(yuvInput[2], anFrameSize[2], 1, hInputYUVFile);

    return 0;
}

int main()
{

    infp = fopen("yb.yuv", "rb");
    if (!infp)
    {
        printf("open in file failed\n");
        return -1;
    }
    
    uint8_t *yuv[3];
    int lumaPlaneSize, chromaPlaneSize;

    lumaPlaneSize = 1920 * 1080;
    chromaPlaneSize = lumaPlaneSize >> 2;

    yuv[0] = new uint8_t[lumaPlaneSize];
    yuv[1] = new uint8_t[chromaPlaneSize];
    yuv[2] = new uint8_t[chromaPlaneSize];
    memset(yuv[0], 0, lumaPlaneSize);
    memset(yuv[1], 0, chromaPlaneSize);
    memset(yuv[2], 0, chromaPlaneSize);
    
    uint64_t file_size = 0;

    _fseeki64(infp, 0, SEEK_END);
    file_size = _ftelli64(infp);
    _fseeki64(infp, 0, SEEK_SET);
    int totalFrames = file_size / (lumaPlaneSize + chromaPlaneSize + chromaPlaneSize);
    
    //遍历每一帧YUV数据
    for (int frm = 0; frm < totalFrames; frm++)
    {
        loadframe(yuv, infp, frm, 1920, 1080);
        //处理yuv数据
        //....
    }
    
    return 0;
}
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门
本栏推荐