1.从文件中读取h264数据
参考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; }