本章只记录h264封装成avi格式视频,参考ffmpeg项目的avienc.c,avi封装格式图请查看这个博客:https://blog.csdn.net/houxiaoni01/article/details/84341885
结构体如下:
typedef struct
{
long fdes; /* File descriptor of AVI file */
u_8 bExtFd; //is extern fd,not close
u_8 recv[3];
long mode; /* 0 for reading, 1 for writing */
long width; /* Width of a video frame */
long height; /* Height of a video frame */
double fps; /* Frames per second */
char compressor[8]; /* Type of compressor, 4 bytes + padding for 0 byte */
char compressor2[8]; /* Type of compressor, 4 bytes + padding for 0 byte */
long video_strn; /* Video stream number */
long video_frames; /* Number of video frames */
char video_tag[4]; /* Tag of video data */
long video_pos; /* Number of next frame to be read
(if index present) */
unsigned long max_len; /* maximum video chunk present */
track_t track[AVI_MAX_TRACKS]; // up to AVI_MAX_TRACKS audio tracks supported
off_t pos; /* position in file */
long n_idx; /* number of index entries actually filled */
long max_idx; /* number of index entries actually allocated */
off_t v_codech_off; /* absolut offset of video codec (strh) info */
off_t v_codecf_off; /* absolut offset of video codec (strf) info */
unsigned char (*idx)[16]; /* index entries (AVI idx1 tag) */
video_index_entry *video_index;
off_t last_pos; /* Position of last frame written */
unsigned long last_len; /* Length of last frame written */
int must_use_index; /* Flag if frames are duplicated */
off_t movi_start;
int anum; // total number of audio tracks
int aptr; // current audio working track
BITMAPINFOHEADER_avilib *bitmap_info_header;
WAVEFORMATEX_avilib *wave_format_ex[AVI_MAX_TRACKS];
s_8* sendBuf;
u_32 sendLen;
u_32 sndOut;
u_32 sendAll;
} avi_t;
伪代码流程:
1.open一个avi文件句柄
2.找一个开始时间戳的I帧;
3.设置视频通道的分辨率、帧率
4.设置音频通道采样率,格式,码率
5.填充avi格式数据
6.写入h264 视频帧
7.close avi文件句柄
ok,先看看ffmpeg是怎么填充avi格式的。如下所示:
static int avi_write_header(AVFormatContext *s)
{
AVIContext *avi = s->priv_data;
AVIOContext *pb = s->pb;
int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
int64_t max_stream_duration = 0;
AVCodecParameters *video_par;
AVStream *video_st = NULL;
int64_t list1, list2, strh, strf;
AVDictionaryEntry *t = NULL;
int padding;
if (s->nb_streams > AVI_MAX_STREAM_COUNT) {
av_log(s, AV_LOG_ERROR, "AVI does not support "
">"AV_STRINGIFY(AVI_MAX_STREAM_COUNT)" streams\n");
return AVERROR(EINVAL);
}
for (n = 0; n < s->nb_streams; n++) {
s->streams[n]->priv_data = av_mallocz(sizeof(AVIStream));
if (!s->streams[n]->priv_data)
return AVERROR(ENOMEM);
}
/* header list */
avi->riff_id = 0;
list1 = avi_start_new_riff(s, pb, "AVI ", "hdrl");
/* avi header */
ffio_wfourcc(pb, "avih");
avio_wl32(pb, 14 * 4);
bitrate = 0;
video_par = NULL;
for (n = 0; n < s->nb_streams; n++) {
AVCodecParameters *par = s->streams[n]->codecpar;
AVStream *st = s->streams[n];
bitrate = FFMIN(bitrate + par->bit_rate, INT32_MAX);
if (st->duration > 0) {
int64_t stream_duration = av_rescale_q(st->duration, st->time_base, AV_TIME_BASE_Q);
max_stream_duration = FFMAX(stream_duration, max_stream_duration);
}
if (par->codec_type == AVMEDIA_TYPE_VIDEO) {
video_par = par;
video_st = st;
}
}
/* guess master index size based on bitrate and duration */
if (!avi->reserve_index_space) {
double duration_est, filesize_est;
if (s->duration > 0)
duration_est = (double)s->duration / AV_TIME_BASE;
else if (max_stream_duration > 0)
duration_est = (double)max_stream_duration / AV_TIME_BASE;
else
duration_est = 10 * 60 * 60; /* default to 10 hours */
filesize_est = duration_est * (bitrate / 8) * 1.10; /* add 10% safety margin for muxer+bitrate */
avi->master_index_max_size = FFMAX((int)ceil(filesize_est / AVI_MAX_RIFF_SIZE) + 1,
avi->master_index_max_size);
av_log(s, AV_LOG_DEBUG, "duration_est:%0.3f, filesize_est:%0.1fGiB, master_index_max_size:%d\n",
duration_est, filesize_est / (1024*1024*1024), avi->master_index_max_size);
}
nb_frames = 0;
// TODO: should be a