1111
1212#include < TwkUtil/EnvVar.h>
1313
14+ #include < algorithm>
15+
1416extern " C"
1517{
18+ #include < libavformat/avformat.h>
1619#include < libavformat/avio.h>
1720#include < libavutil/dict.h>
1821#include < libavutil/error.h>
22+ #include < libavutil/mathematics.h>
1923}
2024
2125// 32 KB
@@ -72,6 +76,35 @@ void StreamerPool::enqueue(const std::string& url, const Options& options)
7276 m_wake.notify_all ();
7377}
7478
79+ void StreamerPool::enqueueWindow (const std::string& url, const Options& options, double startSeconds, double durationSeconds)
80+ {
81+ {
82+ std::lock_guard<std::mutex> lock (m_mutex);
83+
84+ if (m_stopped)
85+ {
86+ return ;
87+ }
88+
89+ m_queue.erase (std::remove_if (m_queue.begin (), m_queue.end (), [](const Job& job) { return job.window ; }), m_queue.end ());
90+
91+ Job job;
92+ job.url = url;
93+ job.options = options;
94+ job.window = true ;
95+ job.startSeconds = startSeconds;
96+ job.durationSeconds = durationSeconds;
97+ m_queue.push_front (job);
98+
99+ if (!m_scheduler.joinable ())
100+ {
101+ m_scheduler = std::thread (&StreamerPool::schedulerLoop, this );
102+ }
103+ }
104+
105+ m_wake.notify_all ();
106+ }
107+
75108void StreamerPool::schedulerLoop ()
76109{
77110 for (;;)
@@ -82,18 +115,32 @@ void StreamerPool::schedulerLoop()
82115 [this ]
83116 {
84117 reapFinished ();
85- return m_stopped || (!m_queue.empty () && m_activeWorkers.size () < m_maxThreads);
118+ return m_stopped
119+ || (!m_queue.empty ()
120+ && (m_queue.front ().window ? m_activeWindowWorkers == 0 : m_activeWorkers.size () < m_maxThreads));
86121 });
87122
88123 if (m_stopped)
89124 {
90125 return ;
91126 }
92127
93- while (!m_queue.empty () && m_activeWorkers. size () < m_maxThreads )
128+ while (!m_queue.empty ())
94129 {
130+ if (m_queue.front ().window )
131+ {
132+ if (m_activeWindowWorkers != 0 )
133+ break ;
134+ }
135+ else if (m_activeWorkers.size () >= m_maxThreads)
136+ {
137+ break ;
138+ }
139+
95140 Job job = m_queue.front ();
96141 m_queue.pop_front ();
142+ if (job.window )
143+ ++m_activeWindowWorkers;
97144 m_activeWorkers.emplace_back (&StreamerPool::workerFunc, this , job);
98145 }
99146 }
@@ -120,10 +167,15 @@ void StreamerPool::reapFinished()
120167
121168void StreamerPool::workerFunc (Job job)
122169{
123- download (job);
170+ if (job.window )
171+ downloadWindow (job);
172+ else
173+ download (job);
124174
125175 {
126176 std::lock_guard<std::mutex> lock (m_mutex);
177+ if (job.window )
178+ --m_activeWindowWorkers;
127179 m_finished.insert (std::this_thread::get_id ());
128180 }
129181
@@ -175,6 +227,58 @@ void StreamerPool::download(const Job& job)
175227 avio_closep (&context);
176228}
177229
230+ void StreamerPool::downloadWindow (const Job& job)
231+ {
232+ AVDictionary* options = nullptr ;
233+ for (const auto & [key, value] : job.options )
234+ {
235+ av_dict_set (&options, key.c_str (), value.c_str (), 0 );
236+ }
237+
238+ AVFormatContext* context = avformat_alloc_context ();
239+ if (context == nullptr )
240+ {
241+ av_dict_free (&options);
242+ return ;
243+ }
244+
245+ context->interrupt_callback .callback = &StreamerPool::interruptCallback;
246+ context->interrupt_callback .opaque = this ;
247+
248+ if (avformat_open_input (&context, job.url .c_str (), nullptr , &options) < 0 )
249+ {
250+ av_dict_free (&options);
251+ avformat_free_context (context);
252+ return ;
253+ }
254+ av_dict_free (&options);
255+
256+ if (avformat_find_stream_info (context, nullptr ) >= 0 )
257+ {
258+ const int64_t mediaStart = context->start_time == AV_NOPTS_VALUE ? 0 : context->start_time ;
259+ const int64_t start = mediaStart + static_cast <int64_t >(job.startSeconds * AV_TIME_BASE );
260+ const int64_t end = start + static_cast <int64_t >(job.durationSeconds * AV_TIME_BASE );
261+
262+ if (avformat_seek_file (context, -1 , INT64_MIN , start, INT64_MAX , AVSEEK_FLAG_BACKWARD ) >= 0 )
263+ {
264+ AVPacket* packet = av_packet_alloc ();
265+ while (packet != nullptr && !m_abort.load () && av_read_frame (context, packet) >= 0 )
266+ {
267+ const int64_t timestamp = packet->pts != AV_NOPTS_VALUE ? packet->pts : packet->dts ;
268+ const int64_t packetTime = timestamp == AV_NOPTS_VALUE
269+ ? start
270+ : av_rescale_q (timestamp, context->streams [packet->stream_index ]->time_base , AV_TIME_BASE_Q );
271+ av_packet_unref (packet);
272+ if (packetTime >= end)
273+ break ;
274+ }
275+ av_packet_free (&packet);
276+ }
277+ }
278+
279+ avformat_close_input (&context);
280+ }
281+
178282void StreamerPool::shutdown ()
179283{
180284 {
0 commit comments