adaptive: always call get_empty
[vlc.git] / modules / demux / adaptive / plumbing / FakeESOut.cpp
blob13348de38bab52f6e5f1edf15ef1cb0d1a4f6e91
1 /*
2 * FakeESOut.cpp
3 *****************************************************************************
4 * Copyright © 2014-2015 VideoLAN and VLC Authors
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation; either version 2.1 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19 *****************************************************************************/
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
25 #include "FakeESOut.hpp"
26 #include "FakeESOutID.hpp"
27 #include "CommandsQueue.hpp"
28 #include <vlc_es_out.h>
29 #include <vlc_block.h>
30 #include <cassert>
32 using namespace adaptive;
34 FakeESOut::FakeESOut( es_out_t *es, CommandsQueue *queue )
35 : real_es_out( es )
36 , extrainfo( NULL )
37 , commandsqueue( queue )
38 , fakeesout( new es_out_t )
39 , timestamps_offset( 0 )
40 , timestamps_expected( 0 )
41 , timestamps_check_done( false )
43 fakeesout->pf_add = esOutAdd_Callback;
44 fakeesout->pf_control = esOutControl_Callback;
45 fakeesout->pf_del = esOutDel_Callback;
46 fakeesout->pf_destroy = esOutDestroy_Callback;
47 fakeesout->pf_send = esOutSend_Callback;
48 fakeesout->p_sys = (es_out_sys_t*) this;
50 vlc_mutex_init(&lock);
53 es_out_t * FakeESOut::getEsOut()
55 return fakeesout;
58 FakeESOut::~FakeESOut()
60 recycleAll();
61 gc();
63 delete fakeesout;
64 vlc_mutex_destroy(&lock);
67 void FakeESOut::setExpectedTimestampOffset(mtime_t offset)
69 vlc_mutex_lock(&lock);
70 timestamps_offset = 0;
71 timestamps_expected = offset;
72 timestamps_check_done = false;
73 vlc_mutex_unlock(&lock);
76 void FakeESOut::setTimestampOffset(mtime_t offset)
78 vlc_mutex_lock(&lock);
79 timestamps_offset = offset;
80 timestamps_check_done = true;
81 vlc_mutex_unlock(&lock);
84 void FakeESOut::setExtraInfoProvider( ExtraFMTInfoInterface *extra )
86 vlc_mutex_lock(&lock);
87 extrainfo = extra;
88 vlc_mutex_unlock(&lock);
91 FakeESOutID * FakeESOut::createNewID( const es_format_t *p_fmt )
93 es_format_t fmtcopy;
94 es_format_Init( &fmtcopy, p_fmt->i_cat, p_fmt->i_codec );
95 es_format_Copy( &fmtcopy, p_fmt );
96 fmtcopy.i_group = 0; /* Always ignore group for adaptive */
97 fmtcopy.i_id = -1;
99 vlc_mutex_lock(&lock);
101 if( extrainfo )
102 extrainfo->fillExtraFMTInfo( &fmtcopy );
104 FakeESOutID *es_id = new (std::nothrow) FakeESOutID( this, &fmtcopy );
105 if(likely(es_id))
106 fakeesidlist.push_back( es_id );
108 vlc_mutex_unlock(&lock);
110 es_format_Clean( &fmtcopy );
112 return es_id;
115 void FakeESOut::createOrRecycleRealEsID( FakeESOutID *es_id )
117 std::list<FakeESOutID *>::iterator it;
118 es_out_id_t *realid = NULL;
120 vlc_mutex_lock(&lock);
122 bool b_select = false;
123 for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
125 FakeESOutID *cand = *it;
126 if ( cand->isCompatible( es_id ) )
128 realid = cand->realESID();
129 cand->setRealESID( NULL );
130 delete *it;
131 recycle_candidates.erase( it );
132 break;
134 else if( cand->getFmt()->i_cat == es_id->getFmt()->i_cat && cand->realESID() )
136 /* We need to enforce same selection when not reused
137 Otherwise the es will select any other compatible track
138 and will end this in a activate/select loop when reactivating a track */
139 es_out_Control( real_es_out, ES_OUT_GET_ES_STATE, cand->realESID(), &b_select );
140 break;
144 if( !realid )
146 realid = es_out_Add( real_es_out, es_id->getFmt() );
147 if( b_select )
148 es_out_Control( real_es_out, ES_OUT_SET_ES_STATE, realid, b_select );
151 es_id->setRealESID( realid );
153 vlc_mutex_unlock(&lock);
156 mtime_t FakeESOut::getTimestampOffset() const
158 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
159 mtime_t time = timestamps_offset;
160 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
161 return time;
164 size_t FakeESOut::esCount() const
166 size_t i_count = 0;
167 std::list<FakeESOutID *>::const_iterator it;
168 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
169 for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
170 if( (*it)->realESID() )
171 i_count++;
172 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
173 return i_count;
176 void FakeESOut::schedulePCRReset()
178 AbstractCommand *command = commandsqueue->factory()->creatEsOutControlResetPCRCommand();
179 if( likely(command) )
180 commandsqueue->Schedule( command );
183 void FakeESOut::scheduleAllForDeletion()
185 std::list<FakeESOutID *>::const_iterator it;
186 vlc_mutex_lock(&lock);
187 for( it=fakeesidlist.begin(); it!=fakeesidlist.end(); ++it )
189 FakeESOutID *es_id = *it;
190 if(!es_id->scheduledForDeletion())
192 AbstractCommand *command = commandsqueue->factory()->createEsOutDelCommand( es_id );
193 if( likely(command) )
195 commandsqueue->Schedule( command );
196 es_id->setScheduledForDeletion();
200 vlc_mutex_unlock(&lock);
203 void FakeESOut::recycleAll()
205 /* Only used when demux is killed and commands queue is cancelled */
206 commandsqueue->Abort( true );
207 assert(commandsqueue->isEmpty());
208 vlc_mutex_lock(&lock);
209 recycle_candidates.splice( recycle_candidates.end(), fakeesidlist );
210 vlc_mutex_unlock(&lock);
213 void FakeESOut::gc()
215 vlc_mutex_lock(&lock);
216 if( recycle_candidates.empty() )
218 vlc_mutex_unlock(&lock);
219 return;
222 std::list<FakeESOutID *>::iterator it;
223 for( it=recycle_candidates.begin(); it!=recycle_candidates.end(); ++it )
225 if( (*it)->realESID() )
227 es_out_Control( real_es_out, ES_OUT_SET_ES_STATE, (*it)->realESID(), false );
228 es_out_Del( real_es_out, (*it)->realESID() );
230 delete *it;
232 recycle_candidates.clear();
233 vlc_mutex_unlock(&lock);
236 bool FakeESOut::hasSelectedEs() const
238 bool b_selected = false;
239 std::list<FakeESOutID *>::const_iterator it;
240 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
241 for( it=fakeesidlist.begin(); it!=fakeesidlist.end() && !b_selected; ++it )
243 FakeESOutID *esID = *it;
244 if( esID->realESID() )
245 es_out_Control( real_es_out, ES_OUT_GET_ES_STATE, esID->realESID(), &b_selected );
247 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
248 return b_selected;
251 bool FakeESOut::decodersDrained()
253 bool b_empty = true;
254 es_out_Control( real_es_out, ES_OUT_GET_EMPTY, &b_empty );
255 return b_empty;
258 bool FakeESOut::restarting() const
260 vlc_mutex_lock(const_cast<vlc_mutex_t *>(&lock));
261 bool b = !recycle_candidates.empty();
262 vlc_mutex_unlock(const_cast<vlc_mutex_t *>(&lock));
263 return b;
266 void FakeESOut::recycle( FakeESOutID *id )
268 vlc_mutex_lock(&lock);
269 fakeesidlist.remove( id );
270 recycle_candidates.push_back( id );
271 vlc_mutex_unlock(&lock);
274 /* Static callbacks */
275 /* Always pass Fake ES ID to slave demuxes, it is just an opaque struct to them */
276 es_out_id_t * FakeESOut::esOutAdd_Callback(es_out_t *fakees, const es_format_t *p_fmt)
278 FakeESOut *me = (FakeESOut *) fakees->p_sys;
280 if( p_fmt->i_cat != VIDEO_ES && p_fmt->i_cat != AUDIO_ES && p_fmt->i_cat != SPU_ES )
281 return NULL;
283 /* Feed the slave demux/stream_Demux with FakeESOutID struct,
284 * we'll create real ES later on main demux on execution */
285 FakeESOutID *es_id = me->createNewID( p_fmt );
286 if( likely(es_id) )
288 assert(!es_id->scheduledForDeletion());
289 AbstractCommand *command = me->commandsqueue->factory()->createEsOutAddCommand( es_id );
290 if( likely(command) )
292 me->commandsqueue->Schedule( command );
293 return reinterpret_cast<es_out_id_t *>(es_id);
295 else
297 delete es_id;
300 return NULL;
303 void FakeESOut::checkTimestampsStart(mtime_t i_start)
305 if( i_start == VLC_TS_INVALID )
306 return;
308 vlc_mutex_lock(&lock);
309 if( !timestamps_check_done )
311 if( i_start < CLOCK_FREQ ) /* Starts 0 */
312 timestamps_offset = timestamps_expected;
313 timestamps_check_done = true;
315 vlc_mutex_unlock(&lock);
318 int FakeESOut::esOutSend_Callback(es_out_t *fakees, es_out_id_t *p_es, block_t *p_block)
320 FakeESOut *me = (FakeESOut *) fakees->p_sys;
321 FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
322 assert(!es_id->scheduledForDeletion());
324 me->checkTimestampsStart( p_block->i_dts );
326 mtime_t offset = me->getTimestampOffset();
327 if( p_block->i_dts > VLC_TS_INVALID )
329 p_block->i_dts += offset;
330 if( p_block->i_pts > VLC_TS_INVALID )
331 p_block->i_pts += offset;
333 AbstractCommand *command = me->commandsqueue->factory()->createEsOutSendCommand( es_id, p_block );
334 if( likely(command) )
336 me->commandsqueue->Schedule( command );
337 return VLC_SUCCESS;
339 return VLC_EGENERIC;
342 void FakeESOut::esOutDel_Callback(es_out_t *fakees, es_out_id_t *p_es)
344 FakeESOut *me = (FakeESOut *) fakees->p_sys;
345 FakeESOutID *es_id = reinterpret_cast<FakeESOutID *>( p_es );
346 AbstractCommand *command = me->commandsqueue->factory()->createEsOutDelCommand( es_id );
347 if( likely(command) )
349 es_id->setScheduledForDeletion();
350 me->commandsqueue->Schedule( command );
354 int FakeESOut::esOutControl_Callback(es_out_t *fakees, int i_query, va_list args)
356 FakeESOut *me = (FakeESOut *) fakees->p_sys;
358 switch( i_query )
360 case ES_OUT_SET_PCR:
361 case ES_OUT_SET_GROUP_PCR:
363 int i_group;
364 if( i_query == ES_OUT_SET_GROUP_PCR )
365 i_group = va_arg( args, int );
366 else
367 i_group = 0;
368 int64_t pcr = va_arg( args, int64_t );
369 me->checkTimestampsStart( pcr );
370 pcr += me->getTimestampOffset();
371 AbstractCommand *command = me->commandsqueue->factory()->createEsOutControlPCRCommand( i_group, pcr );
372 if( likely(command) )
374 me->commandsqueue->Schedule( command );
375 return VLC_SUCCESS;
378 break;
380 case ES_OUT_SET_GROUP_META:
382 static_cast<void>(va_arg( args, int )); /* ignore group */
383 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
384 AbstractCommand *command = me->commandsqueue->factory()->createEsOutMetaCommand( -1, p_meta );
385 if( likely(command) )
387 me->commandsqueue->Schedule( command );
388 return VLC_SUCCESS;
391 break;
393 /* For others, we don't have the delorean, so always lie */
394 case ES_OUT_GET_ES_STATE:
396 static_cast<void>(va_arg( args, es_out_id_t * ));
397 bool *pb = va_arg( args, bool * );
398 *pb = true;
399 return VLC_SUCCESS;
402 case ES_OUT_SET_ES:
403 case ES_OUT_SET_ES_DEFAULT:
404 case ES_OUT_SET_ES_STATE:
405 return VLC_SUCCESS;
408 return VLC_EGENERIC;
411 void FakeESOut::esOutDestroy_Callback(es_out_t *fakees)
413 FakeESOut *me = (FakeESOut *) fakees->p_sys;
414 AbstractCommand *command = me->commandsqueue->factory()->createEsOutDestroyCommand();
415 if( likely(command) )
416 me->commandsqueue->Schedule( command );
418 /* !Static callbacks */