📄 movenc.c
字号:
/*
* MOV, 3GP, MP4 muxer
* Copyright (c) 2003 Thomas Raivio.
* Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>.
*
* 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 "riff.h"
#include "avio.h"
#include "isom.h"
#undef NDEBUG
#include <assert.h>
#define MOV_INDEX_CLUSTER_SIZE 16384
#define globalTimescale 1000
#define MODE_MP4 0
#define MODE_MOV 1
#define MODE_3GP 2
#define MODE_PSP 3 // example working PSP command line:
// ffmpeg -i testinput.avi -f psp -r 14.985 -s 320x240 -b 768 -ar 24000 -ab 32 M4V00001.MP4
#define MODE_3G2 4
typedef struct MOVIentry {
unsigned int flags, size;
uint64_t pos;
unsigned int samplesInChunk;
char key_frame;
unsigned int entries;
int64_t cts;
int64_t dts;
} MOVIentry;
typedef struct MOVIndex {
int mode;
int entry;
long timescale;
long time;
int64_t trackDuration;
long sampleCount;
long sampleSize;
int hasKeyframes;
int hasBframes;
int language;
int trackID;
int tag;
AVCodecContext *enc;
int vosLen;
uint8_t *vosData;
MOVIentry *cluster;
int audio_vbr;
} MOVTrack;
typedef struct MOVContext {
int mode;
int64_t time;
int nb_streams;
offset_t mdat_pos;
uint64_t mdat_size;
long timescale;
MOVTrack tracks[MAX_STREAMS];
} MOVContext;
//FIXME support 64 bit variant with wide placeholders
static offset_t updateSize (ByteIOContext *pb, offset_t pos)
{
offset_t curpos = url_ftell(pb);
url_fseek(pb, pos, SEEK_SET);
put_be32(pb, curpos - pos); /* rewrite size */
url_fseek(pb, curpos, SEEK_SET);
return curpos - pos;
}
/* Chunk offset atom */
static int mov_write_stco_tag(ByteIOContext *pb, MOVTrack* track)
{
int i;
int mode64 = 0; // use 32 bit size variant if possible
offset_t pos = url_ftell(pb);
put_be32(pb, 0); /* size */
if (pos > UINT32_MAX) {
mode64 = 1;
put_tag(pb, "co64");
} else
put_tag(pb, "stco");
put_be32(pb, 0); /* version & flags */
put_be32(pb, track->entry); /* entry count */
for (i=0; i<track->entry; i++) {
if(mode64 == 1)
put_be64(pb, track->cluster[i].pos);
else
put_be32(pb, track->cluster[i].pos);
}
return updateSize (pb, pos);
}
/* Sample size atom */
static int mov_write_stsz_tag(ByteIOContext *pb, MOVTrack* track)
{
int equalChunks = 1;
int i, j, entries = 0, tst = -1, oldtst = -1;
offset_t pos = url_ftell(pb);
put_be32(pb, 0); /* size */
put_tag(pb, "stsz");
put_be32(pb, 0); /* version & flags */
for (i=0; i<track->entry; i++) {
tst = track->cluster[i].size/track->cluster[i].entries;
if(oldtst != -1 && tst != oldtst) {
equalChunks = 0;
}
oldtst = tst;
entries += track->cluster[i].entries;
}
if (equalChunks) {
int sSize = track->cluster[0].size/track->cluster[0].entries;
put_be32(pb, sSize); // sample size
put_be32(pb, entries); // sample count
}
else {
put_be32(pb, 0); // sample size
put_be32(pb, entries); // sample count
for (i=0; i<track->entry; i++) {
for ( j=0; j<track->cluster[i].entries; j++) {
put_be32(pb, track->cluster[i].size /
track->cluster[i].entries);
}
}
}
return updateSize (pb, pos);
}
/* Sample to chunk atom */
static int mov_write_stsc_tag(ByteIOContext *pb, MOVTrack* track)
{
int index = 0, oldval = -1, i;
offset_t entryPos, curpos;
offset_t pos = url_ftell(pb);
put_be32(pb, 0); /* size */
put_tag(pb, "stsc");
put_be32(pb, 0); // version & flags
entryPos = url_ftell(pb);
put_be32(pb, track->entry); // entry count
for (i=0; i<track->entry; i++) {
if(oldval != track->cluster[i].samplesInChunk)
{
put_be32(pb, i+1); // first chunk
put_be32(pb, track->cluster[i].samplesInChunk); // samples per chunk
put_be32(pb, 0x1); // sample description index
oldval = track->cluster[i].samplesInChunk;
index++;
}
}
curpos = url_ftell(pb);
url_fseek(pb, entryPos, SEEK_SET);
put_be32(pb, index); // rewrite size
url_fseek(pb, curpos, SEEK_SET);
return updateSize (pb, pos);
}
/* Sync sample atom */
static int mov_write_stss_tag(ByteIOContext *pb, MOVTrack* track)
{
offset_t curpos, entryPos;
int i, index = 0;
offset_t pos = url_ftell(pb);
put_be32(pb, 0); // size
put_tag(pb, "stss");
put_be32(pb, 0); // version & flags
entryPos = url_ftell(pb);
put_be32(pb, track->entry); // entry count
for (i=0; i<track->entry; i++) {
if(track->cluster[i].key_frame == 1) {
put_be32(pb, i+1);
index++;
}
}
curpos = url_ftell(pb);
url_fseek(pb, entryPos, SEEK_SET);
put_be32(pb, index); // rewrite size
url_fseek(pb, curpos, SEEK_SET);
return updateSize (pb, pos);
}
static int mov_write_amr_tag(ByteIOContext *pb, MOVTrack *track)
{
put_be32(pb, 0x11); /* size */
if (track->mode == MODE_MOV) put_tag(pb, "samr");
else put_tag(pb, "damr");
put_tag(pb, "FFMP");
put_byte(pb, 0); /* decoder version */
put_be16(pb, 0x81FF); /* Mode set (all modes for AMR_NB) */
put_byte(pb, 0x00); /* Mode change period (no restriction) */
put_byte(pb, 0x01); /* Frames per sample */
return 0x11;
}
static int mov_write_enda_tag(ByteIOContext *pb)
{
put_be32(pb, 10);
put_tag(pb, "enda");
put_be16(pb, 1); /* little endian */
return 10;
}
static unsigned int descrLength(unsigned int len)
{
int i;
for(i=1; len>>(7*i); i++);
return len + 1 + i;
}
static void putDescr(ByteIOContext *pb, int tag, unsigned int size)
{
int i= descrLength(size) - size - 2;
put_byte(pb, tag);
for(; i>0; i--)
put_byte(pb, (size>>(7*i)) | 0x80);
put_byte(pb, size & 0x7F);
}
static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track) // Basic
{
offset_t pos = url_ftell(pb);
int decoderSpecificInfoLen = track->vosLen ? descrLength(track->vosLen):0;
put_be32(pb, 0); // size
put_tag(pb, "esds");
put_be32(pb, 0); // Version
// ES descriptor
putDescr(pb, 0x03, 3 + descrLength(13 + decoderSpecificInfoLen) +
descrLength(1));
put_be16(pb, track->trackID);
put_byte(pb, 0x00); // flags (= no flags)
// DecoderConfig descriptor
putDescr(pb, 0x04, 13 + decoderSpecificInfoLen);
// Object type indication
put_byte(pb, codec_get_tag(ff_mp4_obj_type, track->enc->codec_id));
// the following fields is made of 6 bits to identify the streamtype (4 for video, 5 for audio)
// plus 1 bit to indicate upstream and 1 bit set to 1 (reserved)
if(track->enc->codec_type == CODEC_TYPE_AUDIO)
put_byte(pb, 0x15); // flags (= Audiostream)
else
put_byte(pb, 0x11); // flags (= Visualstream)
put_byte(pb, track->enc->rc_buffer_size>>(3+16)); // Buffersize DB (24 bits)
put_be16(pb, (track->enc->rc_buffer_size>>3)&0xFFFF); // Buffersize DB
put_be32(pb, FFMAX(track->enc->bit_rate, track->enc->rc_max_rate)); // maxbitrate (FIXME should be max rate in any 1 sec window)
if(track->enc->rc_max_rate != track->enc->rc_min_rate || track->enc->rc_min_rate==0)
put_be32(pb, 0); // vbr
else
put_be32(pb, track->enc->rc_max_rate); // avg bitrate
if (track->vosLen)
{
// DecoderSpecific info descriptor
putDescr(pb, 0x05, track->vosLen);
put_buffer(pb, track->vosData, track->vosLen);
}
// SL descriptor
putDescr(pb, 0x06, 1);
put_byte(pb, 0x02);
return updateSize (pb, pos);
}
static int mov_write_wave_tag(ByteIOContext *pb, MOVTrack* track)
{
offset_t pos = url_ftell(pb);
put_be32(pb, 0); /* size */
put_tag(pb, "wave");
put_be32(pb, 12); /* size */
put_tag(pb, "frma");
put_le32(pb, track->tag);
if (track->enc->codec_id == CODEC_ID_AAC) {
/* useless atom needed by mplayer, ipod, not needed by quicktime */
put_be32(pb, 12); /* size */
put_tag(pb, "mp4a");
put_be32(pb, 0);
mov_write_esds_tag(pb, track);
} else if (track->enc->codec_id == CODEC_ID_PCM_S24LE ||
track->enc->codec_id == CODEC_ID_PCM_S32LE) {
mov_write_enda_tag(pb);
} else if (track->enc->codec_id == CODEC_ID_AMR_NB) {
mov_write_amr_tag(pb, track);
}
put_be32(pb, 8); /* size */
put_be32(pb, 0); /* null tag */
return updateSize (pb, pos);
}
static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track)
{
offset_t pos = url_ftell(pb);
int version = track->mode == MODE_MOV &&
(track->audio_vbr ||
track->enc->codec_id == CODEC_ID_PCM_S32LE ||
track->enc->codec_id == CODEC_ID_PCM_S24LE);
put_be32(pb, 0); /* size */
put_le32(pb, track->tag); // store it byteswapped
put_be32(pb, 0); /* Reserved */
put_be16(pb, 0); /* Reserved */
put_be16(pb, 1); /* Data-reference index, XXX == 1 */
/* SoundDescription */
put_be16(pb, version); /* Version */
put_be16(pb, 0); /* Revision level */
put_be32(pb, 0); /* Reserved */
if (track->mode == MODE_MOV) {
put_be16(pb, track->enc->channels);
if (track->enc->codec_id == CODEC_ID_PCM_U8 ||
track->enc->codec_id == CODEC_ID_PCM_S8)
put_be16(pb, 8); /* bits per sample */
else
put_be16(pb, 16);
put_be16(pb, track->audio_vbr ? -2 : 0); /* compression ID */
} else { /* reserved for mp4/3gp */
put_be16(pb, 2);
put_be16(pb, 16);
put_be16(pb, 0);
}
put_be16(pb, 0); /* packet size (= 0) */
put_be16(pb, track->timescale); /* Time scale */
put_be16(pb, 0); /* Reserved */
if(version == 1) { /* SoundDescription V1 extended info */
put_be32(pb, track->enc->frame_size); /* Samples per packet */
put_be32(pb, track->sampleSize / track->enc->channels); /* Bytes per packet */
put_be32(pb, track->sampleSize); /* Bytes per frame */
put_be32(pb, 2); /* Bytes per sample */
}
if(track->mode == MODE_MOV &&
(track->enc->codec_id == CODEC_ID_AAC ||
track->enc->codec_id == CODEC_ID_AMR_NB ||
track->enc->codec_id == CODEC_ID_PCM_S24LE ||
track->enc->codec_id == CODEC_ID_PCM_S32LE))
mov_write_wave_tag(pb, track);
else if(track->enc->codec_id == CODEC_ID_AAC)
mov_write_esds_tag(pb, track);
else if(track->enc->codec_id == CODEC_ID_AMR_NB)
mov_write_amr_tag(pb, track);
return updateSize (pb, pos);
}
static int mov_write_d263_tag(ByteIOContext *pb)
{
put_be32(pb, 0xf); /* size */
put_tag(pb, "d263");
put_tag(pb, "FFMP");
put_byte(pb, 0); /* decoder version */
/* FIXME use AVCodecContext level/profile, when encoder will set values */
put_byte(pb, 0xa); /* level */
put_byte(pb, 0); /* profile */
return 0xf;
}
/* TODO: No idea about these values */
static int mov_write_svq3_tag(ByteIOContext *pb)
{
put_be32(pb, 0x15);
put_tag(pb, "SMI ");
put_tag(pb, "SEQH");
put_be32(pb, 0x5);
put_be32(pb, 0xe2c0211d);
put_be32(pb, 0xc0000000);
put_byte(pb, 0);
return 0x15;
}
static uint8_t *avc_find_startcode( uint8_t *p, uint8_t *end )
{
uint8_t *a = p + 4 - ((long)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 ) {
uint32_t x = *(uint32_t*)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-1;
if( p[2] == 0 && p[3] == 1 )
return p;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -