// Copyright (c) 2007-2009 Fredrik Mellbin // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. #include "indexing.h" FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) { SourceFile = Filename; this->FormatContext = FormatContext; if (av_find_stream_info(FormatContext) < 0) { av_close_input_file(FormatContext); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Couldn't find stream information"); } } FFLAVFIndexer::~FFLAVFIndexer() { av_close_input_file(FormatContext); } FFMS_Index *FFLAVFIndexer::DoIndexing() { std::vector AudioContexts(FormatContext->nb_streams, SharedAudioContext(false)); std::vector VideoContexts(FormatContext->nb_streams, SharedVideoContext(false)); std::auto_ptr 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(FormatContext->streams[i]->codec->codec_type))); if (static_cast(FormatContext->streams[i]->codec->codec_type) == FFMS_TYPE_VIDEO && (VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) { AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id); if (VideoCodec == NULL) throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, "Video codec not found"); if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 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->flags = PARSER_FLAG_COMPLETE_FRAMES; } if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_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_open(AudioCodecContext, AudioCodec) < 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, TempPacket; InitNullPacket(Packet); InitNullPacket(TempPacket); std::vector LastValidTS; LastValidTS.resize(FormatContext->nb_streams, ffms_av_nopts_value); 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, FormatContext->file_size, ICPrivate)) throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, "Cancelled by user"); } // Only create index entries for video for now to save space if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_VIDEO) { uint8_t *OB; int OBSize; int RepeatPict = -1; // Duplicated code if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value) LastValidTS[Packet.stream_index] = Packet.pts; if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) (*TrackIndices)[Packet.stream_index].UseDTS = true; if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value) LastValidTS[Packet.stream_index] = Packet.dts; if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, "Invalid initial pts and dts"); // if (VideoContexts[Packet.stream_index].Parser) { av_parser_parse2(VideoContexts[Packet.stream_index].Parser, VideoContexts[Packet.stream_index].CodecContext, &OB, &OBSize, Packet.data, Packet.size, Packet.pts, Packet.dts, Packet.pos); RepeatPict = VideoContexts[Packet.stream_index].Parser->repeat_pict; } (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(LastValidTS[Packet.stream_index], RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); } else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) { int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample; AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec; TempPacket.data = Packet.data; TempPacket.size = Packet.size; TempPacket.flags = Packet.flags; // Duplicated code if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value) LastValidTS[Packet.stream_index] = Packet.pts; if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) (*TrackIndices)[Packet.stream_index].UseDTS = true; if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value) LastValidTS[Packet.stream_index] = Packet.dts; if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, "Invalid initial pts and dts"); // while (TempPacket.size > 0) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); if (Ret < 0) { if (ErrorHandling == FFMS_IEH_ABORT) { throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Audio decoding error"); } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) { (*TrackIndices)[Packet.stream_index].clear(); IndexMask &= ~(1 << Packet.stream_index); break; } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) { IndexMask &= ~(1 << Packet.stream_index); break; } else if (ErrorHandling == FFMS_IEH_IGNORE) { break; } } if (Ret > 0) { TempPacket.size -= Ret; TempPacket.data += Ret; } if (dbsize > 0) AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Packet.stream_index)) WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize); } (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(LastValidTS[Packet.stream_index], StartSample, static_cast(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); } av_free_packet(&Packet); } TrackIndices->Sort(); return TrackIndices.release(); } int FFLAVFIndexer::GetNumberOfTracks() { return FormatContext->nb_streams; } FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) { return static_cast(FormatContext->streams[Track]->codec->codec_type); } const char *FFLAVFIndexer::GetTrackCodec(int Track) { return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->name; }