1 //  Copyright (c) 2007-2017 Fredrik Mellbin
2 //
3 //  Permission is hereby granted, free of charge, to any person obtaining a copy
4 //  of this software and associated documentation files (the "Software"), to deal
5 //  in the Software without restriction, including without limitation the rights
6 //  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 //  copies of the Software, and to permit persons to whom the Software is
8 //  furnished to do so, subject to the following conditions:
9 //
10 //  The above copyright notice and this permission notice shall be included in
11 //  all copies or substantial portions of the Software.
12 //
13 //  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 //  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 //  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 //  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 //  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 //  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 //  THE SOFTWARE.
20 
21 #include "indexing.h"
22 
23 #include "track.h"
24 #include "videoutils.h"
25 #include "zipfile.h"
26 
27 #include <algorithm>
28 #include <limits>
29 #include <numeric>
30 #include <sstream>
31 
32 extern "C" {
33 #include <libavutil/avutil.h>
34 #include <libavutil/sha.h>
35 }
36 
37 #define INDEXID 0x53920873
38 #define INDEX_VERSION 5
39 
~SharedAVContext()40 SharedAVContext::~SharedAVContext() {
41     avcodec_free_context(&CodecContext);
42     if (Parser)
43         av_parser_close(Parser);
44 }
45 
CalculateFileSignature(const char * Filename,int64_t * Filesize,uint8_t Digest[20])46 void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) {
47     FileHandle file(Filename, "rb", FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ);
48 
49     std::unique_ptr<AVSHA, decltype(&av_free)> ctx{ av_sha_alloc(), av_free };
50     av_sha_init(ctx.get(), 160);
51 
52     try {
53         *Filesize = file.Size();
54         std::vector<char> FileBuffer(static_cast<size_t>(std::min<int64_t>(1024 * 1024, *Filesize)));
55         size_t BytesRead = file.Read(FileBuffer.data(), FileBuffer.size());
56         av_sha_update(ctx.get(), reinterpret_cast<const uint8_t*>(FileBuffer.data()), BytesRead);
57 
58         if (*Filesize > static_cast<int64_t>(FileBuffer.size())) {
59             file.Seek(*Filesize - static_cast<int64_t>(FileBuffer.size()), SEEK_SET);
60             BytesRead = file.Read(FileBuffer.data(), FileBuffer.size());
61             av_sha_update(ctx.get(), reinterpret_cast<const uint8_t*>(FileBuffer.data()), BytesRead);
62         }
63     } catch (...) {
64         av_sha_final(ctx.get(), Digest);
65         throw;
66     }
67     av_sha_final(ctx.get(), Digest);
68 }
69 
Finalize(std::vector<SharedAVContext> const & video_contexts,const char * Format)70 void FFMS_Index::Finalize(std::vector<SharedAVContext> const& video_contexts, const char *Format) {
71     for (size_t i = 0, end = size(); i != end; ++i) {
72         FFMS_Track& track = (*this)[i];
73         // H.264 PAFF needs to have some frames hidden
74         //
75         // Don't send any WMV/ASF files, since they (as far as we know) cannot contain PAFF,
76         // but may also have valid, split packets, with pos equal to the previous pos.
77         if (video_contexts[i].CodecContext && video_contexts[i].CodecContext->codec_id == AV_CODEC_ID_H264 && !!strcmp(Format, "asf"))
78             track.MaybeHideFrames();
79         track.FinalizeTrack();
80 
81         if (track.TT != FFMS_TYPE_VIDEO) continue;
82 
83         if (video_contexts[i].CodecContext && video_contexts[i].CodecContext->has_b_frames) {
84             track.MaxBFrames = video_contexts[i].CodecContext->has_b_frames;
85             continue;
86         }
87 
88         // Whether or not has_b_frames gets set during indexing seems
89         // to vary based on version of FFmpeg/Libav, so do an extra
90         // check for b-frames if it's 0.
91         for (size_t f = 0; f < track.size(); ++f) {
92             if (track[f].FrameType == AV_PICTURE_TYPE_B) {
93                 track.MaxBFrames = 1;
94                 break;
95             }
96         }
97     }
98 }
99 
CompareFileSignature(const char * Filename)100 bool FFMS_Index::CompareFileSignature(const char *Filename) {
101     int64_t CFilesize;
102     uint8_t CDigest[20];
103     CalculateFileSignature(Filename, &CFilesize, CDigest);
104     return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest)));
105 }
106 
WriteIndex(ZipFile & zf)107 void FFMS_Index::WriteIndex(ZipFile &zf) {
108     // Write the index file header
109     zf.Write<uint32_t>(INDEXID);
110     zf.Write<uint32_t>(FFMS_VERSION);
111     zf.Write<uint16_t>(INDEX_VERSION);
112     zf.Write<uint32_t>(size());
113     zf.Write<uint32_t>(ErrorHandling);
114     zf.Write<uint32_t>(avutil_version());
115     zf.Write<uint32_t>(avformat_version());
116     zf.Write<uint32_t>(avcodec_version());
117     zf.Write<uint32_t>(swscale_version());
118     zf.Write<int64_t>(Filesize);
119     zf.Write(Digest);
120 
121     for (size_t i = 0; i < size(); ++i)
122         at(i).Write(zf);
123 
124     zf.Finish();
125 }
126 
WriteIndexFile(const char * IndexFile)127 void FFMS_Index::WriteIndexFile(const char *IndexFile) {
128     ZipFile zf(IndexFile, "wb");
129 
130     WriteIndex(zf);
131 }
132 
WriteIndexBuffer(size_t * Size)133 uint8_t *FFMS_Index::WriteIndexBuffer(size_t *Size) {
134     ZipFile zf;
135 
136     WriteIndex(zf);
137 
138     return zf.GetBuffer(Size);
139 }
140 
ReadIndex(ZipFile & zf,const char * IndexFile)141 void FFMS_Index::ReadIndex(ZipFile &zf, const char *IndexFile) {
142     // Read the index file header
143     if (zf.Read<uint32_t>() != INDEXID)
144         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
145             std::string("'") + IndexFile + "' is not a valid index file");
146 
147     if (zf.Read<uint32_t>() != FFMS_VERSION)
148         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
149             std::string("'") + IndexFile + "' was not created with the expected FFMS2 version");
150 
151     if (zf.Read<uint16_t>() != INDEX_VERSION)
152         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
153             std::string("'") + IndexFile + "' is not the expected index version");
154 
155     uint32_t Tracks = zf.Read<uint32_t>();
156     ErrorHandling = zf.Read<uint32_t>();
157 
158     if (zf.Read<uint32_t>() != avutil_version() ||
159         zf.Read<uint32_t>() != avformat_version() ||
160         zf.Read<uint32_t>() != avcodec_version() ||
161         zf.Read<uint32_t>() != swscale_version())
162         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
163             std::string("A different FFmpeg build was used to create '") + IndexFile + "'");
164 
165     Filesize = zf.Read<int64_t>();
166     zf.Read(Digest, sizeof(Digest));
167 
168     reserve(Tracks);
169     try {
170         for (size_t i = 0; i < Tracks; ++i)
171             emplace_back(zf);
172     } catch (FFMS_Exception const&) {
173         throw;
174     } catch (...) {
175         throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
176             std::string("Unknown error while reading index information in '") + IndexFile + "'");
177     }
178 }
179 
FFMS_Index(const char * IndexFile)180 FFMS_Index::FFMS_Index(const char *IndexFile) {
181     ZipFile zf(IndexFile, "rb");
182 
183     ReadIndex(zf, IndexFile);
184 }
185 
FFMS_Index(const uint8_t * Buffer,size_t Size)186 FFMS_Index::FFMS_Index(const uint8_t *Buffer, size_t Size) {
187     ZipFile zf(Buffer, Size);
188 
189     ReadIndex(zf, "User supplied buffer");
190 }
191 
FFMS_Index(int64_t Filesize,uint8_t Digest[20],int ErrorHandling)192 FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20], int ErrorHandling)
193     : ErrorHandling(ErrorHandling)
194     , Filesize(Filesize) {
195     memcpy(this->Digest, Digest, sizeof(this->Digest));
196 }
197 
SetIndexTrack(int Track,bool Index)198 void FFMS_Indexer::SetIndexTrack(int Track, bool Index) {
199     if (Track < 0 || Track >= GetNumberOfTracks())
200         return;
201     if (Index)
202         IndexMask.insert(Track);
203     else
204         IndexMask.erase(Track);
205 };
206 
SetIndexTrackType(int TrackType,bool Index)207 void FFMS_Indexer::SetIndexTrackType(int TrackType, bool Index) {
208     for (int i = 0; i < GetNumberOfTracks(); i++) {
209         if (GetTrackType(i) == TrackType) {
210             if (Index)
211                 IndexMask.insert(i);
212             else
213                 IndexMask.erase(i);
214         }
215     }
216 }
217 
SetErrorHandling(int ErrorHandling_)218 void FFMS_Indexer::SetErrorHandling(int ErrorHandling_) {
219     if (ErrorHandling_ != FFMS_IEH_ABORT && ErrorHandling_ != FFMS_IEH_CLEAR_TRACK &&
220         ErrorHandling_ != FFMS_IEH_STOP_TRACK && ErrorHandling_ != FFMS_IEH_IGNORE)
221         throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT,
222             "Invalid error handling mode specified");
223     ErrorHandling = ErrorHandling_;
224 }
225 
SetProgressCallback(TIndexCallback IC_,void * ICPrivate_)226 void FFMS_Indexer::SetProgressCallback(TIndexCallback IC_, void *ICPrivate_) {
227     IC = IC_;
228     ICPrivate = ICPrivate_;
229 }
230 
CreateIndexer(const char * Filename)231 FFMS_Indexer *CreateIndexer(const char *Filename) {
232     return new FFMS_Indexer(Filename);
233 }
234 
FFMS_Indexer(const char * Filename)235 FFMS_Indexer::FFMS_Indexer(const char *Filename)
236     : SourceFile(Filename) {
237     try {
238         if (avformat_open_input(&FormatContext, Filename, nullptr, nullptr) != 0)
239             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
240                 std::string("Can't open '") + Filename + "'");
241 
242         FFMS_Index::CalculateFileSignature(Filename, &Filesize, Digest);
243 
244         if (avformat_find_stream_info(FormatContext, nullptr) < 0) {
245             avformat_close_input(&FormatContext);
246             throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
247                 "Couldn't find stream information");
248         }
249 
250         for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
251             if (FormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
252                 IndexMask.insert(i);
253 
254         DecodeFrame = av_frame_alloc();
255         if (!DecodeFrame)
256             throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_ALLOCATION_FAILED,
257                 "Couldn't allocate frame");
258     } catch (...) {
259         Free();
260         throw;
261     }
262 }
263 
IndexAudioPacket(int Track,AVPacket * Packet,SharedAVContext & Context,FFMS_Index & TrackIndices)264 uint32_t FFMS_Indexer::IndexAudioPacket(int Track, AVPacket *Packet, SharedAVContext &Context, FFMS_Index &TrackIndices) {
265     AVCodecContext *CodecContext = Context.CodecContext;
266     int64_t StartSample = Context.CurrentSample;
267     int Ret = avcodec_send_packet(CodecContext, Packet);
268     if (Ret != 0) {
269         if (ErrorHandling == FFMS_IEH_ABORT) {
270             throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Audio decoding error");
271         } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
272             TrackIndices[Track].clear();
273             IndexMask.erase(Track);
274         } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
275             IndexMask.erase(Track);
276         }
277     }
278 
279     while (true) {
280         av_frame_unref(DecodeFrame);
281         Ret = avcodec_receive_frame(CodecContext, DecodeFrame);
282         if (Ret == 0) {
283             CheckAudioProperties(Track, CodecContext);
284             Context.CurrentSample += DecodeFrame->nb_samples;
285         } else if (Ret == AVERROR_EOF || Ret == AVERROR(EAGAIN)) {
286             break;
287         } else {
288             if (ErrorHandling == FFMS_IEH_ABORT) {
289                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, "Audio decoding error");
290             } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
291                 TrackIndices[Track].clear();
292                 IndexMask.erase(Track);
293             } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
294                 IndexMask.erase(Track);
295             }
296         }
297     }
298 
299     return static_cast<uint32_t>(Context.CurrentSample - StartSample);
300 }
301 
CheckAudioProperties(int Track,AVCodecContext * Context)302 void FFMS_Indexer::CheckAudioProperties(int Track, AVCodecContext *Context) {
303     auto it = LastAudioProperties.find(Track);
304     if (it == LastAudioProperties.end()) {
305         FFMS_AudioProperties &AP = LastAudioProperties[Track];
306         AP.SampleRate = Context->sample_rate;
307         AP.SampleFormat = Context->sample_fmt;
308         AP.Channels = Context->channels;
309     } else if (it->second.SampleRate != Context->sample_rate ||
310         it->second.SampleFormat != Context->sample_fmt ||
311         it->second.Channels != Context->channels) {
312         std::ostringstream buf;
313         buf <<
314             "Audio format change detected. This is currently unsupported."
315             << " Channels: " << it->second.Channels << " -> " << Context->channels << ";"
316             << " Sample rate: " << it->second.SampleRate << " -> " << Context->sample_rate << ";"
317             << " Sample format: " << av_get_sample_fmt_name((AVSampleFormat)it->second.SampleFormat) << " -> "
318             << av_get_sample_fmt_name(Context->sample_fmt);
319         throw FFMS_Exception(FFMS_ERROR_UNSUPPORTED, FFMS_ERROR_DECODING, buf.str());
320     }
321 }
322 
ParseVideoPacket(SharedAVContext & VideoContext,AVPacket & pkt,int * RepeatPict,int * FrameType,bool * Invisible,enum AVPictureStructure * LastPicStruct)323 void FFMS_Indexer::ParseVideoPacket(SharedAVContext &VideoContext, AVPacket &pkt, int *RepeatPict,
324                                     int *FrameType, bool *Invisible, enum AVPictureStructure *LastPicStruct) {
325     if (VideoContext.Parser) {
326         uint8_t *OB;
327         int OBSize;
328         bool IncompleteFrame = false;
329 
330         av_parser_parse2(VideoContext.Parser,
331             VideoContext.CodecContext,
332             &OB, &OBSize,
333             pkt.data, pkt.size,
334             pkt.pts, pkt.dts, pkt.pos);
335 
336         // H.264 (PAFF) and HEVC may have one field per packet, so we need to track
337         // when we have a full or half frame available, and mark one of them as
338         // hidden, so we do not get duplicate frames.
339         if (VideoContext.CodecContext->codec_id == AV_CODEC_ID_H264 ||
340             VideoContext.CodecContext->codec_id == AV_CODEC_ID_HEVC) {
341             if ((VideoContext.Parser->picture_structure == AV_PICTURE_STRUCTURE_TOP_FIELD &&
342                  *LastPicStruct == AV_PICTURE_STRUCTURE_BOTTOM_FIELD) ||
343                 (VideoContext.Parser->picture_structure == AV_PICTURE_STRUCTURE_BOTTOM_FIELD &&
344                  *LastPicStruct == AV_PICTURE_STRUCTURE_TOP_FIELD)) {
345                 IncompleteFrame = true;
346                 *LastPicStruct = AV_PICTURE_STRUCTURE_UNKNOWN;
347             } else {
348                 *LastPicStruct = VideoContext.Parser->picture_structure;
349             }
350         }
351 
352         *RepeatPict = VideoContext.Parser->repeat_pict;
353         *FrameType = VideoContext.Parser->pict_type;
354         *Invisible = (IncompleteFrame || VideoContext.Parser->repeat_pict < 0 || (pkt.flags & AV_PKT_FLAG_DISCARD));
355     } else {
356         *Invisible = !!(pkt.flags & AV_PKT_FLAG_DISCARD);
357     }
358 
359     if (VideoContext.CodecContext->codec_id == AV_CODEC_ID_VP8)
360         ParseVP8(pkt.data[0], Invisible, FrameType);
361     else if (VideoContext.CodecContext->codec_id == AV_CODEC_ID_VP9)
362         ParseVP9(pkt.data[0], Invisible, FrameType);
363 }
364 
Free()365 void FFMS_Indexer::Free() {
366     av_frame_free(&DecodeFrame);
367     avformat_close_input(&FormatContext);
368 }
369 
~FFMS_Indexer()370 FFMS_Indexer::~FFMS_Indexer() {
371     Free();
372 }
373 
GetNumberOfTracks()374 int FFMS_Indexer::GetNumberOfTracks() {
375     return FormatContext->nb_streams;
376 }
377 
GetFormatName()378 const char *FFMS_Indexer::GetFormatName() {
379     return FormatContext->iformat->name;
380 }
381 
GetTrackType(int Track)382 FFMS_TrackType FFMS_Indexer::GetTrackType(int Track) {
383     return static_cast<FFMS_TrackType>(FormatContext->streams[Track]->codecpar->codec_type);
384 }
385 
GetTrackCodec(int Track)386 const char *FFMS_Indexer::GetTrackCodec(int Track) {
387     AVCodec *codec = avcodec_find_decoder(FormatContext->streams[Track]->codecpar->codec_id);
388     return codec ? codec->name : nullptr;
389 }
390 
DoIndexing()391 FFMS_Index *FFMS_Indexer::DoIndexing() {
392     std::vector<SharedAVContext> AVContexts(FormatContext->nb_streams);
393 
394     auto TrackIndices = make_unique<FFMS_Index>(Filesize, Digest, ErrorHandling);
395     bool UseDTS = !strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts") || !strcmp(FormatContext->iformat->name, "mpegtsraw") || !strcmp(FormatContext->iformat->name, "nuv");
396 
397     for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
398         TrackIndices->emplace_back((int64_t)FormatContext->streams[i]->time_base.num * 1000,
399             FormatContext->streams[i]->time_base.den,
400             static_cast<FFMS_TrackType>(FormatContext->streams[i]->codecpar->codec_type),
401             !!(FormatContext->iformat->flags & AVFMT_TS_DISCONT),
402             UseDTS);
403 
404         if (IndexMask.count(i) && FormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
405             AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codecpar->codec_id);
406             if (!VideoCodec) {
407                 FormatContext->streams[i]->discard = AVDISCARD_ALL;
408                 IndexMask.erase(i);
409                 continue;
410             }
411 
412             AVContexts[i].CodecContext = avcodec_alloc_context3(VideoCodec);
413             if (AVContexts[i].CodecContext == nullptr)
414                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_ALLOCATION_FAILED,
415                     "Could not allocate video codec context");
416 
417             if (avcodec_parameters_to_context(AVContexts[i].CodecContext, FormatContext->streams[i]->codecpar) < 0)
418                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
419                     "Could not copy video codec parameters");
420 
421             if (avcodec_open2(AVContexts[i].CodecContext, VideoCodec, nullptr) < 0)
422                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
423                     "Could not open video codec");
424 
425             AVContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codecpar->codec_id);
426             if (AVContexts[i].Parser)
427                 AVContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
428 
429             if (FormatContext->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC) {
430                 FormatContext->streams[i]->discard = AVDISCARD_ALL;
431                 IndexMask.erase(i);
432             } else {
433                 IndexMask.insert(i);
434             }
435         } else if (IndexMask.count(i) && FormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
436             AVCodec *AudioCodec = avcodec_find_decoder(FormatContext->streams[i]->codecpar->codec_id);
437             if (AudioCodec == nullptr)
438                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
439                     "Audio codec not found");
440 
441             AVContexts[i].CodecContext = avcodec_alloc_context3(AudioCodec);
442             if (AVContexts[i].CodecContext == nullptr)
443                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_ALLOCATION_FAILED,
444                     "Could not allocate audio codec context");
445 
446             if (avcodec_parameters_to_context(AVContexts[i].CodecContext, FormatContext->streams[i]->codecpar) < 0)
447                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
448                     "Could not copy audio codec parameters");
449 
450             if (avcodec_open2(AVContexts[i].CodecContext, AudioCodec, nullptr) < 0)
451                 throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
452                     "Could not open audio codec");
453 
454             (*TrackIndices)[i].HasTS = false;
455         } else {
456             FormatContext->streams[i]->discard = AVDISCARD_ALL;
457             IndexMask.erase(i);
458         }
459     }
460 
461     AVPacket Packet;
462     InitNullPacket(Packet);
463     std::vector<int64_t> LastValidTS(FormatContext->nb_streams, AV_NOPTS_VALUE);
464 
465     int64_t filesize = avio_size(FormatContext->pb);
466     enum AVPictureStructure LastPicStruct = AV_PICTURE_STRUCTURE_UNKNOWN;
467     while (av_read_frame(FormatContext, &Packet) >= 0) {
468         // Update progress
469         // FormatContext->pb can apparently be NULL when opening images.
470         if (IC && FormatContext->pb) {
471             if ((*IC)(FormatContext->pb->pos, filesize, ICPrivate))
472                 throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
473                     "Cancelled by user");
474         }
475         if (!IndexMask.count(Packet.stream_index)) {
476             av_packet_unref(&Packet);
477             continue;
478         }
479 
480         int Track = Packet.stream_index;
481         FFMS_Track &TrackInfo = (*TrackIndices)[Track];
482         bool KeyFrame = !!(Packet.flags & AV_PKT_FLAG_KEY);
483         ReadTS(Packet, LastValidTS[Track], (*TrackIndices)[Track].UseDTS);
484 
485         if (FormatContext->streams[Track]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
486             int64_t PTS = TrackInfo.UseDTS ? Packet.dts : Packet.pts;
487             if (PTS == AV_NOPTS_VALUE) {
488                 // VPx alt-refs are output as packets which lack timestmps or durations, since
489                 // they are invisible. Currently, the timestamp mangling code in libavformat
490                 // will sometimes add a bogus timestamp and duration, if the webm in question
491                 // has the block duration set (which is a hack, and not really a frame duration
492                 // at all). In the future, libav* will only output packets without timestamps
493                 // or duration, so we need to handle it here, regardless. This does not handle
494                 // NVOPs. We set the duration based on the last PTS, for these packets, because
495                 // FFMS2 currently sorts packets by PTS, which will break decoding, otherwise.
496                 bool HasAltRefs = (FormatContext->streams[Track]->codecpar->codec_id == AV_CODEC_ID_VP8 ||
497                                    FormatContext->streams[Track]->codecpar->codec_id == AV_CODEC_ID_VP9);
498                 if (Packet.duration == 0 && !HasAltRefs)
499                     throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER,
500                         "Invalid packet pts, dts, and duration");
501 
502                 if (TrackInfo.empty())
503                     PTS = 0;
504                 else
505                     PTS = TrackInfo.back().PTS + TrackInfo.LastDuration;
506 
507                 TrackInfo.HasTS = false;
508             }
509 
510             int RepeatPict = -1;
511             int FrameType = 0;
512             bool Invisible = false;
513             ParseVideoPacket(AVContexts[Track], Packet, &RepeatPict, &FrameType, &Invisible, &LastPicStruct);
514 
515             TrackInfo.AddVideoFrame(PTS, RepeatPict, KeyFrame,
516                 FrameType, Packet.pos, Invisible);
517         } else if (FormatContext->streams[Track]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
518             // For video seeking timestamps are used only if all packets have
519             // timestamps, while for audio they're used if any have timestamps,
520             // as it's pretty common for only some packets to have timestamps
521             if (LastValidTS[Track] != AV_NOPTS_VALUE)
522                 TrackInfo.HasTS = true;
523 
524             int64_t StartSample = AVContexts[Track].CurrentSample;
525             uint32_t SampleCount = IndexAudioPacket(Track, &Packet, AVContexts[Track], *TrackIndices);
526             TrackInfo.SampleRate = AVContexts[Track].CodecContext->sample_rate;
527 
528             TrackInfo.AddAudioFrame(LastValidTS[Track],
529                 StartSample, SampleCount, KeyFrame, Packet.pos, Packet.flags & AV_PKT_FLAG_DISCARD);
530         }
531 
532         if (!(Packet.flags & AV_PKT_FLAG_DISCARD))
533             TrackInfo.LastDuration = Packet.duration;
534 
535         av_packet_unref(&Packet);
536     }
537 
538     TrackIndices->Finalize(AVContexts, FormatContext->iformat->name);
539     return TrackIndices.release();
540 }
541 
ReadTS(const AVPacket & Packet,int64_t & TS,bool & UseDTS)542 void FFMS_Indexer::ReadTS(const AVPacket &Packet, int64_t &TS, bool &UseDTS) {
543     if (!UseDTS && Packet.pts != AV_NOPTS_VALUE)
544         TS = Packet.pts;
545     if (TS == AV_NOPTS_VALUE)
546         UseDTS = true;
547     if (UseDTS && Packet.dts != AV_NOPTS_VALUE)
548         TS = Packet.dts;
549 }
550