#include "main.h" #include "VideoThread.h" #include "api__in_flv.h" #include "FLVVideoHeader.h" #include #include #include "../nu/threadname.h" #include #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 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(); }