From 408837ac384140af9e5500d18ee928e755d37699 Mon Sep 17 00:00:00 2001 From: TurtleP Date: Sat, 26 Mar 2022 12:51:26 -0400 Subject: [PATCH 01/19] rename some folders, start video stuff --- include/modules/video/videomodule.h | 10 + include/modules/video/wrap_videomodule.h | 11 + include/objects/video/sync/deltasync.h | 34 +++ include/objects/video/sync/framesync.h | 32 +++ include/objects/video/sync/sourcesync.h | 28 +++ include/objects/video/theora/oggdemuxer.h | 56 +++++ include/objects/video/theora/theorastreamc.h | 59 +++++ include/objects/video/utility/stream.h | 24 ++ include/objects/video/videostream.h | 60 +++++ include/objects/video/wrap_videostream.h | 0 platform/3ds/source/objects/videostream.cpp | 5 + platform/switch/shaders/video_fsh.glsl | 32 +++ .../data/{byte => bytedata}/bytedata.cpp | 0 .../data/{byte => bytedata}/wrap_bytedata.cpp | 0 .../compresseddata.cpp | 0 .../wrap_compresseddata.cpp | 0 .../data/{view => dataview}/dataview.cpp | 0 .../data/{view => dataview}/wrap_dataview.cpp | 0 .../randomgenerator.cpp | 0 .../wrap_randomgenerator.cpp | 0 source/objects/video/sync/deltasync.cpp | 44 ++++ source/objects/video/sync/framesync.cpp | 18 ++ source/objects/video/sync/sourcesync.cpp | 36 +++ source/objects/video/theora/oggdemuxer.cpp | 238 ++++++++++++++++++ source/objects/video/videostream.cpp | 40 +++ 25 files changed, 727 insertions(+) create mode 100644 include/modules/video/videomodule.h create mode 100644 include/modules/video/wrap_videomodule.h create mode 100644 include/objects/video/sync/deltasync.h create mode 100644 include/objects/video/sync/framesync.h create mode 100644 include/objects/video/sync/sourcesync.h create mode 100644 include/objects/video/theora/oggdemuxer.h create mode 100644 include/objects/video/theora/theorastreamc.h create mode 100644 include/objects/video/utility/stream.h create mode 100644 include/objects/video/videostream.h create mode 100644 include/objects/video/wrap_videostream.h create mode 100644 platform/3ds/source/objects/videostream.cpp create mode 100644 platform/switch/shaders/video_fsh.glsl rename source/objects/data/{byte => bytedata}/bytedata.cpp (100%) rename source/objects/data/{byte => bytedata}/wrap_bytedata.cpp (100%) rename source/objects/data/{compressed => compresseddata}/compresseddata.cpp (100%) rename source/objects/data/{compressed => compresseddata}/wrap_compresseddata.cpp (100%) rename source/objects/data/{view => dataview}/dataview.cpp (100%) rename source/objects/data/{view => dataview}/wrap_dataview.cpp (100%) rename source/objects/{random => randomgenerator}/randomgenerator.cpp (100%) rename source/objects/{random => randomgenerator}/wrap_randomgenerator.cpp (100%) create mode 100644 source/objects/video/sync/deltasync.cpp create mode 100644 source/objects/video/sync/framesync.cpp create mode 100644 source/objects/video/sync/sourcesync.cpp create mode 100644 source/objects/video/theora/oggdemuxer.cpp create mode 100644 source/objects/video/videostream.cpp diff --git a/include/modules/video/videomodule.h b/include/modules/video/videomodule.h new file mode 100644 index 000000000..099805464 --- /dev/null +++ b/include/modules/video/videomodule.h @@ -0,0 +1,10 @@ +#pragma once + +#include "common/module.h" + +namespace love +{ + class VideoModule : public Module + { + }; +} // namespace love diff --git a/include/modules/video/wrap_videomodule.h b/include/modules/video/wrap_videomodule.h new file mode 100644 index 000000000..e44cb4b07 --- /dev/null +++ b/include/modules/video/wrap_videomodule.h @@ -0,0 +1,11 @@ +#pragma once + +#include "common/luax.h" +#include "modules/video/videomodule.h" + +namespace Wrap_VideoModule +{ + int NewVideo(lua_State* L); + + int Register(lua_State* L); +} // namespace Wrap_VideoModule diff --git a/include/objects/video/sync/deltasync.h b/include/objects/video/sync/deltasync.h new file mode 100644 index 000000000..af948c823 --- /dev/null +++ b/include/objects/video/sync/deltasync.h @@ -0,0 +1,34 @@ +#pragma once + +#include "modules/thread/types/mutex.h" +#include "objects/video/sync/framesync.h" + +namespace love +{ + class DeltaSync : public FrameSync + { + public: + DeltaSync(); + + ~DeltaSync(); + + virtual double GetPosition() const override; + + virtual void Update(double dt) override; + + virtual void Play() override; + + virtual void Pause() override; + + virtual void Seek(double time) override; + + virtual bool IsPlaying() const override; + + private: + bool playing; + double position; + double speed; + + thread::MutexRef mutex; + }; +} // namespace love diff --git a/include/objects/video/sync/framesync.h b/include/objects/video/sync/framesync.h new file mode 100644 index 000000000..4ff27a16e --- /dev/null +++ b/include/objects/video/sync/framesync.h @@ -0,0 +1,32 @@ +#pragma once + +#include "objects/object.h" + +namespace love +{ + class FrameSync : public Object + { + public: + virtual ~FrameSync() + {} + + virtual double GetPosition() const = 0; + + virtual void Update(double) + {} + + void CopyState(const FrameSync* other); + + /* playback api */ + + virtual void Play() = 0; + + virtual void Pause() = 0; + + virtual void Seek(double offset) = 0; + + virtual double Tell() const; + + virtual bool IsPlaying() const = 0; + }; +} // namespace love diff --git a/include/objects/video/sync/sourcesync.h b/include/objects/video/sync/sourcesync.h new file mode 100644 index 000000000..74ba6d386 --- /dev/null +++ b/include/objects/video/sync/sourcesync.h @@ -0,0 +1,28 @@ +#pragma once + +#include "common/strongref.h" + +#include "objects/source/source.h" +#include "objects/video/sync/framesync.h" + +namespace love +{ + class SourceSync : FrameSync + { + public: + SourceSync(Source* source); + + virtual double GetPosition() const override; + + virtual void Play() override; + + virtual void Pause() override; + + virtual void Seek(double time) override; + + virtual bool IsPlaying() const override; + + private: + StrongReference source; + } +} // namespace love diff --git a/include/objects/video/theora/oggdemuxer.h b/include/objects/video/theora/oggdemuxer.h new file mode 100644 index 000000000..70dcbdf13 --- /dev/null +++ b/include/objects/video/theora/oggdemuxer.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include "objects/file/file.h" + +#include + +namespace love +{ + class OggDemuxer + { + public: + OggDemuxer(File* file); + + ~OggDemuxer(); + + enum StreamType + { + TYPE_THEORA, + TYPE_UNKNOWN + }; + + static constexpr int SYNC_VALUE = 0x2000; + static constexpr int THEORA_BYTES_MIN = 0x07; + static constexpr int THEORA_HEADER_TYPE = 0x80; + static constexpr double REWIND_THRESHOLD = 0.01; + + StreamType FindStream(); + + bool ReadPacket(ogg_packet& packet, bool mustSucceed = false); + + void ReSync(); + + bool IsEOS() const; + + const std::string& GetFilename() const; + + bool Seek(ogg_packet& packet, double target, std::function getTime); + + private: + StrongReference file; + + ogg_sync_state sync; + ogg_stream_state stream; + ogg_page page; + + bool intiialized; + int serial; + bool endOfStream; + + bool ReadPage(bool errorEOF = false); + + StreamType DetermineType(); + }; +} // namespace love diff --git a/include/objects/video/theora/theorastreamc.h b/include/objects/video/theora/theorastreamc.h new file mode 100644 index 000000000..59501b461 --- /dev/null +++ b/include/objects/video/theora/theorastreamc.h @@ -0,0 +1,59 @@ +#pragma once + +#include +#include +#include + +#include "modules/thread/types/mutex.h" +#include "objects/file/file.h" +#include "objects/video/theora/oggdemuxer.h" +#include "objects/video/videostream.h" + +namespace love::common +{ + class TheoraStream : public VideoStream + { + public: + TheoraStream(File* file); + + ~TheoraStream(); + + const void* GetFrontBuffer() const; + + size_t GetSize() const; + + void FillBackBuffer(); + + bool SwapBuffers(); + + int GetWidth() const; + + int GetHeight() const; + + const std::string& GetFilename() const; + + void SetSync(FrameSync* other); + + bool IsPlaying() const; + + void ThreadedFillBackBuffer(double dt); + + private: + OggDemuxer demuxer; + bool headerParsed; + + ogg_packet packet; + + th_info info; + th_dec_ctx* decoder; + + thread::MutexRef bufferMutex; + bool frameReady; + + double lastFrame; + double nextFrame; + + void ParseHeader(); + void SeekDecoder(double target); + } +} // namespace love::common diff --git a/include/objects/video/utility/stream.h b/include/objects/video/utility/stream.h new file mode 100644 index 000000000..7d0c6b135 --- /dev/null +++ b/include/objects/video/utility/stream.h @@ -0,0 +1,24 @@ +#pragma once + +#include "objects/object.h" + +namespace love +{ + class Stream : public Object + { + public: + static love::Type type; + + virtual ~Stream() + {} + + virtual void FillBackBuffer() + {} + + virtual const void* GetFrontBuffer() const = 0; + + virtual size_t GetSize() const = 0; + + virtual bool SwapBuffers() = 0; + } +} // namespace love diff --git a/include/objects/video/videostream.h b/include/objects/video/videostream.h new file mode 100644 index 000000000..730a54c69 --- /dev/null +++ b/include/objects/video/videostream.h @@ -0,0 +1,60 @@ +#pragma once + +#include "common/strongref.h" + +#include "objects/video/sync/framesync.h" +#include "objects/video/utility/stream.h" + +namespace love +{ + class VideoStream : public Stream + { + public: + static love::Type type; + + virtual ~VideoStream() + {} + + virtual int GetWidth() const = 0; + + virtual int GetHeight() const = 0; + + virtual const std::string& GetFilename() const = 0; + + /* playback api */ + + virtual void Play(); + + virtual void Pause(); + + virtual void Seek(double offset); + + virtual double Tell() const; + + virtual bool IsPlaying() const; + + /* sync stuff */ + + virtual void SetSync(FrameSync* sync); + + virtual FrameSync* GetSync() const; + + /* data structure */ + struct Frame + { + Frame(); + + ~Frame(); + + int yw, yh; + unsigned char* yplane; + + int cw, ch; + unsigned char* cbplane; + unsigned char* crplane; + }; + + protected: + StrongReference frameSync; + }; +} // namespace love diff --git a/include/objects/video/wrap_videostream.h b/include/objects/video/wrap_videostream.h new file mode 100644 index 000000000..e69de29bb diff --git a/platform/3ds/source/objects/videostream.cpp b/platform/3ds/source/objects/videostream.cpp new file mode 100644 index 000000000..8627c4b89 --- /dev/null +++ b/platform/3ds/source/objects/videostream.cpp @@ -0,0 +1,5 @@ +#include "objects/video/videostream.h" +#include "modules/thread/types/lock.h" + +using namespace love; +using thread::Lock; diff --git a/platform/switch/shaders/video_fsh.glsl b/platform/switch/shaders/video_fsh.glsl new file mode 100644 index 000000000..b05092e59 --- /dev/null +++ b/platform/switch/shaders/video_fsh.glsl @@ -0,0 +1,32 @@ +#version 460 + +layout (location = 0) in vec4 inColor; +layout (location = 1) in vec2 inTexCoord; + +layout (location = 0) out vec4 outColor; + +layout (binding = 0) uniform sampler2D video_y; +layout (binding = 1) uniform sampler2D video_cb; +layout (binding = 2) uniform sampler2D video_cr; + +const vec3 yuv_add = vec3(-0.0627451017, -0.501960814, -0.501960814); + +const vec3 yuv_r = vec3(1.164, 0.000, 1.596); +const vec3 yuv_g = vec3(1.164, -0.391, -0.813); +const vec3 yuv_b = vec3(1.164, 2.018, 0.000); + +void main() +{ + vec3 yuv; + + yuv[0] = texture(video_y, inTexCoord).r; + yuv[1] = texture(video_cb, inTexCoord).r; + yuv[2] = texture(video_cr, inTexCoord).r; + + yuv += yuv_add; + + outColor[0] = dot(yuv, yuv_r); + outColor[1] = dot(yuv, yuv_g); + outColor[2] = dot(yuv, yuv_b); + outColor[3] = 1.0; +} diff --git a/source/objects/data/byte/bytedata.cpp b/source/objects/data/bytedata/bytedata.cpp similarity index 100% rename from source/objects/data/byte/bytedata.cpp rename to source/objects/data/bytedata/bytedata.cpp diff --git a/source/objects/data/byte/wrap_bytedata.cpp b/source/objects/data/bytedata/wrap_bytedata.cpp similarity index 100% rename from source/objects/data/byte/wrap_bytedata.cpp rename to source/objects/data/bytedata/wrap_bytedata.cpp diff --git a/source/objects/data/compressed/compresseddata.cpp b/source/objects/data/compresseddata/compresseddata.cpp similarity index 100% rename from source/objects/data/compressed/compresseddata.cpp rename to source/objects/data/compresseddata/compresseddata.cpp diff --git a/source/objects/data/compressed/wrap_compresseddata.cpp b/source/objects/data/compresseddata/wrap_compresseddata.cpp similarity index 100% rename from source/objects/data/compressed/wrap_compresseddata.cpp rename to source/objects/data/compresseddata/wrap_compresseddata.cpp diff --git a/source/objects/data/view/dataview.cpp b/source/objects/data/dataview/dataview.cpp similarity index 100% rename from source/objects/data/view/dataview.cpp rename to source/objects/data/dataview/dataview.cpp diff --git a/source/objects/data/view/wrap_dataview.cpp b/source/objects/data/dataview/wrap_dataview.cpp similarity index 100% rename from source/objects/data/view/wrap_dataview.cpp rename to source/objects/data/dataview/wrap_dataview.cpp diff --git a/source/objects/random/randomgenerator.cpp b/source/objects/randomgenerator/randomgenerator.cpp similarity index 100% rename from source/objects/random/randomgenerator.cpp rename to source/objects/randomgenerator/randomgenerator.cpp diff --git a/source/objects/random/wrap_randomgenerator.cpp b/source/objects/randomgenerator/wrap_randomgenerator.cpp similarity index 100% rename from source/objects/random/wrap_randomgenerator.cpp rename to source/objects/randomgenerator/wrap_randomgenerator.cpp diff --git a/source/objects/video/sync/deltasync.cpp b/source/objects/video/sync/deltasync.cpp new file mode 100644 index 000000000..446f30600 --- /dev/null +++ b/source/objects/video/sync/deltasync.cpp @@ -0,0 +1,44 @@ +#include "objects/video/sync/deltasync.h" +#include "modules/thread/types/lock.h" + +using namespace love; +using thread::Lock; + +DeltaSync::DeltaSync() : playing(false), position(0), speed(1) +{} + +DeltaSync::~DeltaSync() +{} + +double DeltaSync::GetPosition() const +{ + return this->position; +} + +void DeltaSync::Update(double dt) +{ + Lock lock(this->mutex); + if (this->playing) + this->position += this->speed * dt; +} + +void DeltaSync::Play() +{ + this->playing = true; +} + +void DeltaSync::Pause() +{ + this->playing = false; +} + +void DeltaSync::Seek(double time) +{ + Lock lock(this->mutex); + this->position = time; +} + +void DeltaSync::IsPlaying() const +{ + return this->playing; +} diff --git a/source/objects/video/sync/framesync.cpp b/source/objects/video/sync/framesync.cpp new file mode 100644 index 000000000..8c5bfb08a --- /dev/null +++ b/source/objects/video/sync/framesync.cpp @@ -0,0 +1,18 @@ +#include "objects/video/sync/framesync.h" + +using namespace love; + +void FrameSync::CopyState(const FrameSync* other) +{ + this->Seek(other->Tell()); + + if (other->IsPlaying()) + this->Play(); + else + this->Pause(); +} + +double FrameSync::Tell() const +{ + return this->GetPosition(); +} diff --git a/source/objects/video/sync/sourcesync.cpp b/source/objects/video/sync/sourcesync.cpp new file mode 100644 index 000000000..6b96efeba --- /dev/null +++ b/source/objects/video/sync/sourcesync.cpp @@ -0,0 +1,36 @@ +#include "objects/video/sync/sourcesync.h" + +using namespace love; + +SourceSync::SourceSync(Source* source) : source(source) +{} + +double SourceSync::GetPosition() const +{ + return this->source->Tell(Source::UNIT_SECONDS); +} + +void SourceSync::Play() +{ + this->source->Play(); +} + +void SourceSync::Pause() +{ + this->source->Pause(); +} + +void SourceSync::Pause() +{ + this->source->Pause(); +} + +void SourceSync::Seek(double time) +{ + this->source->Seek(time, Source::UNIT_SECONDS); +} + +bool SourceSync::IsPlaying() const +{ + return this->source->IsPlaying(); +} diff --git a/source/objects/video/theora/oggdemuxer.cpp b/source/objects/video/theora/oggdemuxer.cpp new file mode 100644 index 000000000..86e97055f --- /dev/null +++ b/source/objects/video/theora/oggdemuxer.cpp @@ -0,0 +1,238 @@ +#include "objects/video/theora/oggdemuxer.h" + +using namespace love; + +OggDemuxer::OggDemuxer(File* file) : file(file), intiialized(false), serial(0), endOfStream(false) +{ + ogg_sync_init(&this->sync); +} + +OggDemuxer::~OggDemuxer() +{ + if (this->intiialized) + ogg_stream_clear(&this->stream); + + ogg_sync_clear(&this->sync); +} + +bool OggDemuxer::ReadPage(bool errorEOF) +{ + char* syncBuffer = nullptr; + while (ogg_sync_pageout(&this->sync, &this->page) != 1) + { + if (syncBuffer && !this->intiialized && ogg_stream_check(&this->stream)) + throw love::Exception("Invalid stream."); + + syncBuffer = ogg_sync_buffer(&sync, OggDemuxer::SYNC_VALUE); + size_t read = this->file->Read(syncBuffer, OggDemuxer::SYNC_VALUE); + + if (read == 0 && errorEOF) + return false; + + ogg_sync_wrote(&sync, read); + } + + return true; +} + +bool OggDemuxer::ReadPacket(ogg_packet& packet, bool mustSucceed) +{ + if (!this->intiialized) + throw love::Exception("Reading from OggDemuxer before initialization! (engine bug)"); + + while (ogg_stream_packetout(&this->stream, &packet)) + { + do + { + if (ogg_page_serialno(&this->page) == this->serial && ogg_page_eos(&this->page) && + !mustSucceed) + { + return this->endOfStream = true; + } + + this->ReadPage(); + } while (ogg_page_serialno(&this->page) != this->serial); + } + + return this->endOfStream == false; +} + +void OggDemuxer::ReSync() +{ + ogg_sync_reset(&this->sync); + ogg_sync_pageseek(&this->sync, &this->page); + ogg_stream_reset(&this->stream); +} + +bool OggDemuxer::IsEOS() const +{ + return this->endOfStream; +} + +const std::string& OggDemuxer::GetFilename() const +{ + return this->file->GetFilename(); +} + +OggDemuxer::StreamType OggDemuxer::DetermineType() +{ + ogg_packet packet; + if (ogg_stream_packetpeek(&this->stream, &packet)) + return OggDemuxer::TYPE_UNKNOWN; + + if (packet.bytes >= OggDemuxer::THEORA_BYTES_MIN) + { + uint8_t headerType = packet.packet[0]; + if ((headerType & OggDemuxer::THEORA_HEADER_TYPE) && + (strncmp((const char*)packet.packet + 1, "theora", 6) == 0)) + { + return OggDemuxer::TYPE_THEORA; + } + } + + return OggDemuxer::TYPE_UNKNOWN; +} + +OggDemuxer::StreamType OggDemuxer::FindStream() +{ + if (this->intiialized) + { + this->endOfStream = false; + this->intiialized = false; + + this->file->Seek(0); + + ogg_stream_clear(&this->stream); + ogg_sync_reset(&this->sync); + } + + while (true) + { + if (!this->ReadPage(true)) + return OggDemuxer::TYPE_UNKNOWN; + + if (!ogg_page_bos(&this->page)) + break; + + this->serial = ogg_page_serialno(&this->page); + ogg_stream_init(&this->stream, this->serial); + ogg_stream_pagein(&this->stream, &this->page); + + this->intiialized = true; + + StreamType type = this->DetermineType(); + + switch (type) + { + case TYPE_THEORA: + return type; + default: + break; + } + + ogg_stream_clear(&this->stream); + this->intiialized = false; + } + + if (this->intiialized) + { + this->intiialized = false; + ogg_stream_clear(&this->stream); + } + + ogg_sync_reset(&this->sync); + + return OggDemuxer::TYPE_UNKNOWN; +} + +bool OggDemuxer::Seek(ogg_packet& packet, double target, std::function getTime) +{ + this->endOfStream = false; + + if (target < OggDemuxer::REWIND_THRESHOLD) + { + this->file->Seek(0); + this->ReSync(); + this->ReadPacket(packet, true); + + return true; + } + + double low = 0; + double high = this->file->GetSize(); + + /* + ** if we know our current position + ** we can drastically decrease the search area + */ + if (packet.granulepos != -1) + { + double currentTime = getTime(packet.granulepos); + + if (currentTime < target) + low = file->Tell(); + else if (currentTime > target) + high = file->Tell(); + } + + while (high - low > OggDemuxer::REWIND_THRESHOLD) + { + /* determine our next binary search position */ + double position = (high + low) / 2; + this->file->Seek(position); + + /* do a sync */ + this->ReSync(); + + this->ReadPage(); + this->ReadPacket(packet, false); + + if (this->IsEOS()) + { + high = position; + this->endOfStream = false; + + /* fix for single-paged files */ + if (high < OggDemuxer::REWIND_THRESHOLD) + { + this->file->Seek(0); + this->ReSync(); + this->ReadPacket(packet, true); + } + else + continue; + } + + int result = -1; + for (int index = 0; index < ogg_page_packets(&this->page); ++index) + { + if (index > 0) + this->ReadPacket(packet, true); + + double currentTime = getTime(packet.granulepos); + double nextTime = getTime(packet.granulepos + 1); + + if (currentTime == -1) + continue; + else if (currentTime <= target && nextTime > target) + { + result = 0; + break; + } + else if (currentTime > target) + { + result = 1; + break; + } + } + + if (result == 0) + break; + else if (result < 0) + low = position; + else + high = position; + } + + return true; +} diff --git a/source/objects/video/videostream.cpp b/source/objects/video/videostream.cpp new file mode 100644 index 000000000..a2c1c81c1 --- /dev/null +++ b/source/objects/video/videostream.cpp @@ -0,0 +1,40 @@ +#include "objects/video/videostream.h" + +using namespace love; + +love::Type VideoStream::type("VideoStream", &Stream::type); + +void VideoStream::SetSync(love::FrameSync* sync) +{ + this->frameSync = frameSync; +} + +love::FrameSync* VideoStream::GetSync() const +{ + return this->frameSync; +} + +void VideoStream::Play() +{ + this->frameSync->Play(); +} + +void VideoStream::Pause() +{ + this->frameSync->Pause(); +} + +void VideoStream::Seek(double offset) +{ + this->frameSync->Seek(offset); +} + +double VideoStream::Tell() const +{ + return this->frameSync->Tell(); +} + +bool VideoStream::IsPlaying() const +{ + return this->frameSync->IsPlaying(); +} From 9a4bc597dce7f0855bcd995a086bb076bc3fbf1a Mon Sep 17 00:00:00 2001 From: TurtleP Date: Sat, 26 Mar 2022 14:50:55 -0400 Subject: [PATCH 02/19] finish up theorastream on 3ds --- include/objects/video/theora/theorastreamc.h | 15 +- include/objects/video/videostream.h | 15 -- .../3ds/include/objects/video/theorastream.h | 46 ++++ platform/3ds/source/objects/theorastream.cpp | 255 ++++++++++++++++++ source/objects/video/theora/theorastreamc.cpp | 80 ++++++ 5 files changed, 390 insertions(+), 21 deletions(-) create mode 100644 platform/3ds/include/objects/video/theorastream.h create mode 100644 platform/3ds/source/objects/theorastream.cpp create mode 100644 source/objects/video/theora/theorastreamc.cpp diff --git a/include/objects/video/theora/theorastreamc.h b/include/objects/video/theora/theorastreamc.h index 59501b461..c0cf1ce7a 100644 --- a/include/objects/video/theora/theorastreamc.h +++ b/include/objects/video/theora/theorastreamc.h @@ -18,13 +18,13 @@ namespace love::common ~TheoraStream(); - const void* GetFrontBuffer() const; + virtual const void* GetFrontBuffer() const = 0; - size_t GetSize() const; + virtual size_t GetSize() const = 0; void FillBackBuffer(); - bool SwapBuffers(); + virtual bool SwapBuffers() = 0; int GetWidth() const; @@ -36,9 +36,11 @@ namespace love::common bool IsPlaying() const; - void ThreadedFillBackBuffer(double dt); + virtual void ThreadedFillBackBuffer(double dt) = 0; - private: + virtual void DeleteBuffers() = 0; + + protected: OggDemuxer demuxer; bool headerParsed; @@ -53,7 +55,8 @@ namespace love::common double lastFrame; double nextFrame; - void ParseHeader(); + virtual void ParseHeader() = 0; + void SeekDecoder(double target); } } // namespace love::common diff --git a/include/objects/video/videostream.h b/include/objects/video/videostream.h index 730a54c69..766b55fbb 100644 --- a/include/objects/video/videostream.h +++ b/include/objects/video/videostream.h @@ -39,21 +39,6 @@ namespace love virtual FrameSync* GetSync() const; - /* data structure */ - struct Frame - { - Frame(); - - ~Frame(); - - int yw, yh; - unsigned char* yplane; - - int cw, ch; - unsigned char* cbplane; - unsigned char* crplane; - }; - protected: StrongReference frameSync; }; diff --git a/platform/3ds/include/objects/video/theorastream.h b/platform/3ds/include/objects/video/theorastream.h new file mode 100644 index 000000000..5e53b3878 --- /dev/null +++ b/platform/3ds/include/objects/video/theorastream.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "objects/video/theora/theorastreamc.h" + +namespace love +{ + class TheoraStream : common::TheoraStream + { + TheoraStream(File* file); + + struct Frame + { + Frame(); + + ~Frame(); + + C2D_Image image; + C3D_Tex buffer[2]; + + bool currentBuffer; + th_pixel_fmt format; + + int width, height; + }; + + virtual const void* GetFrontBuffer() const override; + + virtual size_t GetSize() const override; + + virtual bool SwapBuffers() override; + + virtual void ThreadedFillBackBuffer(double dt) override; + + virtual void DeleteBuffers() override; + + protected: + virtual void ParseHeader() override; + + private: + Handle handle; + Frame* frame; + Tex3DS_SubTexture subTexture; + }; +} // namespace love diff --git a/platform/3ds/source/objects/theorastream.cpp b/platform/3ds/source/objects/theorastream.cpp new file mode 100644 index 000000000..737b1f4de --- /dev/null +++ b/platform/3ds/source/objects/theorastream.cpp @@ -0,0 +1,255 @@ +#include "common/lmath.h" + +#include "modules/thread/types/lock.h" +#include "objects/video/theorastream.h" + +using namespace love; + +TheoraStream::TheoraStream(File* file) : common::TheoraStream(file) +{ + th_info_init(&this->info); + + this->frame = new Frame(); + + try + { + this->ParseHeader(); + } + catch (love::Exception& exception) + { + delete this->frame; + + th_info_clear(&this->info); + + throw exception; + } +} + +const void* TheoraStream::GetFrontBuffer() const +{ + return this->frame; +} + +size_t TheoraStream::GetSize() const +{ + return sizeof(Frame); +} + +void TheoraStream::ParseHeader() +{ + if (this->headerParsed) + return; + + th_comment comment; + th_setup_info* setupInfo = nullptr; + + th_comment_init(&comment); + int result = 0; + + this->demuxer.ReadPacket(this->packet); + result = th_decode_headerin(&this->info, &comment, &setupInfo, &packet); + + if (result < 0) + { + th_comment_clear(&comment); + throw love::Exception("Could not find theora header."); + } + + while (result > 0) + { + this->demuxer.ReadPacket(packet); + result = th_decode_headerin(&this->info, &comment, &setupInfo, &packet); + } + + th_comment_clear(&comment); + + this->decoder = th_decode_alloc(&this->info, setupInfo); + th_setup_free(setupInfo); + + switch (this->info.pixel_fmt) + { + case TH_PF_420: + break; + case TH_PF_422: + break; + case TH_PF_444: + /* YUV444 is not supported by Y2R */ + return; + case TH_PF_RSVD: + default: + /* UNKNOWN Chroma sampling! */ + return; + } + + this->frame->format = this->info.pixel_fmt; + + int width = ((this->info.pic_x + this->info.frame_width + 1) & ~1) - (this->info.pic_x & ~1); + int height = ((this->info.pic_y + this->info.frame_height + 1) & ~1) - (this->info.pic_y & ~1); + + this->frame->width = width; + this->frame->height = height; + + int powTwoWidth = NextPO2(width); + int powTwoHeight = NextPO2(height); + + for (int index = 0; index < 2; index++) + { + C3D_Tex* curtex = &this->frame->buffer[index]; + + C3D_TexInit(curtex, width, height, GPU_RGB8); + C3D_TexSetFilter(curtex, GPU_LINEAR, GPU_LINEAR); + + memset(curtex->data, 0, curtex->size); + } + + this->subTexture.width = width; + this->subTexture.height = height; + + this->subTexture.left = 0.0f; + this->subTexture.top = 1.0f; + + this->subTexture.right = (float)width / powTwoWidth; + this->subTexture.bottom = 1.0f - ((float)height / powTwoHeight); + + this->headerParsed = true; + th_decode_packetin(this->decoder, &packet, nullptr); +} + +/* to do.. do not do this */ +static inline size_t getFormatComponents(GPU_TEXCOLOR fmt) +{ + switch (fmt) + { + case GPU_RGBA8: + return 4; + case GPU_RGB8: + return 3; + default: + return 0; + } +} + +void TheoraStream::ThreadedFillBackBuffer(double dt) +{ + this->frameSync->Update(dt); + double position = this->frameSync->GetPosition(); + + if (position < lastFrame) + this->SeekDecoder(position); + + th_ycbcr_buffer bufferInfo; + bool hasFrame = false; + + size_t framesBehind = 0; + bool failedSeek = false; + + while (!this->demuxer.IsEOS() && position >= this->nextFrame) + { + if (framesBehind++ > 5 && !failedSeek) + { + this->SeekDecoder(position); + framesBehind = 0; + failedSeek = true; + } + + th_decode_ycbcr_out(decoder, bufferInfo); + hasFrame = true; + + ogg_int64_t granulePosition = -1; + do + { + if (this->demuxer.ReadPacket(packet)) + return; + } while (th_decode_packetin(this->decoder, &packet, &granulePosition) != 0); + + this->lastFrame = this->nextFrame; + this->nextFrame = th_granule_time(this->decoder, granulePosition); + } + + if (hasFrame) + { + { + thread::Lock lock(this->bufferMutex); + this->frameReady = false; + } + + bool isBusy = true; + bool drawBuffer = !this->frame->currentBuffer; + + C3D_Tex* writeFrame = &this->frame->buffer[drawBuffer]; + if (!bufferInfo[0].data || !bufferInfo[1].data || !bufferInfo[2].data) + return; + + Y2RU_StopConversion(); + + if (isBusy) + Y2RU_IsBusyConversion(&isBusy); + + switch (this->frame->format) + { + case TH_PF_420: + Y2RU_SetInputFormat(Y2RU_InputFormat::INPUT_YUV420_INDIV_8); + break; + case TH_PF_422: + default: + Y2RU_SetInputFormat(Y2RU_InputFormat::INPUT_YUV422_INDIV_8); + break; + } + + Y2RU_SetOutputFormat(Y2RU_OutputFormat::OUTPUT_RGB_24); + Y2RU_SetRotation(Y2RU_Rotation::ROTATION_NONE); + Y2RU_SetBlockAlignment(Y2RU_BlockAlignment::BLOCK_8_BY_8); + Y2RU_SetTransferEndInterrupt(true); + Y2RU_SetInputLineWidth(this->frame->width); + Y2RU_SetInputLines(this->frame->height); + Y2RU_SetStandardCoefficient(Y2RU_StandardCoefficient::COEFFICIENT_ITU_R_BT_601_SCALING); + Y2RU_SetAlpha(0xFF); + + /* set up the YUV datafor Y2RU */ + + Y2RU_SetSendingY(bufferInfo[0].data, this->frame->width * this->frame->height, + this->frame->width, bufferInfo[0].stride - this->frame->width); + + Y2RU_SetSendingU(bufferInfo[1].data, (this->frame->width / 2) * (this->frame->height / 2), + this->frame->width / 2, bufferInfo[1].stride - (this->frame->width >> 1)); + + Y2RU_SetSendingV(bufferInfo[2].data, (this->frame->width / 2) * (this->frame->height / 2), + this->frame->width / 2, bufferInfo[2].stride - (this->frame->width >> 1)); + + size_t formatSize = getFormatComponents(writeFrame->fmt); + + Y2RU_SetReceiving(writeFrame->data, this->frame->width * this->frame->height * formatSize, + this->frame->width * 8 * formatSize, + (NextPO2(this->frame->width) - this->frame->width) * 8 * formatSize); + + /* convert the data */ + + Y2RU_StartConversion(); + + Y2RU_GetTransferEndEvent(&this->handle); + + { + thread::Lock lock(this->bufferMutex); + this->frameReady = true; + } + } +} + +bool TheoraStream::SwapBuffers() +{ + if (this->demuxer.IsEOS()) + return false; + + if (!this->frameSync->IsPlaying()) + return false; + + thread::Lock lock(this->bufferMutex); + if (!this->frameReady) + return false; + + this->frameReady = false; + + this->frame->currentBuffer = !this->frame->currentBuffer; + + return true; +} diff --git a/source/objects/video/theora/theorastreamc.cpp b/source/objects/video/theora/theorastreamc.cpp new file mode 100644 index 000000000..c1e738867 --- /dev/null +++ b/source/objects/video/theora/theorastreamc.cpp @@ -0,0 +1,80 @@ +#include "objects/video/theora/theorastreamc.h" +#include "objects/video/sync/deltasync.h" + +#include "modules/thread/types/lock.h" + +using namespace love::common; +using love::thread::Lock; + +TheoraStream::TheoraStream(File* file) : + demuxer(file), + headerParsed(false), + decoder(nullptr), + frameReady(false), + lastFrame(0), + nextFrame(0) +{ + if (demuxer.FindStream() != OggDemuxer::TYPE_THEORA) + throw love::Exception("Invalid video file, video is not theora."); + + this->frameSync.Set(new DeltaSync(), Acquire::NORETAIN); +} + +TheoraStream::~TheoraStream() +{ + if (this->decoder) + th_decode_free(this->decoder); + + th_info_clear(&this->info); + + this->DeleteBuffers(); +} + +int TheoraStream::GetWidth() const +{ + if (this->headerParsed) + return this->info.pic_width; + + return 0; +} + +int TheoraStream::GetHeight() const +{ + if (this->headerParsed) + return this->info.pic_height; + + return 0; +} + +const std::string& TheoraStream::GetFilename() const +{ + return this->demuxer.GetFilename(); +} + +void TheoraStream::FillBackBuffer() +{} + +void TheoraStream::SeekDecoder(double target) +{ + bool success = this->demuxer.Seek(packet, target, [this](int64_t granulepos) { + return th_granule_time(this->decoder, granulepos); + }); + + if (!success) + return; + + this->lastFrame = this->nextFrame = -1; + th_decode_ctl(this->decoder, TH_DECCTL_SET_GRANPOS, &packet.granulepos, + sizeof(packet.granulepos)); +} + +void TheoraStream::SetSync(FrameSync* other) +{ + Lock lock(this->bufferMutex); + this->frameSync = other; +} + +bool TheoraStream::IsPlaying() const +{ + return this->frameSync->IsPlaying() && !this->demuxer.IsEOS(); +} From 282ec8a8aa2f985db9f9d5fd1320d37cbaeea085 Mon Sep 17 00:00:00 2001 From: TurtleP Date: Sat, 26 Mar 2022 15:49:33 -0400 Subject: [PATCH 03/19] start working on the worker thread for videos --- include/common/delay.h | 6 ++ include/common/module.h | 1 + include/modules/timer/timerc.h | 2 + include/modules/video/video.h | 31 ++++++++ include/modules/video/videomodule.h | 10 --- include/modules/video/worker.h | 33 ++++++++ .../{wrap_videomodule.h => wrap_video.h} | 2 +- include/objects/video/sync/sourcesync.h | 2 +- include/objects/video/theora/theorastreamc.h | 5 +- include/objects/video/utility/stream.h | 2 +- .../3ds/include/objects/video/theorastream.h | 9 ++- platform/switch/include/modules/timer/timer.h | 2 +- source/common/delay.cpp | 17 ++++ source/modules/timer/timerc.cpp | 4 +- source/modules/video/video.cpp | 1 + source/modules/video/worker.cpp | 77 +++++++++++++++++++ source/objects/video/sync/deltasync.cpp | 2 +- source/objects/video/sync/sourcesync.cpp | 5 -- source/objects/video/theora/theorastreamc.cpp | 2 - source/objects/video/utility/stream.cpp | 5 ++ 20 files changed, 188 insertions(+), 30 deletions(-) create mode 100644 include/common/delay.h create mode 100644 include/modules/video/video.h delete mode 100644 include/modules/video/videomodule.h create mode 100644 include/modules/video/worker.h rename include/modules/video/{wrap_videomodule.h => wrap_video.h} (80%) create mode 100644 source/common/delay.cpp create mode 100644 source/modules/video/video.cpp create mode 100644 source/modules/video/worker.cpp create mode 100644 source/objects/video/utility/stream.cpp diff --git a/include/common/delay.h b/include/common/delay.h new file mode 100644 index 000000000..833da47dc --- /dev/null +++ b/include/common/delay.h @@ -0,0 +1,6 @@ +#pragma once + +namespace love +{ + void Sleep(unsigned seconds); +} diff --git a/include/common/module.h b/include/common/module.h index c13daa6a2..2e5cb0a41 100644 --- a/include/common/module.h +++ b/include/common/module.h @@ -31,6 +31,7 @@ namespace love M_TIMER, M_TOUCH, M_WINDOW, + M_VIDEO, M_MAX_ENUM }; diff --git a/include/modules/timer/timerc.h b/include/modules/timer/timerc.h index ebb418383..0380006bb 100644 --- a/include/modules/timer/timerc.h +++ b/include/modules/timer/timerc.h @@ -12,6 +12,8 @@ namespace love::common class Timer : public Module { public: + static constexpr auto SLEEP_DURATION = 1000000ULL; + Timer(); ModuleType GetModuleType() const diff --git a/include/modules/video/video.h b/include/modules/video/video.h new file mode 100644 index 000000000..0928e749c --- /dev/null +++ b/include/modules/video/video.h @@ -0,0 +1,31 @@ +#pragma once + +#include "common/module.h" +#include "objects/file/file.h" +#include "objects/video/videostream.h" + +namespace love +{ + class Worker; + + class Video : public Module + { + virtual ~Video() + {} + + virtual const char* GetName() const + { + return "love.video"; + } + + virtual ModuleType GetModuleType() const + { + return M_VIDEO; + } + + VideoStream* NewVideoStream(File* file); + + private: + Worker* workerThread; + }; +} // namespace love diff --git a/include/modules/video/videomodule.h b/include/modules/video/videomodule.h deleted file mode 100644 index 099805464..000000000 --- a/include/modules/video/videomodule.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#include "common/module.h" - -namespace love -{ - class VideoModule : public Module - { - }; -} // namespace love diff --git a/include/modules/video/worker.h b/include/modules/video/worker.h new file mode 100644 index 000000000..05dda61ac --- /dev/null +++ b/include/modules/video/worker.h @@ -0,0 +1,33 @@ +#pragma once + +#include "modules/thread/types/conditional.h" +#include "modules/thread/types/threadable.h" + +#include "objects/video/theorastream.h" +#include "objects/video/utility/stream.h" + +#include + +namespace love +{ + class Worker : public Threadable + { + public: + Worker(); + virtual ~Worker(); + + void ThreadFunction(); + + void AddStream(TheoraStream* stream); + + void Stop(); + + private: + std::vector> streams; + + thread::MutexRef mutex; + thread::ConditionalRef condition; + + bool stopping; + }; +} // namespace love diff --git a/include/modules/video/wrap_videomodule.h b/include/modules/video/wrap_video.h similarity index 80% rename from include/modules/video/wrap_videomodule.h rename to include/modules/video/wrap_video.h index e44cb4b07..219e02030 100644 --- a/include/modules/video/wrap_videomodule.h +++ b/include/modules/video/wrap_video.h @@ -1,7 +1,7 @@ #pragma once #include "common/luax.h" -#include "modules/video/videomodule.h" +#include "modules/video/video.h" namespace Wrap_VideoModule { diff --git a/include/objects/video/sync/sourcesync.h b/include/objects/video/sync/sourcesync.h index 74ba6d386..17447c43f 100644 --- a/include/objects/video/sync/sourcesync.h +++ b/include/objects/video/sync/sourcesync.h @@ -24,5 +24,5 @@ namespace love private: StrongReference source; - } + }; } // namespace love diff --git a/include/objects/video/theora/theorastreamc.h b/include/objects/video/theora/theorastreamc.h index c0cf1ce7a..1a1545fc5 100644 --- a/include/objects/video/theora/theorastreamc.h +++ b/include/objects/video/theora/theorastreamc.h @@ -5,6 +5,7 @@ #include #include "modules/thread/types/mutex.h" + #include "objects/file/file.h" #include "objects/video/theora/oggdemuxer.h" #include "objects/video/videostream.h" @@ -38,8 +39,6 @@ namespace love::common virtual void ThreadedFillBackBuffer(double dt) = 0; - virtual void DeleteBuffers() = 0; - protected: OggDemuxer demuxer; bool headerParsed; @@ -58,5 +57,5 @@ namespace love::common virtual void ParseHeader() = 0; void SeekDecoder(double target); - } + }; } // namespace love::common diff --git a/include/objects/video/utility/stream.h b/include/objects/video/utility/stream.h index 7d0c6b135..d151ff1ed 100644 --- a/include/objects/video/utility/stream.h +++ b/include/objects/video/utility/stream.h @@ -20,5 +20,5 @@ namespace love virtual size_t GetSize() const = 0; virtual bool SwapBuffers() = 0; - } + }; } // namespace love diff --git a/platform/3ds/include/objects/video/theorastream.h b/platform/3ds/include/objects/video/theorastream.h index 5e53b3878..48586fd3c 100644 --- a/platform/3ds/include/objects/video/theorastream.h +++ b/platform/3ds/include/objects/video/theorastream.h @@ -8,8 +8,14 @@ namespace love { class TheoraStream : common::TheoraStream { + public: TheoraStream(File* file); + ~TheoraStream() + { + delete this->frame; + } + struct Frame { Frame(); @@ -33,13 +39,12 @@ namespace love virtual void ThreadedFillBackBuffer(double dt) override; - virtual void DeleteBuffers() override; - protected: virtual void ParseHeader() override; private: Handle handle; + Frame* frame; Tex3DS_SubTexture subTexture; }; diff --git a/platform/switch/include/modules/timer/timer.h b/platform/switch/include/modules/timer/timer.h index 8b84c98c8..03229882d 100644 --- a/platform/switch/include/modules/timer/timer.h +++ b/platform/switch/include/modules/timer/timer.h @@ -12,4 +12,4 @@ namespace love virtual ~Timer() {} }; -} // namespace love \ No newline at end of file +} // namespace love diff --git a/source/common/delay.cpp b/source/common/delay.cpp new file mode 100644 index 000000000..c729ee5e2 --- /dev/null +++ b/source/common/delay.cpp @@ -0,0 +1,17 @@ +#if defined(__3DS__) + #include <3ds.h> +#elif defined(__SWITCH__) + #include +#endif + +#include "modules/timer/timerc.h" + +using namespace love; + +void Sleep(unsigned seconds) +{ + u32 milliseconds = seconds * 1000.0f; + u64 nanoSeconds = milliseconds * common::Timer::SLEEP_DURATION; + + svcSleepThread(nanoSeconds); +} diff --git a/source/modules/timer/timerc.cpp b/source/modules/timer/timerc.cpp index aead329fe..d1a81d9db 100644 --- a/source/modules/timer/timerc.cpp +++ b/source/modules/timer/timerc.cpp @@ -4,8 +4,6 @@ using namespace love::common; // Löve2D Functions -static constexpr auto SLEEP_DURATION = 1000000ULL; - #if defined(__3DS__) #include <3ds.h> #elif defined(__SWITCH__) @@ -48,7 +46,7 @@ void Timer::Sleep(float seconds) if (seconds >= 0) { u32 milliseconds = seconds * 1000.0f; - u64 nanoSeconds = milliseconds * SLEEP_DURATION; + u64 nanoSeconds = milliseconds * Timer::SLEEP_DURATION; svcSleepThread(nanoSeconds); } diff --git a/source/modules/video/video.cpp b/source/modules/video/video.cpp new file mode 100644 index 000000000..7db03b878 --- /dev/null +++ b/source/modules/video/video.cpp @@ -0,0 +1 @@ +#include "modules/video/video.h" diff --git a/source/modules/video/worker.cpp b/source/modules/video/worker.cpp new file mode 100644 index 000000000..d22be6d1b --- /dev/null +++ b/source/modules/video/worker.cpp @@ -0,0 +1,77 @@ +#include "modules/video/worker.h" + +#include "modules/thread/types/lock.h" +#include "objects/thread/thread.h" + +#include "common/delay.h" +#include "modules/timer/timer.h" + +using namespace love; + +Worker::Worker() : stopping(false) +{ + this->threadName = "VideoWorker"; +} + +Worker::~Worker() +{ + this->Stop(); +} + +void Worker::AddStream(TheoraStream* stream) +{ + thread::Lock lock(this->mutex); + this->streams.push_back(stream); + + this->condition->Broadcast(); +} + +void Worker::Stop() +{ + { + thread::Lock lock(this->mutex); + this->stopping = true; + this->condition->Broadcast(); + } + + this->owner->Wait(); +} + +void Worker::ThreadFunction() +{ + double lastFrame = Timer::GetTime(); + + while (true) + { + love::Sleep(2); + + thread::Lock lock(this->mutex); + + while (!this->stopping && this->streams.empty()) + { + this->condition->Wait(this->mutex); + lastFrame = Timer::GetTime(); + } + + if (this->stopping) + return; + + double currentFrame = Timer::GetTime(); + double delta = currentFrame - lastFrame; + + lastFrame = currentFrame; + + for (auto it = this->streams.begin(); it != this->streams.end(); ++it) + { + TheoraStream* stream = *it; + + if (stream->GetReferenceCount() == 1) + { + this->streams.erase(it); + break; + } + + stream->ThreadedFillBackBuffer(delta); + } + } +} diff --git a/source/objects/video/sync/deltasync.cpp b/source/objects/video/sync/deltasync.cpp index 446f30600..e0248a997 100644 --- a/source/objects/video/sync/deltasync.cpp +++ b/source/objects/video/sync/deltasync.cpp @@ -38,7 +38,7 @@ void DeltaSync::Seek(double time) this->position = time; } -void DeltaSync::IsPlaying() const +bool DeltaSync::IsPlaying() const { return this->playing; } diff --git a/source/objects/video/sync/sourcesync.cpp b/source/objects/video/sync/sourcesync.cpp index 6b96efeba..c2c891643 100644 --- a/source/objects/video/sync/sourcesync.cpp +++ b/source/objects/video/sync/sourcesync.cpp @@ -20,11 +20,6 @@ void SourceSync::Pause() this->source->Pause(); } -void SourceSync::Pause() -{ - this->source->Pause(); -} - void SourceSync::Seek(double time) { this->source->Seek(time, Source::UNIT_SECONDS); diff --git a/source/objects/video/theora/theorastreamc.cpp b/source/objects/video/theora/theorastreamc.cpp index c1e738867..f308011b7 100644 --- a/source/objects/video/theora/theorastreamc.cpp +++ b/source/objects/video/theora/theorastreamc.cpp @@ -26,8 +26,6 @@ TheoraStream::~TheoraStream() th_decode_free(this->decoder); th_info_clear(&this->info); - - this->DeleteBuffers(); } int TheoraStream::GetWidth() const diff --git a/source/objects/video/utility/stream.cpp b/source/objects/video/utility/stream.cpp new file mode 100644 index 000000000..33cf79ce1 --- /dev/null +++ b/source/objects/video/utility/stream.cpp @@ -0,0 +1,5 @@ +#include "objects/video/utility/stream.h" + +using namespace love; + +love::Type Stream::type("Stream", &Object::type); From 196fd4d510eaa2d6ad207135ff03b3b4c9822243 Mon Sep 17 00:00:00 2001 From: TurtleP Date: Sun, 27 Mar 2022 11:25:42 -0400 Subject: [PATCH 04/19] video module --- Makefile | 1 + include/common/delay.h | 2 +- include/modules/video/video.h | 6 +- include/modules/video/wrap_video.h | 6 +- include/objects/video/sync/sourcesync.h | 2 +- include/objects/video/wrap_videostream.h | 27 ++++ .../3ds/include/objects/video/theorastream.h | 2 +- platform/3ds/source/objects/theorastream.cpp | 14 ++ platform/3ds/source/objects/videostream.cpp | 5 - platform/3ds/source/runtime.cpp | 4 + source/common/delay.cpp | 5 +- source/modules/love/love.cpp | 2 + source/modules/love/scripts/boot.lua | 1 + source/modules/video/video.cpp | 22 +++ source/modules/video/wrap_video.cpp | 62 +++++++++ source/objects/video/wrap_videostream.cpp | 128 ++++++++++++++++++ 16 files changed, 273 insertions(+), 16 deletions(-) delete mode 100644 platform/3ds/source/objects/videostream.cpp create mode 100644 source/modules/video/wrap_video.cpp create mode 100644 source/objects/video/wrap_videostream.cpp diff --git a/Makefile b/Makefile index 63899b1a5..b3963e24e 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ export LOVE_VERSION = 11.4.0 #----------------------------------- export LOVE_PORTLIBS = -lmodplug -lvorbisidec -lFLAC -lvorbisidec -logg LOVE_PORTLIBS += -lphysfs -llz4 -lz -lbox2d -ljpeg -lpng `curl-config --libs` +LOVE_PORTLIBS += -ltheora #------------------------------------ # Common configuration for consoles diff --git a/include/common/delay.h b/include/common/delay.h index 833da47dc..3ce116d71 100644 --- a/include/common/delay.h +++ b/include/common/delay.h @@ -2,5 +2,5 @@ namespace love { - void Sleep(unsigned seconds); + void Sleep(float seconds); } diff --git a/include/modules/video/video.h b/include/modules/video/video.h index 0928e749c..ef36f3aa4 100644 --- a/include/modules/video/video.h +++ b/include/modules/video/video.h @@ -10,8 +10,10 @@ namespace love class Video : public Module { - virtual ~Video() - {} + public: + Video(); + + virtual ~Video(); virtual const char* GetName() const { diff --git a/include/modules/video/wrap_video.h b/include/modules/video/wrap_video.h index 219e02030..1a99c10af 100644 --- a/include/modules/video/wrap_video.h +++ b/include/modules/video/wrap_video.h @@ -3,9 +3,9 @@ #include "common/luax.h" #include "modules/video/video.h" -namespace Wrap_VideoModule +namespace Wrap_Video { - int NewVideo(lua_State* L); + int NewVideoStream(lua_State* L); int Register(lua_State* L); -} // namespace Wrap_VideoModule +} // namespace Wrap_Video diff --git a/include/objects/video/sync/sourcesync.h b/include/objects/video/sync/sourcesync.h index 17447c43f..961061297 100644 --- a/include/objects/video/sync/sourcesync.h +++ b/include/objects/video/sync/sourcesync.h @@ -7,7 +7,7 @@ namespace love { - class SourceSync : FrameSync + class SourceSync : public FrameSync { public: SourceSync(Source* source); diff --git a/include/objects/video/wrap_videostream.h b/include/objects/video/wrap_videostream.h index e69de29bb..8cfeb6d79 100644 --- a/include/objects/video/wrap_videostream.h +++ b/include/objects/video/wrap_videostream.h @@ -0,0 +1,27 @@ +#pragma once + +#include "common/luax.h" +#include "objects/video/videostream.h" + +namespace Wrap_VideoStream +{ + int SetSync(lua_State* L); + + int GetFilename(lua_State* L); + + int Play(lua_State* L); + + int Pause(lua_State* L); + + int Seek(lua_State* L); + + int Rewind(lua_State* L); + + int Tell(lua_State* L); + + int IsPlaying(lua_State* L); + + love::VideoStream* CheckVideoStream(lua_State* L, int index); + + int Register(lua_State* L); +} // namespace Wrap_VideoStream diff --git a/platform/3ds/include/objects/video/theorastream.h b/platform/3ds/include/objects/video/theorastream.h index 48586fd3c..5b6f3db33 100644 --- a/platform/3ds/include/objects/video/theorastream.h +++ b/platform/3ds/include/objects/video/theorastream.h @@ -6,7 +6,7 @@ namespace love { - class TheoraStream : common::TheoraStream + class TheoraStream : public common::TheoraStream { public: TheoraStream(File* file); diff --git a/platform/3ds/source/objects/theorastream.cpp b/platform/3ds/source/objects/theorastream.cpp index 737b1f4de..023ad0008 100644 --- a/platform/3ds/source/objects/theorastream.cpp +++ b/platform/3ds/source/objects/theorastream.cpp @@ -5,6 +5,20 @@ using namespace love; +TheoraStream::Frame::Frame() +{} + +TheoraStream::Frame::~Frame() +{ + Y2RU_StopConversion(); + + if (this->buffer[0].data) + { + C3D_TexDelete(&this->buffer[0]); + C3D_TexDelete(&this->buffer[1]); + } +} + TheoraStream::TheoraStream(File* file) : common::TheoraStream(file) { th_info_init(&this->info); diff --git a/platform/3ds/source/objects/videostream.cpp b/platform/3ds/source/objects/videostream.cpp deleted file mode 100644 index 8627c4b89..000000000 --- a/platform/3ds/source/objects/videostream.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "objects/video/videostream.h" -#include "modules/thread/types/lock.h" - -using namespace love; -using thread::Lock; diff --git a/platform/3ds/source/runtime.cpp b/platform/3ds/source/runtime.cpp index d320953a6..51324ea93 100644 --- a/platform/3ds/source/runtime.cpp +++ b/platform/3ds/source/runtime.cpp @@ -33,6 +33,8 @@ extern "C" SOCKET_BUFFER = (u32*)memalign(BUFFER_ALIGN, SOC_BUFSIZE); R_ABORT_LAMBDA_UNLESS(socInit(SOCKET_BUFFER, SOC_BUFSIZE), [&]() { free(SOCKET_BUFFER); }); + R_ABORT_UNLESS(y2rInit()); + /* accelerometer */ HIDUSER_EnableAccelerometer(); @@ -45,6 +47,8 @@ extern "C" void userAppExit() { + y2rExit(); + HIDUSER_DisableGyroscope(); HIDUSER_DisableAccelerometer(); diff --git a/source/common/delay.cpp b/source/common/delay.cpp index c729ee5e2..3391954fc 100644 --- a/source/common/delay.cpp +++ b/source/common/delay.cpp @@ -4,11 +4,10 @@ #include #endif +#include "common/delay.h" #include "modules/timer/timerc.h" -using namespace love; - -void Sleep(unsigned seconds) +void love::Sleep(float seconds) { u32 milliseconds = seconds * 1000.0f; u64 nanoSeconds = milliseconds * common::Timer::SLEEP_DURATION; diff --git a/source/modules/love/love.cpp b/source/modules/love/love.cpp index b94795c63..95937453e 100644 --- a/source/modules/love/love.cpp +++ b/source/modules/love/love.cpp @@ -19,6 +19,7 @@ #include "modules/thread/wrap_threadmodule.h" #include "modules/timer/wrap_timer.h" #include "modules/touch/wrap_touch.h" +#include "modules/video/wrap_video.h" #include "modules/window/wrap_window.h" #include "https/common/HTTPSCommon.h" @@ -62,6 +63,7 @@ static constexpr luaL_Reg modules[] = { "love.thread", Wrap_ThreadModule::Register }, { "love.timer", Wrap_Timer::Register }, { "love.touch", Wrap_Touch::Register }, + { "love.video", Wrap_Video::Register }, { "love.window", Wrap_Window::Register }, { "love.nogame", love::NoGame }, { "love.arg", love::LoadArgs }, diff --git a/source/modules/love/scripts/boot.lua b/source/modules/love/scripts/boot.lua index d329b52ce..e4e20c534 100644 --- a/source/modules/love/scripts/boot.lua +++ b/source/modules/love/scripts/boot.lua @@ -259,6 +259,7 @@ function love.init() "image", "font", "window", + "video", "graphics", "math", "physics", diff --git a/source/modules/video/video.cpp b/source/modules/video/video.cpp index 7db03b878..b71a2d69f 100644 --- a/source/modules/video/video.cpp +++ b/source/modules/video/video.cpp @@ -1 +1,23 @@ #include "modules/video/video.h" +#include "modules/video/worker.h" + +using namespace love; + +Video::Video() +{ + this->workerThread = new Worker(); + this->workerThread->Start(); +} + +Video::~Video() +{ + delete this->workerThread; +} + +VideoStream* Video::NewVideoStream(File* file) +{ + TheoraStream* stream = new TheoraStream(file); + this->workerThread->AddStream(stream); + + return stream; +} diff --git a/source/modules/video/wrap_video.cpp b/source/modules/video/wrap_video.cpp new file mode 100644 index 000000000..fd33e44f1 --- /dev/null +++ b/source/modules/video/wrap_video.cpp @@ -0,0 +1,62 @@ +#include "modules/video/wrap_video.h" +#include "objects/video/wrap_videostream.h" + +#include "modules/filesystem/wrap_filesystem.h" +#include "objects/file/file.h" +#include "objects/video/videostream.h" + +using namespace love; + +#define instance() (Module::GetInstance