📄 avienc.c
字号:
/*
* AVI muxer
* Copyright (c) 2000 Fabrice Bellard.
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "avformat.h"
#include "avi.h"
#include "riff.h"
/*
* TODO:
* - fill all fields if non streamed (nb_frames for example)
*/
#ifdef CONFIG_AVI_MUXER
typedef struct AVIIentry {
unsigned int flags, pos, len;
} AVIIentry;
#define AVI_INDEX_CLUSTER_SIZE 16384
typedef struct AVIIndex {
offset_t indx_start;
int entry;
int ents_allocated;
AVIIentry** cluster;
} AVIIndex;
typedef struct {
offset_t riff_start, movi_list, odml_list;
offset_t frames_hdr_all, frames_hdr_strm[MAX_STREAMS];
int audio_strm_length[MAX_STREAMS];
int riff_id;
int packet_count[MAX_STREAMS];
AVIIndex indexes[MAX_STREAMS];
} AVIContext;
static inline AVIIentry* avi_get_ientry(AVIIndex* idx, int ent_id)
{
int cl = ent_id / AVI_INDEX_CLUSTER_SIZE;
int id = ent_id % AVI_INDEX_CLUSTER_SIZE;
return &idx->cluster[cl][id];
}
static offset_t avi_start_new_riff(AVIContext *avi, ByteIOContext *pb,
const char* riff_tag, const char* list_tag)
{
offset_t loff;
int i;
avi->riff_id++;
for (i=0; i<MAX_STREAMS; i++)
avi->indexes[i].entry = 0;
avi->riff_start = start_tag(pb, "RIFF");
put_tag(pb, riff_tag);
loff = start_tag(pb, "LIST");
put_tag(pb, list_tag);
return loff;
}
static char* avi_stream2fourcc(char* tag, int index, enum CodecType type)
{
tag[0] = '0';
tag[1] = '0' + index;
if (type == CODEC_TYPE_VIDEO) {
tag[2] = 'd';
tag[3] = 'c';
} else {
tag[2] = 'w';
tag[3] = 'b';
}
tag[4] = '\0';
return tag;
}
static void avi_write_info_tag(ByteIOContext *pb, const char *tag, const char *str)
{
int len = strlen(str);
if (len > 0) {
len++;
put_tag(pb, tag);
put_le32(pb, len);
put_strz(pb, str);
if (len & 1)
put_byte(pb, 0);
}
}
static int avi_write_counters(AVFormatContext* s, int riff_id)
{
ByteIOContext *pb = &s->pb;
AVIContext *avi = s->priv_data;
int n, au_byterate, au_ssize, au_scale, nb_frames = 0;
offset_t file_size;
AVCodecContext* stream;
file_size = url_ftell(pb);
for(n = 0; n < s->nb_streams; n++) {
assert(avi->frames_hdr_strm[n]);
stream = s->streams[n]->codec;
url_fseek(pb, avi->frames_hdr_strm[n], SEEK_SET);
ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
if(au_ssize == 0) {
put_le32(pb, avi->packet_count[n]);
} else {
put_le32(pb, avi->audio_strm_length[n] / au_ssize);
}
if(stream->codec_type == CODEC_TYPE_VIDEO)
nb_frames = FFMAX(nb_frames, avi->packet_count[n]);
}
if(riff_id == 1) {
assert(avi->frames_hdr_all);
url_fseek(pb, avi->frames_hdr_all, SEEK_SET);
put_le32(pb, nb_frames);
}
url_fseek(pb, file_size, SEEK_SET);
return 0;
}
static int avi_write_header(AVFormatContext *s)
{
AVIContext *avi = s->priv_data;
ByteIOContext *pb = &s->pb;
int bitrate, n, i, nb_frames, au_byterate, au_ssize, au_scale;
AVCodecContext *stream, *video_enc;
offset_t list1, list2, strh, strf;
/* header list */
avi->riff_id = 0;
list1 = avi_start_new_riff(avi, pb, "AVI ", "hdrl");
/* avi header */
put_tag(pb, "avih");
put_le32(pb, 14 * 4);
bitrate = 0;
video_enc = NULL;
for(n=0;n<s->nb_streams;n++) {
stream = s->streams[n]->codec;
bitrate += stream->bit_rate;
if (stream->codec_type == CODEC_TYPE_VIDEO)
video_enc = stream;
}
nb_frames = 0;
if(video_enc){
put_le32(pb, (uint32_t)(INT64_C(1000000) * video_enc->time_base.num / video_enc->time_base.den));
} else {
put_le32(pb, 0);
}
put_le32(pb, bitrate / 8); /* XXX: not quite exact */
put_le32(pb, 0); /* padding */
if (url_is_streamed(pb))
put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_ISINTERLEAVED); /* flags */
else
put_le32(pb, AVIF_TRUSTCKTYPE | AVIF_HASINDEX | AVIF_ISINTERLEAVED); /* flags */
avi->frames_hdr_all = url_ftell(pb); /* remember this offset to fill later */
put_le32(pb, nb_frames); /* nb frames, filled later */
put_le32(pb, 0); /* initial frame */
put_le32(pb, s->nb_streams); /* nb streams */
put_le32(pb, 1024 * 1024); /* suggested buffer size */
if(video_enc){
put_le32(pb, video_enc->width);
put_le32(pb, video_enc->height);
} else {
put_le32(pb, 0);
put_le32(pb, 0);
}
put_le32(pb, 0); /* reserved */
put_le32(pb, 0); /* reserved */
put_le32(pb, 0); /* reserved */
put_le32(pb, 0); /* reserved */
/* stream list */
for(i=0;i<n;i++) {
list2 = start_tag(pb, "LIST");
put_tag(pb, "strl");
stream = s->streams[i]->codec;
/* stream generic header */
strh = start_tag(pb, "strh");
switch(stream->codec_type) {
case CODEC_TYPE_VIDEO: put_tag(pb, "vids"); break;
case CODEC_TYPE_AUDIO: put_tag(pb, "auds"); break;
// case CODEC_TYPE_TEXT : put_tag(pb, "txts"); break;
case CODEC_TYPE_DATA : put_tag(pb, "dats"); break;
}
if(stream->codec_type == CODEC_TYPE_VIDEO)
put_le32(pb, stream->codec_tag);
else
put_le32(pb, 1);
put_le32(pb, 0); /* flags */
put_le16(pb, 0); /* priority */
put_le16(pb, 0); /* language */
put_le32(pb, 0); /* initial frame */
ff_parse_specific_params(stream, &au_byterate, &au_ssize, &au_scale);
put_le32(pb, au_scale); /* scale */
put_le32(pb, au_byterate); /* rate */
av_set_pts_info(s->streams[i], 64, au_scale, au_byterate);
put_le32(pb, 0); /* start */
avi->frames_hdr_strm[i] = url_ftell(pb); /* remember this offset to fill later */
if (url_is_streamed(pb))
put_le32(pb, AVI_MAX_RIFF_SIZE); /* FIXME: this may be broken, but who cares */
else
put_le32(pb, 0); /* length, XXX: filled later */
/* suggested buffer size */ //FIXME set at the end to largest chunk
if(stream->codec_type == CODEC_TYPE_VIDEO)
put_le32(pb, 1024 * 1024);
else if(stream->codec_type == CODEC_TYPE_AUDIO)
put_le32(pb, 12 * 1024);
else
put_le32(pb, 0);
put_le32(pb, -1); /* quality */
put_le32(pb, au_ssize); /* sample size */
put_le32(pb, 0);
put_le16(pb, stream->width);
put_le16(pb, stream->height);
end_tag(pb, strh);
if(stream->codec_type != CODEC_TYPE_DATA){
strf = start_tag(pb, "strf");
switch(stream->codec_type) {
case CODEC_TYPE_VIDEO:
put_bmp_header(pb, stream, codec_bmp_tags, 0);
break;
case CODEC_TYPE_AUDIO:
if (put_wav_header(pb, stream) < 0) {
av_free(avi);
return -1;
}
break;
default:
return -1;
}
end_tag(pb, strf);
}
if (!url_is_streamed(pb)) {
unsigned char tag[5];
int j;
/* Starting to lay out AVI OpenDML master index.
* We want to make it JUNK entry for now, since we'd
* like to get away without making AVI an OpenDML one
* for compatibility reasons.
*/
avi->indexes[i].entry = avi->indexes[i].ents_allocated = 0;
avi->indexes[i].indx_start = start_tag(pb, "JUNK");
put_le16(pb, 4); /* wLongsPerEntry */
put_byte(pb, 0); /* bIndexSubType (0 == frame index) */
put_byte(pb, 0); /* bIndexType (0 == AVI_INDEX_OF_INDEXES) */
put_le32(pb, 0); /* nEntriesInUse (will fill out later on) */
put_tag(pb, avi_stream2fourcc(&tag[0], i, stream->codec_type));
/* dwChunkId */
put_le64(pb, 0); /* dwReserved[3]
put_le32(pb, 0); Must be 0. */
for (j=0; j < AVI_MASTER_INDEX_SIZE * 2; j++)
put_le64(pb, 0);
end_tag(pb, avi->indexes[i].indx_start);
}
end_tag(pb, list2);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -