aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/Input/in_mp4/VideoThread.cpp
diff options
context:
space:
mode:
authorJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
committerJef <jef@targetspot.com>2024-09-24 08:54:57 -0400
commit20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/Input/in_mp4/VideoThread.cpp
parent537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff)
downloadwinamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz
Initial community commit
Diffstat (limited to 'Src/Plugins/Input/in_mp4/VideoThread.cpp')
-rw-r--r--Src/Plugins/Input/in_mp4/VideoThread.cpp215
1 files changed, 215 insertions, 0 deletions
diff --git a/Src/Plugins/Input/in_mp4/VideoThread.cpp b/Src/Plugins/Input/in_mp4/VideoThread.cpp
new file mode 100644
index 00000000..8b844800
--- /dev/null
+++ b/Src/Plugins/Input/in_mp4/VideoThread.cpp
@@ -0,0 +1,215 @@
+#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, &timestamp))
+ {
+ 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;
+} \ No newline at end of file