?? mov.c
字號:
/* * MOV decoder. * Copyright (c) 2001 Fabrice Bellard. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <limits.h> #include "avformat.h"#include "avi.h"#ifdef CONFIG_ZLIB#include <zlib.h>#endif/* * First version by Francois Revol revol@free.fr * * Features and limitations: * - reads most of the QT files I have (at least the structure), * the exceptions are .mov with zlib compressed headers ('cmov' section). It shouldn't be hard to implement. * FIXED, Francois Revol, 07/17/2002 * - ffmpeg has nearly none of the usual QuickTime codecs, * although I succesfully dumped raw and mp3 audio tracks off .mov files. * Sample QuickTime files with mp3 audio can be found at: http://www.3ivx.com/showcase.html * - .mp4 parsing is still hazardous, although the format really is QuickTime with some minor changes * (to make .mov parser crash maybe ?), despite what they say in the MPEG FAQ at * http://mpeg.telecomitalialab.com/faq.htm * - the code is quite ugly... maybe I won't do it recursive next time :-) * * Funny I didn't know about http://sourceforge.net/projects/qt-ffmpeg/ * when coding this :) (it's a writer anyway) * * Reference documents: * http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt * Apple: * http://developer.apple.com/techpubs/quicktime/qtdevdocs/QTFF/qtff.html * http://developer.apple.com/techpubs/quicktime/qtdevdocs/PDF/QTFileFormat.pdf * QuickTime is a trademark of Apple (AFAIK :)) *///#define DEBUG#ifdef DEBUG#include <stdio.h>#include <fcntl.h>#endif#include "qtpalette.h"/* allows chunk splitting - should work now... *//* in case you can't read a file, try commenting */#define MOV_SPLIT_CHUNKS/* Special handling for movies created with Minolta Dimaxe Xi*//* this fix should not interfere with other .mov files, but just in case*/#define MOV_MINOLTA_FIX/* some streams in QT (and in MP4 mostly) aren't either video nor audio *//* so we first list them as this, then clean up the list of streams we give back, *//* getting rid of these */#define CODEC_TYPE_MOV_OTHER (enum CodecType) 2static const CodecTag mov_video_tags[] = {/* { CODEC_ID_, MKTAG('c', 'v', 'i', 'd') }, *//* Cinepak *//* { CODEC_ID_H263, MKTAG('r', 'a', 'w', ' ') }, *//* Uncompressed RGB *//* { CODEC_ID_H263, MKTAG('Y', 'u', 'v', '2') }, *//* Uncompressed YUV422 *//* { CODEC_ID_RAWVIDEO, MKTAG('A', 'V', 'U', 'I') }, *//* YUV with alpha-channel (AVID Uncompressed) *//* Graphics *//* Animation *//* Apple video *//* Kodak Photo CD */ { CODEC_ID_MJPEG, MKTAG('j', 'p', 'e', 'g') }, /* PhotoJPEG */ { CODEC_ID_MPEG1VIDEO, MKTAG('m', 'p', 'e', 'g') }, /* MPEG */ { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'a') }, /* Motion-JPEG (format A) */ { CODEC_ID_MJPEG, MKTAG('m', 'j', 'p', 'b') }, /* Motion-JPEG (format B) */ { CODEC_ID_MJPEG, MKTAG('A', 'V', 'D', 'J') }, /* MJPEG with alpha-channel (AVID JFIF meridien compressed) *//* { CODEC_ID_MJPEG, MKTAG('A', 'V', 'R', 'n') }, *//* MJPEG with alpha-channel (AVID ABVB/Truevision NuVista) *//* { CODEC_ID_GIF, MKTAG('g', 'i', 'f', ' ') }, *//* embedded gif files as frames (usually one "click to play movie" frame) *//* Sorenson video */ { CODEC_ID_SVQ1, MKTAG('S', 'V', 'Q', '1') }, /* Sorenson Video v1 */ { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', '1') }, /* Sorenson Video v1 */ { CODEC_ID_SVQ1, MKTAG('s', 'v', 'q', 'i') }, /* Sorenson Video v1 (from QT specs)*/ { CODEC_ID_SVQ3, MKTAG('S', 'V', 'Q', '3') }, /* Sorenson Video v3 */ { CODEC_ID_MPEG4, MKTAG('m', 'p', '4', 'v') }, { CODEC_ID_MPEG4, MKTAG('D', 'I', 'V', 'X') }, /* OpenDiVX *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag */ { CODEC_ID_MPEG4, MKTAG('X', 'V', 'I', 'D') },/* { CODEC_ID_, MKTAG('I', 'V', '5', '0') }, *//* Indeo 5.0 */ { CODEC_ID_H263, MKTAG('h', '2', '6', '3') }, /* H263 */ { CODEC_ID_H263, MKTAG('s', '2', '6', '3') }, /* H263 ?? works */ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', ' ') }, /* DV NTSC */ { CODEC_ID_DVVIDEO, MKTAG('d', 'v', 'c', 'p') }, /* DV PAL *//* { CODEC_ID_DVVIDEO, MKTAG('A', 'V', 'd', 'v') }, *//* AVID dv */ { CODEC_ID_VP3, MKTAG('V', 'P', '3', '1') }, /* On2 VP3 */ { CODEC_ID_RPZA, MKTAG('r', 'p', 'z', 'a') }, /* Apple Video (RPZA) */ { CODEC_ID_CINEPAK, MKTAG('c', 'v', 'i', 'd') }, /* Cinepak */ { CODEC_ID_8BPS, MKTAG('8', 'B', 'P', 'S') }, /* Planar RGB (8BPS) */ { CODEC_ID_SMC, MKTAG('s', 'm', 'c', ' ') }, /* Apple Graphics (SMC) */ { CODEC_ID_QTRLE, MKTAG('r', 'l', 'e', ' ') }, /* Apple Animation (RLE) */ { CODEC_ID_NONE, 0 },};static const CodecTag mov_audio_tags[] = {/* { CODEC_ID_PCM_S16BE, MKTAG('N', 'O', 'N', 'E') }, *//* uncompressed */ { CODEC_ID_PCM_S16BE, MKTAG('t', 'w', 'o', 's') }, /* 16 bits */ /* { CODEC_ID_PCM_S8, MKTAG('t', 'w', 'o', 's') },*/ /* 8 bits */ { CODEC_ID_PCM_U8, MKTAG('r', 'a', 'w', ' ') }, /* 8 bits unsigned */ { CODEC_ID_PCM_S16LE, MKTAG('s', 'o', 'w', 't') }, /* */ { 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') }, /* IMA-4 ADPCM */ { CODEC_ID_MACE3, MKTAG('M', 'A', 'C', '3') }, /* Macintosh Audio Compression and Expansion 3:1 */ { CODEC_ID_MACE6, MKTAG('M', 'A', 'C', '6') }, /* Macintosh Audio Compression and Expansion 6:1 */ { CODEC_ID_MP2, MKTAG('.', 'm', 'p', '3') }, /* MPEG layer 3 */ /* sample files at http://www.3ivx.com/showcase.html use this tag */ { CODEC_ID_MP2, 0x6D730055 }, /* MPEG layer 3 */ { CODEC_ID_MP2, 0x5500736D }, /* MPEG layer 3 *//* XXX: check endianness *//* { CODEC_ID_OGG_VORBIS, MKTAG('O', 'g', 'g', 'S') }, *//* sample files at http://heroinewarrior.com/xmovie.php3 use this tag *//* MP4 tags */ { CODEC_ID_MPEG4AAC, MKTAG('m', 'p', '4', 'a') }, /* MPEG-4 AAC */ /* The standard for mpeg4 audio is still not normalised AFAIK anyway */ { CODEC_ID_AMR_NB, MKTAG('s', 'a', 'm', 'r') }, /* AMR-NB 3gp */ { CODEC_ID_AMR_WB, MKTAG('s', 'a', 'w', 'b') }, /* AMR-WB 3gp */ { CODEC_ID_AC3, MKTAG('m', 's', 0x20, 0x00) }, /* Dolby AC-3 */ { CODEC_ID_NONE, 0 },};/* the QuickTime file format is quite convoluted... * it has lots of index tables, each indexing something in another one... * Here we just use what is needed to read the chunks */typedef struct MOV_sample_to_chunk_tbl { long first; long count; long id;} MOV_sample_to_chunk_tbl;typedef struct { uint32_t type; int64_t offset; int64_t size; /* total size (excluding the size and type fields) */} MOV_atom_t;typedef struct { int seed; int flags; int size; void* clrs;} MOV_ctab_t;typedef struct { uint8_t version; uint32_t flags; // 24bit /* 0x03 ESDescrTag */ uint16_t es_id;#define MP4ODescrTag 0x01#define MP4IODescrTag 0x02#define MP4ESDescrTag 0x03#define MP4DecConfigDescrTag 0x04#define MP4DecSpecificDescrTag 0x05#define MP4SLConfigDescrTag 0x06#define MP4ContentIdDescrTag 0x07#define MP4SupplContentIdDescrTag 0x08#define MP4IPIPtrDescrTag 0x09#define MP4IPMPPtrDescrTag 0x0A#define MP4IPMPDescrTag 0x0B#define MP4RegistrationDescrTag 0x0D#define MP4ESIDIncDescrTag 0x0E#define MP4ESIDRefDescrTag 0x0F#define MP4FileIODescrTag 0x10#define MP4FileODescrTag 0x11#define MP4ExtProfileLevelDescrTag 0x13#define MP4ExtDescrTagsStart 0x80#define MP4ExtDescrTagsEnd 0xFE uint8_t stream_priority; /* 0x04 DecConfigDescrTag */ uint8_t object_type_id; uint8_t stream_type; /* XXX: really streamType is * only 6bit, followed by: * 1bit upStream * 1bit reserved */ uint32_t buffer_size_db; // 24 uint32_t max_bitrate; uint32_t avg_bitrate; /* 0x05 DecSpecificDescrTag */ uint8_t decoder_cfg_len; uint8_t *decoder_cfg; /* 0x06 SLConfigDescrTag */ uint8_t sl_config_len; uint8_t *sl_config;} MOV_esds_t;struct MOVParseTableEntry;typedef struct MOVStreamContext { int ffindex; /* the ffmpeg stream id */ int is_ff_stream; /* Is this stream presented to ffmpeg ? i.e. is this an audio or video stream ? */ long next_chunk; long chunk_count; int64_t *chunk_offsets; long sample_to_chunk_sz; MOV_sample_to_chunk_tbl *sample_to_chunk; long sample_to_chunk_index; long sample_size; long sample_count; long *sample_sizes; long keyframe_count; long *keyframes; int time_scale; long current_sample; long left_in_chunk; /* how many samples before next chunk */ /* specific MPEG4 header which is added at the beginning of the stream */ int header_len; uint8_t *header_data; MOV_esds_t esds;} MOVStreamContext;typedef struct MOVContext { int mp4; /* set to 1 as soon as we are sure that the file is an .mp4 file (even some header parsing depends on this) */ AVFormatContext *fc; int time_scale; int duration; /* duration of the longest track */ int found_moov; /* when both 'moov' and 'mdat' sections has been found */ int found_mdat; /* we suppose we have enough data to read the file */ int64_t mdat_size; int64_t mdat_offset; int total_streams; /* some streams listed here aren't presented to the ffmpeg API, since they aren't either video nor audio * but we need the info to be able to skip data from those streams in the 'mdat' section */ MOVStreamContext *streams[MAX_STREAMS]; int64_t next_chunk_offset; MOVStreamContext *partial; /* != 0 : there is still to read in the current chunk */ int ctab_size; MOV_ctab_t **ctab; /* color tables */ const struct MOVParseTableEntry *parse_table; /* could be eventually used to change the table */ /* NOTE: for recursion save to/ restore from local variable! */ AVPaletteControl palette_control;} MOVContext;/* XXX: it's the first time I make a recursive parser I think... sorry if it's ugly :P *//* those functions parse an atom *//* return code: 1: found what I wanted, exit 0: continue to parse next atom -1: error occured, exit */typedef int (*mov_parse_function)(MOVContext *ctx, ByteIOContext *pb, MOV_atom_t atom);/* links atom IDs to parse functions */typedef struct MOVParseTableEntry { uint32_t type; mov_parse_function func;} MOVParseTableEntry;#ifdef DEBUG/* * XXX: static sux, even more in a multithreaded environment... * Avoid them. This is here just to help debugging. */static int debug_indent = 0;void print_atom(const char *str, MOV_atom_t atom){ unsigned int tag, i; tag = (unsigned int) atom.type; i=debug_indent; if(tag == 0) tag = MKTAG('N', 'U', 'L', 'L'); while(i--) printf("|"); printf("parse:"); printf(" %s: tag=%c%c%c%c offset=0x%x size=0x%x\n", str, tag & 0xff, (tag >> 8) & 0xff, (tag >> 16) & 0xff, (tag >> 24) & 0xff, (unsigned int)atom.offset, (unsigned int)atom.size); assert((unsigned int)atom.size < 0x7fffffff);// catching errors}#else#define print_atom(a,b)#endifstatic int mov_read_leaf(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom){ print_atom("leaf", atom); if (atom.size>1) url_fskip(pb, atom.size);/* url_seek(pb, atom_offset+atom.size, SEEK_SET); */ return 0;}static int mov_read_default(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom){ int64_t total_size = 0; MOV_atom_t a; int i; int err = 0;#ifdef DEBUG print_atom("default", atom); debug_indent++;#endif a.offset = atom.offset; if (atom.size < 0) atom.size = 0x7fffffffffffffffLL; while(((total_size + 8) < atom.size) && !url_feof(pb) && !err) { a.size = atom.size; a.type=0L; if(atom.size >= 8) { a.size = get_be32(pb); a.type = get_le32(pb); } total_size += 8; a.offset += 8; //printf("type: %08x %.4s sz: %Lx %Lx %Lx\n", type, (char*)&type, size, atom.size, total_size); if (a.size == 1) { /* 64 bit extended size */ a.size = get_be64(pb) - 8; a.offset += 8; total_size += 8; } if (a.size == 0) { a.size = atom.size - total_size; if (a.size <= 8) break; } for (i = 0; c->parse_table[i].type != 0L && c->parse_table[i].type != a.type; i++) /* empty */; a.size -= 8;// printf(" i=%ld\n", i); if (c->parse_table[i].type == 0) { /* skip leaf atoms data */// url_seek(pb, atom.offset+atom.size, SEEK_SET);#ifdef DEBUG print_atom("unknown", a);#endif url_fskip(pb, a.size); } else {#ifdef DEBUG //char b[5] = { type & 0xff, (type >> 8) & 0xff, (type >> 16) & 0xff, (type >> 24) & 0xff, 0 }; //print_atom(b, type, offset, size);#endif err = (c->parse_table[i].func)(c, pb, a); } a.offset += a.size; total_size += a.size; } if (!err && total_size < atom.size && atom.size < 0x7ffff) { //printf("RESET %Ld %Ld err:%d\n", atom.size, total_size, err); url_fskip(pb, atom.size - total_size); }#ifdef DEBUG debug_indent--;#endif return err;}static int mov_read_ctab(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom){ unsigned int len; MOV_ctab_t *t; //url_fskip(pb, atom.size); // for now c->ctab = av_realloc(c->ctab, ++c->ctab_size); t = c->ctab[c->ctab_size]; t->seed = get_be32(pb); t->flags = get_be16(pb); t->size = get_be16(pb) + 1; len = 2 * t->size * 4; if (len > 0) { t->clrs = av_malloc(len); // 16bit A R G B if (t->clrs) get_buffer(pb, t->clrs, len); } return 0;}static int mov_read_hdlr(MOVContext *c, ByteIOContext *pb, MOV_atom_t atom){ AVStream *st = c->fc->streams[c->fc->nb_streams-1]; int len = 0; uint32_t type; uint32_t ctype; print_atom("hdlr", atom); get_byte(pb); /* version */ get_byte(pb); get_byte(pb); get_byte(pb); /* flags */ /* component type */ ctype = get_le32(pb); type = get_le32(pb); /* component subtype */#ifdef DEBUG printf("ctype= %c%c%c%c (0x%08lx)\n", *((char *)&ctype), ((char *)&ctype)[1], ((char *)&ctype)[2], ((char *)&ctype)[3], (long) ctype); printf("stype= %c%c%c%c\n", *((char *)&type), ((char *)&type)[1], ((char *)&type)[2], ((char *)&type)[3]);#endif#ifdef DEBUG/* XXX: yeah this is ugly... */ if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */ if(type == MKTAG('v', 'i', 'd', 'e')) puts("hdlr: vide"); else if(type == MKTAG('s', 'o', 'u', 'n')) puts("hdlr: soun"); } else if(ctype == 0) { /* MP4 */ if(type == MKTAG('v', 'i', 'd', 'e')) puts("hdlr: vide"); else if(type == MKTAG('s', 'o', 'u', 'n')) puts("hdlr: soun"); else if(type == MKTAG('o', 'd', 's', 'm')) puts("hdlr: odsm"); else if(type == MKTAG('s', 'd', 's', 'm')) puts("hdlr: sdsm"); } else puts("hdlr: meta");#endif if(ctype == MKTAG('m', 'h', 'l', 'r')) { /* MOV */ /* helps parsing the string hereafter... */ c->mp4 = 0; if(type == MKTAG('v', 'i', 'd', 'e')) st->codec.codec_type = CODEC_TYPE_VIDEO; else if(type == MKTAG('s', 'o', 'u', 'n')) st->codec.codec_type = CODEC_TYPE_AUDIO; } else if(ctype == 0) { /* MP4 */ /* helps parsing the string hereafter... */ c->mp4 = 1; if(type == MKTAG('v', 'i', 'd', 'e')) st->codec.codec_type = CODEC_TYPE_VIDEO; else if(type == MKTAG('s', 'o', 'u', 'n'))
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -