Initial community commit
This commit is contained in:
parent
537bcbc862
commit
fc06254474
16440 changed files with 4239995 additions and 2 deletions
428
Src/Plugins/Input/in_avi/VideoThread.cpp
Normal file
428
Src/Plugins/Input/in_avi/VideoThread.cpp
Normal file
|
@ -0,0 +1,428 @@
|
|||
#include "main.h"
|
||||
#include "api__in_avi.h"
|
||||
#include "../nu/AutoLock.h"
|
||||
#include "../nu/SampleQueue.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "interfaces.h"
|
||||
#include "../nsavi/nsavi.h"
|
||||
#include "../nu/ThreadName.h"
|
||||
#include "player.h"
|
||||
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
|
||||
|
||||
extern nsavi::HeaderList header_list;
|
||||
extern int video_stream_num;
|
||||
|
||||
int width, height;
|
||||
extern IVideoOutput *video_output;
|
||||
static HANDLE video_thread=0;
|
||||
extern ifc_avivideodecoder *video_decoder;
|
||||
bool video_opened=false;
|
||||
static HANDLE coded_frames_event=0;
|
||||
static Nullsoft::Utility::LockGuard coded_frames_guard;
|
||||
uint64_t video_total_time=0;
|
||||
HANDLE video_break=0, video_flush=0, video_flush_done=0, video_resume=0, video_ready=0;
|
||||
|
||||
static int GetStreamNumber(uint32_t id)
|
||||
{
|
||||
char *stream_data = (char *)(&id);
|
||||
if (!isxdigit(stream_data[0]) || !isxdigit(stream_data[1]))
|
||||
return -1;
|
||||
|
||||
stream_data[2] = 0;
|
||||
int stream_number = strtoul(stream_data, 0, 16);
|
||||
return stream_number;
|
||||
}
|
||||
|
||||
void Video_Init()
|
||||
{
|
||||
video_opened=false;
|
||||
video_decoder=0;
|
||||
video_thread=0;
|
||||
width=0;
|
||||
height=0;
|
||||
video_total_time=0;
|
||||
|
||||
if (coded_frames_event == 0)
|
||||
coded_frames_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
/* video events */
|
||||
if (!video_break)
|
||||
video_break = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (!video_flush_done)
|
||||
video_flush_done = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
|
||||
if (!video_flush)
|
||||
video_flush = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (!video_resume)
|
||||
video_resume = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (!video_ready)
|
||||
video_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
struct FRAMEDATA
|
||||
{
|
||||
FRAMEDATA()
|
||||
{
|
||||
type = 0;
|
||||
data=0;
|
||||
length=0;
|
||||
}
|
||||
|
||||
~FRAMEDATA()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
free(data);
|
||||
data=0;
|
||||
length=0;
|
||||
type = 0;
|
||||
}
|
||||
void Set(uint16_t _type, void *_data, size_t _length)
|
||||
{
|
||||
type = _type;
|
||||
data = _data;
|
||||
length = _length;
|
||||
}
|
||||
void *data;
|
||||
size_t length;
|
||||
uint16_t type;
|
||||
};
|
||||
|
||||
static SampleQueue<FRAMEDATA> coded_frames;
|
||||
|
||||
extern int GetOutputTime();
|
||||
struct VideoContext
|
||||
{
|
||||
nsavi::avi_reader *reader;
|
||||
nsavi::Demuxer *demuxer;
|
||||
int video_stream_num;
|
||||
};
|
||||
|
||||
static void DecodeVideo(FRAMEDATA *frame_data)
|
||||
{
|
||||
HANDLE handles[] = {killswitch, video_break};
|
||||
if (WaitForMultipleObjects(2, handles, FALSE, 0) == WAIT_TIMEOUT)
|
||||
{
|
||||
int decodeResult = video_decoder->DecodeChunk(frame_data->type, frame_data->data, frame_data->length);
|
||||
|
||||
if (decodeResult == ifc_avivideodecoder::AVI_SUCCESS)
|
||||
{
|
||||
void *data, *decoder_data;
|
||||
while (video_decoder->GetPicture(&data, &decoder_data) == ifc_avivideodecoder::AVI_SUCCESS)
|
||||
{
|
||||
if (!video_opened)
|
||||
{
|
||||
int color_format;
|
||||
double aspect_ratio=1.0;
|
||||
int flip=0;
|
||||
if (video_decoder->GetOutputProperties(&width, &height, &color_format, &aspect_ratio, &flip) == ifc_avivideodecoder::AVI_SUCCESS)
|
||||
{
|
||||
nsavi::VPRP *vprp = header_list.stream_list[video_stream_num].video_properties;
|
||||
if (vprp)
|
||||
{
|
||||
uint32_t asp = vprp->aspect_ratio;
|
||||
uint32_t aspect_x = HIWORD(asp);
|
||||
uint32_t aspect_y = LOWORD(asp);
|
||||
|
||||
aspect_ratio = (double)vprp->frame_width / (double)vprp->frame_height / ((double)aspect_x / (double)aspect_y);
|
||||
}
|
||||
else
|
||||
aspect_ratio = 1.0/aspect_ratio;
|
||||
|
||||
video_output->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
|
||||
video_output->open(width, height, flip, aspect_ratio, color_format);
|
||||
video_opened=true;
|
||||
}
|
||||
}
|
||||
if (video_opened)
|
||||
{
|
||||
int timestamp=(int)(video_total_time*1000ULL/(uint64_t) header_list.stream_list[video_stream_num].stream_header->rate);
|
||||
again:
|
||||
int real_time =(int)GetOutputTime();
|
||||
if (timestamp > (real_time+5))
|
||||
{
|
||||
HANDLE handles[] = {killswitch, video_break};
|
||||
int ret=WaitForMultipleObjects(2, handles, FALSE, timestamp-real_time);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
video_decoder->FreePicture(data, decoder_data);
|
||||
frame_data->Reset();
|
||||
return ;
|
||||
}
|
||||
goto again; // TODO: handle paused state a little better than this
|
||||
}
|
||||
RGB32 *palette=0;
|
||||
if (video_decoder->GetPalette(&palette) == ifc_avivideodecoder::AVI_SUCCESS)
|
||||
{
|
||||
video_output->extended(VIDUSER_SET_PALETTE, (INT_PTR)palette, 0);
|
||||
}
|
||||
video_output->draw(data);
|
||||
}
|
||||
video_total_time += header_list.stream_list[video_stream_num].stream_header->scale;
|
||||
video_decoder->FreePicture(data, decoder_data);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// frame_data->Reset();
|
||||
}
|
||||
|
||||
static DWORD CALLBACK VideoProcedure(LPVOID param)
|
||||
{
|
||||
SetThreadName(-1,"AVI_VideoProcedure");
|
||||
HANDLE wait_handles[] = { killswitch, video_break, video_flush, video_resume, coded_frames_event };
|
||||
while (1)
|
||||
{
|
||||
int ret = WaitForMultipleObjects(5, wait_handles, FALSE, INFINITE);
|
||||
|
||||
if (ret == WAIT_OBJECT_0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (ret == WAIT_OBJECT_0 + 1) // video break
|
||||
{
|
||||
ResetEvent(coded_frames_event);
|
||||
ResetEvent(video_break);
|
||||
SetEvent(video_flush_done);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 2) // video flush
|
||||
{
|
||||
if (video_decoder)
|
||||
video_decoder->Flush();
|
||||
ResetEvent(video_flush);
|
||||
coded_frames.Trim();
|
||||
SetEvent(video_flush_done);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 3) // video resume
|
||||
{
|
||||
ResetEvent(video_resume);
|
||||
SetEvent(coded_frames_event);
|
||||
SetEvent(video_flush_done);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 4) // data from demuxer
|
||||
{
|
||||
FRAMEDATA *frame_data = 0;
|
||||
while (frame_data = coded_frames.PopProcessed())
|
||||
{
|
||||
DecodeVideo(frame_data);
|
||||
frame_data->Reset();
|
||||
coded_frames.PushFree(frame_data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (video_opened && video_output)
|
||||
video_output->close();
|
||||
video_opened=false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD CALLBACK VideoReaderProcedure(LPVOID param)
|
||||
{
|
||||
VideoContext *ctx = (VideoContext *)param;
|
||||
nsavi::avi_reader *reader = ctx->reader;
|
||||
nsavi::Demuxer *demuxer = ctx->demuxer;
|
||||
SetThreadName(-1,"AVI_VideoProcedure");
|
||||
HANDLE wait_handles[] = { killswitch, video_break, video_flush, video_resume };
|
||||
int waitTime = 0;
|
||||
while (1)
|
||||
{
|
||||
int ret = WaitForMultipleObjects(4, wait_handles, FALSE, waitTime);
|
||||
|
||||
if (ret == WAIT_OBJECT_0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (ret == WAIT_OBJECT_0 + 1) // video break
|
||||
{
|
||||
ResetEvent(coded_frames_event);
|
||||
ResetEvent(video_break);
|
||||
SetEvent(video_flush_done);
|
||||
waitTime = INFINITE;
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 2) // video flush
|
||||
{
|
||||
if (video_decoder)
|
||||
video_decoder->Flush();
|
||||
ResetEvent(video_flush);
|
||||
coded_frames.Trim();
|
||||
SetEvent(video_flush_done);
|
||||
waitTime = 0;
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 3) // video resume
|
||||
{
|
||||
ResetEvent(video_resume);
|
||||
SetEvent(coded_frames_event);
|
||||
SetEvent(video_flush_done);
|
||||
waitTime = 0;
|
||||
}
|
||||
else if (ret == WAIT_TIMEOUT)
|
||||
{
|
||||
void *data=0;
|
||||
uint32_t data_size = 0;
|
||||
uint32_t data_type = 0;
|
||||
int ret = demuxer->GetNextMovieChunk(reader, &data, &data_size, &data_type, video_stream_num);
|
||||
if (ret != nsavi::READ_OK)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int stream_number = GetStreamNumber(data_type);
|
||||
uint16_t type = (data_type>>16);
|
||||
|
||||
if (stream_number == video_stream_num)
|
||||
{
|
||||
FRAMEDATA frame_data;
|
||||
frame_data.data = data;
|
||||
frame_data.length = data_size;
|
||||
frame_data.type = type;
|
||||
DecodeVideo(&frame_data);
|
||||
frame_data.Reset();
|
||||
}
|
||||
else
|
||||
free(data);
|
||||
}
|
||||
};
|
||||
|
||||
if (video_opened && video_output)
|
||||
video_output->close();
|
||||
video_opened=false;
|
||||
free(ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CreateVideoReaderThread(nsavi::Demuxer *demuxer, nsavi::avi_reader *video_reader)
|
||||
{
|
||||
if (!video_decoder || video_only)
|
||||
return false;
|
||||
|
||||
VideoContext *context = (VideoContext *)calloc(1, sizeof(VideoContext));
|
||||
|
||||
context->reader = video_reader;
|
||||
context->demuxer = demuxer;
|
||||
DWORD threadId = 0;
|
||||
video_thread = CreateThread(0, 0, VideoReaderProcedure, context, 0, &threadId);
|
||||
SetThreadPriority(video_thread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OnVideo(uint16_t type, void *data, size_t length)
|
||||
{
|
||||
if (!video_decoder)
|
||||
return false;
|
||||
|
||||
if (!video_only && !video_thread)
|
||||
{
|
||||
DWORD threadId;
|
||||
video_thread = CreateThread(0, 0, VideoProcedure, 0, 0, &threadId);
|
||||
SetThreadPriority(video_thread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
}
|
||||
|
||||
if (video_thread)
|
||||
{
|
||||
FRAMEDATA *new_frame = coded_frames.PopFree();
|
||||
if (new_frame)
|
||||
{
|
||||
new_frame->Set(type, data, length);
|
||||
coded_frames.PushProcessed(new_frame);
|
||||
SetEvent(coded_frames_event);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if (video_only)
|
||||
{
|
||||
FRAMEDATA *new_frame = coded_frames.PopFree();
|
||||
if (new_frame)
|
||||
{
|
||||
new_frame->Set(type, data, length);
|
||||
DecodeVideo(new_frame);
|
||||
new_frame->Reset();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Video_Stop()
|
||||
{
|
||||
SetEvent(killswitch);
|
||||
if (video_only)
|
||||
{
|
||||
video_thread=0;
|
||||
|
||||
ResetEvent(coded_frames_event);
|
||||
Nullsoft::Utility::AutoLock l(coded_frames_guard);
|
||||
coded_frames.Trim();
|
||||
if (video_opened && video_output)
|
||||
video_output->close();
|
||||
video_opened=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (video_thread)
|
||||
WaitForSingleObject(video_thread, INFINITE);
|
||||
video_thread=0;
|
||||
|
||||
ResetEvent(coded_frames_event);
|
||||
Nullsoft::Utility::AutoLock l(coded_frames_guard);
|
||||
coded_frames.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
void Video_Close()
|
||||
{
|
||||
video_opened=false;
|
||||
|
||||
if (video_decoder)
|
||||
{
|
||||
video_decoder->Close();
|
||||
video_decoder=0;
|
||||
}
|
||||
}
|
||||
|
||||
void Video_Break()
|
||||
{
|
||||
if (!video_only && video_decoder)
|
||||
{
|
||||
SetEvent(video_break);
|
||||
HANDLE events[2] = {video_flush_done, video_thread};
|
||||
WaitForMultipleObjects(2, events, FALSE, INFINITE);
|
||||
}
|
||||
}
|
||||
|
||||
void Video_Flush()
|
||||
{
|
||||
if (video_decoder)
|
||||
{
|
||||
if (video_only)
|
||||
{
|
||||
video_decoder->Flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
SetEvent(video_flush);
|
||||
HANDLE events[2] = {video_flush_done, video_thread};
|
||||
WaitForMultipleObjects(2, events, FALSE, INFINITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
bool Video_DecoderReady()
|
||||
{
|
||||
if (!video_decoder)
|
||||
return true;
|
||||
|
||||
return !!video_decoder->Ready();
|
||||
}
|
||||
*/
|
Loading…
Add table
Add a link
Reference in a new issue