FFMS_Index::FFMS_Index(const char *IndexFile) : RefCount(1) { ZipFile zf(IndexFile, "rb"); // Read the index file header if (zf.Read<uint32_t>() != INDEXID) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("'") + IndexFile + "' is not a valid index file"); if (zf.Read<uint32_t>() != FFMS_VERSION) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("'") + IndexFile + "' is not the expected index version"); uint32_t Tracks = zf.Read<uint32_t>(); Decoder = zf.Read<uint32_t>(); ErrorHandling = zf.Read<uint32_t>(); if (!(Decoder & FFMS_GetEnabledSources())) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, "The source which this index was created with is not available"); if (zf.Read<uint32_t>() != avutil_version() || zf.Read<uint32_t>() != avformat_version() || zf.Read<uint32_t>() != avcodec_version() || zf.Read<uint32_t>() != swscale_version()) throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("A different FFmpeg build was used to create '") + IndexFile + "'"); Filesize = zf.Read<int64_t>(); zf.Read(Digest, sizeof(Digest)); reserve(Tracks); try { for (size_t i = 0; i < Tracks; ++i) push_back(FFMS_Track(zf)); } catch (FFMS_Exception const&) { throw; } catch (...) { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, std::string("Unknown error while reading index information in '") + IndexFile + "'"); } }
FFMS_Index *FFLAVFIndexer::DoIndexing() { std::vector<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false)); std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false)); std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest)); TrackIndices->Decoder = FFMS_SOURCE_LAVF; for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { TrackIndices->push_back(FFMS_Track((int64_t)FormatContext->streams[i]->time_base.num * 1000, FormatContext->streams[i]->time_base.den, static_cast<FFMS_TrackType>(FormatContext->streams[i]->codec->codec_type))); if (FormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id); if (!VideoCodec) throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, "Video codec not found"); if (avcodec_open2(FormatContext->streams[i]->codec, VideoCodec, NULL) < 0) throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Could not open video codec"); VideoContexts[i].CodecContext = FormatContext->streams[i]->codec; VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id); if (VideoContexts[i].Parser) VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; IndexMask |= 1 << i; } else if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec; AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); if (AudioCodec == NULL) throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, "Audio codec not found"); if (avcodec_open2(AudioCodecContext, AudioCodec, NULL) < 0) throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Could not open audio codec"); AudioContexts[i].CodecContext = AudioCodecContext; } else { IndexMask &= ~(1 << i); } } AVPacket Packet; InitNullPacket(Packet); std::vector<int64_t> LastValidTS(FormatContext->nb_streams, ffms_av_nopts_value); std::vector<int> LastDuration(FormatContext->nb_streams, 0); #if (LIBAVFORMAT_VERSION_INT) < (AV_VERSION_INT(52,106,0)) int64_t filesize = FormatContext->file_size; #else int64_t filesize = avio_size(FormatContext->pb); #endif while (av_read_frame(FormatContext, &Packet) >= 0) { // Update progress // FormatContext->pb can apparently be NULL when opening images. if (IC && FormatContext->pb) { if ((*IC)(FormatContext->pb->pos, filesize, ICPrivate)) throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, "Cancelled by user"); } if (!(IndexMask & (1 << Packet.stream_index))) { av_free_packet(&Packet); continue; } int Track = Packet.stream_index; bool KeyFrame = !!(Packet.flags & AV_PKT_FLAG_KEY); ReadTS(Packet, LastValidTS[Track], (*TrackIndices)[Track].UseDTS); if (FormatContext->streams[Track]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { int64_t PTS = LastValidTS[Track]; if (PTS == ffms_av_nopts_value) { if (Packet.duration == 0) throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, "Invalid initial pts, dts, and duration"); if ((*TrackIndices)[Track].empty()) PTS = 0; else PTS = (*TrackIndices)[Track].back().PTS + LastDuration[Track]; (*TrackIndices)[Track].HasTS = false; LastDuration[Track] = Packet.duration; } int RepeatPict = -1; int FrameType = 0; ParseVideoPacket(VideoContexts[Track], Packet, &RepeatPict, &FrameType); (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(PTS, RepeatPict, KeyFrame, FrameType, Packet.pos)); } else if (FormatContext->streams[Track]->codec->codec_type == AVMEDIA_TYPE_AUDIO) { int64_t StartSample = AudioContexts[Track].CurrentSample; int64_t SampleCount = IndexAudioPacket(Track, &Packet, AudioContexts[Track], *TrackIndices); if (SampleCount != 0) (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(LastValidTS[Track], StartSample, SampleCount, KeyFrame, Packet.pos)); } av_free_packet(&Packet); } TrackIndices->Sort(); return TrackIndices.release(); }