aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_flv/VideoThread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/Input/in_flv/VideoThread.cpp')
-rw-r--r--Src/Plugins/Input/in_flv/VideoThread.cpp291
1 files changed, 291 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_flv/VideoThread.cpp b/Src/Plugins/Input/in_flv/VideoThread.cpp
new file mode 100644
index 00000000..e07d58d0
--- /dev/null
+++ b/Src/Plugins/Input/in_flv/VideoThread.cpp
@@ -0,0 +1,291 @@
+#include "main.h"
+#include "VideoThread.h"
+#include "api__in_flv.h"
+#include "FLVVideoHeader.h"
+#include <shlwapi.h>
+#include <windows.h>
+#include "../nu/threadname.h"
+#include <api/service/waservicefactory.h>
+#include "../nu/AutoLock.h"
+#include "../nu/SampleQueue.h"
+
+int width, height;
+IVideoOutput *videoOutput=0;
+static HANDLE videoThread=0;
+static volatile LONG video_flush=0;
+static ifc_flvvideodecoder *videoDecoder=0;
+bool video_opened=false;
+static HANDLE coded_frames_event=0;
+static HANDLE video_flush_event=0;
+static Nullsoft::Utility::LockGuard coded_frames_guard;
+extern bool video_only;
+
+void Video_Init()
+{
+ video_opened=false;
+ videoDecoder=0;
+ videoThread=0;
+ width=0;
+ height=0;
+
+ if (coded_frames_event == 0)
+ coded_frames_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (video_flush_event == 0)
+ video_flush_event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ video_flush=0;
+}
+
+struct FRAMEDATA
+{
+ FRAMEDATA()
+ {
+ data=0;
+ length=0;
+ timestamp=0;
+ }
+
+ ~FRAMEDATA()
+ {
+ free(data);
+ }
+ void Reset()
+ {
+ free(data);
+ data=0;
+ length=0;
+ timestamp=0;
+ }
+ void Set(void *_data, size_t _length, uint32_t ts)
+ {
+ data = _data;
+ length = _length;
+ timestamp = ts;
+ }
+ void *data;
+ size_t length;
+ uint32_t timestamp;
+};
+
+static SampleQueue<FRAMEDATA> coded_frames;
+
+extern int GetOutputTime();
+static void DecodeVideo(FRAMEDATA *framedata)
+{
+ if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
+ {
+ int decodeResult = videoDecoder->DecodeSample(framedata->data, framedata->length, framedata->timestamp);
+
+ if (decodeResult == FLV_VIDEO_SUCCESS)
+ {
+ void *data, *decoder_data;
+ uint64_t timestamp=framedata->timestamp;
+ while (videoDecoder->GetPicture(&data, &decoder_data, &timestamp) == FLV_VIDEO_SUCCESS)
+ {
+ if (!video_opened)
+ {
+ int color_format;
+ if (videoDecoder->GetOutputFormat(&width, &height, &color_format) == FLV_VIDEO_SUCCESS)
+ {
+ videoOutput->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
+ videoOutput->open(width, height, 0, 1.0, color_format);
+ video_opened=true;
+ }
+ }
+ if (video_opened)
+ {
+again:
+ int realTime =(int)GetOutputTime();
+ if (timestamp > (realTime+5))
+ {
+ HANDLE handles[] = {killswitch, video_flush_event};
+ int ret=WaitForMultipleObjects(2, handles, FALSE, (DWORD)(timestamp-realTime));
+ if (ret != WAIT_TIMEOUT)
+ {
+ videoDecoder->FreePicture(data, decoder_data);
+ framedata->Reset();
+ return ;
+ }
+ goto again; // TODO: handle paused state a little better than this
+ }
+ videoOutput->draw(data);
+ }
+ videoDecoder->FreePicture(data, decoder_data);
+ }
+ }
+ }
+
+ framedata->Reset();
+}
+
+DWORD CALLBACK VideoProcedure(LPVOID param)
+{
+ SetThreadName(-1,"FLV_VideoProcedure");
+ HANDLE wait_handles[] = { killswitch, video_flush_event, coded_frames_event};
+ int ret;
+ do
+ {
+ ret = WaitForMultipleObjects(3, wait_handles, FALSE, INFINITE);
+ if (ret == WAIT_OBJECT_0 + 1)
+ {
+ if (video_flush)
+ {
+ InterlockedDecrement(&video_flush);
+ if (videoDecoder)
+ videoDecoder->Flush();
+ }
+ ResetEvent(video_flush_event);
+ }
+ else if (ret == WAIT_OBJECT_0 + 2)
+ {
+ FRAMEDATA *frame_data = 0;
+ while (frame_data = coded_frames.PopProcessed())
+ {
+ DecodeVideo(frame_data);
+ frame_data->Reset();
+ coded_frames.PushFree(frame_data);
+ }
+ }
+ } while (ret != WAIT_OBJECT_0);
+
+ if (video_opened && videoOutput)
+ videoOutput->close();
+ video_opened=false;
+ return 0;
+}
+
+bool Video_IsSupported(int type)
+{
+ size_t n = 0;
+ waServiceFactory *factory = NULL;
+ while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
+ {
+ svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
+ if (creator)
+ {
+ int supported = creator->HandlesVideo(type);
+ factory->releaseInterface(creator);
+ if (supported == svc_flvdecoder::CREATEDECODER_SUCCESS)
+ return true;
+ }
+ }
+ return false;
+}
+
+static ifc_flvvideodecoder *CreateVideoDecoder(int type)
+{
+ ifc_flvvideodecoder *video_decoder = 0;
+ size_t n = 0;
+ waServiceFactory *factory = NULL;
+ while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
+ {
+ svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
+ if (creator)
+ {
+ if (creator->CreateVideoDecoder(type, width, height, &video_decoder) == FLV_VIDEO_SUCCESS)
+ return video_decoder;
+
+ factory->releaseInterface(creator);
+ }
+ }
+ return 0;
+}
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+bool OnVideo(void *data, size_t length, int type, unsigned __int32 timestamp)
+{
+ if (!videoDecoder)
+ {
+ videoDecoder = CreateVideoDecoder(type);
+ }
+
+ if (videoDecoder)
+ {
+ if (!video_only && !videoThread)
+ {
+ DWORD threadId;
+ videoThread = CreateThread(0, 0, VideoProcedure, 0, 0, &threadId);
+ SetThreadPriority(videoThread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
+ }
+
+ FRAMEDATA *new_frame = coded_frames.PopFree();
+ if (new_frame)
+ {
+ new_frame->Set(data, length, timestamp);
+ if (video_only)
+ {
+ DecodeVideo(new_frame);
+ new_frame->Reset();
+ coded_frames.PushFree(new_frame);
+ }
+ else
+ {
+ coded_frames.PushProcessed(new_frame);
+ SetEvent(coded_frames_event);
+ }
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+void Video_Stop()
+{
+ if (video_only)
+ {
+ ResetEvent(coded_frames_event);
+ Nullsoft::Utility::AutoLock l(coded_frames_guard);
+ coded_frames.Trim();
+ if (video_opened && videoOutput)
+ videoOutput->close();
+ video_opened=false;
+ }
+ else
+ {
+ if (videoThread)
+ WaitForSingleObject(videoThread, INFINITE);
+ videoThread=0;
+
+ InterlockedIncrement(&video_flush);
+ ResetEvent(coded_frames_event);
+ Nullsoft::Utility::AutoLock l(coded_frames_guard);
+ coded_frames.Trim();
+ }
+}
+
+void Video_Close()
+{
+ video_opened=false;
+
+ if (videoDecoder)
+ {
+ videoDecoder->Close();
+ videoDecoder=0;
+ }
+}
+
+void VideoFlush()
+{
+ if (video_only)
+ {
+ if (videoDecoder)
+ videoDecoder->Flush();
+ }
+ else if (videoThread)
+ {
+ InterlockedIncrement(&video_flush);
+ ResetEvent(coded_frames_event);
+ coded_frames.Trim();
+ SetEvent(video_flush_event);
+ }
+}
+
+bool Video_DecoderReady()
+{
+ if (!videoDecoder)
+ return true;
+
+ return !!videoDecoder->Ready();
+}