Loading...
Searching...
No Matches
FrameAccess.hpp
Go to the documentation of this file.
1#pragma once
2#include <Media/Libav.hpp>
3#include <Video/VideoEnums.hpp>
4
5#if SCORE_HAS_LIBAV
6extern "C" {
7#include <libavcodec/avcodec.h>
8#include <libavformat/avformat.h>
9}
10
11#include <algorithm>
12
26namespace Video
27{
29{
30 FrameAccess access{FrameAccess::Unknown};
31 int max_gap{-1};
32};
33
34namespace keyframe_probe
35{
37inline constexpr int max_short_gop = 15;
38
39inline FrameAccess fromGap(int gap) noexcept
40{
41 return gap == 0 ? FrameAccess::EveryFrame
42 : gap <= max_short_gop ? FrameAccess::ShortGop
43 : FrameAccess::LongGop;
44}
45
46inline bool canSeek(const AVFormatContext& fmt) noexcept
47{
48 if(fmt.pb)
49 return fmt.pb->seekable & AVIO_SEEKABLE_NORMAL;
50 // Demuxers doing their own I/O (AVFMT_NOFILE): image sequences seek and
51 // know their length, live protocols (RTSP...) do not.
52 return fmt.duration != AV_NOPTS_VALUE && fmt.duration > 0;
53}
54
56inline FrameAccessProbe fromIndex(const AVStream& st) noexcept
57{
58#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 78, 100)
59 const int n = avformat_index_get_entries_count(&st);
60 if(st.nb_frames <= 0 || n < st.nb_frames)
61 return {};
62
63 bool sawKey = false;
64 int gap = 0, maxGap = 0;
65 for(int i = 0; i < n; i++)
66 {
67 const AVIndexEntry* e = avformat_index_get_entry(const_cast<AVStream*>(&st), i);
68 if(e && (e->flags & AVINDEX_KEYFRAME))
69 {
70 if(sawKey)
71 maxGap = std::max(maxGap, gap);
72 sawKey = true;
73 gap = 0;
74 }
75 else
76 {
77 gap++;
78 }
79 }
80 if(!sawKey)
81 return {FrameAccess::LongGop, n};
82 maxGap = std::max(maxGap, gap);
83 return {fromGap(maxGap), maxGap};
84#else
85 return {};
86#endif
87}
88
92inline FrameAccessProbe fromPackets(const char* url) noexcept
93{
94 AVFormatContext* fmt{};
95 if(!url || avformat_open_input(&fmt, url, nullptr, nullptr) != 0)
96 return {};
97 AVPacket* pkt = av_packet_alloc();
98 if(!pkt)
99 {
100 avformat_close_input(&fmt);
101 return {};
102 }
103
104 // Demuxers that create their streams while reading (MPEG-TS) have none yet:
105 // the first video stream a packet belongs to is then the one.
106 int video = -1;
107 for(unsigned i = 0; i < fmt->nb_streams && video < 0; i++)
108 if(fmt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
109 video = int(i);
110
111 // All-intra is settled once more consecutive keyframes than any short GOP
112 // went by; a short GOP needs its second keyframe; a long one is settled
113 // as soon as a run exceeds the short limit.
114 constexpr int max_packets = 2 * (max_short_gop + 1);
115 int seen = 0, keys = 0, gap = 0, maxGap = 0;
116 bool timed = false;
117 while(seen < max_packets && av_read_frame(fmt, pkt) >= 0)
118 {
119 if(video < 0 && pkt->stream_index < int(fmt->nb_streams)
120 && fmt->streams[pkt->stream_index]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
121 video = pkt->stream_index;
122
123 if(pkt->stream_index == video)
124 {
125 seen++;
126 timed |= pkt->pts != AV_NOPTS_VALUE || pkt->dts != AV_NOPTS_VALUE;
127 if(pkt->flags & AV_PKT_FLAG_KEY)
128 {
129 if(keys > 0)
130 maxGap = std::max(maxGap, gap);
131 keys++;
132 gap = 0;
133 }
134 else
135 {
136 gap++;
137 }
138 }
139 av_packet_unref(pkt);
140
141 if(gap > max_short_gop)
142 break;
143 if(keys > max_short_gop && maxGap == 0 && gap == 0)
144 break;
145 }
146 av_packet_free(&pkt);
147 avformat_close_input(&fmt);
148
149 if(seen == 0)
150 return {};
151 // The run still open when reading stopped is at least that long.
152 maxGap = std::max(maxGap, gap);
153 // Raw elementary streams carry no timestamps: no time can be sought.
154 if(!timed)
155 return {FrameAccess::Sequential, maxGap};
156 if(keys == 0)
157 return {FrameAccess::LongGop, maxGap};
158 return {fromGap(maxGap), maxGap};
159}
160}
161
166inline FrameAccessProbe
167classifyFrameAccess(AVFormatContext& fmt, AVStream& st, const char* url) noexcept
168{
169 using namespace keyframe_probe;
170 if(!canSeek(fmt))
171 return {FrameAccess::Sequential, -1};
172
173 // Raw elementary streams (H.264, HEVC, VVC, MJPEG, Dirac...) carry no
174 // timestamps, which their demuxers declare: no time can be sought.
175 if(fmt.iformat && (fmt.iformat->flags & AVFMT_NOTIMESTAMPS))
176 return {FrameAccess::Sequential, -1};
177
178 if(auto desc = avcodec_descriptor_get(st.codecpar->codec_id);
179 desc && (desc->props & AV_CODEC_PROP_INTRA_ONLY))
180 return {FrameAccess::EveryFrame, 0};
181
182 if(auto p = fromIndex(st); p.access != FrameAccess::Unknown)
183 return p;
184
185 return fromPackets(url);
186}
187}
188#endif
FrameAccessProbe classifyFrameAccess(AVFormatContext &fmt, AVStream &st, const char *url) noexcept
Definition FrameAccess.hpp:167
FrameAccessProbe fromPackets(const char *url) noexcept
Definition FrameAccess.hpp:92
FrameAccessProbe fromIndex(const AVStream &st) noexcept
Longest non-keyframe run from an index that lists every frame.
Definition FrameAccess.hpp:56
constexpr int max_short_gop
XDCAM HD and HDV use 12- to 15-frame GOPs: still worth replaying on a seek.
Definition FrameAccess.hpp:37
Definition FrameAccess.hpp:29