看此文章之前,建议先看一看:https://blog.csdn.net/weixin_42462202/article/details/88182605
使用ffmpeg读取H264并不能直接得到NALU单元,必须从读取出来的AVPacket与AVFormatContext->streams[video_index]->codec->extradata提取出来。
输出H.264格式,我们需要将NALU提取出来,然后组织成下面的数据结构。
提取流程
1、打开MP4文件,循环读取
2、从AVPacket获取IDR(nalu type==5),从AVFormatContext->streams[video_index]->codec->extradata获取SPS与PPS。
3、添加start code,给SPS与PPS前加上00 00 00 01,IDR加上00 00 01
4、按照上图的数据结构写入文件中
5、继续提取NALU,添加start code(00 00 01),写入文件中
几个注意的点
1、SPS与PPS存在于AVFormatContext->streams[video_index]->codec->extradata
2、其他的NALU存在于读取出来的AVPacket中
3、AVPacket->data前四个字节表示当前NALU的大小,根据这一条件可以获取NALU
4、AVFormatContext->streams[video_index]->codec->extradata + 5,之后两个字节表示SPS的个数,
SPS的数据结束后的两个字节表示PPS的个数
源码
#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
/* 获取SPS与PPS */
static int h264_extradata_to_annexb(const unsigned char *pCodecExtraData, const int codecExtraDataSize,
AVPacket *pOutExtradata, int padding)
{
const unsigned char *pExtraData = NULL; /* 前四个字节没用 */
int len = 0;
int spsUnitNum, ppsUnitNum;
int unitSize, totolSize = 0;
unsigned char startCode[] = {0, 0, 0, 1};
unsigned char *pOut = NULL;
int err;
pExtraData = pCodecExtraData+4;
len = (*pExtraData++ & 0x3) + 1;
/* 获取SPS */
spsUnitNum = (*pExtraData++ & 0x1f); /* SPS数量 */
while(spsUnitNum--)
{
unitSize = (pExtraData[0]<<8 | pExtraData[1]); /* 两个字节表示这个unit的长度 */
pExtraData += 2;
totolSize += unitSize + sizeof(startCode);
printf("unitSize:%d\n", unitSize);
if(totolSize > INT_MAX - padding)
{
av_log(NULL, AV_LOG_ERROR,
"Too big extradata size, corrupted stream or invalid MP4/AVCC bitstream\n");
av_free(pOut);
return AVERROR(EINVAL);
}
if(pExtraData + unitSize > pCodecExtraData + codecExtraDataSize)
{
av_log(NULL, AV_LOG_ERROR, "Packet header is not contained in global extradata, "
"corrupted stream or invalid MP4/AVCC bitstream\n");
av_free(pOut);
return AVERROR(EINVAL);
}
if((err = av_reallocp(&pOut, totolSize + padding)) < 0)
return err;
memcpy(pOut+totolSize-unitSize-sizeof(startCode), startCode, sizeof(startCode));
memcpy(pOut+totolSize-unitSize, pExtraData, unitSize);
pExtraData += unitSize;
}
/* 获取PPS */
ppsUnitNum = (*pExtraData++ & 0x1f); /* PPS数量 */
while(ppsUnitNum--)
{
unitSize = (pExtraData[0]<<8 | pExtraData[1]); /* 两个字节表示这个unit的长度 */
pExtraData += 2;
totolSize += unitSize + sizeof(startCode);
printf("unitSize:%d\n", unitSize);
if(totolSize > INT_MAX - padding)
{
av_log(NULL, AV_LOG_ERROR,
"Too big extradata size, corrupted stream or invalid MP4/AVCC bitstream\n");
av_free(pOut);
return AVERROR(EINVAL);
}
if(pExtraData + unitSize > pCodecExtraData + codecExtraDataSize)
{
av_log(NULL, AV_LOG_ERROR, "Packet header is not contained in global extradata, "
"corrupted stream or invalid MP4/AVCC bitstream\n");
av_free(pOut);
return AVERROR(EINVAL);
}
if((err = av_reallocp(&pOut, totolSize + padding)) < 0)
return err;
memcpy(pOut+totolSize-unitSize-sizeof(startCode), startCode, sizeof(startCode));
memcpy(pOut+totolSize-unitSize, pExtraData, unitSize);
pExtraData += unitSize;
}
pOutExtradata->data = pOut;
pOutExtradata->size = totolSize;
return len;
}
/* 将数据复制并且增加start code */
static int alloc_and_copy(AVPacket *pOutPkt, const uint8_t *spspps, uint32_t spsppsSize,
const uint8_t *pIn, uint32_t inSize)
{
int err;
int startCodeLen = 3; /* start code长度 */
/* 给pOutPkt->data分配内存 */
err = av_grow_packet(pOutPkt, spsppsSize + inSize + startCodeLen);
if (err < 0)
return err;
if (spspps)
{
memcpy(pOutPkt->data , spspps, spsppsSize); /* 拷贝SPS与PPS(前面分离的时候已经加了startcode(00 00 00 01)) */
}
/* 将真正的原始数据写入packet中 */
(pOutPkt->data + spsppsSize)[0] = 0;
(pOutPkt->data + spsppsSize)[1] = 0;
(pOutPkt->data + spsppsSize)[2] = 1;
memcpy(pOutPkt->data + spsppsSize + startCodeLen , pIn, inSize);
return 0;
}
static int h264Mp4ToAnnexb(AVFormatContext *pAVFormatContext, AVPacket *pAvPkt, FILE *pFd)
{
unsigned char *pData = pAvPkt->data; /* 帧数据 */
unsigned char *pEnd = NULL;
int dataSize = pAvPkt->size; /* pAvPkt->data的数据量 */
int curSize = 0;
int naluSize = 0;
int i;
unsigned char nalHeader, nalType;
AVPacket spsppsPkt;
AVPacket *pOutPkt;
int ret;
int len;
pOutPkt = av_packet_alloc();
pOutPkt->data = NULL;
pOutPkt->size = 0;
spsppsPkt.data = NULL;
spsppsPkt.size = 0;
pEnd = pData + dataSize;
while(curSize < dataSize)
{
if(pEnd-pData < 4)
goto fail;
/* 前四个字节表示当前NALU的大小 */
for(i = 0; i < 4; i++)
{
naluSize <<= 8;
naluSize |= pData[i];
}
pData += 4;
if(naluSize > (pEnd-pData+1) || naluSize <= 0)
{
goto fail;
}
nalHeader = *pData;
nalType = nalHeader&0x1F;
if(nalType == 5)
{
/* 得到SPS与PPS(存在与codec->extradata中) */
h264_extradata_to_annexb(pAVFormatContext->streams[pAvPkt->stream_index]->codec->extradata,
pAVFormatContext->streams[pAvPkt->stream_index]->codec->extradata_size,
&spsppsPkt, AV_INPUT_BUFFER_PADDING_SIZE);
/* 添加start code */
ret = alloc_and_copy(pOutPkt, spsppsPkt.data, spsppsPkt.size, pData, naluSize);
if(ret < 0)
goto fail;
}
else
{
/* 添加start code */
ret = alloc_and_copy(pOutPkt, NULL, 0, pData, naluSize);
if(ret < 0)
goto fail;
}
/* 将处理好的数据写入文件中 */
len = fwrite(pOutPkt->data, 1, pOutPkt->size, pFd);
if(len != pOutPkt->size)
{
av_log(NULL, AV_LOG_DEBUG, "fwrite warning(%d, %d)!\n", len, pOutPkt->size);
}
/* 将数据从缓冲区写入磁盘 */
fflush(pFd);
curSize += (naluSize+4);
pData += naluSize; /* 处理下一个NALU */
}
fail:
av_packet_free(&pOutPkt);
if(spsppsPkt.data)
{
free(spsppsPkt.data);
spsppsPkt.data = NULL;
}
return 0;
}
static int parseH264FromMp4(char *pSrc, char *pDst)
{
AVFormatContext *pAVFormatContext = NULL;
AVInputFormat avInputFormat;
AVPacket avPkt;
FILE *pFd = NULL;
int videoStreamIndex; /* 视频流对应的index */
int ret;
int len;
av_log_set_level(AV_LOG_DEBUG); /* 设置日志打印级别 */
av_register_all(); /* 对ffmpeg的初始化 */
/* 打开video文件 */
ret = avformat_open_input(&pAVFormatContext, pSrc, NULL, NULL);
if(ret < 0)
{
av_log(NULL, AV_LOG_ERROR, "avformat_open_input:%s\n", av_err2str(ret));
return -1;
}
/* 打印video文件信息 */
av_dump_format(pAVFormatContext, 0, pSrc, 0);
/* 找到最好的一路视频流 */
videoStreamIndex = av_find_best_stream(pAVFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
if(videoStreamIndex < 0)
{
av_log(NULL, AV_LOG_ERROR, "av_find_best_stream error!\n");
avformat_close_input(&pAVFormatContext);
}
/* 初始化一个数据包 */
av_init_packet(&avPkt);
/* 读取到的数据存储在这里 */
avPkt.data = NULL;
avPkt.size = 0;
/* 打开输出文件 */
pFd = fopen(pDst, "wb");
if(!pFd)
{
av_log(NULL, AV_LOG_ERROR, "fopen error!\n");
avformat_close_input(&pAVFormatContext);
}
/* 开始读取数据包 */
while(av_read_frame(pAVFormatContext, &avPkt) >= 0)
{
if(avPkt.stream_index == videoStreamIndex) /* 当前数据包属于视频流 */
{
/* 处理每一个数据包 */
h264Mp4ToAnnexb(pAVFormatContext, &avPkt, pFd);
}
av_packet_unref(&avPkt);
}
/* 关闭video文件 */
avformat_close_input(&pAVFormatContext);
if(pFd)
fclose(pFd);
return 0;
}
int main(int argc, char *argv[])
{
if(argc != 3)
{
av_log(NULL, AV_LOG_INFO, "arg error\n");
return -1;
}
/* argv[1]为输入MP4文件,argv[2]为输出的h264文件 */
parseH264FromMp4(argv[1], argv[2]);
return 0;
}