📄 movenc.c
字号:
/* * MOV, 3GP, MP4 encoder. * Copyright (c) 2003 Thomas Raivio. * Copyright (c) 2004 Gildas Bazin <gbazin at videolan dot org>. * * This library 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 of the License, or (at your option) any later version. * * This library 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 this library; 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 "avio.h"#include "mov.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;} MOVIentry;typedef struct MOVIndex { int mode; int entry; uint64_t mdat_size; int ents_allocated; long timescale; long time; int64_t trackDuration; long sampleCount; long sampleDuration; int hasKeyframes; int hasBframes; int language; int trackID; AVCodecContext *enc; int vosLen; uint8_t *vosData; MOVIentry** cluster;} MOVTrack;typedef struct MOVContext { int mode; int64_t time; int nb_streams; int mdat_written; offset_t mdat_pos; long timescale; MOVTrack tracks[MAX_STREAMS];} MOVContext;static int mov_write_esds_tag(ByteIOContext *pb, MOVTrack* track);/* output language code from iso639 language name */extern int ff_mov_iso639_to_lang(const char *lang, int mp4);//FIXME supprt 64bit varaint 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++) { int cl = i / MOV_INDEX_CLUSTER_SIZE; int id = i % MOV_INDEX_CLUSTER_SIZE; if(mode64 == 1) put_be64(pb, track->cluster[cl][id].pos); else put_be32(pb, track->cluster[cl][id].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++) { int cl = i / MOV_INDEX_CLUSTER_SIZE; int id = i % MOV_INDEX_CLUSTER_SIZE; tst = track->cluster[cl][id].size/track->cluster[cl][id].entries; if(oldtst != -1 && tst != oldtst) { equalChunks = 0; } oldtst = tst; entries += track->cluster[cl][id].entries; } if (equalChunks) { int sSize = track->cluster[0][0].size/track->cluster[0][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++) { int cl = i / MOV_INDEX_CLUSTER_SIZE; int id = i % MOV_INDEX_CLUSTER_SIZE; for ( j=0; j<track->cluster[cl][id].entries; j++) { put_be32(pb, track->cluster[cl][id].size / track->cluster[cl][id].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++) { int cl = i / MOV_INDEX_CLUSTER_SIZE; int id = i % MOV_INDEX_CLUSTER_SIZE; if(oldval != track->cluster[cl][id].samplesInChunk) { put_be32(pb, i+1); // first chunk put_be32(pb, track->cluster[cl][id].samplesInChunk); // samples per chunk put_be32(pb, 0x1); // sample description index oldval = track->cluster[cl][id].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++) { int cl = i / MOV_INDEX_CLUSTER_SIZE; int id = i % MOV_INDEX_CLUSTER_SIZE; if(track->cluster[cl][id].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_damr_tag(ByteIOContext *pb){ put_be32(pb, 0x11); /* size */ put_tag(pb, "damr"); put_tag(pb, "FFMP"); put_byte(pb, 0); put_be16(pb, 0x80); /* Mode set (all modes for AMR_NB) */ put_be16(pb, 0xa); /* Mode change period (no restriction) */ //put_be16(pb, 0x81ff); /* Mode set (all modes for AMR_NB) */ //put_be16(pb, 1); /* Mode change period (no restriction) */ return 0x11;}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_tag(pb, "mp4a"); put_be32(pb, 12); /* size */ put_tag(pb, "mp4a"); put_be32(pb, 0); mov_write_esds_tag(pb, track); put_be32(pb, 12); /* size */ put_tag(pb, "srcq"); put_be32(pb, 0x40); put_be32(pb, 8); /* size */ put_be32(pb, 0); /* null tag */ return updateSize (pb, pos);}static const CodecTag codec_movaudio_tags[] = { { CODEC_ID_PCM_MULAW, MKTAG('u', 'l', 'a', 'w') }, { CODEC_ID_PCM_ALAW, MKTAG('a', 'l', 'a', 'w') }, { CODEC_ID_ADPCM_IMA_QT, MKTAG('i', 'm', 'a', '4') }, { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, { CODEC_ID_AAC, MKTAG('m', 'p', '4', 'a') }, { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, { CODEC_ID_MP3, MKTAG('.', 'm', 'p', '3') }, { CODEC_ID_NONE, 0 },};static int mov_write_audio_tag(ByteIOContext *pb, MOVTrack* track){ offset_t pos = url_ftell(pb); int tag; put_be32(pb, 0); /* size */ tag = track->enc->codec_tag; if (!tag) tag = codec_get_tag(codec_movaudio_tags, track->enc->codec_id); // if no mac fcc found, try with Microsoft tags if (!tag) { int tmp = codec_get_tag(codec_wav_tags, track->enc->codec_id); tag = MKTAG('m', 's', ((tmp >> 8) & 0xff), (tmp & 0xff)); } put_le32(pb, tag); // store it byteswapped put_be32(pb, 0); /* Reserved */ put_be16(pb, 0); /* Reserved */ put_be16(pb, 1); /* Data-reference index, XXX == 1 */ /* SoundDescription */ if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) put_be16(pb, 1); /* Version 1 */ else put_be16(pb, 0); /* Version 0 */ put_be16(pb, 0); /* Revision level */ put_be32(pb, 0); /* Reserved */ put_be16(pb, track->enc->channels); /* Number of channels */ /* TODO: Currently hard-coded to 16-bit, there doesn't seem to be a good way to get number of bits of audio */ put_be16(pb, 0x10); /* Reserved */ if(track->enc->codec_id == CODEC_ID_AAC || track->enc->codec_id == CODEC_ID_MP3) { put_be16(pb, 0xfffe); /* compression ID (vbr)*/ } else { put_be16(pb, 0); /* compression ID (= 0) */ } put_be16(pb, 0); /* packet size (= 0) */ put_be16(pb, track->timescale); /* Time scale */ put_be16(pb, 0); /* Reserved */ if(track->mode == MODE_MOV && track->enc->codec_id == CODEC_ID_AAC) { /* SoundDescription V1 extended info */ put_be32(pb, track->enc->frame_size); /* Samples per packet */ put_be32(pb, 1536); /* Bytes per packet */ put_be32(pb, 2); /* Bytes per frame */ put_be32(pb, 2); /* Bytes per sample */ } if(track->enc->codec_id == CODEC_ID_AAC) { if( track->mode == MODE_MOV ) mov_write_wave_tag(pb, track); else mov_write_esds_tag(pb, track); } if(track->enc->codec_id == CODEC_ID_AMR_NB) mov_write_damr_tag(pb); 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_be16(pb, 0x0a); put_byte(pb, 0); 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 - ((int)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; if( p[2] == 0 && p[3] == 1 ) return p+1; } if( p[3] == 0 ) { if( p[2] == 0 && p[4] == 1 ) return p+2; if( p[4] == 0 && p[5] == 1 ) return p+3; } } } for( end += 3; p < end; p++ ) { if( p[0] == 0 && p[1] == 0 && p[2] == 1 ) return p; } return end + 3;}static void avc_parse_nal_units(uint8_t **buf, int *size){ ByteIOContext pb; uint8_t *p = *buf; uint8_t *end = p + *size; uint8_t *nal_start, *nal_end; url_open_dyn_buf(&pb); nal_start = avc_find_startcode(p, end); while (nal_start < end) { while(!*(nal_start++)); nal_end = avc_find_startcode(nal_start, end); put_be32(&pb, nal_end - nal_start); put_buffer(&pb, nal_start, nal_end - nal_start); nal_start = nal_end; } av_freep(buf); *size = url_close_dyn_buf(&pb, buf);}static int mov_write_avcc_tag(ByteIOContext *pb, MOVTrack *track){ offset_t pos = url_ftell(pb); put_be32(pb, 0); put_tag(pb, "avcC"); if (track->vosLen > 6) { /* check for h264 start code */ if (BE_32(track->vosData) == 0x00000001) { uint8_t *buf, *end; uint32_t sps_size=0, pps_size=0; uint8_t *sps=0, *pps=0; avc_parse_nal_units(&track->vosData, &track->vosLen); buf = track->vosData; end = track->vosData + track->vosLen; put_byte(pb, 1); /* version */ put_byte(pb, 77); /* profile */ put_byte(pb, 64); /* profile compat */ put_byte(pb, 30); /* level */ put_byte(pb, 0xff); /* 6 bits reserved (111111) + 2 bits nal size length - 1 (11) */ put_byte(pb, 0xe1); /* 3 bits reserved (111) + 5 bits number of sps (00001) */ /* look for sps and pps */ while (buf < end) { unsigned int size; uint8_t nal_type; size = BE_32(buf); nal_type = buf[4] & 0x1f; if (nal_type == 7) { /* SPS */ sps = buf + 4; sps_size = size; } else if (nal_type == 8) { /* PPS */ pps = buf + 4; pps_size = size; } buf += size + 4; } assert(sps); assert(pps); put_be16(pb, sps_size); put_buffer(pb, sps, sps_size); put_byte(pb, 1); /* number of pps */ put_be16(pb, pps_size);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -