H264文件解析出nalu数据,送给ffmpeg解码,opencv显示
2017-08-15 22:20
477 查看
本博客主要是H264的视频码流有ffmpeg 解码后,有opencv先,这里贴出全部代码,你只需自己建个工程,配置一下ffmpeg库和opencv3.0库就好了。(这里采用自己打开h264文件,解析出来每个nalu数据,把每一个nalu数据送给ffmpeg库,解码出yuv,然后把yuv转成BGR格式,用opencv显示出来)
头文件 H264.h
H264.cpp
main.cpp
头文件 H264.h
#include <stdio.h> #include <stdlib.h> #include <conio.h> #include <string.h> #include <winsock2.h> typedef struct { int startcodeprefix_len; //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested) unsigned len; //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU) unsigned max_size; //! Nal Unit Buffer size int forbidden_bit; //! should be always FALSE int nal_reference_idc; //! NALU_PRIORITY_xxxx int nal_unit_type; //! NALU_TYPE_xxxx char *buf; //! contains the first byte followed by the EBSP unsigned short lost_packets; //! true, if packet loss is detected } NALU_t; int GetNalu(); void FreeNALU(NALU_t *n); NALU_t *AllocNALU(int buffersize); void OpenBitstreamFile(char *fn); int GetAnnexbNALU(NALU_t *nalu); void dump(NALU_t *nal); int DumpChar(char * filename, char * buf, int len); FILE * getFile();
H264.cpp
// rtspSend.cpp : Defines the entry point for the console application. // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <memory.h> #include "h264.h" static char* dumpRoot = ".\\dump\\"; static char file2open[1024]; #define UDP_MAX_SIZE 1400 FILE *bits = NULL; //!< the bit stream file static int FindStartCode2(unsigned char *Buf);//查找开始字符0x000001 static int FindStartCode3(unsigned char *Buf);//查找开始字符0x00000001 // static int info2=0, info3=0; // RTP_FIXED_HEADER *rtp_hdr; // NALU_HEADER *nalu_hdr; // FU_INDICATOR *fu_ind; // FU_HEADER *fu_hdr; FILE * getFile() { return bits; } //为NALU_t结构体分配内存空间 NALU_t *AllocNALU(int buffersize) { NALU_t *nal =NULL; if ((nal = (NALU_t*)calloc (1, sizeof (NALU_t))) == NULL) { printf("AllocNALU: n"); exit(0); } nal->max_size=buffersize; if ((nal->buf = (char*)calloc (buffersize, sizeof (char))) == NULL) { free (nal); printf ("AllocNALU: nal->buf"); exit(0); } return nal; } //释放 void FreeNALU(NALU_t *n) { if (n) { if (n->buf) { free(n->buf); n->buf=NULL; } free (n); } } void OpenBitstreamFile (char *fn) { if (NULL == (bits=fopen(fn, "rb"))) { printf("open file error\n"); exit(0); } } //这个函数输入为一个NAL结构体,主要功能为得到一个完整的NALU并保存在NALU_t的buf中, //获取他的长度,填充F,IDC,TYPE位。 //并且返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度 int GetAnnexbNALU (NALU_t *nalu) { int pos = 0; int StartCodeFound, rewind; unsigned char *Buf; if ((Buf = (unsigned char*)calloc (nalu->max_size , sizeof(char))) == NULL) printf ("GetAnnexbNALU: Could not allocate Buf memory\n"); nalu->startcodeprefix_len=3;//初始化码流序列的开始字符为3个字节 if (3 != fread (Buf, 1, 3, bits))//从码流中读3个字节 { free(Buf); return 0; } info2 = FindStartCode2 (Buf);//判断是否为0x000001 if(info2 != 1) { //如果不是,再读一个字节 if(1 != fread(Buf+3, 1, 1, bits))//读一个字节 { free(Buf); return 0; } info3 = FindStartCode3 (Buf);//判断是否为0x00000001 if (info3 != 1)//如果不是,返回-1 { free(Buf); return -1; } else { //如果是0x00000001,得到开始前缀为4个字节 pos = 4; nalu->startcodeprefix_len = 4; } } else { //如果是0x000001,得到开始前缀为3个字节 nalu->startcodeprefix_len = 3; pos = 3; } //查找下一个开始字符的标志位 StartCodeFound = 0; info2 = 0; info3 = 0; while (!StartCodeFound) { if (feof (bits))//判断是否到了文件尾 { nalu->len = (pos-1)-nalu->startcodeprefix_len; memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len); nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit free(Buf); return pos-1; } Buf[pos++] = fgetc (bits);//读一个字节到BUF中 info3 = FindStartCode3(&Buf[pos-4]);//判断是否为0x00000001 if(info3 != 1) info2 = FindStartCode2(&Buf[pos-3]);//判断是否为0x000001 StartCodeFound = (info2 == 1 || info3 == 1); } // Here, we have found another start code (and read length of startcode bytes more than we should // have. Hence, go back in the file rewind = (info3 == 1)? -4 : -3; if (0 != fseek (bits, rewind, SEEK_CUR))//把文件指针指向前一个NALU的末尾 { free(Buf); printf("GetAnnexbNALU: Cannot fseek in the bit stream file"); } // Here the Start code, the complete NALU, and the next start code is in the Buf. // The size of Buf is pos, pos+rewind are the number of bytes excluding the next // start code, and (pos+rewind)-startcodeprefix_len is the size of the NALU excluding the start code nalu->len = (pos+rewind)-nalu->startcodeprefix_len; memcpy (nalu->buf, &Buf[nalu->startcodeprefix_len], nalu->len);//拷贝一个完整NALU,不拷贝起始前缀0x000001或0x00000001 nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit nalu->nal_reference_idc = nalu->buf[0] & 0x60; //2 bit nalu->nal_unit_type = (nalu->buf[0]) & 0x1f; //5 bit free(Buf); return (pos+rewind);//返回两个开始字符之间间隔的字节数,即包含有前缀的NALU的长度 } static int FindStartCode2 (unsigned char *Buf) { if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=1) return 0; //判断是否为0x000001,如果是返回1 else return 1; } static int FindStartCode3 (unsigned char *Buf) { if(Buf[0]!=0 || Buf[1]!=0 || Buf[2] !=0 || Buf[3] !=1) return 0;//判断是否为0x00000001,如果是返回1 else return 1; } int rtpnum = 0; //输出NALU长度和TYPE void dump(NALU_t *nal) { if (!nal) return; printf("%3d, len: %6d ",rtpnum++, nal->len); printf("nal_unit_type: %x\n", nal->nal_unit_type); } int DumpChar(char * filename, char * buf, int len) { FILE* file; int w, h; unsigned char * temp = (unsigned char *)buf; sprintf(file2open, "%s%s", dumpRoot, filename); int mHeight = 0; int mWidth = 100; int mYu = 0; mHeight = len / 100; mYu = len % 100; file = fopen(file2open, "w+"); for (h = 0; h < mHeight; h++) { for (w = 0; w < mWidth - 1; w++) { fprintf_s(file, "%3x,", temp[h * mWidth + w]); } fprintf_s(file, "%3x\n", temp[h * mWidth + w]); } for (w = 0; w < mYu - 1; w++) { fprintf_s(file, "%3x,", temp[h * mWidth + w]); } fprintf_s(file, "%3x\n", temp[h * mWidth + w]); fclose(file); return 0; }
main.cpp
#include "H264.h" extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libswscale/swscale.h" #include "libavutil/avutil.h" }; #include "opencv2/opencv.hpp" AVCodec *pCodec = NULL; AVCodecContext *pCodecCtx = NULL; SwsContext *img_convert_ctx = NULL; AVFrame *pFrame = NULL; AVFrame *pFrameBGR = NULL; int H264_Init(void) { /* register all the codecs */ avcodec_register_all(); /* find the h264 video decoder */ pCodec = avcodec_find_decoder(CODEC_ID_H264); if (!pCodec) { fprintf(stderr, "codec not found\n"); } pCodecCtx = avcodec_alloc_context3(pCodec); //初始化参数,下面的参数应该由具体的业务决定 //pCodecCtx->time_base.num = 1; //pCodecCtx->frame_number = 1; //每包一个视频帧 //pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO; //pCodecCtx->bit_rate = 0; //pCodecCtx->time_base.den = 30;//帧率 //pCodecCtx->width = 960;//视频宽 //pCodecCtx->height = 544;//视频高 /* open the coderc */ if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) { fprintf(stderr, "could not open codec\n"); } // Allocate video frame pFrame = avcodec_alloc_frame(); if (pFrame == NULL) return -1; // Allocate an AVFrame structure pFrameBGR = avcodec_alloc_frame(); if (pFrameBGR == NULL) return -1; return 0; } static int dumpCount = 0; static int first = 0; static uint8_t *out_buffer = NULL; int H264_2_RGB(char *inputbuf, int frame_size, unsigned char *outputbuf, unsigned int*outsize) { int decode_size; int numBytes; int av_result; uint8_t *buffer = NULL; printf("Video decoding\n"); int ret, got_picture; AVPacket packet; av_init_packet(&packet); packet.size = frame_size; packet.data = (uint8_t *)inputbuf; ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, &packet); if (ret < 0) { printf("Decode Error. ret = %d(解码错误)\n", ret); return -1; } if (first == 0) { out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height)); avpicture_fill((AVPicture *)pFrameBGR, out_buffer, AV_PIX_FMT_BGR24, pCodecCtx->width, pCodecCtx->height); first = 1; } struct SwsContext *img_convert_ctx; img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL); sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameBGR->data, pFrameBGR->linesize); memcpy(outputbuf, pFrameBGR->data[0], pCodecCtx->width * pCodecCtx->height * 3); *outsize = pCodecCtx->width * pCodecCtx->height * 3; return 0; } void H264_Release(void) { avcodec_close(pCodecCtx); av_free(pCodecCtx); av_free(pFrame); av_free(pFrameBGR); } int main(int argc, char* argv[]) { OpenBitstreamFile("./x264.h264"); NALU_t *nal; char fName[300]; int Frame = 0; nal = AllocNALU(8000000);//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针 H264_Init(); unsigned char *outputbuf = (unsigned char *)calloc(1000 * 1000, sizeof(char)); unsigned int outsize = 0; unsigned char *m_pData = (unsigned char *)calloc(1000 * 1000, sizeof(char)); int sizeHeBing = 0; while (!feof(getFile())) { GetAnnexbNALU(nal);//每执行一次,文件的指针指向本次找到的NALU的末尾, //下一个位置即为下个NALU的起始码0x000001 dump(nal);//输出NALU长度和TYPE sprintf(fName, "dump[Len=%d][%d].txt", nal->len, Frame); memset(m_pData, 0, 4); m_pData[3] = 1; memcpy(m_pData + 4, nal->buf, nal->len); sizeHeBing = nal->len + 4; Frame++; int ret = H264_2_RGB((char *)m_pData, sizeHeBing, outputbuf, &outsize); if(ret != 0) continue; cv::Mat image = cv::Mat(pCodecCtx->height, pCodecCtx->width, CV_8UC3); memcpy(image.data, outputbuf, pCodecCtx->height * pCodecCtx->width * 3); cv::imshow("xxx", image); cv::waitKey(40); //DumpChar(fName, nal->buf, nal->len); //Sleep(33); } FreeNALU(nal); return 0; }
相关文章推荐
- ffmpeg解码h264文件,opencv显示
- H264解码深度解析——DM8168 OMX从H264文件读取一帧数据(do chunking of h264)
- rtsp获取视频帧 ffmpeg解码h264数据 D3D显示yv12数据
- android jni中将大数据回调到java层的时候用法,比如视频流,音频流等,图片流等 比如我用ffmpeg解码好视频流,想送到java层使用opengGL进行显示,opencv进行人脸识别等等
- FFmpeg解码-Opencv数据显示-双线程调度
- ffmpeg解码数据转为Mat通过opencv函数显示
- linux之x86裁剪移植---ffmpeg的H264解码显示(420、422)
- 视频学习笔记:Android ffmpeg解码多路h264视频并显示
- ffmpeg实战教程(一)Mp4,mkv等格式解码为h264和yuv数据
- FFmpeg解码H264裸流并转换成opencv Mat
- ffmpeg 解码h264数据
- ffmpeg 解码h264数据
- 嵌入式linux------ffmpeg移植 解码H264(am335x解码H264到yuv420并通过SDL显示)
- (原)从mp4,flv文件中解析出h264和aac,送解码器解码失败
- 嵌入式linux------ffmpeg移植 解码H264(am335x解码H264到yuv420并通过SDL显示)
- 【ffmpeg学习】利用SDL2.0显示ffmpeg解码出来的数据
- 用jsp将xml文件解析到网页显示,并把数据提交保存到数据库
- ffmpeg和opencv 播放视频文件并显示
- FFMPEG+SDL2.0流媒体开发3---简易MP4视频播放器,提取MP4的H264视频序列解码并且显示
- Android中数据文件解析(Json解析【从服务器端获取数据并且解析,显示在客户端上面】)