📄 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"#include "avc.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 4typedef 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; ///< stsd fourcc 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 placeholdersstatic 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_glbl_tag(ByteIOContext *pb, MOVTrack* track){ put_be32(pb, track->vosLen+8); put_tag(pb, "glbl"); put_buffer(pb, track->vosData, track->vosLen); return 8+track->vosLen;}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->tag == MKTAG('m','p','4','a')) mov_write_esds_tag(pb, track); else if(track->enc->codec_id == CODEC_ID_AMR_NB) mov_write_amr_tag(pb, track); else if(track->vosLen > 0) mov_write_glbl_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 int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track){ offset_t pos = url_ftell(pb); put_be32(pb, 0); put_tag(pb, "avcC"); ff_isom_write_avcc(pb, track->vosData, track->vosLen); return updateSize(pb, pos);}/* also used by all avid codecs (dv, imx, meridien) and their variants */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -