diff options
Diffstat (limited to 'Src/Plugins/Input/in_flv/VideoThread.cpp')
-rw-r--r-- | Src/Plugins/Input/in_flv/VideoThread.cpp | 291 |
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, ×tamp) == 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(); +} |