📄 mov.c
字号:
if(sc) { av_free(sc->chunk_offsets); av_free(sc->sample_to_chunk); av_free(sc->sample_sizes); av_free(sc->keyframes); av_free(sc->header_data); av_free(sc->stts_data); av_free(sc); }}static inline uint32_t mov_to_tag(uint8_t *buf){ return MKTAG(buf[0], buf[1], buf[2], buf[3]);}static inline uint32_t to_be32(uint8_t *buf){ return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];}/* XXX: is it sufficient ? */static int mov_probe(AVProbeData *p){ unsigned int offset; uint32_t tag; /* check file header */ if (p->buf_size <= 12) return 0; offset = 0; for(;;) { /* ignore invalid offset */ if ((offset + 8) > (unsigned int)p->buf_size) return 0; tag = mov_to_tag(p->buf + offset + 4); switch(tag) { case MKTAG( 'm', 'o', 'o', 'v' ): case MKTAG( 'w', 'i', 'd', 'e' ): case MKTAG( 'f', 'r', 'e', 'e' ): case MKTAG( 'm', 'd', 'a', 't' ): case MKTAG( 'p', 'n', 'o', 't' ): /* detect movs with preview pics like ew.mov and april.mov */ case MKTAG( 'u', 'd', 't', 'a' ): /* Packet Video PVAuthor adds this and a lot of more junk */ return AVPROBE_SCORE_MAX; case MKTAG( 'f', 't', 'y', 'p' ): case MKTAG( 's', 'k', 'i', 'p' ): offset = to_be32(p->buf+offset) + offset; break; default: /* unrecognized tag */ return 0; } } return 0;}static int mov_read_header(AVFormatContext *s, AVFormatParameters *ap){ MOVContext *mov = (MOVContext *) s->priv_data; ByteIOContext *pb = &s->pb; int i, j, nb, err; MOV_atom_t atom = { 0, 0, 0 }; mov->fc = s; mov->parse_table = mov_default_parse_table;#if 0 /* XXX: I think we should auto detect */ if(s->iformat->name[1] == 'p') mov->mp4 = 1;#endif if(!url_is_streamed(pb)) /* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */ atom.size = url_filesize(url_fileno(pb)); else atom.size = 0x7FFFFFFFFFFFFFFFLL;#ifdef DEBUG av_log(NULL, AV_LOG_DEBUG, "filesz=%Ld\n", atom.size);#endif /* check MOV header */ err = mov_read_default(mov, pb, atom); if (err<0 || (!mov->found_moov && !mov->found_mdat)) { av_log(s, AV_LOG_ERROR, "mov: header not found !!! (err:%d, moov:%d, mdat:%d) pos:%lld\n", err, mov->found_moov, mov->found_mdat, url_ftell(pb)); return -1; }#ifdef DEBUG av_log(NULL, AV_LOG_DEBUG, "on_parse_exit_offset=%d\n", (int) url_ftell(pb));#endif /* some cleanup : make sure we are on the mdat atom */ if(!url_is_streamed(pb) && (url_ftell(pb) != mov->mdat_offset)) url_fseek(pb, mov->mdat_offset, SEEK_SET); mov->next_chunk_offset = mov->mdat_offset; /* initialise reading */#ifdef DEBUG av_log(NULL, AV_LOG_DEBUG, "mdat_reset_offset=%d\n", (int) url_ftell(pb));#endif#ifdef DEBUG av_log(NULL, AV_LOG_DEBUG, "streams= %d\n", s->nb_streams);#endif mov->total_streams = nb = s->nb_streams;#if 1 for(i=0; i<s->nb_streams;) { if(s->streams[i]->codec.codec_type == CODEC_TYPE_MOV_OTHER) {/* not audio, not video, delete */ av_free(s->streams[i]); for(j=i+1; j<s->nb_streams; j++) s->streams[j-1] = s->streams[j]; s->nb_streams--; } else i++; } for(i=0; i<s->nb_streams;i++) { MOVStreamContext *sc; sc = (MOVStreamContext *)s->streams[i]->priv_data; sc->ffindex = i; sc->is_ff_stream = 1; }#endif#ifdef DEBUG av_log(NULL, AV_LOG_DEBUG, "real streams= %d\n", s->nb_streams);#endif return 0;}/* Yes, this is ugly... I didn't write the specs of QT :p *//* XXX:remove useless commented code sometime */static int mov_read_packet(AVFormatContext *s, AVPacket *pkt){ MOVContext *mov = (MOVContext *) s->priv_data; MOVStreamContext *sc; int64_t offset = 0x0FFFFFFFFFFFFFFFLL; int i, a, b, m; int size; size = 0x0FFFFFFF;#ifdef MOV_SPLIT_CHUNKS if (mov->partial) { int idx; sc = mov->partial; idx = sc->sample_to_chunk_index; if (idx < 0) return 0; size = sc->sample_sizes[sc->current_sample]; sc->current_sample++; sc->left_in_chunk--; if (sc->left_in_chunk <= 0) mov->partial = 0; offset = mov->next_chunk_offset; /* extract the sample */ goto readchunk; }#endifagain: sc = 0; for(i=0; i<mov->total_streams; i++) { MOVStreamContext *msc = mov->streams[i]; //av_log(NULL, AV_LOG_DEBUG, "MOCHUNK %ld %d %p pos:%Ld\n", mov->streams[i]->next_chunk, mov->total_streams, mov->streams[i], url_ftell(&s->pb)); if ((msc->next_chunk < msc->chunk_count) && msc->next_chunk >= 0 && (msc->chunk_offsets[msc->next_chunk] < offset)) { sc = msc; offset = msc->chunk_offsets[msc->next_chunk]; //av_log(NULL, AV_LOG_DEBUG, "SELETED %Ld i:%d\n", offset, i); } } if (!sc || offset==0x0FFFFFFFFFFFFFFFLL) return -1; sc->next_chunk++; if(mov->next_chunk_offset < offset) { /* some meta data */ url_fskip(&s->pb, (offset - mov->next_chunk_offset)); mov->next_chunk_offset = offset; }//av_log(NULL, AV_LOG_DEBUG, "chunk: [%i] %lli -> %lli\n", st_id, mov->next_chunk_offset, offset); if(!sc->is_ff_stream) { url_fskip(&s->pb, (offset - mov->next_chunk_offset)); mov->next_chunk_offset = offset; offset = 0x0FFFFFFFFFFFFFFFLL; goto again; } /* now get the chunk size... */ for(i=0; i<mov->total_streams; i++) { MOVStreamContext *msc = mov->streams[i]; if ((msc->next_chunk < msc->chunk_count) && ((msc->chunk_offsets[msc->next_chunk] - offset) < size)) size = msc->chunk_offsets[msc->next_chunk] - offset; }#ifdef MOV_MINOLTA_FIX //Make sure that size is according to sample_size (Needed by .mov files //created on a Minolta Dimage Xi where audio chunks contains waste data in the end) //Maybe we should really not only check sc->sample_size, but also sc->sample_sizes //but I have no such movies if (sc->sample_size > 0) { int foundsize=0; for(i=0; i<(sc->sample_to_chunk_sz); i++) { if( (sc->sample_to_chunk[i].first)<=(sc->next_chunk) && (sc->sample_size>0) ) { // I can't figure out why for PCM audio sample_size is always 1 // (it should actually be channels*bits_per_second/8) but it is. AVCodecContext* cod = &s->streams[sc->ffindex]->codec; if (sc->sample_size == 1 && (cod->codec_id == CODEC_ID_PCM_S16BE || cod->codec_id == CODEC_ID_PCM_S16LE)) foundsize=(sc->sample_to_chunk[i].count*cod->channels*cod->bits_per_sample)/8; else foundsize=sc->sample_to_chunk[i].count*sc->sample_size; }#ifdef DEBUG /*av_log(NULL, AV_LOG_DEBUG, "sample_to_chunk first=%ld count=%ld, id=%ld\n", sc->sample_to_chunk[i].first, sc->sample_to_chunk[i].count, sc->sample_to_chunk[i].id);*/#endif } if( (foundsize>0) && (foundsize<size) ) {#ifdef DEBUG /*av_log(NULL, AV_LOG_DEBUG, "this size should actually be %d\n",foundsize);*/#endif size=foundsize; } }#endif //MOV_MINOLTA_FIX#ifdef MOV_SPLIT_CHUNKS /* split chunks into samples */ if (sc->sample_size == 0) { int idx = sc->sample_to_chunk_index; if ((idx + 1 < sc->sample_to_chunk_sz) && (sc->next_chunk >= sc->sample_to_chunk[idx + 1].first)) idx++; sc->sample_to_chunk_index = idx; if (idx >= 0 && sc->sample_to_chunk[idx].count != 1) { mov->partial = sc; /* we'll have to get those samples before next chunk */ sc->left_in_chunk = sc->sample_to_chunk[idx].count - 1; size = sc->sample_sizes[sc->current_sample]; } sc->current_sample++; }#endifreadchunk://av_log(NULL, AV_LOG_DEBUG, "chunk: [%i] %lli -> %lli (%i)\n", st_id, offset, offset + size, size); if(size == 0x0FFFFFFF) size = mov->mdat_size + mov->mdat_offset - offset; if(size < 0) return -1; if(size == 0) return -1; url_fseek(&s->pb, offset, SEEK_SET); //av_log(NULL, AV_LOG_DEBUG, "READCHUNK hlen: %d %d off: %Ld pos:%Ld\n", size, sc->header_len, offset, url_ftell(&s->pb)); if (sc->header_len > 0) { av_new_packet(pkt, size + sc->header_len); memcpy(pkt->data, sc->header_data, sc->header_len); get_buffer(&s->pb, pkt->data + sc->header_len, size); /* free header */ av_freep(&sc->header_data); sc->header_len = 0; } else { av_new_packet(pkt, size); get_buffer(&s->pb, pkt->data, pkt->size); } pkt->stream_index = sc->ffindex; // If the keyframes table exists, mark any samples that are in the table as key frames. // If no table exists, treat very sample as a key frame. if (sc->keyframes) { a = 0; b = sc->keyframe_count - 1; while (a < b) { m = (a + b + 1) >> 1; if (sc->keyframes[m] > sc->current_sample) { b = m - 1; } else { a = m; } } if (sc->keyframes[a] == sc->current_sample) pkt->flags |= PKT_FLAG_KEY; } else pkt->flags |= PKT_FLAG_KEY;#ifdef DEBUG/* av_log(NULL, AV_LOG_DEBUG, "Packet (%d, %d, %ld) ", pkt->stream_index, st_id, pkt->size); for(i=0; i<8; i++) av_log(NULL, AV_LOG_DEBUG, "%02x ", pkt->data[i]); for(i=0; i<8; i++) av_log(NULL, AV_LOG_DEBUG, "%c ", (pkt->data[i]) & 0x7F); puts("");*/#endif mov->next_chunk_offset = offset + size; return 0;}#if defined(MOV_SPLIT_CHUNKS) && defined(MOV_SEEK)/** * Seek method based on the one described in the Appendix C of QTFileFormat.pdf */static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t timestamp){ MOVContext* mov = (MOVContext *) s->priv_data; MOVStreamContext* sc; int32_t i, a, b, m; int64_t sample_time; int64_t start_time; int32_t seek_sample, sample; int32_t duration; int32_t count; int32_t chunk; int32_t left_in_chunk; int64_t chunk_file_offset; int64_t sample_file_offset; int32_t first_chunk_sample; int32_t sample_to_chunk_idx; int mov_idx; // Find the corresponding mov stream for (mov_idx = 0; mov_idx < mov->total_streams; mov_idx++) if (mov->streams[mov_idx]->ffindex == stream_index) break; if (mov_idx == mov->total_streams) { av_log(s, AV_LOG_ERROR, "mov: requested stream was not found in mov streams (idx=%i)\n", stream_index); return -1; } sc = mov->streams[mov_idx]; // Step 1. Find the edit that contains the requested time (elst) if (sc->edit_count) { // FIXME should handle edit list av_log(s, AV_LOG_ERROR, "mov: does not handle seeking in files that contain edit list (c:%d)\n", sc->edit_count); return -1; } // Step 2. Find the corresponding sample using the Time-to-sample atom (stts) */#ifdef DEBUG av_log(s, AV_LOG_DEBUG, "Searching for time %li in stream #%i (time_scale=%i)\n", (long)timestamp, mov_idx, sc->time_scale);#endif // convert timestamp from time_base unit to timescale unit sample_time = av_rescale( timestamp, (int64_t)sc->time_scale * s->streams[stream_index]->time_base.num, (int64_t)s->streams[stream_index]->time_base.den); start_time = 0; // FIXME use elst atom sample = 1; // sample are 0 based in table#ifdef DEBUG av_log(s, AV_LOG_DEBUG, "Searching for sample_time %li \n", (long)sample_time);#endif for (i = 0; i < sc->stts_count; i++) { count = (uint32_t)(sc->stts_data[i]>>32); duration = (uint32_t)(sc->stts_data[i]&0xffff);//av_log(s, AV_LOG_DEBUG, "> sample_time %lli \n", (long)sample_time); //av_log(s, AV_LOG_DEBUG, "> count=%i duration=%i\n", count, duration); if ((start_time + count*duration) > sample_time) { sample += (sample_time - start_time) / duration; break; } sample += count; start_time += count * duration; } /* NOTE: despite what qt doc say, the dt value (Display Time in qt vocabulary) computed with the stts atom is a decoding time stamp (dts
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -