[vlc-commits] sout: sdi: rework buffering
Francois Cartegnie
git at videolan.org
Thu Jul 11 15:15:06 CEST 2019
vlc | branch: master | Francois Cartegnie <fcvlcdev at free.fr> | Tue Jun 4 18:58:55 2019 +0200| [f2bcfd9758eabd19c82a078af2ef0f6cc857fb35] | committer: Francois Cartegnie
sout: sdi: rework buffering
Full rework of the SDI buffering to handle stream
interleaving, truncation, decoder output pacing,
threaded frame conversion & filtering.
Now also does proper preroll.
Remaining frame drops are due to munmap()
> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=f2bcfd9758eabd19c82a078af2ef0f6cc857fb35
---
modules/stream_out/sdi/DBMSDIOutput.cpp | 286 ++++++++++++++++++++++-----
modules/stream_out/sdi/DBMSDIOutput.hpp | 26 ++-
modules/stream_out/sdi/SDIAudioMultiplex.cpp | 12 +-
modules/stream_out/sdi/SDIAudioMultiplex.hpp | 4 +
modules/stream_out/sdi/SDIOutput.cpp | 28 ++-
modules/stream_out/sdi/SDIOutput.hpp | 1 +
modules/stream_out/sdi/SDIStream.cpp | 253 ++++++++++++++++++++----
modules/stream_out/sdi/SDIStream.hpp | 41 +++-
8 files changed, 558 insertions(+), 93 deletions(-)
diff --git a/modules/stream_out/sdi/DBMSDIOutput.cpp b/modules/stream_out/sdi/DBMSDIOutput.cpp
index 431ea4eeb6..19de3288e1 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.cpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.cpp
@@ -47,6 +47,12 @@
#include <arpa/inet.h>
+#define DECKLINK_CARD_BUFFER (CLOCK_FREQ)
+#define DECKLINK_PREROLL (CLOCK_FREQ*3/4)
+#define DECKLINK_SCHED_OFFSET (CLOCK_FREQ/20)
+
+static_assert(DECKLINK_CARD_BUFFER > DECKLINK_PREROLL + DECKLINK_SCHED_OFFSET, "not in card buffer limits");
+
using namespace sdi_sout;
DBMSDIOutput::DBMSDIOutput(sout_stream_t *p_stream) :
@@ -58,10 +64,21 @@ DBMSDIOutput::DBMSDIOutput(sout_stream_t *p_stream) :
clock.offset = 0;
lasttimestamp = 0;
b_running = false;
+ streamStartTime = VLC_TICK_INVALID;
+ vlc_mutex_init(&feeder.lock);
+ vlc_cond_init(&feeder.cond);
}
DBMSDIOutput::~DBMSDIOutput()
{
+ if(p_output)
+ {
+ while(!isDrained())
+ vlc_tick_wait(vlc_tick_now() + CLOCK_FREQ/60);
+ vlc_cancel(feeder.thread);
+ vlc_join(feeder.thread, NULL);
+ }
+
es_format_Clean(&video.configuredfmt);
if(p_output)
{
@@ -73,6 +90,9 @@ DBMSDIOutput::~DBMSDIOutput()
}
if(p_card)
p_card->Release();
+
+ vlc_cond_destroy(&feeder.cond);
+ vlc_mutex_destroy(&feeder.lock);
}
AbstractStream *DBMSDIOutput::Add(const es_format_t *fmt)
@@ -92,16 +112,6 @@ AbstractStream *DBMSDIOutput::Add(const es_format_t *fmt)
return s;
}
-int DBMSDIOutput::Send(AbstractStream *s, block_t *b)
-{
- if(!b_running && b->i_dts != VLC_TICK_INVALID)
- {
- if( videoStream && (!audioStreams.empty() || audio.i_channels == 0) )
- Start(b->i_dts);
- }
- return SDIOutput::Send(s, b);
-}
-
#define CHECK(message) do { \
if (result != S_OK) \
{ \
@@ -114,6 +124,43 @@ int DBMSDIOutput::Send(AbstractStream *s, block_t *b)
} \
} while(0)
+HRESULT STDMETHODCALLTYPE DBMSDIOutput::QueryInterface( REFIID, LPVOID * )
+{
+ return E_NOINTERFACE;
+}
+
+ULONG STDMETHODCALLTYPE DBMSDIOutput::AddRef()
+{
+ return 1;
+}
+
+ULONG STDMETHODCALLTYPE DBMSDIOutput::Release()
+{
+ return 1;
+}
+
+HRESULT STDMETHODCALLTYPE DBMSDIOutput::ScheduledFrameCompleted
+ (IDeckLinkVideoFrame *, BMDOutputFrameCompletionResult result)
+{
+ if(result == bmdOutputFrameDropped)
+ msg_Warn(p_stream, "dropped frame");
+ else if(result == bmdOutputFrameDisplayedLate)
+ msg_Warn(p_stream, "late frame");
+
+ bool b_active;
+ vlc_mutex_lock(&feeder.lock);
+ if((S_OK == p_output->IsScheduledPlaybackRunning(&b_active)) && b_active)
+ vlc_cond_signal(&feeder.cond);
+ vlc_mutex_unlock(&feeder.lock);
+
+ return S_OK;
+}
+
+HRESULT DBMSDIOutput::ScheduledPlaybackHasStopped (void)
+{
+ return S_OK;
+}
+
int DBMSDIOutput::Open()
{
HRESULT result;
@@ -159,6 +206,9 @@ int DBMSDIOutput::Open()
decklink_iterator->Release();
+ if(vlc_clone(&feeder.thread, feederThreadCallback, this, VLC_THREAD_PRIORITY_INPUT))
+ goto error;
+
return VLC_SUCCESS;
error:
@@ -216,6 +266,13 @@ int DBMSDIOutput::ConfigureAudio(const audio_format_t *)
maxchannels,
bmdAudioOutputStreamTimestamped);
CHECK("Could not start audio output");
+
+ if(S_OK != p_output->BeginAudioPreroll())
+ {
+ p_output->DisableAudioOutput();
+ goto error;
+ }
+
audio.b_configured = true;
p_attributes->Release();
@@ -378,6 +435,8 @@ int DBMSDIOutput::ConfigureVideo(const video_format_t *vfmt)
result = p_output->EnableVideoOutput(mode_id, flags);
CHECK("Could not enable video output");
+ p_output->SetScheduledFrameCompletionCallback(this);
+
video_format_Copy(fmt, vfmt);
fmt->i_width = fmt->i_visible_width = p_display_mode->GetWidth();
fmt->i_height = fmt->i_visible_height = p_display_mode->GetHeight();
@@ -416,7 +475,7 @@ error:
return VLC_EGENERIC;
}
-int DBMSDIOutput::Start(vlc_tick_t startTime)
+int DBMSDIOutput::StartPlayback()
{
HRESULT result;
if(FAKE_DRIVER && !b_running)
@@ -427,7 +486,10 @@ int DBMSDIOutput::Start(vlc_tick_t startTime)
if(b_running)
return VLC_EGENERIC;
- result = p_output->StartScheduledPlayback(startTime, CLOCK_FREQ, 1.0);
+ if(audio.b_configured)
+ p_output->EndAudioPreroll();
+
+ result = p_output->StartScheduledPlayback(streamStartTime, CLOCK_FREQ, 1.0);
CHECK("Could not start playback");
b_running = true;
return VLC_SUCCESS;
@@ -436,45 +498,163 @@ error:
return VLC_EGENERIC;
}
-int DBMSDIOutput::Process()
+int DBMSDIOutput::FeedOneFrame()
{
- if((!p_output && !FAKE_DRIVER) || !b_running)
- return VLC_EGENERIC;
+ picture_t *p = reinterpret_cast<picture_t *>(videoBuffer.Dequeue());
+ if(p)
+ return ProcessVideo(p, reinterpret_cast<block_t *>(captionsBuffer.Dequeue()));
- picture_t *p;
- while((p = reinterpret_cast<picture_t *>(videoBuffer.Dequeue())))
- {
- vlc_tick_t bufferStart = audioMultiplex->bufferStart();
- unsigned i_samples_per_frame =
- audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
+ return VLC_SUCCESS;
+}
+int DBMSDIOutput::FeedAudio(vlc_tick_t start, vlc_tick_t preroll, bool b_truncate)
+{
#ifdef SDI_MULTIPLEX_DEBUG
audioMultiplex->Debug();
#endif
+ vlc_tick_t bufferStart = audioMultiplex->bufferStart();
+ unsigned i_samples_per_frame =
+ audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
- while(bufferStart <= p->date &&
- audioMultiplex->availableVirtualSamples(bufferStart) >= i_samples_per_frame)
+ unsigned buffered = 0;
+ while(bufferStart <= (start+preroll) &&
+ audioMultiplex->availableVirtualSamples(bufferStart) >= i_samples_per_frame)
+ {
+ block_t *out = audioMultiplex->Extract(i_samples_per_frame);
+ if(out)
{
- block_t *out = audioMultiplex->Extract(i_samples_per_frame);
- if(out)
- {
#ifdef SDI_MULTIPLEX_DEBUG
- msg_Dbg(p_stream, "extracted %u samples pts %ld i_samples_per_frame %u",
- out->i_nb_samples, out->i_dts, i_samples_per_frame);
+ msg_Dbg(p_stream, "extracted %u samples pts %ld i_samples_per_frame %u",
+ out->i_nb_samples, out->i_dts, i_samples_per_frame);
#endif
+ if(b_truncate && out->i_pts < start)
+ {
+#ifdef SDI_MULTIPLEX_DEBUG
+ msg_Err(p_stream,"dropping %u samples at %ld (-%ld)",
+ i_samples_per_frame, out->i_pts, (start - out->i_pts));
+#endif
+ block_Release(out);
+ }
+ else
+ {
ProcessAudio(out);
- }
- else break;
- bufferStart = audioMultiplex->bufferStart();
- i_samples_per_frame = audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
+ }
}
+ else break;
+ bufferStart = audioMultiplex->bufferStart();
+ i_samples_per_frame = audioMultiplex->alignedInterleaveInSamples(bufferStart, SAMPLES_PER_FRAME);
+ buffered += i_samples_per_frame;
+ }
- ProcessVideo(p, reinterpret_cast<block_t *>(captionsBuffer.Dequeue()));
+ return buffered > 0 ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+void * DBMSDIOutput::feederThreadCallback(void *me)
+{
+ reinterpret_cast<DBMSDIOutput *>(me)->feederThread();
+ return NULL;
+}
+
+void DBMSDIOutput::feederThread()
+{
+ vlc_tick_t maxdelay = CLOCK_FREQ/60;
+ for(;;)
+ {
+ vlc_mutex_lock(&feeder.lock);
+ mutex_cleanup_push(&feeder.lock);
+ vlc_cond_timedwait(&feeder.cond, &feeder.lock, vlc_tick_now() + maxdelay);
+ vlc_cleanup_pop();
+ int cancel = vlc_savecancel();
+ doSchedule();
+ if(timescale)
+ maxdelay = CLOCK_FREQ * frameduration / timescale;
+ vlc_restorecancel(cancel);
+ vlc_mutex_unlock(&feeder.lock);
}
+}
+
+int DBMSDIOutput::Process()
+{
+ if((!p_output && !FAKE_DRIVER))
+ return VLC_SUCCESS;
+
+ vlc_mutex_lock(&feeder.lock);
+ vlc_cond_signal(&feeder.cond);
+ vlc_mutex_unlock(&feeder.lock);
return VLC_SUCCESS;
}
+int DBMSDIOutput::doSchedule()
+{
+ const vlc_tick_t preroll = DECKLINK_PREROLL;
+ vlc_tick_t next = videoBuffer.NextPictureTime();
+ if(next == VLC_TICK_INVALID ||
+ (!b_running && !ReachedPlaybackTime(next + preroll + SAMPLES_PER_FRAME*CLOCK_FREQ/48000)))
+ return VLC_SUCCESS;
+
+ if(FAKE_DRIVER)
+ {
+ FeedOneFrame();
+ FeedAudio(next, preroll, false);
+ b_running = true;
+ return VLC_SUCCESS;
+ }
+
+ uint32_t bufferedFramesCount;
+ uint32_t bufferedAudioCount = 0;
+ if(S_OK != p_output->GetBufferedVideoFrameCount(&bufferedFramesCount))
+ return VLC_EGENERIC;
+
+ uint32_t bufferedFramesTarget = (uint64_t)timescale*preroll/frameduration/CLOCK_FREQ;
+ if( bufferedFramesTarget > bufferedFramesCount )
+ {
+ for(size_t i=0; i<bufferedFramesTarget - bufferedFramesCount; i++)
+ {
+ FeedOneFrame();
+ if(b_running)
+ break;
+ }
+ p_output->GetBufferedVideoFrameCount(&bufferedFramesCount);
+ }
+
+ /* no frames got in ?? */
+ if(streamStartTime == VLC_TICK_INVALID)
+ {
+ assert(bufferedFramesTarget);
+ assert(!b_running);
+ return VLC_EGENERIC;
+ }
+
+ if(audio.b_configured)
+ {
+ if(S_OK != p_output->GetBufferedAudioSampleFrameCount(&bufferedAudioCount))
+ return VLC_EGENERIC;
+
+ uint32_t bufferedAudioTarget = 48000*preroll/CLOCK_FREQ;
+ if(bufferedAudioCount < bufferedAudioTarget)
+ {
+ vlc_tick_t audioSamplesDuration = (bufferedAudioTarget - bufferedAudioCount)
+ * CLOCK_FREQ / 48000;
+ if(b_running)
+ FeedAudio(next, audioSamplesDuration, false);
+ else
+ FeedAudio(streamStartTime, audioSamplesDuration, true);
+
+ p_output->GetBufferedAudioSampleFrameCount(&bufferedAudioCount);
+ }
+ }
+
+ if(!b_running && bufferedFramesCount >= bufferedFramesTarget)
+ {
+ msg_Dbg(p_stream, "Preroll end with %d frames/%d samples",
+ bufferedFramesCount, bufferedAudioCount);
+ StartPlayback();
+ }
+
+ return (bufferedFramesCount < bufferedFramesTarget) ? VLC_ENOITEM : VLC_SUCCESS;
+}
+
int DBMSDIOutput::ProcessAudio(block_t *p_block)
{
if(FAKE_DRIVER)
@@ -489,14 +669,13 @@ int DBMSDIOutput::ProcessAudio(block_t *p_block)
return VLC_EGENERIC;
}
- p_block->i_pts -= clock.offset;
-
uint32_t sampleFrameCount = p_block->i_nb_samples;
uint32_t written;
+ p_block->i_pts -= clock.offset;
+ const BMDTimeValue scheduleTime = p_block->i_pts + DECKLINK_SCHED_OFFSET;
HRESULT result = p_output->ScheduleAudioSamples(
p_block->p_buffer, p_block->i_nb_samples,
- p_block->i_pts + CLOCK_FREQ,
- CLOCK_FREQ, &written);
+ scheduleTime, CLOCK_FREQ, &written);
if (result != S_OK)
msg_Err(p_stream, "Failed to schedule audio sample: 0x%X", result);
@@ -525,8 +704,8 @@ int DBMSDIOutput::ProcessVideo(picture_t *picture, block_t *p_cc)
double playbackSpeed;
if(S_OK == p_output->GetScheduledStreamTime(CLOCK_FREQ, &streamTime, &playbackSpeed))
{
- if(picture->date + CLOCK_FREQ - streamTime >
- VLC_TICK_FROM_SEC(video.nosignal_delay))
+ if(picture->date - streamTime >
+ VLC_TICK_FROM_SEC(video.nosignal_delay))
{
msg_Info(p_stream, "no signal");
picture_Hold(video.pic_nosignal);
@@ -543,6 +722,7 @@ int DBMSDIOutput::doProcessVideo(picture_t *picture, block_t *p_cc)
{
HRESULT result;
int w, h, stride, length, ret = VLC_EGENERIC;
+ BMDTimeValue scheduleTime;
IDeckLinkMutableVideoFrame *pDLVideoFrame = NULL;
w = video.configuredfmt.video.i_visible_width;
h = video.configuredfmt.video.i_visible_height;
@@ -613,15 +793,21 @@ int DBMSDIOutput::doProcessVideo(picture_t *picture, block_t *p_cc)
// compute frame duration in CLOCK_FREQ units
length = (frameduration * CLOCK_FREQ) / timescale;
picture->date -= clock.offset;
- result = p_output->ScheduleVideoFrame(pDLVideoFrame,
- picture->date + CLOCK_FREQ,
- length, CLOCK_FREQ);
+ scheduleTime = picture->date + DECKLINK_SCHED_OFFSET;
+ result = p_output->ScheduleVideoFrame(pDLVideoFrame, scheduleTime, length, CLOCK_FREQ);
if (result != S_OK) {
msg_Err(p_stream, "Dropped Video frame %" PRId64 ": 0x%x",
picture->date, result);
goto error;
}
- lasttimestamp = __MAX(picture->date, lasttimestamp);
+ lasttimestamp = __MAX(scheduleTime, lasttimestamp);
+
+ if(!b_running) /* preroll */
+ {
+ if(streamStartTime == VLC_TICK_INVALID ||
+ picture->date < streamStartTime)
+ streamStartTime = picture->date;
+ }
end:
ret = VLC_SUCCESS;
@@ -629,6 +815,7 @@ end:
error:
if(p_cc)
block_Release(p_cc);
+
picture_Release(picture);
if (pDLVideoFrame)
pDLVideoFrame->Release();
@@ -639,7 +826,7 @@ error:
void DBMSDIOutput::checkClockDrift()
{
BMDTimeValue hardwareTime, timeInFrame, ticksPerFrame;
- if(FAKE_DRIVER)
+ if(!b_running || FAKE_DRIVER)
return;
if(S_OK == p_output->GetHardwareReferenceClock(CLOCK_FREQ,
&hardwareTime,
@@ -664,3 +851,14 @@ void DBMSDIOutput::checkClockDrift()
}
}
}
+
+bool DBMSDIOutput::isDrained()
+{
+ if(b_running)
+ {
+ if(!videoStream->isEOS() || !videoBuffer.isEOS())
+ return false;
+ }
+
+ return true;
+}
diff --git a/modules/stream_out/sdi/DBMSDIOutput.hpp b/modules/stream_out/sdi/DBMSDIOutput.hpp
index 09a614186b..32b77759d5 100644
--- a/modules/stream_out/sdi/DBMSDIOutput.hpp
+++ b/modules/stream_out/sdi/DBMSDIOutput.hpp
@@ -28,7 +28,8 @@
namespace sdi_sout
{
- class DBMSDIOutput : public SDIOutput
+ class DBMSDIOutput : public SDIOutput,
+ public IDeckLinkVideoOutputCallback
{
public:
DBMSDIOutput(sout_stream_t *);
@@ -36,7 +37,12 @@ namespace sdi_sout
virtual AbstractStream *Add(const es_format_t *); /* reimpl */
virtual int Open(); /* impl */
virtual int Process(); /* impl */
- virtual int Send(AbstractStream *, block_t *); /* reimpl */
+
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface (REFIID, LPVOID *);
+ virtual ULONG STDMETHODCALLTYPE AddRef ();
+ virtual ULONG STDMETHODCALLTYPE Release ();
+ virtual HRESULT ScheduledFrameCompleted (IDeckLinkVideoFrame *, BMDOutputFrameCompletionResult);
+ virtual HRESULT ScheduledPlaybackHasStopped (void);
protected:
int ProcessVideo(picture_t *, block_t *);
@@ -59,9 +65,23 @@ namespace sdi_sout
BMDTimeValue hardware_reference;
} clock;
bool b_running;
- int Start(vlc_tick_t);
+ bool b_prerolled;
+ vlc_tick_t streamStartTime;
+ int StartPlayback();
+ struct
+ {
+ vlc_mutex_t lock; /* Driver calls callback... until buffer is empty :/ */
+ vlc_cond_t cond;
+ vlc_thread_t thread;
+ } feeder;
+ static void *feederThreadCallback(void *);
+ void feederThread();
+ int doSchedule();
int doProcessVideo(picture_t *, block_t *);
+ int FeedOneFrame();
+ int FeedAudio(vlc_tick_t, vlc_tick_t, bool);
void checkClockDrift();
+ bool isDrained();
};
}
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.cpp b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
index a156cf735f..a4ce26944a 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.cpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.cpp
@@ -32,7 +32,7 @@ using namespace sdi_sout;
SDIAudioMultiplexBuffer::SDIAudioMultiplexBuffer(vlc_object_t *obj)
: AES3AudioBuffer(obj, 2), AbstractStreamOutputBuffer()
{
-
+ b_draining = false;
}
SDIAudioMultiplexBuffer::~SDIAudioMultiplexBuffer()
@@ -55,6 +55,16 @@ void * SDIAudioMultiplexBuffer::Dequeue()
return NULL;
}
+void SDIAudioMultiplexBuffer::Drain()
+{
+ b_draining = true;
+}
+
+bool SDIAudioMultiplexBuffer::isEOS()
+{
+ return b_draining && AES3AudioBuffer::bufferStart() == VLC_TICK_INVALID;
+}
+
static void ConfigureChannels(unsigned i, es_format_t *fmt)
{
if( i>=8 )
diff --git a/modules/stream_out/sdi/SDIAudioMultiplex.hpp b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
index ea9f2d26d8..10b7d8c76a 100644
--- a/modules/stream_out/sdi/SDIAudioMultiplex.hpp
+++ b/modules/stream_out/sdi/SDIAudioMultiplex.hpp
@@ -39,6 +39,10 @@ namespace sdi_sout
virtual void FlushQueued(); /* impl */
virtual void Enqueue(void *); /* impl */
virtual void * Dequeue(); /* impl */
+ virtual void Drain(); /* impl */
+ virtual bool isEOS(); /* impl */
+ private:
+ bool b_draining;
};
class SDIAudioMultiplexConfig
diff --git a/modules/stream_out/sdi/SDIOutput.cpp b/modules/stream_out/sdi/SDIOutput.cpp
index 0d584ebffa..84fbe8cc89 100644
--- a/modules/stream_out/sdi/SDIOutput.cpp
+++ b/modules/stream_out/sdi/SDIOutput.cpp
@@ -74,6 +74,8 @@ SDIOutput::~SDIOutput()
audioStreams.pop_front();
}
delete audioMultiplex;
+ delete captionsStream;
+ delete videoStream;
if(video.pic_nosignal)
picture_Release(video.pic_nosignal);
es_format_Clean(&video.configuredfmt);
@@ -153,15 +155,6 @@ void SDIOutput::Del(AbstractStream *s)
{
s->Drain();
Process();
- if(videoStream == s)
- videoStream = NULL;
- else if(dynamic_cast<AudioDecodedStream *>(s))
- {
- audioStreams.remove(static_cast<AudioDecodedStream *>(s));
- }
- else if(captionsStream == s)
- captionsStream = NULL;
- delete s;
}
int SDIOutput::Control(int i_query, va_list args)
@@ -176,6 +169,23 @@ int SDIOutput::Control(int i_query, va_list args)
};
}
+bool SDIOutput::ReachedPlaybackTime(vlc_tick_t t)
+{
+ if (captionsStream &&
+ !captionsStream->ReachedPlaybackTime(t))
+ return false;
+
+ for(auto it = audioStreams.begin(); it != audioStreams.end(); ++it)
+ if(!(*it)->ReachedPlaybackTime(t))
+ return false;
+
+ if(!videoStream || /* must have video */
+ !videoStream->ReachedPlaybackTime(t))
+ return false;
+
+ return true;
+}
+
AbstractStream *SDIOutput::createStream(const StreamID &id,
const es_format_t *fmt,
AbstractStreamOutputBuffer *buffer,
diff --git a/modules/stream_out/sdi/SDIOutput.hpp b/modules/stream_out/sdi/SDIOutput.hpp
index c21f5b09e9..5b4be98129 100644
--- a/modules/stream_out/sdi/SDIOutput.hpp
+++ b/modules/stream_out/sdi/SDIOutput.hpp
@@ -47,6 +47,7 @@ namespace sdi_sout
bool = true);
virtual int ConfigureVideo(const video_format_t *) = 0;
virtual int ConfigureAudio(const audio_format_t *) = 0;
+ virtual bool ReachedPlaybackTime(vlc_tick_t);
sout_stream_t *p_stream;
VideoDecodedStream *videoStream;
std::list<AbstractStream *> audioStreams;
diff --git a/modules/stream_out/sdi/SDIStream.cpp b/modules/stream_out/sdi/SDIStream.cpp
index 993ca3c5ce..4845466a1a 100644
--- a/modules/stream_out/sdi/SDIStream.cpp
+++ b/modules/stream_out/sdi/SDIStream.cpp
@@ -40,26 +40,51 @@ AbstractStreamOutputBuffer::~AbstractStreamOutputBuffer()
{
}
+AbstractQueueStreamOutputBuffer::AbstractQueueStreamOutputBuffer()
+{
+ b_draining = false;
+}
+
+AbstractQueueStreamOutputBuffer::~AbstractQueueStreamOutputBuffer()
+{
+
+}
+
void AbstractQueueStreamOutputBuffer::Enqueue(void *p)
{
- queue_mutex.lock();
+ buffer_mutex.lock();
queued.push(p);
- queue_mutex.unlock();
+ buffer_mutex.unlock();
}
void *AbstractQueueStreamOutputBuffer::Dequeue()
{
void *p = NULL;
- queue_mutex.lock();
+ buffer_mutex.lock();
if(!queued.empty())
{
p = queued.front();
queued.pop();
}
- queue_mutex.unlock();
+ buffer_mutex.unlock();
return p;
}
+void AbstractQueueStreamOutputBuffer::Drain()
+{
+ buffer_mutex.lock();
+ b_draining = true;
+ buffer_mutex.unlock();
+}
+
+bool AbstractQueueStreamOutputBuffer::isEOS()
+{
+ buffer_mutex.lock();
+ bool b = b_draining && queued.empty();
+ buffer_mutex.unlock();
+ return b;
+}
+
BlockStreamOutputBuffer::BlockStreamOutputBuffer()
: AbstractQueueStreamOutputBuffer()
{
@@ -82,12 +107,27 @@ void BlockStreamOutputBuffer::FlushQueued()
PictureStreamOutputBuffer::PictureStreamOutputBuffer()
: AbstractQueueStreamOutputBuffer()
{
-
+ vlc_sem_init(&pool_semaphore, 16);
}
PictureStreamOutputBuffer::~PictureStreamOutputBuffer()
{
+ vlc_sem_destroy(&pool_semaphore);
+}
+void * PictureStreamOutputBuffer::Dequeue()
+{
+ void *p = AbstractQueueStreamOutputBuffer::Dequeue();
+ if(p)
+ vlc_sem_post(&pool_semaphore);
+ return p;
+}
+
+void PictureStreamOutputBuffer::Enqueue(void *p)
+{
+ if(p)
+ vlc_sem_wait(&pool_semaphore);
+ AbstractQueueStreamOutputBuffer::Enqueue(p);
}
void PictureStreamOutputBuffer::FlushQueued()
@@ -97,6 +137,18 @@ void PictureStreamOutputBuffer::FlushQueued()
picture_Release(p);
}
+vlc_tick_t PictureStreamOutputBuffer::NextPictureTime()
+{
+ vlc_tick_t t;
+ buffer_mutex.lock();
+ if(!queued.empty())
+ t = reinterpret_cast<picture_t *>(queued.front())->date;
+ else
+ t = VLC_TICK_INVALID;
+ buffer_mutex.unlock();
+ return t;
+}
+
unsigned StreamID::i_next_sequence_id = 0;
StreamID::StreamID(int i_stream_id)
@@ -172,20 +224,39 @@ AbstractDecodedStream::AbstractDecodedStream(vlc_object_t *p_obj,
{
p_decoder = NULL;
es_format_Init(&requestedoutput, 0, 0);
+ vlc_mutex_init(&inputLock);
+ vlc_cond_init(&inputWait);
+ threadEnd = false;
+ status = DECODING;
+ pcr = VLC_TICK_INVALID;
}
AbstractDecodedStream::~AbstractDecodedStream()
{
+ Flush();
+ deinit();
es_format_Clean(&requestedoutput);
+ vlc_cond_destroy(&inputWait);
+ vlc_mutex_destroy(&inputLock);
+}
- if(!p_decoder)
- return;
-
- struct decoder_owner *p_owner;
- p_owner = container_of(p_decoder, struct decoder_owner, dec);
- es_format_Clean(&p_owner->decoder_out);
- es_format_Clean(&p_owner->last_fmt_update);
- decoder_Destroy( p_decoder );
+void AbstractDecodedStream::deinit()
+{
+ if(p_decoder)
+ {
+ Flush();
+ vlc_mutex_lock(&inputLock);
+ vlc_cond_signal(&inputWait);
+ threadEnd = true;
+ vlc_mutex_unlock(&inputLock);
+ vlc_join(thread, NULL);
+ struct decoder_owner *p_owner;
+ p_owner = container_of(p_decoder, struct decoder_owner, dec);
+ es_format_Clean(&p_owner->decoder_out);
+ es_format_Clean(&p_owner->last_fmt_update);
+ decoder_Destroy(p_decoder);
+ p_decoder = NULL;
+ }
}
bool AbstractDecodedStream::init(const es_format_t *p_fmt)
@@ -226,46 +297,134 @@ bool AbstractDecodedStream::init(const es_format_t *p_fmt)
return false;
}
+ if(vlc_clone(&thread, decoderThreadCallback, this, VLC_THREAD_PRIORITY_VIDEO))
+ {
+ es_format_Clean(&p_owner->decoder_out);
+ es_format_Clean(&p_owner->last_fmt_update);
+ decoder_Destroy( p_decoder );
+ p_decoder = NULL;
+ return false;
+ }
+
return true;
}
-int AbstractDecodedStream::Send(block_t *p_block)
+void * AbstractDecodedStream::decoderThreadCallback(void *me)
{
- assert(p_decoder);
+ reinterpret_cast<AbstractDecodedStream *>(me)->decoderThread();
+ return NULL;
+}
+void AbstractDecodedStream::decoderThread()
+{
struct decoder_owner *p_owner =
container_of(p_decoder, struct decoder_owner, dec);
- if(!p_owner->b_error)
+ vlc_savecancel();
+ vlc_mutex_lock(&inputLock);
+ for(;;)
{
- int ret = p_decoder->pf_decode(p_decoder, p_block);
- switch(ret)
+ while(inputQueue.empty() && !threadEnd)
+ vlc_cond_wait(&inputWait, &inputLock);
+ if(threadEnd)
{
- case VLCDEC_SUCCESS:
- break;
- case VLCDEC_ECRITICAL:
- p_owner->b_error = true;
- break;
- case VLCDEC_RELOAD:
- p_owner->b_error = true;
- if(p_block)
- block_Release(p_block);
- break;
- default:
- vlc_assert_unreachable();
+ vlc_mutex_unlock(&inputLock);
+ break;
+ }
+
+ block_t *p_block = inputQueue.front();
+ inputQueue.pop();
+
+ bool b_draincall = (status == DRAINING) && (p_block == NULL);
+ vlc_mutex_unlock(&inputLock);
+
+ if(!p_owner->b_error)
+ {
+ int ret = p_decoder->pf_decode(p_decoder, p_block);
+ switch(ret)
+ {
+ case VLCDEC_SUCCESS:
+ break;
+ case VLCDEC_ECRITICAL:
+ p_owner->b_error = true;
+ break;
+ case VLCDEC_RELOAD:
+ p_owner->b_error = true;
+ if(p_block)
+ block_Release(p_block);
+ break;
+ default:
+ vlc_assert_unreachable();
+ }
+ }
+
+ vlc_mutex_lock(&inputLock);
+ if(p_owner->b_error)
+ {
+ status = FAILED;
+ outputbuffer->Drain();
+ }
+ else if(b_draincall)
+ {
+ status = DRAINED;
+ outputbuffer->Drain();
}
}
+}
- return p_owner->b_error ? VLC_EGENERIC : VLC_SUCCESS;
+int AbstractDecodedStream::Send(block_t *p_block)
+{
+ assert(p_decoder);
+ vlc_mutex_lock(&inputLock);
+ inputQueue.push(p_block);
+ if(p_block)
+ {
+ vlc_tick_t t = std::min(p_block->i_dts, p_block->i_pts);
+ if(t == VLC_TICK_INVALID)
+ t = std::max(p_block->i_dts, p_block->i_pts);
+ pcr = std::max(pcr, t);
+ }
+ vlc_cond_signal(&inputWait);
+ vlc_mutex_unlock(&inputLock);
+ return VLC_SUCCESS;
}
void AbstractDecodedStream::Flush()
{
+ vlc_mutex_lock(&inputLock);
+ while(!inputQueue.empty())
+ {
+ if(inputQueue.front())
+ block_Release(inputQueue.front());
+ inputQueue.pop();
+ }
+ vlc_mutex_unlock(&inputLock);
}
void AbstractDecodedStream::Drain()
{
Send(NULL);
+ vlc_mutex_lock(&inputLock);
+ if(status != FAILED && status != DRAINED)
+ status = DRAINING;
+ vlc_mutex_unlock(&inputLock);
+}
+
+bool AbstractDecodedStream::isEOS()
+{
+ vlc_mutex_lock(&inputLock);
+ bool b = (status == FAILED || status == DRAINED);
+ vlc_mutex_unlock(&inputLock);
+ return b;
+}
+
+bool AbstractDecodedStream::ReachedPlaybackTime(vlc_tick_t t)
+{
+ vlc_mutex_lock(&inputLock);
+ bool b = (pcr != VLC_TICK_INVALID) && t < pcr;
+ b |= (status == DRAINED) || (status == FAILED);
+ vlc_mutex_unlock(&inputLock);
+ return b;
}
void AbstractDecodedStream::setOutputFormat(const es_format_t *p_fmt)
@@ -285,6 +444,7 @@ VideoDecodedStream::VideoDecodedStream(vlc_object_t *p_obj,
VideoDecodedStream::~VideoDecodedStream()
{
+ deinit();
if(p_filters_chain)
filter_chain_Delete(p_filters_chain);
}
@@ -334,7 +494,6 @@ int VideoDecodedStream::VideoDecCallback_update_format(decoder_t *p_dec)
return VLC_SUCCESS;
}
-
static picture_t *transcode_video_filter_buffer_new(filter_t *p_filter)
{
p_filter->fmt_out.video.i_chroma = p_filter->fmt_out.i_codec;
@@ -403,7 +562,6 @@ void VideoDecodedStream::Output(picture_t *p_pic)
if(p_filters_chain)
p_pic = filter_chain_VideoFilter(p_filters_chain, p_pic);
-
if(p_pic)
outputbuffer->Enqueue(p_pic);
}
@@ -423,6 +581,7 @@ AudioDecodedStream::AudioDecodedStream(vlc_object_t *p_obj,
AudioDecodedStream::~AudioDecodedStream()
{
+ deinit();
if(p_filters)
aout_FiltersDelete(p_stream, p_filters);
}
@@ -513,7 +672,8 @@ AbstractRawStream::AbstractRawStream(vlc_object_t *p_obj, const StreamID &id,
AbstractStreamOutputBuffer *buffer)
: AbstractStream(p_obj, id, buffer)
{
-
+ pcr = VLC_TICK_INVALID;
+ b_draining = false;
}
AbstractRawStream::~AbstractRawStream()
@@ -523,10 +683,16 @@ AbstractRawStream::~AbstractRawStream()
int AbstractRawStream::Send(block_t *p_block)
{
+ vlc_tick_t t = std::min(p_block->i_dts, p_block->i_pts);
+ if(t == VLC_TICK_INVALID)
+ t = std::max(p_block->i_dts, p_block->i_pts);
if(p_block->i_buffer)
outputbuffer->Enqueue(p_block);
else
block_Release(p_block);
+ buffer_mutex.lock();
+ pcr = std::max(pcr, t);
+ buffer_mutex.unlock();
return VLC_SUCCESS;
}
@@ -537,7 +703,26 @@ void AbstractRawStream::Flush()
void AbstractRawStream::Drain()
{
+ buffer_mutex.lock();
+ b_draining = true;
+ buffer_mutex.unlock();
+}
+bool AbstractRawStream::ReachedPlaybackTime(vlc_tick_t t)
+{
+ buffer_mutex.lock();
+ bool b = (pcr != VLC_TICK_INVALID) && t < pcr;
+ b |= b_draining;
+ buffer_mutex.unlock();
+ return b;
+}
+
+bool AbstractRawStream::isEOS()
+{
+ buffer_mutex.lock();
+ bool b = b_draining;
+ buffer_mutex.unlock();
+ return b;
}
void AbstractRawStream::FlushQueued()
diff --git a/modules/stream_out/sdi/SDIStream.hpp b/modules/stream_out/sdi/SDIStream.hpp
index 669de6dab1..2e714646c6 100644
--- a/modules/stream_out/sdi/SDIStream.hpp
+++ b/modules/stream_out/sdi/SDIStream.hpp
@@ -39,16 +39,22 @@ namespace sdi_sout
virtual void FlushQueued() = 0;
virtual void Enqueue(void *) = 0;
virtual void * Dequeue() = 0;
+ virtual void Drain() = 0;
};
class AbstractQueueStreamOutputBuffer : public AbstractStreamOutputBuffer
{
public:
+ AbstractQueueStreamOutputBuffer();
+ ~AbstractQueueStreamOutputBuffer();
virtual void Enqueue(void *);
virtual void * Dequeue();
+ virtual void Drain();
+ virtual bool isEOS();
- private:
- std::mutex queue_mutex;
+ protected:
+ bool b_draining;
+ std::mutex buffer_mutex;
std::queue<void *> queued;
};
@@ -66,6 +72,12 @@ namespace sdi_sout
PictureStreamOutputBuffer();
virtual ~PictureStreamOutputBuffer();
virtual void FlushQueued();
+ vlc_tick_t NextPictureTime();
+ virtual void Enqueue(void *); /* reimpl */
+ virtual void * Dequeue(); /* reimpl */
+
+ private:
+ vlc_sem_t pool_semaphore;
};
class StreamID
@@ -93,6 +105,8 @@ namespace sdi_sout
virtual int Send(block_t*) = 0;
virtual void Drain() = 0;
virtual void Flush() = 0;
+ virtual bool ReachedPlaybackTime(vlc_tick_t) = 0;
+ virtual bool isEOS() = 0;
const StreamID & getID() const;
protected:
@@ -113,12 +127,30 @@ namespace sdi_sout
virtual int Send(block_t*);
virtual void Flush();
virtual void Drain();
+ virtual bool ReachedPlaybackTime(vlc_tick_t); /* impl */
+ virtual bool isEOS(); /* impl */
void setOutputFormat(const es_format_t *);
protected:
decoder_t *p_decoder;
virtual void setCallbacks() = 0;
+ static void *decoderThreadCallback(void *);
+ void decoderThread();
+ void deinit();
es_format_t requestedoutput;
+ std::queue<block_t *> inputQueue;
+ vlc_mutex_t inputLock;
+ vlc_cond_t inputWait;
+ vlc_thread_t thread;
+ bool threadEnd;
+ enum
+ {
+ DECODING,
+ DRAINING,
+ DRAINED,
+ FAILED,
+ } status;
+ vlc_tick_t pcr;
};
class VideoDecodedStream : public AbstractDecodedStream
@@ -167,8 +199,13 @@ namespace sdi_sout
virtual int Send(block_t*); /* impl */
virtual void Flush(); /* impl */
virtual void Drain(); /* impl */
+ virtual bool ReachedPlaybackTime(vlc_tick_t); /* impl */
+ virtual bool isEOS(); /* impl */
protected:
+ std::mutex buffer_mutex;
+ vlc_tick_t pcr;
+ bool b_draining;
void FlushQueued();
};
More information about the vlc-commits
mailing list