123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215 |
- #include "main.h"
- #include "VideoThread.h"
- #include "../Winamp/wa_ipc.h"
- VideoSample *video_sample=0;
- IVideoOutput *videoOutput=0;
- static bool video_reopen=false;
- static int height=0;
- static int width=0;
- static bool video_opened=false;
- static int consecutive_early_frames;
- HANDLE video_flush = 0, video_start_flushing=0, video_flush_done = 0, video_resume = 0;
- static HANDLE video_thread = 0;
- MP4SampleId nextVideoSampleId=1; // set in conjunction with video_flush
- static void OpenVideo()
- {
- if (!video_opened || video_reopen)
- {
- consecutive_early_frames = 0;
- if (!videoOutput)
- videoOutput = (IVideoOutput *)SendMessage(mod.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
- int color_format;
- double aspect_ratio=1.0;
- if (video && video->GetOutputFormat(&width, &height, &color_format, &aspect_ratio) == MP4_VIDEO_SUCCESS)
- {
- videoOutput->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
- videoOutput->open(width, height, 0, 1.0/aspect_ratio, color_format);
- video_opened = true;
- video_reopen = false;
- }
- }
- }
- static DecodedVideoSample *GetNextPicture()
- {
- void *data, *decoder_data;
- MP4Timestamp timestamp=video_sample?(video_sample->timestamp):0;
- switch(video->GetPicture(&data, &decoder_data, ×tamp))
- {
- case MP4_VIDEO_OUTPUT_FORMAT_CHANGED:
- video_reopen=true;
- // fall through
- case MP4_VIDEO_SUCCESS:
- DecodedVideoSample *decoded = new DecodedVideoSample;
- decoded->decoder = video;
- decoded->decoder_data = decoder_data;
- decoded->timestamp = timestamp;
- decoded->output = data;
- return decoded;
- }
- return 0;
- }
- static void OutputPicture(DecodedVideoSample *decoded_video_sample)
- {
- if (decoded_video_sample)
- {
- int outputTime = (int)((decoded_video_sample->timestamp*1000ULL)/(uint64_t)m_video_timescale);
- again:
- MP4Duration realTime = GetClock();
- int time_diff = outputTime - realTime;
- if (time_diff > 12 && consecutive_early_frames) // plenty of time, go ahead and turn off frame dropping
- {
- if (--consecutive_early_frames == 0)
- video->HurryUp(0);
- }
- else if (time_diff < -50) // shit we're way late, start dropping frames
- {
- video->HurryUp(1);
- consecutive_early_frames += 3;
- }
- if (time_diff > 3)
- {
- HANDLE handles[] = {killEvent, video_start_flushing};
- if (WaitForMultipleObjects(2, handles, FALSE, outputTime-realTime) != WAIT_TIMEOUT)
- {
- delete decoded_video_sample;
- decoded_video_sample=0;
- return;
- }
- goto again; // TODO: handle paused state a little better than this
- }
- OpenVideo(); // open video if we havn't already
- videoOutput->draw(decoded_video_sample->output);
- delete decoded_video_sample;
- decoded_video_sample=0;
- /* TODO: probably want separate audio and video done flags
- if (temp->sampleId == numSamples) // done!
- done = true;
- */
- }
- }
- static bool ReadNextVideoSample()
- {
- while (nextVideoSampleId <= numVideoSamples)
- {
- VideoSample &sample=*video_sample;
- unsigned __int32 buffer_size = sample.inputSize;
- bool isSync=false;
- MP4Duration duration, offset;
- play_mp4_guard.Lock();
- bool sample_read=MP4ReadSample(MP4hFile, video_track, nextVideoSampleId++, (unsigned __int8 **)&sample.input, &buffer_size, &sample.timestamp, &duration, &offset, &isSync);
- play_mp4_guard.Unlock();
- if (sample_read)
- {
- // some buggy movies store signed int32 offsets, so let's deal with it
- offset = (uint32_t)offset;
- int32_t signed_offset = (int32_t)offset;
- sample.timestamp += signed_offset;
- //int outputTime = (int)((sample.timestamp*1000ULL) /(uint64_t)m_video_timescale);
- sample.inputValid = buffer_size;
- return true;
- }
- }
- return false;
- }
- static DWORD WINAPI VideoPlayThread(LPVOID parameter)
- {
- DWORD waitTime = 0;
- HANDLE handles[] = {killEvent, video_flush, video_start_flushing, video_resume};
- while (1)
- {
- int ret = WaitForMultipleObjects(4, handles, FALSE, waitTime);
- if (ret == WAIT_OBJECT_0) // kill
- break;
- else if (ret == WAIT_OBJECT_0+1) // flush
- {
- if (video)
- video->Flush();
- ResetEvent(video_flush);
- waitTime = 0;
- SetEvent(video_flush_done);
- }
- else if (ret == WAIT_OBJECT_0+2) // start flushing
- {
- waitTime = INFINITE; // this will stop us from decoding samples for a while
- ResetEvent(video_start_flushing);
- SetEvent(video_flush_done);
- }
- else if (ret == WAIT_OBJECT_0+3) // resume playback (like flush but don't flush the decoder)
- {
- ResetEvent(video_resume);
- waitTime = 0;
- SetEvent(video_flush_done);
- }
- else if (ret == WAIT_TIMEOUT)
- {
- if (ReadNextVideoSample())
- {
- int ret = video->DecodeSample(video_sample->input, video_sample->inputValid, video_sample->timestamp);
- if (ret == MP4_VIDEO_OUTPUT_FORMAT_CHANGED)
- video_reopen=true;
- if (ret == MP4_VIDEO_AGAIN)
- nextVideoSampleId--;
- DecodedVideoSample *picture = 0;
- while (picture = GetNextPicture())
- {
- OutputPicture(picture);
- }
- waitTime = 0;
- }
- else
- {
- // TODO: tell decoder end-of-file and get any buffers in queue
- if (!audio)
- PostMessage(mod.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
- waitTime = INFINITE; // out of stuff to do, wait for kill or flush
- }
- }
- else // error
- break;
- }
- if (videoOutput)
- videoOutput->close();
- return 0;
- }
- void Video_Init()
- {
- width=0;
- height=0;
- video_reopen=false;
- video_opened=false;
- video_flush = CreateEvent(NULL, TRUE, FALSE, NULL);
- video_start_flushing = CreateEvent(NULL, TRUE, FALSE, NULL);
- video_flush_done = CreateEvent(NULL, FALSE, FALSE, NULL);
- video_resume = CreateEvent(NULL, TRUE, FALSE, NULL);
- video_thread = CreateThread(0, 0, VideoPlayThread, 0, 0, 0);
- }
- void Video_Close()
- {
- WaitForSingleObject(video_thread, INFINITE);
- CloseHandle(video_thread);
- video_thread = 0;
- CloseHandle(video_start_flushing);
- video_start_flushing=0;
- CloseHandle(video_flush);
- video_flush=0;
- CloseHandle(video_resume);
- video_resume=0;
- CloseHandle(video_flush_done);
- video_flush_done = 0;
-
- delete video_sample;
- video_sample=0;
- }
|