winamp/Src/Plugins/Input/in_flv/VideoThread.cpp

292 lines
6.2 KiB
C++

#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();
}