summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2025-02-04 21:05:18 -0500
committerAndrew Opalach <andrew@akon.city> 2025-02-04 21:05:18 -0500
commit0060d4f3df9b264e4a4adb77da67336709b41b6b (patch)
treea59d4e2f218cec89af56bdb2036d0a2ecd488349
parentcdd1f574312722ab305cba6771d9bcc5da0b0c38 (diff)
downloadcamu-0060d4f3df9b264e4a4adb77da67336709b41b6b.tar.gz
camu-0060d4f3df9b264e4a4adb77da67336709b41b6b.tar.bz2
camu-0060d4f3df9b264e4a4adb77da67336709b41b6b.zip
Audio buffer and sink fixes, unsigned width/height
- Screen feature testing - Older FFmpeg support - Build configuration fixes Signed-off-by: Andrew Opalach <andrew@akon.city>
-rw-r--r--.gitignore3
-rw-r--r--README.md1
-rw-r--r--README.txt19
-rw-r--r--cross/i686-w64-mingw32.txt1
-rw-r--r--cross/x86_64-w64-mingw32.txt3
-rw-r--r--flake.lock18
-rw-r--r--meson.build12
-rw-r--r--meson_options.txt4
-rw-r--r--src/buffer/audio.c147
-rw-r--r--src/buffer/audio.h4
-rw-r--r--src/buffer/frame_queue.h2
-rw-r--r--src/buffer/video.c79
-rw-r--r--src/buffer/video.h2
-rw-r--r--src/codec/codec.h10
-rw-r--r--src/codec/ffmpeg/common.c10
-rw-r--r--src/codec/ffmpeg/common.h5
-rw-r--r--src/codec/ffmpeg/decoder.c5
-rw-r--r--src/codec/ffmpeg/packet_ext.c6
-rw-r--r--src/codec/ffmpeg/resampler.c8
-rw-r--r--src/codec/ffmpeg/scaler.c6
-rw-r--r--src/codec/stb_image/client.c7
-rw-r--r--src/codec/stb_image/server.c4
-rw-r--r--src/fruits/cmsrv/cmsrv.c2
-rw-r--r--src/fruits/cmv/cmv.c2
-rw-r--r--src/liana/client.c9
-rw-r--r--src/liana/client.h14
-rw-r--r--src/liana/handler.h38
-rw-r--r--src/liana/handlers/cdio.h7
-rw-r--r--src/liana/handlers/cdio_client.c2
-rw-r--r--src/liana/handlers/cdio_server.c2
-rw-r--r--src/liana/handlers/codec.h7
-rw-r--r--src/liana/handlers/codec_client.c16
-rw-r--r--src/liana/handlers/codec_server.c6
-rw-r--r--src/liana/handlers/dvd.h11
-rw-r--r--src/liana/handlers/dvd_server.c2
-rw-r--r--src/liana/server.c22
-rw-r--r--src/libsink/common.h2
-rw-r--r--src/libsink/sink.c230
-rw-r--r--src/libsink/sink.h5
-rw-r--r--src/mixer/audio_miniaudio.c19
-rw-r--r--src/render/meson.build75
-rw-r--r--src/render/queue_libplacebo.c9
-rw-r--r--src/render/queue_tiger.c6
-rw-r--r--src/render/renderer.h6
-rw-r--r--src/render/renderer_libplacebo.c49
-rw-r--r--src/render/renderer_libplacebo.h1
-rw-r--r--src/render/renderer_tiger.c39
-rw-r--r--src/screen/screen.c158
-rw-r--r--src/screen/screen.h14
-rw-r--r--src/screen/view.c29
-rw-r--r--src/screen/view.h15
-rw-r--r--src/server/db.c10
-rw-r--r--src/server/server.c4
-rw-r--r--src/server/server.h2
-rw-r--r--subprojects/ffmpeg.wrap4
-rw-r--r--subprojects/libalabaster.wrap2
-rw-r--r--subprojects/stela.wrap2
57 files changed, 692 insertions, 485 deletions
diff --git a/.gitignore b/.gitignore
index 6a76bd2..0d17fc6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,5 @@
**/__pycache__/
+*.html
.cache/
build*/
compile_commands.json
@@ -72,3 +73,5 @@ subprojects/wayland-protocols/
subprojects/wlr-protocols.wrap
subprojects/wlr-protocols/
subprojects/wuffs-*/
+subprojects/zlib-*/
+subprojects/zlib.wrap
diff --git a/README.md b/README.md
deleted file mode 100644
index fdaad82..0000000
--- a/README.md
+++ /dev/null
@@ -1 +0,0 @@
-## camu
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..a833268
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,19 @@
+:source-highlighter: pygments
+
+== camu
+
+=== Simple Build and Test
+[source,sh]
+----
+(optional) $ nix develop .
+$ meson setup build
+$ cd build && ninja
+$ ./src/fruits/cmv/cmv [path_to_file]
+----
+
+==== Binds
+*Left/Right Arrow* - Next/Previous +
+*Space* - Pause/Resume +
+*Control + Left Click* - Seek to percentage of screen width (temp) +
+
+// vim: set syntax=asciidoc:
diff --git a/cross/i686-w64-mingw32.txt b/cross/i686-w64-mingw32.txt
index 3e2392f..0153449 100644
--- a/cross/i686-w64-mingw32.txt
+++ b/cross/i686-w64-mingw32.txt
@@ -3,6 +3,7 @@ c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-gcc-ar'
strip = 'i686-w64-mingw32-strip'
+windres = 'i686-w64-mingw32-windres'
exe_wrapper = 'wine'
[properties]
diff --git a/cross/x86_64-w64-mingw32.txt b/cross/x86_64-w64-mingw32.txt
index 55fc1e6..aa0e051 100644
--- a/cross/x86_64-w64-mingw32.txt
+++ b/cross/x86_64-w64-mingw32.txt
@@ -1,10 +1,11 @@
-# ubuntu: sudo apt install mingw-w64 mingw-w64-tools binutils-mingw-w64 meson cmake nasm libz-mingw-w64-dev
+# ubuntu/debian: sudo apt install mingw-w64 mingw-w64-tools binutils-mingw-w64 nasm meson cmake libz-mingw-w64-dev
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-gcc-ar'
strip = 'x86_64-w64-mingw32-strip'
+windres = 'x86_64-w64-mingw32-windres'
exe_wrapper = 'wine64'
[properties]
diff --git a/flake.lock b/flake.lock
index aa28dca..a6b8fac 100644
--- a/flake.lock
+++ b/flake.lock
@@ -23,11 +23,11 @@
]
},
"locked": {
- "lastModified": 1738178313,
- "narHash": "sha256-/8TLf6LkXGRGERzcWMNDeXjYaHSbexmfV+ofheo7K6k=",
+ "lastModified": 1738709900,
+ "narHash": "sha256-8Bo5xFlCH5q72ExvAnH7TzStMlLZldKOSLMClRSfmTc=",
"owner": "nix-community",
"repo": "home-manager",
- "rev": "420a0d9506b5dac4d86a68b9ef8e763624ad86c6",
+ "rev": "f2d32e46fac9d51da6912948ae1156044c71774b",
"type": "github"
},
"original": {
@@ -39,11 +39,11 @@
},
"nixos-hardware": {
"locked": {
- "lastModified": 1737751639,
- "narHash": "sha256-ZEbOJ9iT72iwqXsiEMbEa8wWjyFvRA9Ugx8utmYbpz4=",
+ "lastModified": 1738638143,
+ "narHash": "sha256-ZYMe4c4OCtIUBn5hx15PEGr0+B1cNEpl2dsaLxwY2W0=",
"owner": "NixOS",
"repo": "nixos-hardware",
- "rev": "dfad538f751a5aa5d4436d9781ab27a6128ec9d4",
+ "rev": "9bdd53f5908453e4d03f395eb1615c3e9a351f70",
"type": "github"
},
"original": {
@@ -91,11 +91,11 @@
},
"nixpkgs_2": {
"locked": {
- "lastModified": 1738021509,
- "narHash": "sha256-JNUiceGsr7cVBUQxLBF1ILCe99E0qLxsVuet6GsZUuw=",
+ "lastModified": 1738680400,
+ "narHash": "sha256-ooLh+XW8jfa+91F1nhf9OF7qhuA/y1ChLx6lXDNeY5U=",
"owner": "NixOS",
"repo": "nixpkgs",
- "rev": "9db269672dbdbb519e0bd3ea24f01506c135e46f",
+ "rev": "799ba5bffed04ced7067a91798353d360788b30d",
"type": "github"
},
"original": {
diff --git a/meson.build b/meson.build
index c04e6fa..1e6e42a 100644
--- a/meson.build
+++ b/meson.build
@@ -11,9 +11,9 @@ is_windows = host_machine.system() == 'windows'
is_android = host_machine.system() == 'android'
alabaster = subproject('libalabaster', default_options: ['tests=false'])
-naunet_opts = []
-if not get_option('sink-only')
- naunet_opts += ['json=enabled', 'curl=enabled', 'tests=false']
+naunet_opts = ['tests=false']
+if get_option('portal').enabled()
+ naunet_opts += ['json=enabled', 'curl=enabled']
endif
naunet = subproject('libnaunet', default_options: naunet_opts)
naunet_has_curl = naunet.get_variable('naunet_has_curl')
@@ -30,7 +30,11 @@ if not no_video
else
stela_opts += 'window=glfw'
endif
- stela_opts += get_option('sink-use-vulkan') ? 'api=vulkan' : 'api=opengl'
+ if get_option('sink-use-vulkan')
+ stela_opts += 'api=vulkan'
+ else
+ stela_opts += get_option('renderer') == 'tiger' ? 'api=gles' : 'api=gl'
+ endif
if get_option('sink-for-rpi')
stela_opts += 'egl-brcm=true'
endif
diff --git a/meson_options.txt b/meson_options.txt
index 20e034b..27efc08 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -5,6 +5,6 @@ option('sink-for-rpi', type: 'boolean', value: false)
option('codecs', type: 'array', choices: ['ffmpeg', 'stb_image', 'spng', 'wuffs'], value: ['ffmpeg'])
option('renderer', type: 'combo', choices: ['libplacebo', 'tiger'], value: 'libplacebo')
option('server', type: 'feature', value: 'enabled')
-option('client', type: 'feature', value: 'enabled')
-option('portal', type: 'feature', value: 'enabled')
+option('client', type: 'feature', value: 'disabled')
+option('portal', type: 'feature', value: 'disabled')
option('tests', type: 'boolean', value: false)
diff --git a/src/buffer/audio.c b/src/buffer/audio.c
index 2e65a97..eaac16f 100644
--- a/src/buffer/audio.c
+++ b/src/buffer/audio.c
@@ -14,13 +14,15 @@
#define BUFFER_MARK_BUFFERED 1.0
#ifdef CAMU_AUDIO_BUFFER_FADE
-#define FADE_STEP(fmt) (1.75f / (fmt)->sample_rate)
+#define FADE_STEP(fmt) (2.50f / (fmt)->sample_rate)
+#define FADE_MIN(fmt) (8000.f / (fmt)->sample_rate)
#endif
enum {
PAUSE_PAUSED = 0,
#ifdef CAMU_AUDIO_BUFFER_FADE
- PAUSE_FADING_OUT,
+ PAUSE_FADING,
+ PAUSE_FADING_COMPLETE,
#endif
PAUSE_PLAYING
};
@@ -29,12 +31,8 @@ static void reset_buffer_state(struct camu_audio_buffer *buf)
{
al_atomic_store(f64)(&buf->pts, -1.0, AL_ATOMIC_RELAXED);
buf->pause = PAUSE_PAUSED;
- al_atomic_store(s32)(&buf->unpause, 0, AL_ATOMIC_RELAXED);
- al_atomic_store(s32)(&buf->volume.set, 0, AL_ATOMIC_RELAXED);
-#ifdef CAMU_AUDIO_BUFFER_FADE
- buf->fade.offset = 0;
- buf->fade.volume = -1.f;
-#endif
+ al_atomic_store(u32)(&buf->unpause, 0, AL_ATOMIC_RELAXED);
+ al_atomic_store(u32)(&buf->volume.set, 0, AL_ATOMIC_RELAXED);
buf->buffered = false;
al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
al_atomic_store(ptrdiff_t)(&buf->uncork_at, 0, AL_ATOMIC_RELAXED);
@@ -47,6 +45,10 @@ bool camu_audio_buffer_init(struct camu_audio_buffer *buf, struct camu_clock *cl
buf->ignore_desync = false;
al_atomic_store(bool)(&buf->no_video, false, AL_ATOMIC_RELAXED);
reset_buffer_state(buf);
+#ifdef CAMU_AUDIO_BUFFER_FADE
+ // Persist fade volume across resets.
+ buf->fade.volume = -1.f;
+#endif
#ifdef CAMU_MIXER_THREADED
al_atomic_store(u8)(&buf->ref, 0, AL_ATOMIC_RELAXED);
#endif
@@ -58,6 +60,10 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code
{
camu_audio_format_copy(&buf->fmt.in, &stream->audio.fmt);
camu_mixer_pick_format(mixer, &buf->fmt);
+
+ struct camu_audio_format *in = &buf->fmt.in;
+ struct camu_audio_format *req = &buf->fmt.req;
+
buf->fmt.resampler_needed = !camu_resampler_format_matches(&buf->fmt);
if (buf->fmt.resampler_needed) {
#ifdef CAMU_HAVE_FFMPEG
@@ -71,18 +77,16 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code
#endif
}
- const char *in_format_name = camu_audio_format_name(buf->fmt.in.format);
- const char *req_format_name = camu_audio_format_name(buf->fmt.req.format);
al_log_info("audio_buffer", "Stream: %s (%dch) %dHz -> %s (%dch) %dHz.",
- in_format_name, buf->fmt.in.channel_count, buf->fmt.in.sample_rate,
- req_format_name, buf->fmt.req.channel_count, buf->fmt.req.sample_rate);
+ camu_audio_format_name(in->format), in->channel_count, in->sample_rate,
+ camu_audio_format_name(req->format), req->channel_count, req->sample_rate);
- buf->size = (ptrdiff_t)camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_SIZE);
+ buf->size = (ptrdiff_t)camu_audio_format_sec_to_bytes(req, BUFFER_SIZE);
buf->data = (u8 *)al_malloc(buf->size);
al_ring_buffer_init(&buf->rb, buf->data, buf->size);
- buf->mark.min = (ptrdiff_t)camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_MARK_MIN);
- buf->mark.buffered = (ptrdiff_t)camu_audio_format_sec_to_bytes(&buf->fmt.req, BUFFER_MARK_BUFFERED);
+ buf->mark.min = (ptrdiff_t)camu_audio_format_sec_to_bytes(req, BUFFER_MARK_MIN);
+ buf->mark.buffered = (ptrdiff_t)camu_audio_format_sec_to_bytes(req, BUFFER_MARK_BUFFERED);
camu_peak_buffer_init(&buf->peak, KB(16));
buf->stream = stream;
@@ -93,7 +97,7 @@ bool camu_audio_buffer_configure(struct camu_audio_buffer *buf, struct camu_code
void camu_audio_buffer_set_volume(struct camu_audio_buffer *buf, f32 volume)
{
al_atomic_store(f32)(&buf->volume.queued, volume, AL_ATOMIC_RELAXED);
- al_atomic_add(s32)(&buf->volume.set, 1, AL_ATOMIC_RELAXED);
+ al_atomic_add(u32)(&buf->volume.set, 1, AL_ATOMIC_RELAXED);
}
void camu_audio_buffer_set_latency(struct camu_audio_buffer *buf, f64 latency)
@@ -111,13 +115,6 @@ void camu_audio_buffer_set_no_video(struct camu_audio_buffer *buf, bool no_video
al_atomic_store(bool)(&buf->no_video, no_video, AL_ATOMIC_RELAXED);
}
-#ifdef _DEBUG_
-#define BUFFERED_SECONDS_DEBUG(buf) \
- camu_audio_format_bytes_to_sec(&buf->fmt.req, al_ring_buffer_occupied(&buf->rb))
-#else
-#define BUFFERED_SECONDS_DEBUG(buf) 0
-#endif
-
// A return value of false signals that we pushed to the peak buffer.
static bool push_internal(struct camu_audio_buffer *buf, f64 pts, u8 **data, s32 sample_count)
{
@@ -153,7 +150,8 @@ static bool push_internal(struct camu_audio_buffer *buf, f64 pts, u8 **data, s32
ptrdiff_t space = al_ring_buffer_space(&buf->rb);
if (!buf->buffered && buf->size - space > buf->mark.buffered) {
- al_log_debug("audio_buffer", "Buffered (mark: %.2fs).", BUFFERED_SECONDS_DEBUG(buf));
+ al_log_debug("audio_buffer", "Buffered (mark: %.2fs).",
+ camu_audio_format_bytes_to_sec(&buf->fmt.req, buf->size - space));
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
buf->buffered = true;
}
@@ -166,7 +164,7 @@ static bool push_internal(struct camu_audio_buffer *buf, f64 pts, u8 **data, s32
peak += have;
if (peak >= buf->mark.min) {
// If this happens the writer of this buffer is taking way too long to stop.
- al_log_warn("audio_buffer", "Overrun likely, discarding peak buffer (audio will be desynced).");
+ al_log_warn("audio_buffer", "Overrun likely, discarding peak buffer (audio will desync).");
camu_peak_buffer_flush(&buf->peak);
peak = 0;
}
@@ -232,7 +230,8 @@ void camu_audio_buffer_flush(struct camu_audio_buffer *buf)
}
if (!buf->buffered) {
- al_log_debug("audio_buffer", "Buffered (mark: %.2fs).", BUFFERED_SECONDS_DEBUG(buf));
+ al_log_debug("audio_buffer", "Buffered (mark: %.2fs).",
+ camu_audio_format_bytes_to_sec(&buf->fmt.req, al_ring_buffer_occupied(&buf->rb)));
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
buf->buffered = true;
}
@@ -253,44 +252,45 @@ void camu_audio_buffer_reset(struct camu_audio_buffer *buf)
void camu_audio_buffer_unpause(struct camu_audio_buffer *buf)
{
- al_atomic_add(s32)(&buf->unpause, 1, AL_ATOMIC_RELAXED);
+ al_atomic_add(u32)(&buf->unpause, 1, AL_ATOMIC_RELAXED);
}
// PAUSE_PAUSED signifies that the last read was silence.
// This means we can skip around in the buffer without worrying about pops.
ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdiff_t req)
{
- if (al_atomic_load(s32)(&buf->volume.set, AL_ATOMIC_ACQUIRE) > 0) {
- buf->volume.user = al_atomic_load(f32)(&buf->volume.queued, AL_ATOMIC_RELAXED);
-#ifdef CAMU_AUDIO_BUFFER_FADE
- if (buf->fade.volume == -1.f) {
- buf->fade.volume = buf->volume.user;
- }
-#endif
- al_atomic_sub(s32)(&buf->volume.set, 1, AL_ATOMIC_RELEASE);
- }
+ struct camu_audio_format *fmt = &buf->fmt.req;
+ ptrdiff_t ret, signal = req;
+ f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
bool set_clock = al_atomic_load(bool)(&buf->no_video, AL_ATOMIC_RELAXED);
f64 pts = camu_clock_get_pts(buf->clock, buf->latency, set_clock);
if (pts == -1.0) {
#ifdef CAMU_AUDIO_BUFFER_FADE
if (buf->pause == PAUSE_PLAYING) {
- buf->pause = PAUSE_FADING_OUT;
+ buf->pause = PAUSE_FADING;
+ // fade offset should only ever be read when pause = FADING.
buf->fade.offset = 0;
- } else if (buf->pause == PAUSE_PAUSED || buf->fade.volume == 0.f) {
+ } else if (buf->pause == PAUSE_PAUSED || buf->pause == PAUSE_FADING_COMPLETE ||
+ (buf->fade.volume == 0.f)) {
al_memset(data, 0, req);
- if (buf->pause != PAUSE_PAUSED) {
- // Fade out finished.
- buf->pause = PAUSE_PAUSED;
+ if (buf->pause == PAUSE_FADING_COMPLETE) {
buf->callback(buf->userdata, CAMU_BUFFER_PAUSED);
+ buf->pause = PAUSE_PAUSED;
+ } else if (buf->pause != PAUSE_PAUSED) {
+ // Don't signal PAUSED until the next read to ensure at least 1 silent
+ // frame is included in the fade out.
+ buf->pause = PAUSE_FADING_COMPLETE;
}
- buf->fade.offset = 0;
return req;
}
- } else if (buf->pause == PAUSE_FADING_OUT || buf->pause == PAUSE_PAUSED) {
- if (buf->pause == PAUSE_FADING_OUT) {
- // If unpaused during a fade out, resume immediately to not break the stream.
- buf->pause = PAUSE_PLAYING;
+ } else if (buf->pause == PAUSE_FADING || buf->pause == PAUSE_FADING_COMPLETE) {
+ // If unpaused during a fade out, resume immediately to not break the stream.
+ buf->pause = PAUSE_PLAYING;
+ // Though, we will still jump back (possible pop) unless we are ignoring sync.
+ if (buf->ignore_desync) {
+ ret = al_ring_buffer_discard(&buf->rb, buf->fade.offset);
+ base_pts += camu_audio_format_bytes_to_sec(fmt, ret);
}
#else // No fade and clock paused.
al_memset(data, 0, req);
@@ -302,32 +302,38 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
#endif
}
- if (al_atomic_load(s32)(&buf->unpause, AL_ATOMIC_ACQUIRE) > 0) {
- // If pause != PLAYING here this will likely cause unexpected behavior.
+ if (al_atomic_load(u32)(&buf->volume.set, AL_ATOMIC_ACQUIRE) > 0) {
+ buf->volume.user = al_atomic_load(f32)(&buf->volume.queued, AL_ATOMIC_RELAXED);
+#ifdef CAMU_AUDIO_BUFFER_FADE
+ if (buf->fade.volume == -1.f) {
+ buf->fade.volume = buf->volume.user;
+ }
+#endif
+ al_atomic_sub(u32)(&buf->volume.set, 1, AL_ATOMIC_RELEASE);
+ }
+
+ if (!buf->ignore_desync && al_atomic_load(u32)(&buf->unpause, AL_ATOMIC_ACQUIRE) > 0) {
+ // Queuing multiple resyncs before resuming the stream will cause pops!
+ al_log_debug("audio_buffer", "Forcing resync.");
buf->pause = PAUSE_PAUSED;
- al_atomic_sub(s32)(&buf->unpause, 1, AL_ATOMIC_RELEASE);
+ al_atomic_sub(u32)(&buf->unpause, 1, AL_ATOMIC_RELEASE);
}
- struct camu_audio_format *fmt = &buf->fmt.req;
- ptrdiff_t ret, signal = req;
ptrdiff_t have = al_ring_buffer_occupied(&buf->rb);
#ifdef CAMU_AUDIO_BUFFER_FADE
- // Cut off fade if it's reaching too far.
- if (have < buf->fade.offset) have = 0;
- else have -= buf->fade.offset;
+ if (buf->pause == PAUSE_FADING) {
+ // Cut off fade if it's reaching too far.
+ if (have < buf->fade.offset) have = 0;
+ else have -= buf->fade.offset;
+ }
#endif
- // @NOTE: We are not safe to increment buf->pts from a different thread.
- // We would need to accumulate the difference and do an atomic add instead
- // of the load/store we do here.
- f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
// Unpause and possibly attempt syncing to the clock.
if (UNLIKELY(buf->pause == PAUSE_PAUSED)) {
if (!buf->ignore_desync) {
pts -= base_pts;
if (pts > 0.0) { // Skip.
- ret = (ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts);
- ret = MIN(ret, have);
+ ret = MIN((ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts), have);
al_log_info("audio_buffer", "Skipping %fs of audio (%zd bytes).", pts, ret);
ret = al_ring_buffer_discard(&buf->rb, ret);
have -= ret;
@@ -335,14 +341,13 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
// Could go on to underrun.
} else if (pts < 0.0) { // Delay.
pts = -pts;
- ret = (ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts);
- ret = MIN(ret, req);
+ ret = MIN((ptrdiff_t)camu_audio_format_sec_to_bytes(fmt, pts), req);
al_log_info("audio_buffer", "Delaying audio by %fs.", pts);
al_memset(data, 0, ret);
data += ret;
req -= ret;
// We can continue to delay.
- if (req == 0) return signal;
+ if (req == 0) goto out;
}
}
buf->pause = PAUSE_PLAYING;
@@ -388,7 +393,7 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
if (LIKELY(req > 0)) {
#ifdef CAMU_AUDIO_BUFFER_FADE
- if (buf->pause == PAUSE_FADING_OUT) {
+ if (buf->pause == PAUSE_FADING) {
// Peeking into the ring buffer won't consume data. So, when resumed we
// will fade in from the same position that the fade out started.
ret = al_ring_buffer_peek(&buf->rb, data, buf->fade.offset, req);
@@ -403,7 +408,7 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
}
f32 step = 0.f;
- if (buf->pause == PAUSE_FADING_OUT || buf->fade.volume > buf->volume.user) {
+ if (buf->pause == PAUSE_FADING || buf->fade.volume > buf->volume.user) {
step = -FADE_STEP(fmt);
} else if (buf->fade.volume < buf->volume.user) {
step = FADE_STEP(fmt);
@@ -411,10 +416,11 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
if (step != 0.f || buf->fade.volume != 1.f) {
if (buf->volume.user > 1.f) {
- // Only adjust the step if it's an increase to avoid drawn-out
+ // Only adjust the step if it's an increase to avoid drawn out
// fades if the volume is low.
step *= buf->volume.user;
}
+ step *= MAX(buf->fade.volume, FADE_MIN(fmt));
buf->fade.volume = apply_volume(data, req, fmt, buf->fade.volume, buf->volume.user, step);
}
#else
@@ -424,11 +430,9 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
#endif
}
- al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE);
-
// Check if we should request to uncork.
#ifdef CAMU_AUDIO_BUFFER_FADE
- if (flow == FLOWING && buf->pause != PAUSE_FADING_OUT) {
+ if (flow == FLOWING && buf->pause != PAUSE_FADING) {
#else
if (flow == FLOWING) {
#endif
@@ -438,6 +442,11 @@ ptrdiff_t camu_audio_buffer_read(struct camu_audio_buffer *buf, u8 *data, ptrdif
}
}
+out:
+ // We are not safe to increment buf->pts from a different thread.
+ // We would need to accumulate the difference and do an atomic add here.
+ al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE);
+
// To signal EOF, return less then req.
return signal;
}
diff --git a/src/buffer/audio.h b/src/buffer/audio.h
index 901becf..ef907bd 100644
--- a/src/buffer/audio.h
+++ b/src/buffer/audio.h
@@ -21,7 +21,7 @@ struct camu_audio_buffer {
atomic(bool) no_video;
u8 pause;
- atomic(s32) unpause;
+ atomic(u32) unpause;
struct camu_resampler_format fmt;
struct camu_resampler *resampler;
@@ -39,8 +39,8 @@ struct camu_audio_buffer {
struct {
f32 user;
+ atomic(u32) set;
atomic(f32) queued;
- atomic(s32) set;
} volume;
#ifdef CAMU_AUDIO_BUFFER_FADE
diff --git a/src/buffer/frame_queue.h b/src/buffer/frame_queue.h
index e40971d..fdb49b5 100644
--- a/src/buffer/frame_queue.h
+++ b/src/buffer/frame_queue.h
@@ -17,7 +17,7 @@ enum {
struct camu_frame_queue {
struct camu_video_buffer *buf;
#ifdef CAMU_HAVE_FFMPEG
- bool (*configure_subtitles)(struct camu_frame_queue *, s32, s32, AVCodecParameters *);
+ bool (*configure_subtitles)(struct camu_frame_queue *, u32, u32, AVCodecParameters *);
#endif
void (*push)(struct camu_frame_queue *, struct camu_codec_frame *, f64);
#ifdef CAMU_HAVE_FFMPEG
diff --git a/src/buffer/video.c b/src/buffer/video.c
index 41aa9ed..2fc5773 100644
--- a/src/buffer/video.c
+++ b/src/buffer/video.c
@@ -4,14 +4,16 @@
#include "common.h"
#include "common_internal.h"
-#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
#ifdef CAMU_HAVE_FFMPEG
-#include "../codec/ffmpeg/scaler.h"
+#include "../codec/ffmpeg/common.h"
#endif
+
+#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
+#include "../codec/ffmpeg/scaler.h"
#endif
#define BUFFER_MARK_LOW ((1.0 / 30.0) * 6)
-#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 5)
+#define BUFFER_MARK_BUFFERED ((1.0 / 30.0) * 5) // Must be >1.
#define BUFFER_MARK_HIGH ((1.0 / 30.0) * 12)
#define BUFFER_MARK_RESET (BUFFER_MARK_HIGH * 2.0)
@@ -27,6 +29,7 @@ bool camu_video_buffer_init(struct camu_video_buffer *buf, struct camu_clock *cl
buf->avg_frame_duration = 0.0;
buf->queue = NULL;
buf->buffered = false;
+ buf->buffered_with_one_frame = false;
buf->weighted_read = true;
al_atomic_store(u8)(&buf->flow, FLOWING, AL_ATOMIC_RELAXED);
#ifdef CAMU_SCREEN_THREADED
@@ -53,7 +56,7 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
const char *format_name = camu_pixel_format_name(fmt->format);
if (!format_name) format_name = "unknown";
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s.",
+ al_log_info("video_buffer", "Stream: %s (%ux%u) %s.",
format_name, fmt->width, fmt->height, "IMAGE");
break;
@@ -63,20 +66,20 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
AVCodecParameters *codecpar = stream->av.stream->codecpar;
AVRational frame_rate = stream->av.stream->avg_frame_rate;
- fmt->width = codecpar->width;
- fmt->height = codecpar->height;
+ fmt->width = (u32)codecpar->width;
+ fmt->height = (u32)codecpar->height;
fmt->format = codecpar->format;
camu_video_format_copy(&buf->fmt.in, fmt);
buf->single_frame = stream->duration == 0 || frame_rate.den == 0;
- buf->avg_frame_duration = buf->single_frame ? 0.0 : av_q2d(av_inv_q(frame_rate));
+ buf->avg_frame_duration = frame_rate.den > 0 ? av_q2d(av_inv_q(frame_rate)) : 0.0;
- const char *format_name = av_get_pix_fmt_name(buf->fmt.in.format);
+ const char *format_name = av_get_pix_fmt_name(fmt->format);
if (buf->single_frame) {
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s.",
+ al_log_info("video_buffer", "Stream: %s (%ux%u) %s.",
format_name, fmt->width, fmt->height, "IMAGE");
} else {
- al_log_info("video_buffer", "Stream: %s (%dx%d) %s %.3ffps.",
+ al_log_info("video_buffer", "Stream: %s (%ux%u) %s %.3ffps.",
format_name, fmt->width, fmt->height, "VIDEO", av_q2d(frame_rate));
}
@@ -86,8 +89,8 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
}
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
#ifdef CAMU_HAVE_FFMPEG
- if (fmt->width > 0 && fmt->height > 0 && fmt->format != CAMU_PIXEL_FORMAT_RGBA &&
- fmt->format != CAMU_PIXEL_FORMAT_RGB) {
+ if (fmt->width > 0 && fmt->height > 0 &&
+ (fmt->format != CAMU_PIXEL_FORMAT_RGBA && fmt->format != CAMU_PIXEL_FORMAT_RGB)) {
buf->fmt.scaler_needed = true;
buf->fmt.req.width = fmt->width;
buf->fmt.req.height = fmt->height;
@@ -95,7 +98,7 @@ bool camu_video_buffer_configure(struct camu_video_buffer *buf, struct camu_code
buf->scaler = camu_ff_scaler_create();
if (buf->scaler->init(buf->scaler, &buf->fmt)) {
const char *format_name = av_get_pix_fmt_name(buf->fmt.req.format);
- al_log_info("video_buffer", "Scaling to: %s (%dx%d).",
+ al_log_info("video_buffer", "Scaling to: %s (%ux%u).",
format_name, buf->fmt.req.width, buf->fmt.req.height);
} else {
// Scaler will be freed in video_buffer_free().
@@ -134,21 +137,19 @@ bool camu_video_buffer_is_single_frame(struct camu_video_buffer *buf)
static void after_push_internal(struct camu_video_buffer *buf)
{
- f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration;
+ s32 count = buf->queue->count(buf->queue);
+ f64 have = count * buf->avg_frame_duration;
if (!buf->buffered && (buf->single_frame || have >= BUFFER_MARK_BUFFERED)) {
- // Preserve order of: flush -> callback -> set flow, for single frames.
+ // Preserve order of: set flow -> flush -> callback, for single frames.
if (buf->single_frame) {
+ al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
buf->queue->flush(buf->queue);
}
-
+ buf->buffered = true;
+ buf->buffered_with_one_frame = count == 1;
al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have);
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
- buf->buffered = true;
-
- if (buf->single_frame) {
- al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
- }
}
if (have >= BUFFER_MARK_RESET) {
@@ -164,12 +165,12 @@ static bool push_av_frame_internal(struct camu_video_buffer *buf, AVFrame *frame
{
AVStream *stream = buf->stream->av.stream;
f64 pts = frame->best_effort_timestamp * av_q2d(stream->time_base);
- f64 duration = frame->duration * av_q2d(stream->time_base);
- f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
- if (!buf->single_frame && frame_is_late(buf->clock, base, pts, duration)) {
+ f64 duration = camu_ff_frame_duration(frame) * av_q2d(stream->time_base);
+ f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
+ if (!buf->single_frame && frame_is_late(buf->clock, base_pts, pts, duration)) {
return false;
}
- if (base == -1.0) al_atomic_store(f64)(&buf->pts, pts, AL_ATOMIC_RELEASE);
+ if (base_pts == -1.0) al_atomic_store(f64)(&buf->pts, pts, AL_ATOMIC_RELEASE);
#ifdef CAMU_VIDEO_BUFFER_FORCE_SCALER
if (buf->fmt.scaler_needed) {
if (!buf->scaler->scale(buf->scaler, (const u8 **)frame->data, frame->linesize)) {
@@ -193,8 +194,8 @@ void camu_video_buffer_push(struct camu_video_buffer *buf, struct camu_codec_fra
}
switch (frame->mode) {
case CAMU_NORMAL: {
- f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
- if (base == -1.0) {
+ f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
+ if (base_pts == -1.0) {
al_atomic_store(f64)(&buf->pts, 0.0, AL_ATOMIC_RELEASE);
}
buf->queue->push(buf->queue, frame, 0.0);
@@ -225,14 +226,16 @@ void camu_video_buffer_push_subtitle(struct camu_video_buffer *buf, AVPacket *pk
void camu_video_buffer_flush(struct camu_video_buffer *buf)
{
al_log_debug("video_buffer", "Flush requested.");
+ al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
buf->queue->flush(buf->queue);
if (!buf->buffered) {
- f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration;
+ s32 count = buf->queue->count(buf->queue);
+ f64 have = count * buf->avg_frame_duration;
+ buf->buffered = true;
+ buf->buffered_with_one_frame = count == 1;
al_log_debug("video_buffer", "Buffered (mark: %.2fs).", have);
buf->callback(buf->userdata, CAMU_BUFFER_BUFFERED);
- buf->buffered = true;
}
- al_atomic_store(u8)(&buf->flow, FLUSHED, AL_ATOMIC_RELAXED);
}
// Not thread-safe, must be called while the buffer is not being read from or written to.
@@ -247,29 +250,33 @@ void camu_video_buffer_reset(struct camu_video_buffer *buf)
bool camu_video_buffer_read(struct camu_video_buffer *buf, void *out, bool *weighted)
{
- f64 base = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
+ f64 base_pts = al_atomic_load(f64)(&buf->pts, AL_ATOMIC_ACQUIRE);
if (!buf->single_frame) {
f64 pts = camu_clock_get_pts(buf->clock, buf->latency, !buf->weighted_read);
- if (pts > base) {
- base = pts;
- al_atomic_store(f64)(&buf->pts, base, AL_ATOMIC_RELEASE);
+ if (pts > base_pts) {
+ base_pts = pts;
+ al_atomic_store(f64)(&buf->pts, base_pts, AL_ATOMIC_RELEASE);
}
}
u8 flow = al_atomic_load(u8)(&buf->flow, AL_ATOMIC_ACQUIRE);
if (flow == ERRORED) return false;
- u8 ret = buf->queue->read(buf->queue, base, out);
+ u8 ret = buf->queue->read(buf->queue, base_pts, out);
if (ret == CAMU_QUEUE_ERR) {
buf->callback(buf->userdata, CAMU_BUFFER_ERRORED);
al_atomic_store(u8)(&buf->flow, ERRORED, AL_ATOMIC_RELEASE);
return false;
}
- if (flow == FLUSHED && (ret == CAMU_QUEUE_EOF || (buf->single_frame && ret == CAMU_QUEUE_OK))) {
+ // If a buffer only ever has 1 frame, libplacebo will never return EOF.
+ // In our code that 1 frame buffer might be a video, in which case that behavior is erroneous.
+ bool eof = ret == CAMU_QUEUE_EOF || (buf->buffered_with_one_frame && ret == CAMU_QUEUE_OK);
+ if (flow == FLUSHED && eof) {
al_log_debug("video_buffer", "Flushed.");
buf->callback(buf->userdata, CAMU_BUFFER_EOF);
al_atomic_store(u8)(&buf->flow, SIGNALED, AL_ATOMIC_RELEASE);
} else if (flow == FLOWING) {
+ // Never attempt to uncork if flow = FLUSHED.
f64 have = buf->queue->count(buf->queue) * buf->avg_frame_duration;
if (have <= BUFFER_MARK_LOW) {
buf->callback(buf->userdata, CAMU_BUFFER_UNCORK);
diff --git a/src/buffer/video.h b/src/buffer/video.h
index 33991a2..0814251 100644
--- a/src/buffer/video.h
+++ b/src/buffer/video.h
@@ -29,6 +29,8 @@ struct camu_video_buffer {
struct camu_frame_queue *queue;
bool buffered;
+ // We need to manually trigger EOF in read().
+ bool buffered_with_one_frame;
// Flush the renderer on this read and don't allow it to set the clock.
bool weighted_read;
diff --git a/src/codec/codec.h b/src/codec/codec.h
index af824d7..e8d2b6d 100644
--- a/src/codec/codec.h
+++ b/src/codec/codec.h
@@ -120,8 +120,8 @@ struct camu_audio_format {
};
struct camu_video_format {
- s32 width;
- s32 height;
+ u32 width;
+ u32 height;
s32 format;
};
@@ -158,8 +158,8 @@ struct camu_codec_frame {
s32 format;
f64 pts;
struct {
- s32 width;
- s32 height;
+ u32 width;
+ u32 height;
} video;
struct {
u32 sample_count;
@@ -177,7 +177,7 @@ struct camu_demuxer {
bool (*seek)(struct camu_demuxer *, u64);
void (*free)(struct camu_demuxer **);
array(struct camu_codec_stream) streams;
- s32 subscribed;
+ u32 subscribed;
};
struct camu_renderer;
diff --git a/src/codec/ffmpeg/common.c b/src/codec/ffmpeg/common.c
index 95bf247..c5fe7cd 100644
--- a/src/codec/ffmpeg/common.c
+++ b/src/codec/ffmpeg/common.c
@@ -4,6 +4,15 @@
#include "common.h"
+s64 camu_ff_frame_duration(AVFrame *frame)
+{
+#if LIBAVUTIL_VERSION_INT >= AV_VERSION_INT(57, 30, 100)
+ return frame->duration;
+#else
+ return frame->pkt_duration;
+#endif
+}
+
void camu_ff_set_log_callback(void (*callback)(void *, s32, const char *, va_list))
{
av_log_set_callback(callback);
@@ -15,6 +24,7 @@ static size_t pos = 0;
// If a line takes more than 2 steps to print, make sure we stay within AL_LOG_MESSAGE_SIZE.
#define CHUNK_SIZE (AL_LOG_MESSAGE_SIZE / 2u)
+// @TODO: thread-saftey.
static void av_log_callback(void *userdata, s32 level, const char *fmt, va_list args)
{
(void)userdata;
diff --git a/src/codec/ffmpeg/common.h b/src/codec/ffmpeg/common.h
index 229ffb0..885dd4d 100644
--- a/src/codec/ffmpeg/common.h
+++ b/src/codec/ffmpeg/common.h
@@ -2,6 +2,11 @@
#include <al/types.h>
#include <al/log.h>
+#include <libavutil/frame.h>
+
+void camu_ff_common_init(void);
+
+s64 camu_ff_frame_duration(AVFrame *frame);
// userdata, level, fmt, args
void camu_ff_set_log_callback(void (*callback)(void *, int, const char *, va_list));
diff --git a/src/codec/ffmpeg/decoder.c b/src/codec/ffmpeg/decoder.c
index d3c9f41..dd0cc0b 100644
--- a/src/codec/ffmpeg/decoder.c
+++ b/src/codec/ffmpeg/decoder.c
@@ -326,9 +326,8 @@ static s32 receive_frames(struct camu_ff_decoder *av)
continue;
}
if (avframe->best_effort_timestamp == AV_NOPTS_VALUE) {
- // This trips under what I think are normal conditions,
- // more testing is needed.
- al_assert(avframe->duration == 0);
+ // This trips under what I think are normal conditions, more testing is needed.
+ //al_assert(avframe->duration == 0);
avframe->best_effort_timestamp = 0;
}
av->callback(av->userdata, frame);
diff --git a/src/codec/ffmpeg/packet_ext.c b/src/codec/ffmpeg/packet_ext.c
index 2ff425e..f3704f2 100644
--- a/src/codec/ffmpeg/packet_ext.c
+++ b/src/codec/ffmpeg/packet_ext.c
@@ -16,7 +16,9 @@ void nn_packet_write_av_codec_parameters(struct nn_packet *packet, AVCodecParame
NNWT_PACKET_WRITE_TYPE(packet, s32, codecpar->width);
NNWT_PACKET_WRITE_TYPE(packet, s32, codecpar->height);
NNWT_PACKET_WRITE_TYPE(packet, AVRational, codecpar->sample_aspect_ratio);
+#if LIBAVCODEC_VERSION_INT >= 60
NNWT_PACKET_WRITE_TYPE(packet, AVRational, codecpar->framerate);
+#endif
NNWT_PACKET_WRITE_TYPE(packet, enum AVFieldOrder, codecpar->field_order);
NNWT_PACKET_WRITE_TYPE(packet, enum AVColorRange, codecpar->color_range);
NNWT_PACKET_WRITE_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries);
@@ -43,7 +45,7 @@ void nn_packet_write_av_dictionary(struct nn_packet *packet, AVDictionary *dict)
s32 count = av_dict_count(dict);
NNWT_PACKET_WRITE_TYPE(packet, s32, count);
const AVDictionaryEntry *entry = NULL;
- while ((entry = av_dict_iterate(dict, entry))) {
+ while ((entry = av_dict_get(dict, "", entry, AV_DICT_IGNORE_SUFFIX))) {
size_t len = al_strlen(entry->key);
al_assert(len <= UINT32_MAX);
NNWT_PACKET_WRITE_TYPE(packet, u32, len);
@@ -112,7 +114,9 @@ void nn_packet_read_av_codec_parameters(struct nn_packet *packet, AVCodecParamet
NNWT_PACKET_READ_TYPE(packet, s32, codecpar->width);
NNWT_PACKET_READ_TYPE(packet, s32, codecpar->height);
NNWT_PACKET_READ_TYPE(packet, AVRational, codecpar->sample_aspect_ratio);
+#if LIBAVCODEC_VERSION_INT >= 60
NNWT_PACKET_READ_TYPE(packet, AVRational, codecpar->framerate);
+#endif
NNWT_PACKET_READ_TYPE(packet, enum AVFieldOrder, codecpar->field_order);
NNWT_PACKET_READ_TYPE(packet, enum AVColorRange, codecpar->color_range);
NNWT_PACKET_READ_TYPE(packet, enum AVColorPrimaries, codecpar->color_primaries);
diff --git a/src/codec/ffmpeg/resampler.c b/src/codec/ffmpeg/resampler.c
index 4f5a2ab..d2c8f62 100644
--- a/src/codec/ffmpeg/resampler.c
+++ b/src/codec/ffmpeg/resampler.c
@@ -19,7 +19,7 @@ static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampl
&fmt->req.channel_layout, fmt->req.format, fmt->req.sample_rate,
&fmt->in.channel_layout, fmt->in.format, fmt->in.sample_rate, 0, NULL);
if (ret != 0) {
- al_log_error("lav_resampler", "Failed to configure resampler context.");
+ al_log_error("ff_resampler", "Failed to configure resampler context.");
return false;
}
@@ -30,7 +30,7 @@ static bool ff_resampler_init(struct camu_resampler *resamp, struct camu_resampl
if (swr_init(av->resample_context) != 0) {
swr_free(&av->resample_context);
- al_log_error("lav_resampler", "Failed to init resampler context.");
+ al_log_error("ff_resampler", "Failed to init resampler context.");
return false;
}
@@ -60,7 +60,7 @@ static s32 ff_resampler_convert(struct camu_resampler *resamp, const u8 **in_dat
s32 resample_count = swr_convert(av->resample_context, av->data,
out_resample_count, in_data, in_samples);
if (resample_count < 0) {
- al_log_error("lav_resampler", "Failed to convert samples (%d).", resample_count);
+ al_log_error("ff_resampler", "Failed to convert samples (%d).", resample_count);
}
return resample_count;
}
@@ -72,7 +72,7 @@ static s32 ff_resampler_flush(struct camu_resampler *resamp)
av->sample_count, NULL, 0);
if (swr_init(av->resample_context) != 0) {
swr_free(&av->resample_context);
- al_log_error("lav_resampler", "Failed to re-init resampler context after flush.");
+ al_log_error("ff_resampler", "Failed to re-init resampler context after flush.");
return -1;
}
return resample_count;
diff --git a/src/codec/ffmpeg/scaler.c b/src/codec/ffmpeg/scaler.c
index bc8d41b..a76c0a5 100644
--- a/src/codec/ffmpeg/scaler.c
+++ b/src/codec/ffmpeg/scaler.c
@@ -63,9 +63,11 @@ static bool ff_scaler_scale(struct camu_scaler *scale, const u8 **in_slice, s32
{
struct camu_ff_scaler *av = (struct camu_ff_scaler *)scale;
AVFrame *frame = av->frame.av.frame;
+ struct camu_scaler_format *fmt = &av->fmt;
+ s32 slice_height = (s32)fmt->in.height;
s32 ret = sws_scale(av->scaler_context, in_slice, in_strides, 0,
- av->fmt.in.height, frame->data, frame->linesize);
- if (ret != av->fmt.req.height) {
+ slice_height, frame->data, frame->linesize);
+ if (ret != (s32)fmt->req.height) {
al_log_error("ff_scaler", "Failed to scale frame (%s).", av_err2str(ret));
return false;
}
diff --git a/src/codec/stb_image/client.c b/src/codec/stb_image/client.c
index 3742def..250876f 100644
--- a/src/codec/stb_image/client.c
+++ b/src/codec/stb_image/client.c
@@ -22,15 +22,16 @@ static s32 stbi_decoder_push(struct camu_decoder *dec, struct camu_codec_packet
u8 *data = nn_buffer_get_ptr(packet->buffer, 0);
u8 *pixels = stbi_load_from_memory(data, packet->buffer->size, &w, &h, &channels, 0);
if (!pixels) return CAMU_ERR_ERROR;
+
struct camu_video_format *fmt = &stb->stream->video.fmt;
- al_assert(w == fmt->width && h == fmt->height);
+ al_assert(w > 0 && h > 0 && (u32)w == fmt->width && (u32)h == fmt->height);
struct camu_codec_frame *frame = al_alloc_object(struct camu_codec_frame);
frame->mode = CAMU_NORMAL;
frame->flags = 0;
frame->data = pixels;
- frame->video.width = w;
- frame->video.height = h;
+ frame->video.width = (u32)w;
+ frame->video.height = (u32)h;
frame->format = camu_pixel_format_from_channels(channels);
al_assert(frame->format == fmt->format);
frame->pts = 0.0;
diff --git a/src/codec/stb_image/server.c b/src/codec/stb_image/server.c
index 3642a64..79327d0 100644
--- a/src/codec/stb_image/server.c
+++ b/src/codec/stb_image/server.c
@@ -28,8 +28,8 @@ static bool stbi_demuxer_init(struct camu_demuxer *demux, struct cch_handle *han
stream.mode = CAMU_NORMAL;
stream.type = CAMU_STREAM_VIDEO;
struct camu_video_format *fmt = &stream.video.fmt;
- fmt->width = w;
- fmt->height = h;
+ fmt->width = (u32)w;
+ fmt->height = (u32)h;
fmt->format = camu_pixel_format_from_channels(channels);
al_array_push(stb->demux.streams, stream);
diff --git a/src/fruits/cmsrv/cmsrv.c b/src/fruits/cmsrv/cmsrv.c
index 0149246..be620a7 100644
--- a/src/fruits/cmsrv/cmsrv.c
+++ b/src/fruits/cmsrv/cmsrv.c
@@ -63,6 +63,8 @@ static u8 server_line_callback(void *userdata, str *line)
#elif defined NAUNET_HAS_CURL
if (camu_is_url(line, 0)) {
nn_packet_write_u8(packet, CAMU_RESOURCE_HTTP);
+#else
+ if (0) {
#endif
#if CACHE_HAVE_CDIO
} else if (al_str_cmp(line, &al_str_c("cdda://"), 0, 7) == 0) {
diff --git a/src/fruits/cmv/cmv.c b/src/fruits/cmv/cmv.c
index ac6f6e8..ebc2236 100644
--- a/src/fruits/cmv/cmv.c
+++ b/src/fruits/cmv/cmv.c
@@ -139,6 +139,8 @@ s32 main(s32 argc, char *argv[])
#elif defined NAUNET_HAS_CURL
if (camu_is_url(&arg, 0)) {
nn_packet_write_u8(packet, CAMU_RESOURCE_HTTP);
+#else
+ if (0) {
#endif
#if CACHE_HAVE_CDIO
} else if (al_str_cmp(&arg, &al_str_c("cdda://"), 0, 7) == 0) {
diff --git a/src/liana/client.c b/src/liana/client.c
index 1da27c1..44cba88 100644
--- a/src/liana/client.c
+++ b/src/liana/client.c
@@ -56,8 +56,8 @@ static void parse_info_packet(struct lia_client *client, struct nn_packet *packe
#endif
} else if (type == CAMU_STREAM_VIDEO) {
struct camu_video_format *fmt = &track->stream.video.fmt;
- fmt->width = nn_packet_read_s32(packet);
- fmt->height = nn_packet_read_s32(packet);
+ fmt->width = nn_packet_read_u32(packet);
+ fmt->height = nn_packet_read_u32(packet);
fmt->format = nn_packet_read_s32(packet);
}
break;
@@ -139,6 +139,7 @@ skip:
client->callback(client->userdata, LIANA_CLIENT_CONFIGURE, track->client->stream, track);
lia_vcr_add_track(&client->vcr, track);
}
+ client->callback(client->userdata, LIANA_CLIENT_CONFIGURE_COMPLETE, NULL, NULL);
}
static void info_packet_callback(void *userdata, struct nn_packet_stream *stream, struct nn_packet *packet)
@@ -154,7 +155,7 @@ static void info_packet_callback(void *userdata, struct nn_packet_stream *stream
}
stream->packet_callback = data_packet_callback;
struct nn_packet *rpacket = nn_packet_create();
- nn_packet_write_s32(rpacket, client->mask);
+ nn_packet_write_u32(rpacket, client->mask);
nn_packet_stream_send_packet(stream, rpacket);
lia_vcr_start(&client->vcr);
}
@@ -191,7 +192,7 @@ static bool connection_callback(void *userdata, struct nn_packet_stream *stream)
struct nn_packet *packet = nn_packet_create();
nn_packet_write_u32(packet, client->node_id);
nn_packet_write_u32(packet, client->connection_id);
- nn_packet_write_s32(packet, client->mask);
+ nn_packet_write_u32(packet, client->mask);
nn_packet_write_u64(packet, client->pos);
if (client->mask == 0) {
stream->packet_callback = info_packet_callback;
diff --git a/src/liana/client.h b/src/liana/client.h
index 9fe676b..5d5fc90 100644
--- a/src/liana/client.h
+++ b/src/liana/client.h
@@ -6,10 +6,22 @@
#include "vcr.h"
+enum {
+ LIANA_CLIENT_CONFIGURE = 0,
+ LIANA_CLIENT_CONFIGURE_COMPLETE,
+ LIANA_CLIENT_DATA,
+ LIANA_CLIENT_SUBTITLE,
+ LIANA_CLIENT_REMOVE_BUFFERS,
+ LIANA_CLIENT_RESUME_AT,
+ LIANA_CLIENT_RECONNECTED,
+ LIANA_CLIENT_EOF,
+ LIANA_CLIENT_CLOSED
+};
+
struct lia_client {
struct nn_event_loop *loop;
u32 node_id;
- s32 mask;
+ u32 mask;
u64 pos;
u64 at;
u8 reconnect;
diff --git a/src/liana/handler.h b/src/liana/handler.h
index 9f93e5c..1fa77a0 100644
--- a/src/liana/handler.h
+++ b/src/liana/handler.h
@@ -5,10 +5,16 @@
#include "../codec/codec.h"
#include "../cache/handle.h"
+enum {
+ LIANA_PACKET_DATA = 0,
+ LIANA_PACKET_EOF,
+ LIANA_PACKET_ERROR
+};
+
struct lia_server_handler {
bool (*init)(struct lia_server_handler *, struct cch_handle *);
void (*write_info)(struct lia_server_handler *, struct nn_packet *);
- void (*subscribe)(struct lia_server_handler *, s32);
+ void (*subscribe)(struct lia_server_handler *, u32);
u64 (*get_duration)(struct lia_server_handler *);
bool (*seek)(struct lia_server_handler *, u64);
void (*step)(struct lia_server_handler *);
@@ -17,36 +23,6 @@ struct lia_server_handler {
s32 status;
};
-enum {
- LIANA_PACKET_DATA = 0,
- LIANA_PACKET_EOF,
- LIANA_PACKET_ERROR
-};
-
-enum {
- LIANA_CLIENT_CONFIGURE = 0,
- LIANA_CLIENT_DATA,
- LIANA_CLIENT_SUBTITLE,
- LIANA_CLIENT_REMOVE_BUFFERS,
- LIANA_CLIENT_RESUME_AT,
- LIANA_CLIENT_RECONNECTED,
- LIANA_CLIENT_EOF,
- LIANA_CLIENT_CLOSED
-};
-
-struct lia_resume_req {
- u64 start;
- u64 offset;
-};
-
-struct lia_seek_req {
- u64 base;
- u64 ts;
- u64 delay;
- bool paused;
- u64 paused_at;
-};
-
struct lia_client_handler {
bool (*init)(struct lia_client_handler *, struct camu_renderer *, struct camu_codec_stream *);
bool (*handle_packet)(struct lia_client_handler *, struct nn_packet *);
diff --git a/src/liana/handlers/cdio.h b/src/liana/handlers/cdio.h
index 4c40986..3b0e991 100644
--- a/src/liana/handlers/cdio.h
+++ b/src/liana/handlers/cdio.h
@@ -2,6 +2,7 @@
#include "../handler.h"
+#ifdef LIANA_SERVER
struct lia_cdio_server {
struct lia_server_handler handler;
struct cch_handle *handle;
@@ -10,9 +11,13 @@ struct lia_cdio_server {
f64 pts;
};
+struct lia_server_handler *lia_cdio_server_create();
+#endif
+
+#ifdef LIANA_CLIENT
struct lia_cdio_client {
struct lia_client_handler handler;
};
-struct lia_server_handler *lia_cdio_server_create();
struct lia_client_handler *lia_cdio_client_create(void);
+#endif
diff --git a/src/liana/handlers/cdio_client.c b/src/liana/handlers/cdio_client.c
index 665858f..6d61e59 100644
--- a/src/liana/handlers/cdio_client.c
+++ b/src/liana/handlers/cdio_client.c
@@ -1,3 +1,5 @@
+#include "../client.h"
+
#include "cdio.h"
static bool cdio_client_init(struct lia_client_handler *handler, struct camu_renderer *renderer,
diff --git a/src/liana/handlers/cdio_server.c b/src/liana/handlers/cdio_server.c
index 01a9265..6ea1422 100644
--- a/src/liana/handlers/cdio_server.c
+++ b/src/liana/handlers/cdio_server.c
@@ -39,7 +39,7 @@ static void cdio_server_write_info(struct lia_server_handler *handler, struct nn
nn_packet_write_s32(packet, cdio->fmt.channel_count);
}
-static void cdio_server_subscribe(struct lia_server_handler *handler, s32 mask)
+static void cdio_server_subscribe(struct lia_server_handler *handler, u32 mask)
{
(void)handler;
(void)mask;
diff --git a/src/liana/handlers/codec.h b/src/liana/handlers/codec.h
index 29d513b..696cbb8 100644
--- a/src/liana/handlers/codec.h
+++ b/src/liana/handlers/codec.h
@@ -5,6 +5,7 @@
#include "../server.h"
#include "../handler.h"
+#ifdef LIANA_SERVER
struct lia_codec_server {
struct lia_server_handler handler;
struct camu_demuxer *demux;
@@ -14,10 +15,14 @@ struct lia_codec_server {
struct camu_codec_packet packet;
};
+struct lia_server_handler *lia_codec_server_create(void);
+#endif
+
+#ifdef LIANA_CLIENT
struct lia_codec_client {
struct lia_client_handler handler;
struct camu_decoder *dec;
};
-struct lia_server_handler *lia_codec_server_create(void);
struct lia_client_handler *lia_codec_client_create(void);
+#endif
diff --git a/src/liana/handlers/codec_client.c b/src/liana/handlers/codec_client.c
index 0aef69f..36e475e 100644
--- a/src/liana/handlers/codec_client.c
+++ b/src/liana/handlers/codec_client.c
@@ -1,5 +1,4 @@
#include "../../codec/codec.h"
-#include "../../server/common.h"
#ifdef CAMU_HAVE_FFMPEG
#include "../../codec/ffmpeg/decoder.h"
@@ -11,6 +10,7 @@
#include "../../codec/wuffs/decoder.h"
#include "../vcr.h"
+#include "../client.h"
#include "codec.h"
@@ -87,13 +87,13 @@ static bool codec_client_handle_packet(struct lia_client_handler *handler, struc
switch (mode) {
case CAMU_NORMAL: {
if (codec->dec) {
-#ifdef CAMU_DIRECT_MODE
- success = push_packet(codec, (struct nn_buffer *)packet->opaque);
-#else
- struct nn_buffer buffer;
- nn_packet_read_buffer(packet, &buffer);
- success = push_packet(codec, &buffer);
-#endif
+ if (packet->opaque) {
+ success = push_packet(codec, (struct nn_buffer *)packet->opaque);
+ } else {
+ struct nn_buffer buffer;
+ nn_packet_read_buffer(packet, &buffer);
+ success = push_packet(codec, &buffer);
+ }
} else {
success = true;
}
diff --git a/src/liana/handlers/codec_server.c b/src/liana/handlers/codec_server.c
index dcbfe17..7868a42 100644
--- a/src/liana/handlers/codec_server.c
+++ b/src/liana/handlers/codec_server.c
@@ -63,8 +63,8 @@ static void codec_server_write_info(struct lia_server_handler *handler, struct n
switch (stream->mode) {
case CAMU_NORMAL: {
struct camu_video_format *fmt = &stream->video.fmt;
- nn_packet_write_s32(packet, fmt->width);
- nn_packet_write_s32(packet, fmt->height);
+ nn_packet_write_u32(packet, fmt->width);
+ nn_packet_write_u32(packet, fmt->height);
nn_packet_write_s32(packet, fmt->format);
break;
}
@@ -78,7 +78,7 @@ static void codec_server_write_info(struct lia_server_handler *handler, struct n
}
}
-static void codec_server_subscribe(struct lia_server_handler *handler, s32 mask)
+static void codec_server_subscribe(struct lia_server_handler *handler, u32 mask)
{
struct lia_codec_server *codec = (struct lia_codec_server *)handler;
codec->demux->subscribed = mask;
diff --git a/src/liana/handlers/dvd.h b/src/liana/handlers/dvd.h
index 4b71ec8..dd5ac0a 100644
--- a/src/liana/handlers/dvd.h
+++ b/src/liana/handlers/dvd.h
@@ -1,9 +1,10 @@
#pragma once
-#include <dvdnav/dvdnav.h>
-
#include "../handler.h"
+#ifdef LIANA_SERVER
+#include <dvdnav/dvdnav.h>
+
struct lia_dvd_server {
struct lia_server_handler handler;
struct cch_handle *handle;
@@ -11,9 +12,13 @@ struct lia_dvd_server {
struct nn_buffer buffer;
};
+struct lia_server_handler *lia_dvd_server_create(void);
+#endif
+
+#ifdef LIANA_CLIENT
struct lia_dvd_client {
struct lia_client_handler handler;
};
-struct lia_server_handler *lia_dvd_server_create(void);
struct lia_client_handler *lia_dvd_client_create(void);
+#endif
diff --git a/src/liana/handlers/dvd_server.c b/src/liana/handlers/dvd_server.c
index afb2c07..f564325 100644
--- a/src/liana/handlers/dvd_server.c
+++ b/src/liana/handlers/dvd_server.c
@@ -53,7 +53,7 @@ static void dvd_server_write_info(struct lia_server_handler *handler, struct nn_
(void)packet;
}
-static void dvd_server_subscribe(struct lia_server_handler *handler, s32 mask)
+static void dvd_server_subscribe(struct lia_server_handler *handler, u32 mask)
{
(void)handler;
(void)mask;
diff --git a/src/liana/server.c b/src/liana/server.c
index 6459a0e..24c4bfc 100644
--- a/src/liana/server.c
+++ b/src/liana/server.c
@@ -152,11 +152,9 @@ static void data_connection_closed_callback(void *userdata, struct nn_packet_str
}
}
-static void subscribe_packet_callback(void *userdata, struct nn_packet_stream *stream, struct nn_packet *packet)
+static void start_connection_handler(struct lia_node_connection *conn, u32 mask)
{
- struct lia_node_connection *conn = (struct lia_node_connection *)userdata;
- s32 mask = nn_packet_read_s32(packet);
- nn_packet_stream_return_packet(stream, packet);
+ struct nn_packet_stream *stream = conn->stream;
conn->handler->subscribe(conn->handler, mask);
stream->packet_callback = discard_packet_callback;
stream->packet_sent_callback = data_packet_sent_callback;
@@ -165,6 +163,14 @@ static void subscribe_packet_callback(void *userdata, struct nn_packet_stream *s
nn_thread_create(&conn->thread, handler_thread, conn);
}
+static void subscribe_packet_callback(void *userdata, struct nn_packet_stream *stream, struct nn_packet *packet)
+{
+ struct lia_node_connection *conn = (struct lia_node_connection *)userdata;
+ u32 mask = nn_packet_read_u32(packet);
+ nn_packet_stream_return_packet(stream, packet);
+ start_connection_handler(conn, mask);
+}
+
static void subscribe_packet_sent_callback(void *userdata, struct nn_packet *packet)
{
(void)userdata;
@@ -183,7 +189,7 @@ static void handle_connection(struct lia_node_connection *conn, struct nn_packet
{
struct nn_packet_stream *stream = conn->stream;
- s32 mask = nn_packet_read_s32(packet);
+ u32 mask = nn_packet_read_u32(packet);
u64 seek_pos = nn_packet_read_u64(packet);
al_assert(!conn->ref);
@@ -204,12 +210,8 @@ static void handle_connection(struct lia_node_connection *conn, struct nn_packet
stream->connection_closed_callback = subscribe_connection_closed_callback;
nn_packet_stream_send_packet(stream, rpacket);
} else {
+ start_connection_handler(conn, mask);
conn->handler->subscribe(conn->handler, mask);
- stream->packet_callback = discard_packet_callback;
- stream->packet_sent_callback = data_packet_sent_callback;
- stream->packets_sent_callback = data_packets_sent_callback;
- stream->connection_closed_callback = data_connection_closed_callback;
- nn_thread_create(&conn->thread, handler_thread, conn);
}
}
diff --git a/src/libsink/common.h b/src/libsink/common.h
index 0bb3ded..254e246 100644
--- a/src/libsink/common.h
+++ b/src/libsink/common.h
@@ -1,7 +1,5 @@
#pragma once
-#define CAMU_SINK_LOCAL
-
enum {
CAMU_SINK_SET = 0,
CAMU_SINK_PAUSE,
diff --git a/src/libsink/sink.c b/src/libsink/sink.c
index 03dd379..716990e 100644
--- a/src/libsink/sink.c
+++ b/src/libsink/sink.c
@@ -2,21 +2,21 @@
#include <nnwt/multiplex.h>
#include "../server/common.h"
-
-#include "../liana/list.h"
-#include "../liana/handler.h"
-
#include "../buffer/common.h"
+#include "../liana/list.h"
#include "sink.h"
#include "common.h"
+#define CAMU_SINK_LOCAL
//#define CAMU_SINK_ONESHOT
//#define CAMU_SINK_TRACE
#ifndef CAMU_SINK_NO_VIDEO
+#ifdef CAMU_RENDERER_LIBPLACEBO
#include "../render/renderer_libplacebo.h"
#endif
+#endif
// Requested state of the sinks outputs.
enum {
@@ -91,7 +91,7 @@ enum {
#define VIDEO_IS_SINGLE_FRAME(entry) camu_video_buffer_is_single_frame(&(entry)->video.buf)
#endif
-#if defined CAMU_SCREEN_THREADED && defined CAMU_MIXER_THREADED_START_STOP
+#if defined CAMU_SCREEN_THREADED && defined CAMU_MIXER_THREADED
#define BLOCKING_SLEEP(delay) nn_thread_sleep(delay)
#else
#define BLOCKING_SLEEP(delay) nn_event_loop_sleep(sink->loop, delay)
@@ -151,7 +151,9 @@ static void remove_entry_audio_buffer(struct camu_sink *sink, struct camu_sink_e
{
al_assert(!entry->ended && entry->audio.state != BUFFER_ENDED);
al_assert(entry->audio.state != BUFFER_INIT);
+
if (entry->audio.state == BUFFER_ADDED) {
+ entry->audio.state = BUFFER_SET_OR_BUFFERED;
#ifdef CAMU_MIXER_THREADED_START_STOP
sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_AUDIO, &entry->audio.buf);
#else
@@ -161,7 +163,6 @@ static void remove_entry_audio_buffer(struct camu_sink *sink, struct camu_sink_e
.opaque = entry
});
#endif
- entry->audio.state = BUFFER_SET_OR_BUFFERED;
} else if (entry->audio.state == BUFFER_SET_OR_BUFFERED) {
entry->audio.state = BUFFER_CONFIGURED;
} else if (entry->audio.state == BUFFER_QUEUED) {
@@ -175,9 +176,10 @@ static void remove_entry_video_buffer(struct camu_sink *sink, struct camu_sink_e
// Don't assert !entry->ended here because of single frame handling.
al_assert(entry->video.state != BUFFER_ENDED);
al_assert(entry->video.state != BUFFER_INIT);
+
if (entry->video.state == BUFFER_ADDED) {
- sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_VIDEO, &entry->video.buf);
entry->video.state = BUFFER_SET_OR_BUFFERED;
+ sink->callback(sink->userdata, CAMU_SINK_REMOVE_BUFFER, CAMU_SINK_VIDEO, &entry->video.buf);
} else if (entry->video.state == BUFFER_SET_OR_BUFFERED) {
entry->video.state = BUFFER_CONFIGURED;
} else if (entry->video.state == BUFFER_QUEUED) {
@@ -235,7 +237,7 @@ static void add_or_queue_entry(struct camu_sink_entry *entry)
static void sink_local_pause(struct camu_sink *sink, struct camu_sink_entry *entry)
{
if (camu_clock_is_paused(&entry->clock)) {
- entry->audio.buffer_paused = false;
+ entry->buffers_paused = false;
camu_clock_resume(&entry->clock, 0);
if (!AUDIO_EMPTY(entry) && sink->audio.state == SINK_PAUSED) {
sink->callback(sink->userdata, CAMU_SINK_START, CAMU_SINK_AUDIO, NULL);
@@ -248,7 +250,7 @@ static void sink_local_pause(struct camu_sink *sink, struct camu_sink_entry *ent
}
#endif
} else {
- entry->audio.buffer_paused = true;
+ entry->buffers_paused = true;
camu_clock_pause(&entry->clock, 0);
#ifndef CAMU_SINK_NO_VIDEO
if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry) && sink->video.state == SINK_PLAYING) {
@@ -598,90 +600,102 @@ static void maybe_cleanup_old_entries(struct camu_sink *sink)
void add_audio_if_set_and_buffered(struct camu_sink_entry *entry)
{
al_assert(!entry->ended);
- al_assert(entry->audio.state != BUFFER_ADDED);
+ al_assert(entry->audio.state != BUFFER_INIT &&
+ (entry->audio.state != BUFFER_QUEUED) &&
+ (entry->audio.state != BUFFER_ADDED));
+
if (entry->audio.state == BUFFER_ENDED) {
al_log_warn("sink", "Tried to add an ended audio buffer.");
return;
}
+
if (entry->audio.state == BUFFER_CONFIGURED) {
entry->audio.state = BUFFER_SET_OR_BUFFERED;
} else if (entry->audio.state == BUFFER_SET_OR_BUFFERED) {
entry->audio.state = BUFFER_ADDED;
- bool ignore_video = true;
+
#ifndef CAMU_SINK_NO_VIDEO
- ignore_video = VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry);
- if (ignore_video) {
+ if (VIDEO_EMPTY(entry)) {
struct camu_sink *sink = entry->sink;
sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, CAMU_SINK_VIDEO, NULL);
}
#endif
- camu_audio_buffer_set_no_video(&entry->audio.buf, ignore_video);
-#ifdef CAMU_SINK_LOCAL
- // If we're local we don't have to worry about syncing audio-only entries.
- camu_audio_buffer_set_ignore_desync(&entry->audio.buf, ignore_video);
-#else
- // Force resync.
- camu_audio_buffer_unpause(&entry->audio.buf);
-#endif
if (VIDEO_ADDED_OR_EMPTY(entry)) {
- add_entry_audio_buffer(entry);
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = entry->audio.buffer_paused ? STOP : START,
- .value.i = CAMU_SINK_AUDIO
- });
#ifndef CAMU_SINK_NO_VIDEO
- if (!ignore_video) {
- // Single frames are unconditionally added in add_video_if_set_and_buffered().
+ bool single_frame = VIDEO_IS_SINGLE_FRAME(entry);
+
+ // Single frames are unconditionally added in add_video_if_set_and_buffered().
+ if (!VIDEO_EMPTY(entry) && !single_frame) {
add_entry_video_buffer(entry);
}
#endif
+ add_entry_audio_buffer(entry);
+
+ // This entry could be in previous. This is well defined but confusing.
maybe_remove_previous(entry->sink);
+
#ifndef CAMU_SINK_NO_VIDEO
- if (VIDEO_EMPTY(entry)) {
- // This must come after maybe_remove_previous().
+ // This must come after maybe_remove_previous().
+ if (!single_frame) {
queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = STOP,
+ .op = VIDEO_EMPTY(entry) || entry->buffers_paused ? STOP : START,
.value.i = CAMU_SINK_VIDEO
});
}
#endif
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = entry->buffers_paused ? STOP : START,
+ .value.i = CAMU_SINK_AUDIO
+ });
}
-
}
}
#ifndef CAMU_SINK_NO_VIDEO
void add_video_if_set_and_buffered(struct camu_sink_entry *entry)
{
- al_assert(entry->video.state != BUFFER_ADDED);
+ al_assert(entry->video.state != BUFFER_INIT &&
+ (entry->video.state != BUFFER_QUEUED) &&
+ (entry->video.state != BUFFER_ADDED));
+
if (entry->video.state == BUFFER_ENDED) {
al_log_warn("sink", "Tried to add an ended video buffer.");
return;
}
+
if (entry->video.state == BUFFER_CONFIGURED) {
entry->video.state = BUFFER_SET_OR_BUFFERED;
} else if (entry->video.state == BUFFER_SET_OR_BUFFERED) {
entry->video.state = BUFFER_ADDED;
+
bool single_frame = VIDEO_IS_SINGLE_FRAME(entry);
+
if (AUDIO_ADDED_OR_EMPTY(entry)) {
- if (entry->audio.state == BUFFER_ADDED) {
+ add_entry_video_buffer(entry);
+ if (!AUDIO_EMPTY(entry)) {
add_entry_audio_buffer(entry);
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = entry->audio.buffer_paused ? STOP : START,
- .value.i = CAMU_SINK_AUDIO
- });
}
- add_entry_video_buffer(entry);
+
+ // This entry could be in previous.
maybe_remove_previous(entry->sink);
+
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = single_frame || entry->buffers_paused ? STOP : START,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = AUDIO_EMPTY(entry) || entry->buffers_paused ? STOP : START,
+ .value.i = CAMU_SINK_AUDIO
+ });
} else if (single_frame) {
add_entry_video_buffer(entry);
+ // Stopping video here is needed if skipping from a video to an image.
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_VIDEO
+ });
}
- // Stopping video here is needed if skipping from a video to an image.
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = single_frame ? STOP : START,
- .value.i = CAMU_SINK_VIDEO
- });
}
}
#endif
@@ -714,14 +728,30 @@ static void switch_to(struct camu_sink *sink, struct camu_sink_entry *target)
if (!target->ended) {
add_or_queue_entry(target);
+ } else {
#ifndef CAMU_SINK_NO_VIDEO
- } else if (VIDEO_IS_SINGLE_FRAME(target)) {
- add_video_if_set_and_buffered(target);
+ if (VIDEO_IS_SINGLE_FRAME(target)) {
+ add_video_if_set_and_buffered(target);
+ } else {
+ // @TODO: current-less
+ sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, CAMU_SINK_VIDEO, NULL);
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ }
#endif
+ queue_cmd(sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_AUDIO
+ });
}
sink->current = target;
sink->current->audio.ignore_paused = false;
+ if (!sink->current->buffers_paused) {
+ camu_audio_buffer_unpause(&sink->current->audio.buf);
+ }
}
#ifndef CAMU_SINK_LOCAL
@@ -811,7 +841,7 @@ static void audio_buffer_callback(void *userdata, u8 op)
remove_entry_audio_buffer(sink, entry);
}
// This assert likely doesn't matter due to the handling of the ENDED state.
- //al_assert(entry->audio.state == BUFFER_SET_OR_BUFFERED);
+ al_assert(entry->audio.state == BUFFER_SET_OR_BUFFERED);
entry->audio.state = BUFFER_ENDED;
bool end_entry = VIDEO_ENDED_OR_EMPTY(entry);
#ifndef CAMU_SINK_NO_VIDEO
@@ -859,7 +889,7 @@ static void video_buffer_callback(void *userdata, u8 op)
if (entry->video.state == BUFFER_ADDED) {
remove_entry_video_buffer(sink, entry);
}
- //al_assert(entry->video.state == BUFFER_SET_OR_BUFFERED);
+ al_assert(entry->video.state == BUFFER_SET_OR_BUFFERED);
entry->video.state = BUFFER_ENDED;
if (AUDIO_ENDED_OR_EMPTY(entry)) {
swapped = end_entry_and_advance_queue(sink, entry);
@@ -881,17 +911,6 @@ static void video_buffer_callback(void *userdata, u8 op)
}
#endif
-static void maybe_unset_current(struct camu_sink *sink)
-{
- if (sink->current) {
- if (!sink->current->ended) {
- remove_entry_buffers(sink, sink->current);
- }
- // @TODO: A current-less state is not properly handled.
- sink->current = NULL;
- }
-}
-
static void clock_callback(void *userdata, u8 op)
{
struct camu_sink_entry *entry = (struct camu_sink_entry *)userdata;
@@ -909,11 +928,12 @@ static void clock_callback(void *userdata, u8 op)
}
}
-static void evaluate_and_set_latency(struct camu_sink *sink, struct camu_sink_entry *entry)
+static void evaluate_and_set_buffer_params(struct camu_sink *sink, struct camu_sink_entry *entry)
{
#ifdef CAMU_SINK_LOCAL
#ifndef CAMU_SINK_NO_VIDEO
- if (!AUDIO_EMPTY(entry) && !VIDEO_EMPTY(entry)) {
+ bool ignore_video = VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry);
+ if (!AUDIO_EMPTY(entry) && !ignore_video) {
f64 audio = camu_mixer_get_latency(sink->audio.mixer);
s32 frames = audio / entry->video.buf.avg_frame_duration;
frames -= sink->video.renderer->get_latency(sink->video.renderer);
@@ -922,7 +942,11 @@ static void evaluate_and_set_latency(struct camu_sink *sink, struct camu_sink_en
#else
(void)sink;
(void)entry;
+ bool ignore_video = true;
#endif
+ // If we're local we don't have to worry about syncing audio-only entries.
+ camu_audio_buffer_set_ignore_desync(&entry->audio.buf, ignore_video);
+ camu_audio_buffer_set_no_video(&entry->audio.buf, ignore_video);
#else
// To sync clients with differing audio latencies our only option is to factor the mixer
// latency directly into the audio buffer.
@@ -933,8 +957,12 @@ static void evaluate_and_set_latency(struct camu_sink *sink, struct camu_sink_en
frames += sink->video.renderer->get_latency(sink->video.renderer);
camu_video_buffer_set_latency(&entry->video.buf, frames);
}
+ bool ignore_video = VIDEO_EMPTY(entry) || VIDEO_IS_SINGLE_FRAME(entry);
+#else
+ bool ignore_video = true;
#endif
camu_audio_buffer_set_latency(&entry->audio.buf, audio);
+ camu_audio_buffer_set_no_video(&entry->audio.buf, ignore_video);
#endif
}
@@ -957,7 +985,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
struct camu_sink *sink = entry->sink;
switch (op) {
case LIANA_CLIENT_CONFIGURE: {
- // No data can be sent until all selected streams are configured.
+ // No data will be sent until all selected streams are configured.
switch (stream->type) {
case CAMU_STREAM_AUDIO:
entry->audio.track = (struct lia_vcr_track *)opaque;
@@ -974,9 +1002,6 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
} else {
al_assert(false);
}
- if (VIDEO_ADDED_OR_EMPTY(entry)) {
- evaluate_and_set_latency(sink, entry);
- }
nn_mutex_unlock(&sink->mutex);
break;
#ifndef CAMU_SINK_NO_VIDEO
@@ -995,31 +1020,37 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
} else {
al_assert(false);
}
- if (AUDIO_ADDED_OR_EMPTY(entry)) {
- evaluate_and_set_latency(sink, entry);
- }
nn_mutex_unlock(&sink->mutex);
break;
case CAMU_STREAM_SUBTITLE:
#ifdef CAMU_HAVE_FFMPEG
if (!camu_video_buffer_configure_subtitles(&entry->video.buf, stream->av.stream->codecpar)) {
- // @TODO:
+ al_log_warn("sink", "Video buffer failed to configure subtitles.");
}
#endif
break;
case CAMU_STREAM_ATTACHMENT: {
#ifdef CAMU_HAVE_FFMPEG
+#ifdef CAMU_RENDERER_LIBPLACEBO
struct camu_renderer_lp *lp = (struct camu_renderer_lp *)sink->video.renderer;
AVDictionaryEntry *entry = av_dict_get(stream->av.stream->metadata, "filename", NULL, 0);
AVCodecParameters *codecpar = stream->av.stream->codecpar;
ass_add_font(lp->ass, entry->value, (const char *)codecpar->extradata, codecpar->extradata_size);
#endif
+#endif
break;
}
#endif
}
break;
}
+ case LIANA_CLIENT_CONFIGURE_COMPLETE: {
+ // All present buffers were configured.
+ nn_mutex_lock(&sink->mutex);
+ evaluate_and_set_buffer_params(sink, entry);
+ nn_mutex_unlock(&sink->mutex);
+ break;
+ }
case LIANA_CLIENT_DATA: {
struct camu_codec_frame *frame = (struct camu_codec_frame *)opaque;
switch (stream->type) {
@@ -1098,7 +1129,9 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
}
#endif
- // Resolve any queued REMOVE_BUFFER requests before waiting.
+ // Resolve any queued REMOVE_BUFFER requests before blocking.
+ // This is why we are safe to block the event loop thread even if
+ // MIXER_THREADED_START_STOP is not set.
run_queue_by_opaque(sink, entry);
nn_mutex_unlock(&sink->mutex);
@@ -1112,7 +1145,7 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
) { BLOCKING_SLEEP(NNWT_TS_FROM_USEC(2000)); }
// Reset possible ENDED state here in case the entry ended at
- // some pointer after unlocking to block above.
+ // some point after unlocking to block above.
nn_mutex_lock(&sink->mutex);
if (reconnect) {
@@ -1205,19 +1238,33 @@ static void client_callback(void *userdata, u8 op, struct camu_codec_stream *str
nn_mutex_lock(&sink->mutex);
if (entry == sink->target) {
- maybe_unset_current(sink);
+ // @TODO: current-less
+ // current could be paused and targeting this entry.
+ // That could cause an error in the logic of set_command_callback()
+ // where we check if target (prev_target) is set.
sink->target = NULL;
} else if (entry == sink->current) {
if (sink->reconnecting) {
al_assert(sink->reconnecting == sink->current);
sink->reconnecting = NULL;
}
- sink->current = NULL;
if (sink->target) {
switch_to(sink, sink->target);
sink->target = NULL;
+ } else {
+ // @TODO: current-less
+ sink->current = NULL;
+ sink->callback(sink->userdata, CAMU_SINK_REFRESH_VIDEO, CAMU_SINK_VIDEO, NULL);
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_AUDIO
+ });
}
- // If current was never fully added we need to do this here.
+ // If current was never fully added we need to call this here.
maybe_remove_previous(sink);
} else if (entry == sink->queued) {
sink->queued = NULL;
@@ -1249,6 +1296,7 @@ static struct camu_sink_entry *create_entry(struct camu_sink *sink, u32 id)
entry->ended = false;
camu_clock_init(&entry->clock, clock_callback, entry);
+ entry->buffers_paused = false;
entry->audio.state = BUFFER_INIT;
entry->audio.ignore_paused = false;
@@ -1293,7 +1341,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
u8 op = nn_packet_read_u8(packet);
if (op == LIANA_SINK_UNSET) {
- maybe_unset_current(sink);
+ // @TODO: current-less
goto out;
}
@@ -1327,7 +1375,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
camu_clock_set(&entry->clock, seek_pos / 1000000.0);
// ended here does not map to entry->ended, we use it as a hint.
if (!ended) {
- entry->audio.buffer_paused = pause == LIANA_PAUSE_NONE || pause == LIANA_PAUSE_PAUSE;
+ entry->buffers_paused = pause == LIANA_PAUSE_NONE || pause == LIANA_PAUSE_PAUSE;
} else {
camu_clock_resume(&entry->clock, 0);
}
@@ -1363,7 +1411,7 @@ static bool set_command_callback(void *userdata, struct nn_rpc_connection *conn,
if (!entry->ended && camu_clock_is_paused(&entry->clock)) {
// This will resume user-paused entries, but whatever.
- entry->audio.buffer_paused = false;
+ entry->buffers_paused = false;
entry->audio.ignore_paused = false;
camu_clock_resume(&entry->clock, 0);
}
@@ -1473,17 +1521,31 @@ static bool pause_command_callback(void *userdata, struct nn_rpc_connection *con
#else
switch (pause) {
case LIANA_PAUSE_PAUSE:
- entry->audio.buffer_paused = true;
+ entry->buffers_paused = true;
camu_clock_pause(&entry->clock, at);
+ if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) {
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = STOP,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ }
break;
case LIANA_PAUSE_RESUME:
- entry->audio.buffer_paused = false;
+ entry->buffers_paused = false;
camu_clock_resume(&entry->clock, at);
- camu_audio_buffer_unpause(&entry->audio.buf);
- queue_cmd(entry->sink, (struct camu_sink_cmd){
- .op = START,
- .value.i = CAMU_SINK_AUDIO
- });
+ if (!AUDIO_EMPTY(entry)) {
+ camu_audio_buffer_unpause(&entry->audio.buf);
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = START,
+ .value.i = CAMU_SINK_AUDIO
+ });
+ }
+ if (!VIDEO_EMPTY(entry) && !VIDEO_IS_SINGLE_FRAME(entry)) {
+ queue_cmd(entry->sink, (struct camu_sink_cmd){
+ .op = START,
+ .value.i = CAMU_SINK_VIDEO
+ });
+ }
break;
}
#endif
diff --git a/src/libsink/sink.h b/src/libsink/sink.h
index 6845c6d..9bb408d 100644
--- a/src/libsink/sink.h
+++ b/src/libsink/sink.h
@@ -40,16 +40,15 @@ enum {
struct camu_sink_entry {
u32 id;
+ struct lia_client client;
s32 sequence;
u16 lru;
bool ended;
u32 reset_id;
struct camu_clock clock;
- struct lia_client client;
+ bool buffers_paused;
struct {
u8 state;
- // Don't start the audio output for this entry.
- bool buffer_paused;
// Don't stop the audio output when this entry is paused.
bool ignore_paused;
struct camu_audio_buffer buf;
diff --git a/src/mixer/audio_miniaudio.c b/src/mixer/audio_miniaudio.c
index ba093f4..13f4b8f 100644
--- a/src/mixer/audio_miniaudio.c
+++ b/src/mixer/audio_miniaudio.c
@@ -75,9 +75,6 @@ static bool audio_miniaudio_init(struct camu_audio *audio, str *name)
ma_log_register_callback(&ma->log, ma_log_callback_init(miniaudio_log_callback, NULL));
ma->context_config = ma_context_config_init();
ma->context_config.pLog = &ma->log;
-#ifdef MA_ENABLE_ALSA
- ma->context_config.alsa.useVerboseDeviceEnumeration = MA_TRUE;
-#endif
#ifdef MA_ENABLE_PULSEAUDIO
char *c_str = al_str_to_c_str(name);
ma->context_config.pulse.pApplicationName = c_str;
@@ -85,6 +82,9 @@ static bool audio_miniaudio_init(struct camu_audio *audio, str *name)
#else
(void)name;
#endif
+#ifdef MA_ENABLE_ALSA
+ ma->context_config.alsa.useVerboseDeviceEnumeration = MA_TRUE;
+#endif
s32 ret = ma_context_init(backends, ARRAY_SIZE(backends), &ma->context_config, &ma->context);
if (ret != MA_SUCCESS) {
al_log_error("audio_miniaudio", "Failed to initialize context (%d).", ret);
@@ -177,9 +177,18 @@ static bool audio_miniaudio_configure_stream(struct camu_audio *audio, void *opa
ma->config.noPreSilencedOutputBuffer = MA_TRUE;
ma->config.noFixedSizedCallback = MA_TRUE;
ma->config.noClip = MA_TRUE;
+ ma->config.playback.shareMode = ma_share_mode_shared;
+#ifdef MA_ENABLE_PULSEAUDIO
+ ma->config.pulse.pStreamNamePlayback = ma->context_config.pulse.pApplicationName;
+#endif
+#ifdef MA_ENABLE_ALSA
+ ma->config.alsa.noAutoFormat = MA_TRUE;
+ ma->config.alsa.noAutoChannels = MA_TRUE;
+ ma->config.alsa.noAutoResample = MA_TRUE;
+#endif
#ifdef MA_ENABLE_WASAPI
- ma->config.wasapi.noAutoConvertSRC = MA_FALSE;
- ma->config.wasapi.noDefaultQualitySRC = MA_FALSE;
+ ma->config.wasapi.noAutoConvertSRC = MA_TRUE;
+ ma->config.wasapi.noDefaultQualitySRC = MA_TRUE;
#endif
#ifdef MA_ENABLE_AAUDIO
ma->config.aaudio.usage = ma_aaudio_usage_media;
diff --git a/src/render/meson.build b/src/render/meson.build
index 668c3d4..75cde7c 100644
--- a/src/render/meson.build
+++ b/src/render/meson.build
@@ -59,46 +59,49 @@ elif get_option('renderer') == 'libplacebo'
render_args += ['-DCAMU_RENDERER_LIBPLACEBO']
endif
-libass_opts = []
-if not is_windows
- libass_opts += ['fontconfig=enabled']
- # libass(e46aede):meson.build:126
- fontconfig = dependency('fontconfig',
- version: '>=2.10.92',
+libass = dependency('libass', required: false, allow_fallback: false)
+if not libass.found()
+ libass_opts = []
+ if not is_windows
+ libass_opts += ['fontconfig=enabled']
+ # libass(e46aede):meson.build:126
+ fontconfig = dependency('fontconfig',
+ version: '>=2.10.92',
+ default_options: [
+ 'doc=disabled',
+ 'nls=disabled',
+ 'tests=disabled',
+ 'tools=disabled',
+ 'cache-build=disabled'
+ ])
+ endif
+ # libass(e46aede):meson.build:90
+ fribidi = dependency('fribidi',
+ version: '>=0.19.1',
+ default_options: [
+ 'docs=false',
+ 'tests=false',
+ 'bin=false'
+ ])
+ # libass(e46aede):meson.build:84
+ # fontconfig(72b9a48):meson.build:28
+ freetype = dependency('freetype2',
+ version: '>=9.17.3',
+ default_options: [
+ 'harfbuzz=disabled'
+ ])
+ # freetype2(42608f7):meson.build:346
+ harfbuz = dependency('harfbuzz',
+ version: '>=2.0.0',
default_options: [
- 'doc=disabled',
- 'nls=disabled',
+ 'freetype=disabled',
'tests=disabled',
- 'tools=disabled',
- 'cache-build=disabled'
+ 'introspection=disabled',
+ 'docs=disabled',
+ 'utilities=disabled'
])
+ libass = dependency('libass', default_options: libass_opts)
endif
-# libass(e46aede):meson.build:90
-fribidi = dependency('fribidi',
- version: '>=0.19.1',
- default_options: [
- 'docs=false',
- 'tests=false',
- 'bin=false'
- ])
-# libass(e46aede):meson.build:84
-# fontconfig(72b9a48):meson.build:28
-freetype = dependency('freetype2',
- version: '>=9.17.3',
- default_options: [
- 'harfbuzz=disabled'
- ])
-# freetype2(42608f7):meson.build:346
-harfbuz = dependency('harfbuzz',
- version: '>=2.0.0',
- default_options: [
- 'freetype=disabled',
- 'tests=disabled',
- 'introspection=disabled',
- 'docs=disabled',
- 'utilities=disabled'
- ])
-libass = dependency('libass', default_options: libass_opts)
render_deps += [libass]
render = declare_dependency(sources: render_src, dependencies: render_deps, compile_args: render_args)
diff --git a/src/render/queue_libplacebo.c b/src/render/queue_libplacebo.c
index 22df3dc..165fc4b 100644
--- a/src/render/queue_libplacebo.c
+++ b/src/render/queue_libplacebo.c
@@ -3,6 +3,7 @@
#ifdef CAMU_HAVE_FFMPEG
#define PL_LIBAV_IMPLEMENTATION 1
#include <libplacebo/utils/libav.h>
+#include "../codec/ffmpeg/common.h"
#endif
#include "../buffer/video.h"
@@ -10,7 +11,7 @@
#include "queue_libplacebo.h"
#ifdef CAMU_HAVE_FFMPEG
-static bool queue_lp_configure_subtitiles(struct camu_frame_queue *queue, s32 width, s32 height, AVCodecParameters *codecpar)
+static bool queue_lp_configure_subtitiles(struct camu_frame_queue *queue, u32 width, u32 height, AVCodecParameters *codecpar)
{
struct camu_frame_queue_lp *lq = (struct camu_frame_queue_lp *)queue;
if (!lq->ass) return false;
@@ -86,7 +87,7 @@ static bool map_frame(pl_gpu gpu, pl_tex *tex, const struct pl_source_frame *src
out_frame->planes[0] = plane;
out_frame->repr = pl_color_repr_unknown;
out_frame->color = pl_color_space_monitor;
- out_frame->crop = (pl_rect2df){ 0, 0, frame->video.width, frame->video.height };
+ out_frame->crop = (pl_rect2df){ 0.f, 0.f, frame->video.width, frame->video.height };
//out_frame->profile =
camu_codec_frame_discard(frame);
@@ -168,7 +169,7 @@ static struct camu_overlay_lp *create_subtitle_overlay(pl_gpu gpu, ASS_Image *as
// https://github.com/mpv-player/mpv/blob/70aaba71d6e3071a732069a1d222d1eb4293faf2/sub/ass_mp.c#L195
current = (struct pl_overlay *)&overlay->overlays[overlay->num++];
struct pl_overlay_part *part = (struct pl_overlay_part *)&current->parts[0];
- part->src = (pl_rect2df){ 0, 0, ass_frame->w, ass_frame->h };
+ part->src = (pl_rect2df){ 0.f, 0.f, ass_frame->w, ass_frame->h };
part->dst = (pl_rect2df){
ass_frame->dst_x,
ass_frame->dst_y,
@@ -280,7 +281,7 @@ static void queue_lp_push_av_frame(struct camu_frame_queue *queue, AVFrame *fram
frame->opaque = lq;
pl_queue_push(lq->queue, &(struct pl_source_frame){
.pts = pts,
- .duration = frame->duration * av_q2d(stream->time_base),
+ .duration = camu_ff_frame_duration(frame) * av_q2d(stream->time_base),
.map = map_av_frame,
.unmap = unmap_av_frame,
.discard = discard_av_frame,
diff --git a/src/render/queue_tiger.c b/src/render/queue_tiger.c
index d21236d..df072d2 100644
--- a/src/render/queue_tiger.c
+++ b/src/render/queue_tiger.c
@@ -5,7 +5,7 @@
#define GL_UNPACK_ROW_LENGTH 0x0CF2
-static void upload_texture(struct camu_frame_queue_tiger *tq, u8 *data, s32 width, s32 height, s32 format, s32 linesize)
+static void upload_texture(struct camu_frame_queue_tiger *tq, u8 *data, u32 width, u32 height, s32 format, s32 linesize)
{
glGenTextures(1, &tq->texture_id);
glActiveTexture(GL_TEXTURE0);
@@ -13,7 +13,7 @@ static void upload_texture(struct camu_frame_queue_tiger *tq, u8 *data, s32 widt
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//if (frame->flags & CAMU_FRAME_DEVICE_ALLOCATED) {
// glBindBuffer(GL_PIXEL_UNPACK_BUFFER, tq->tr->texture_buffer);
//}
@@ -31,7 +31,7 @@ static void upload_texture(struct camu_frame_queue_tiger *tq, u8 *data, s32 widt
default:
al_assert(false);
}
- glGenerateMipmap(GL_TEXTURE_2D);
+ //glGenerateMipmap(GL_TEXTURE_2D);
}
static void queue_tiger_push(struct camu_frame_queue *queue, struct camu_codec_frame *frame, f64 pts)
diff --git a/src/render/renderer.h b/src/render/renderer.h
index c78e1c1..101dda0 100644
--- a/src/render/renderer.h
+++ b/src/render/renderer.h
@@ -19,7 +19,7 @@ struct camu_frame_queue;
struct camu_screen;
struct camu_renderer {
bool (*create_renderer)(
- struct camu_renderer *, s32 *, s32 *,
+ struct camu_renderer *, u32 *, u32 *,
#if defined CAMU_RENDERER_VULKAN
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
@@ -39,13 +39,11 @@ struct camu_renderer {
void *priv);
struct camu_frame_queue *(*create_queue)(struct camu_renderer *);
u32 (*get_latency)(struct camu_renderer *);
- void (*resize)(struct camu_renderer *, s32 *, s32 *, bool);
+ void (*resize)(struct camu_renderer *, u32 *, u32 *);
void (*render)(struct camu_renderer *, struct camu_screen *, bool);
#ifdef CAMU_HAVE_FFMPEG
s32 (*get_buffer2)(struct AVCodecContext *s, AVFrame *frame, s32 flags);
void *opaque;
#endif
- void *(*get_device_ptr)(struct camu_renderer *, size_t);
- void (*commit_device_ptr)(struct camu_renderer *);
void (*free)(struct camu_renderer **);
};
diff --git a/src/render/renderer_libplacebo.c b/src/render/renderer_libplacebo.c
index 0ff0326..2c632ee 100644
--- a/src/render/renderer_libplacebo.c
+++ b/src/render/renderer_libplacebo.c
@@ -13,7 +13,17 @@
static f32 clear_color[4] = { 0.f, 0.f, 0.f, 1.f };
-static void renderer_lp_resize(struct camu_renderer *renderer, s32 *width, s32 *height, bool double_swap)
+static inline void pl_swapchain_resize_compat(pl_swapchain swapchain, u32 *width, u32 *height)
+{
+ s32 req_width = (s32)*width;
+ s32 req_height = (s32)*height;
+ pl_swapchain_resize(swapchain, &req_width, &req_height);
+ al_assert(req_width > 0 && req_height > 0);
+ *width = (u32)req_width;
+ *height = (u32)req_height;
+}
+
+static void renderer_lp_resize(struct camu_renderer *renderer, u32 *width, u32 *height)
{
struct camu_renderer_lp *lr = (struct camu_renderer_lp *)renderer;
if (lr->swapchain) {
@@ -21,8 +31,7 @@ static void renderer_lp_resize(struct camu_renderer *renderer, s32 *width, s32 *
pl_swapchain_submit_frame(lr->swapchain);
lr->have_frame = false;
}
- pl_swapchain_resize(lr->swapchain, width, height);
- lr->double_swap = double_swap;
+ pl_swapchain_resize_compat(lr->swapchain, width, height);
}
}
@@ -36,7 +45,7 @@ static void log_callback(void *userdata, enum pl_log_level level, const char *me
}
}
-static bool renderer_lp_create_renderer(struct camu_renderer *renderer, s32 *width, s32 *height,
+static bool renderer_lp_create_renderer(struct camu_renderer *renderer, u32 *width, u32 *height,
#if defined CAMU_RENDERER_VULKAN
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
VkResult (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
@@ -144,9 +153,7 @@ static bool renderer_lp_create_renderer(struct camu_renderer *renderer, s32 *wid
lr->renderer = pl_renderer_create(lr->logger, lr->gpu);
- pl_swapchain_resize(lr->swapchain, width, height);
- lr->double_swap = false;
-
+ pl_swapchain_resize_compat(lr->swapchain, width, height);
lr->have_frame = false;
al_memset(&lr->params, 0, sizeof(struct pl_render_params));
@@ -220,11 +227,6 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
}
#endif
- if (lr->double_swap) {
- pl_swapchain_swap_buffers(lr->swapchain);
- lr->double_swap = false;
- }
-
if (!lr->have_frame) {
struct pl_swapchain_frame frame;
pl_swapchain_start_frame(lr->swapchain, &frame);
@@ -250,7 +252,10 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
do_gpu_finish |= weighted;
// Terrible hack. Let's us distinguish single frames with the same dimensions.
// Tied to a libplacebo patch to consider info_priv in the hash.
- lr->params.info_priv = video->buf;
+ // Add in last_pts to account for buffer resets. This is a completely
+ // broken "hash" of the f64 pts value but hopefully works for now.
+ // https://www.virtualdub.org/blog2/entry_259.html
+ lr->params.info_priv = video->buf + (intptr_t)*((u64 *)&video->buf->last_pts);
target->crop = mix.frames[0]->crop;
target->crop.x1 *= video->view.zoom / video->view.stretch;
target->crop.y1 *= video->view.zoom * video->view.stretch;
@@ -281,25 +286,29 @@ static void renderer_lp_render(struct camu_renderer *renderer, struct camu_scree
if (!scr->videos.count && !force) {
// Don't spin too hard on a potential error state.
- nn_thread_sleep(NNWT_TS_FROM_USEC(26));
+ //al_log_warn("render_libplacebo", "Spinning in renderer.");
+ nn_thread_sleep(NNWT_TS_FROM_USEC(128));
return;
}
pl_swapchain_submit_frame(lr->swapchain);
- pl_swapchain_swap_buffers(lr->swapchain);
lr->have_frame = false;
+ pl_swapchain_swap_buffers(lr->swapchain);
+
if (do_gpu_finish) {
// Block until render completes.
pl_gpu_finish(lr->gpu);
}
- f64 tick = nn_get_tick();
- f64 frame_time = tick - lr->last_render_tick;
- if (frame_time > (1.0 / 24.0)) {
- al_log_info("render_libplacebo", "FRAME_TIME: %fs", frame_time);
+ if (!force) {
+ f64 tick = nn_get_tick();
+ f64 frame_time = tick - lr->last_render_tick;
+ if (frame_time > (1.0 / 24.0)) {
+ al_log_info("render_libplacebo", "FRAME_TIME: %fs", frame_time);
+ }
+ lr->last_render_tick = tick;
}
- lr->last_render_tick = tick;
}
void renderer_lp_free(struct camu_renderer **renderer)
diff --git a/src/render/renderer_libplacebo.h b/src/render/renderer_libplacebo.h
index a4bd40e..38407bb 100644
--- a/src/render/renderer_libplacebo.h
+++ b/src/render/renderer_libplacebo.h
@@ -26,7 +26,6 @@ struct camu_renderer_lp {
pl_gpu gpu;
pl_log logger;
pl_swapchain swapchain;
- bool double_swap;
struct pl_frame target;
bool have_frame;
pl_renderer renderer;
diff --git a/src/render/renderer_tiger.c b/src/render/renderer_tiger.c
index 74db594..ab6e85b 100644
--- a/src/render/renderer_tiger.c
+++ b/src/render/renderer_tiger.c
@@ -19,7 +19,7 @@ static const GLfloat default_vertices[] = {
1.f, 1.f, 0.f, 1.f, 1.f
};
-static f32 clear_color[4] = { 0.0f, 0.0f, 0.0f, 1.f };
+static GLfloat clear_color[4] = { 0.f, 0.f, 0.f, 1.f };
static const GLchar *default_vs = \
"#version 100\n" \
@@ -86,7 +86,7 @@ static bool compile_shaders(struct camu_renderer_tiger *tr)
}
#endif
-static void renderer_tiger_resize(struct camu_renderer *renderer, s32 *width, s32 *height)
+static void renderer_tiger_resize(struct camu_renderer *renderer, u32 *width, u32 *height)
{
struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
(void)tr;
@@ -94,12 +94,16 @@ static void renderer_tiger_resize(struct camu_renderer *renderer, s32 *width, s3
(void)height;
}
-static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, s32 *width, s32 *height,
+static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, u32 *width, u32 *height,
#if defined CAMU_RENDERER_VULKAN
VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL (*get_vk_proc_address)(VkInstance, const char *),
void (*vk_create_surface)(void *, VkInstance, VkSurfaceKHR *),
const char *const *(*vk_get_extensions)(u32 *),
#elif defined CAMU_RENDERER_OPENGL
+#ifdef STELA_USE_EGL
+ EGLDisplay display,
+ EGLContext context,
+#endif
void (*get_gl_proc_address(char const *procname))(void),
bool (*gl_load_loader)(void *),
bool (*gl_make_current)(void *),
@@ -109,7 +113,11 @@ static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, s32 *
void *priv)
{
struct camu_renderer_tiger *tr = (struct camu_renderer_tiger *)renderer;
+ (void)width;
+ (void)height;
#ifdef CAMU_RENDERER_OPENGL
+ (void)display;
+ (void)context;
(void)get_gl_proc_address;
tr->load_loader = gl_load_loader;
@@ -120,9 +128,9 @@ static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, s32 *
tr->make_current(tr->priv);
- if (!tr->load_loader(tr->priv)) return false;
-
- tr->r.resize(&tr->r, width, height);
+ if (!tr->load_loader(tr->priv)) {
+ return false;
+ }
clear_color[0] = ((global_color_palette.colors[0] >> 16) & 0xff) / 255.f;
clear_color[1] = ((global_color_palette.colors[0] >> 8) & 0xff) / 255.f;
@@ -145,7 +153,7 @@ static bool renderer_tiger_create_renderer(struct camu_renderer *renderer, s32 *
glVertexAttribPointer(pos, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void *)0);
if (stl_gl_check_error()) {
al_log_error("render_tiger", "glVertexAttribPointer returned error, use GLES context.");
- al_assert(false);
+ al_assert_and_return(false);
}
glEnableVertexAttribArray(pos);
GLint texcoord = glGetAttribLocation(tr->program, "a_TexCoord");
@@ -216,11 +224,6 @@ static void set_uniform(struct camu_renderer_tiger *tr, const GLchar *name,
#endif
}
-#ifdef CAMU_RENDERER_OPENGL
-static mat4x4 model = { 0 };
-static GLuint texture0 = 0;
-#endif
-
static void renderer_tiger_render(struct camu_renderer *renderer, struct camu_screen *scr, bool force)
{
#ifdef CAMU_RENDERER_OPENGL
@@ -241,19 +244,23 @@ static void renderer_tiger_render(struct camu_renderer *renderer, struct camu_sc
}
#endif
- if (scr->videos.size > 0) {
+ if (scr->videos.count > 0) {
struct camu_screen_video *video = &al_array_last(scr->videos);
GLuint texture_id;
- if (camu_video_buffer_read(video->buf, &texture_id)) {
+ bool weighted;
+ if (camu_video_buffer_read(video->buf, &texture_id, &weighted)) {
glBindTexture(GL_TEXTURE_2D, texture_id);
+ mat4x4 model;
mat4x4_identity(model);
mat4x4_ortho(model, 0.f, (f32)scr->width, (f32)scr->height, 0.f, -1.f, 1.f);
mat4x4_translate_in_place(model, (f32)video->view.x_offset, (f32)video->view.y_offset, 0.f);
mat4x4_scale_aniso(model, model, (f32)video->view.zoom, (f32)video->view.zoom, 1.f);
mat4x4_scale_aniso(model, model, (f32)video->view.width, (f32)video->view.height, 1.f);
set_uniform(tr, "g_ModelViewProjectionMatrix", TYPE_MAT4, model);
+
+ GLuint texture0 = 0;
set_uniform(tr, "g_Texture0", TYPE_SAMPLER2D, &texture0);
glViewport(0, 0, scr->width, scr->height);
@@ -267,6 +274,7 @@ static void renderer_tiger_render(struct camu_renderer *renderer, struct camu_sc
tr->release_current(tr->priv);
}
+/*
static void *renderer_tiger_get_device_ptr(struct camu_renderer *renderer, size_t length)
{
(void)renderer;
@@ -289,6 +297,7 @@ static void renderer_tiger_commit_device_ptr(struct camu_renderer *renderer)
//glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
#endif
}
+*/
static void renderer_tiger_free(struct camu_renderer **renderer)
{
@@ -307,8 +316,6 @@ struct camu_renderer *camu_renderer_tiger_create(void)
tr->r.get_latency = renderer_tiger_get_latency;
tr->r.resize = renderer_tiger_resize;
tr->r.render = renderer_tiger_render;
- tr->r.get_device_ptr = renderer_tiger_get_device_ptr;
- tr->r.commit_device_ptr = renderer_tiger_commit_device_ptr;
tr->r.free = renderer_tiger_free;
return (struct camu_renderer *)tr;
}
diff --git a/src/screen/screen.c b/src/screen/screen.c
index b4f65c2..cb49188 100644
--- a/src/screen/screen.c
+++ b/src/screen/screen.c
@@ -5,6 +5,8 @@
#include "view.h"
#include "screen.h"
+#define SCREEN_INVALID_TS ((u64)-1)
+
static struct camu_view *get_view_from_mouse_pos(struct camu_screen *scr)
{
struct camu_view *view = NULL;
@@ -27,24 +29,20 @@ static void should_close_callback(void *userdata)
scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL);
}
-static void resize_callback(void *userdata, s32 width, s32 height)
+static void resize_callback(void *userdata, u32 width, u32 height)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
- bool fullscreen = scr->window->fullscreen;
if (scr->renderer) {
- // Checking if window->fullscreen changed here relies on the order of resize events.
- // If it's possible for a different resize to be buffered before the resize
- // of the fullscreen toggle, this may not work as expected.
- scr->renderer->resize(scr->renderer, &width, &height, fullscreen != scr->last_fullscreen);
+ scr->renderer->resize(scr->renderer, &width, &height);
}
- scr->width = width;
- scr->height = height;
- scr->last_fullscreen = fullscreen;
+ al_assert(width > 0 && height > 0);
+ scr->width = (u32)width;
+ scr->height = (u32)height;
struct camu_screen_video *video;
al_array_foreach_ptr(scr->videos, i, video) {
camu_view_calculate(&video->view, scr->width, scr->height);
}
- scr->force_refresh = true;
+ al_atomic_store(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
}
static void refresh_callback(void *userdata)
@@ -68,26 +66,25 @@ static bool pointer_pos_callback(void *userdata, f64 x, f64 y)
struct camu_screen *scr = (struct camu_screen *)userdata;
struct camu_view *view = get_view_from_mouse_pos(scr);
bool queue_refresh = false;
- if (view) {
- if (scr->flags & CAMU_SCREEN_DRAGGING) {
- if (!(scr->flags & CAMU_SCREEN_MODIFIER) &&
- (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE)) {
- f64 dx = x - scr->last_mouse_x;
- f64 dy = y - scr->last_mouse_y;
- if (camu_view_pan_simple(view, scr->width, scr->height, dx, dy)) {
- view->mode = CAMU_VIEW_DETACHED;
- queue_refresh = true;
- }
- if (fabs(dx) + fabs(dy) > 3.0) scr->last_click_ts = 0;
- } else {
- u64 now = nn_get_timestamp();
- if (!scr->last_click_ts) {
- scr->last_click_ts = now;
- }
- if (now - scr->last_seek_ts > 100000) {
- scr->last_seek_ts = now;
- seek_to_percent_at_pointer(scr);
- }
+ if (scr->flags & CAMU_SCREEN_DRAGGING) {
+ if (scr->flags & CAMU_SCREEN_MOD_CONTROL) {
+ u64 now = nn_get_timestamp();
+ if (scr->last_seek_ts == SCREEN_INVALID_TS) {
+ scr->last_seek_ts = now;
+ }
+ if (now - scr->last_seek_ts > 100000) {
+ scr->last_seek_ts = now;
+ seek_to_percent_at_pointer(scr);
+ }
+ } else if (view && scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) {
+ f64 dx = x - scr->last_mouse_x;
+ f64 dy = y - scr->last_mouse_y;
+ if (camu_view_pan_simple(view, scr->width, scr->height, dx, dy)) {
+ view->mode = CAMU_VIEW_DETACHED;
+ queue_refresh = true;
+ }
+ if (fabs(dx) + fabs(dy) > 3.0) {
+ scr->last_click_ts = SCREEN_INVALID_TS;
}
}
}
@@ -105,14 +102,17 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button)
case STELA_BUTTON_PRESSED:
scr->flags |= CAMU_SCREEN_DRAGGING;
scr->last_click_ts = nn_get_timestamp();
+ //scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
break;
case STELA_BUTTON_RELEASED:
scr->flags &= ~CAMU_SCREEN_DRAGGING;
- if (scr->last_click_ts && nn_get_timestamp() - scr->last_click_ts <= 300000) {
- if (scr->flags & CAMU_SCREEN_MODIFIER) {
+ //scr->callback(scr->userdata, CAMU_SCREEN_TOGGLE_PAUSE, NULL);
+ if (scr->last_click_ts != SCREEN_INVALID_TS &&
+ (nn_get_timestamp() - scr->last_click_ts <= 300000)) {
+ if (scr->flags & CAMU_SCREEN_MOD_CONTROL) {
seek_to_percent_at_pointer(scr);
} else {
- if (scr->last_mouse_x >= scr->width / 2.f) {
+ if (scr->last_mouse_x >= scr->width / 2.0) {
scr->callback(scr->userdata, CAMU_SCREEN_NEXT, NULL);
} else {
scr->callback(scr->userdata, CAMU_SCREEN_PREVIOUS, NULL);
@@ -125,10 +125,10 @@ static bool mouse_button_callback(void *userdata, u8 state, u8 button)
case STELA_MOUSE2:
switch (state) {
case STELA_BUTTON_PRESSED:
- scr->flags |= CAMU_SCREEN_MODIFIER;
+ scr->flags |= CAMU_SCREEN_MOD_CONTROL;
break;
case STELA_BUTTON_RELEASED:
- scr->flags &= ~CAMU_SCREEN_MODIFIER;
+ scr->flags &= ~CAMU_SCREEN_MOD_CONTROL;
break;
}
break;
@@ -157,7 +157,7 @@ static bool scroll_callback(void *userdata, f64 y)
{
struct camu_screen *scr = (struct camu_screen *)userdata;
y = -y / SCROLL_MULTIPLIER;
- if (!(scr->flags & CAMU_SCREEN_MODIFIER)) {
+ if (!(scr->flags & CAMU_SCREEN_MOD_CONTROL)) {
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
if (scr->flags & CAMU_SCREEN_ZOOM_PAN_SIMPLE) {
@@ -180,8 +180,11 @@ static bool key_callback(void *userdata, u8 state, u8 button)
case STELA_BUTTON_PRESSED:
//al_printf("%x\n", button);
switch (button) {
+ case 0x2a: // shift
+ scr->flags |= CAMU_SCREEN_MOD_SHIFT;
+ break;
case 0x1d: // control
- scr->flags |= CAMU_SCREEN_MODIFIER;
+ scr->flags |= CAMU_SCREEN_MOD_CONTROL;
break;
case 0x10: // q
scr->callback(scr->userdata, CAMU_SCREEN_CLOSE, NULL);
@@ -215,9 +218,23 @@ static bool key_callback(void *userdata, u8 state, u8 button)
case 0x13: { // r
struct camu_view *view = get_view_from_mouse_pos(scr);
if (view) {
- if (!(scr->flags & CAMU_SCREEN_MODIFIER)) {
- view->mode = CAMU_DEFAULT_VIEW;
- } else {
+ if (scr->flags & CAMU_SCREEN_MOD_SHIFT) {
+ switch (view->rotation) {
+ case CAMU_VIEW_ROTATION_0:
+ case CAMU_VIEW_ROTATION_360:
+ view->rotation = CAMU_VIEW_ROTATION_90;
+ break;
+ case CAMU_VIEW_ROTATION_270:
+ view->rotation = CAMU_VIEW_ROTATION_0;
+ break;
+ case CAMU_VIEW_ROTATION_180:
+ view->rotation = CAMU_VIEW_ROTATION_270;
+ break;
+ case CAMU_VIEW_ROTATION_90:
+ view->rotation = CAMU_VIEW_ROTATION_180;
+ break;
+ }
+ } else if (scr->flags & CAMU_SCREEN_MOD_CONTROL) {
switch (view->rotation) {
case CAMU_VIEW_ROTATION_0:
case CAMU_VIEW_ROTATION_360:
@@ -233,6 +250,8 @@ static bool key_callback(void *userdata, u8 state, u8 button)
view->rotation = CAMU_VIEW_ROTATION_0;
break;
}
+ } else {
+ view->mode = CAMU_DEFAULT_VIEW;
}
camu_view_calculate(view, scr->width, scr->height);
return true;
@@ -261,8 +280,11 @@ static bool key_callback(void *userdata, u8 state, u8 button)
break;
case STELA_BUTTON_RELEASED:
switch (button) {
+ case 0x2a: // shift
+ scr->flags &= ~CAMU_SCREEN_MOD_SHIFT;
+ break;
case 0x1d: // control
- scr->flags &= ~CAMU_SCREEN_MODIFIER;
+ scr->flags &= ~CAMU_SCREEN_MOD_CONTROL;
break;
}
default:
@@ -279,6 +301,10 @@ static void key_immediate_callback(void *userdata, u8 state, u8 button)
switch (button) {
case 0x21: // f
scr->window->toggle_fullscreen(scr->window);
+ // Least intrusive workaround I can think of for the egl resizing issue.
+ // Will only work for fullscreening, things like toggling floating state
+ // on sway are still broken.
+ al_atomic_store(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
break;
}
break;
@@ -301,10 +327,12 @@ bool camu_screen_init(struct camu_screen *scr, void *context)
scr->window->should_close_callback = should_close_callback;
scr->window->userdata = scr;
scr->renderer = NULL;
- scr->force_refresh = false;
+ al_atomic_store(u32)(&scr->force_refresh, 0, AL_ATOMIC_RELAXED);
scr->flags = CAMU_SCREEN_ZOOM_PAN_SIMPLE;
+ scr->last_click_ts = SCREEN_INVALID_TS;
scr->last_mouse_x = 0.0;
scr->last_mouse_y = 0.0;
+ scr->last_seek_ts = SCREEN_INVALID_TS;
al_array_init(scr->videos);
#ifdef CAMU_SCREEN_THREADED
al_array_init(scr->add_queue);
@@ -334,7 +362,6 @@ bool camu_screen_create_window(struct camu_screen *scr, const char *name)
}
scr->width = scr->window->width;
scr->height = scr->window->height;
- scr->last_fullscreen = false;
#ifdef STELA_EVENT_BUFFER
nn_thread_create(&scr->thread, event_thread, scr);
#endif
@@ -365,9 +392,9 @@ bool camu_screen_create_renderer(struct camu_screen *scr, struct camu_renderer *
static void add_buffer_internal(struct camu_screen *scr, struct camu_video_buffer *buf)
{
- struct camu_screen_video *rvideo;
- al_array_foreach_ptr(scr->videos, i, rvideo) {
- al_assert(rvideo->buf != buf);
+ struct camu_screen_video *added;
+ al_array_foreach_ptr(scr->videos, i, added) {
+ al_assert(added->buf != buf);
}
struct camu_screen_video video = {
.buf = buf
@@ -383,17 +410,17 @@ static void add_buffer_internal(struct camu_screen *scr, struct camu_video_buffe
#endif
} else {
video.view.mode = CAMU_DEFAULT_VIEW;
+ video.view.rotation = CAMU_VIEW_ROTATION_0;
video.view.width = fmt->width;
video.view.height = fmt->height;
- video.view.stretch = 1.0;
- video.view.rotation = CAMU_VIEW_ROTATION_0;
video.view.zindex = 0;
+ video.view.stretch = 1.0;
}
camu_view_calculate(&video.view, scr->width, scr->height);
#ifdef CAMU_SCREEN_THREADED
al_atomic_store(u8)(&buf->ref, 1, AL_ATOMIC_RELAXED);
#endif
-
+ //al_atomic_store(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
al_array_push(scr->videos, video);
}
@@ -428,7 +455,6 @@ static void run_queue_internal(struct camu_screen *scr)
add_buffer_internal(scr, buf);
}
scr->add_queue.count = 0;
- //scr->force_refresh = scr->videos.count == 0;
}
#endif
@@ -436,15 +462,15 @@ void camu_screen_add_buffer(struct camu_screen *scr, struct camu_video_buffer *b
{
#ifdef CAMU_SCREEN_THREADED
nn_mutex_lock(&scr->mutex);
- struct camu_video_buffer *rbuf;
- al_array_foreach(scr->add_queue, i, rbuf) {
- if (rbuf == buf) {
+ struct camu_video_buffer *queued;
+ al_array_foreach(scr->add_queue, i, queued) {
+ if (queued == buf) {
nn_mutex_unlock(&scr->mutex);
return;
}
}
- al_array_foreach_rev(scr->rem_queue, i, rbuf) {
- if (rbuf == buf) {
+ al_array_foreach_rev(scr->rem_queue, i, queued) {
+ if (queued == buf) {
al_array_remove_at(scr->rem_queue, i);
bool queue_empty = scr->add_queue.count + scr->rem_queue.count == 0;
if (queue_empty) {
@@ -467,15 +493,15 @@ void camu_screen_remove_buffer(struct camu_screen *scr, struct camu_video_buffer
{
#ifdef CAMU_SCREEN_THREADED
nn_mutex_lock(&scr->mutex);
- struct camu_video_buffer *rbuf;
- al_array_foreach(scr->rem_queue, i, rbuf) {
- if (rbuf == buf) {
+ struct camu_video_buffer *queued;
+ al_array_foreach(scr->rem_queue, i, queued) {
+ if (queued == buf) {
nn_mutex_unlock(&scr->mutex);
return;
}
}
- al_array_foreach_rev(scr->add_queue, i, rbuf) {
- if (rbuf == buf) {
+ al_array_foreach_rev(scr->add_queue, i, queued) {
+ if (queued == buf) {
al_array_remove_at(scr->add_queue, i);
bool queue_empty = scr->add_queue.count + scr->rem_queue.count == 0;
if (queue_empty) {
@@ -517,12 +543,13 @@ void camu_screen_clear(struct camu_screen *scr)
void camu_screen_set_state(struct camu_screen *scr, s32 state)
{
+ // @TODO: What to do if a buffer is queued for removal and we pause here.
al_atomic_store(s32)(&scr->state, state, AL_ATOMIC_RELAXED);
}
void camu_screen_force_refresh(struct camu_screen *scr)
{
- scr->force_refresh = true;
+ al_atomic_store(u32)(&scr->force_refresh, 1, AL_ATOMIC_RELAXED);
camu_screen_wake(scr);
}
@@ -552,9 +579,12 @@ bool camu_screen_tick(struct camu_screen *scr, bool *force)
al_assert(state != CAMU_SCREEN_STOPPED);
bool paused = state == CAMU_SCREEN_PAUSED;
bool do_render = camu_screen_poll(scr, paused) || !paused;
- do_render |= scr->force_refresh;
- *force = scr->force_refresh;
- scr->force_refresh = false;
+ u32 force_refresh = al_atomic_load(u32)(&scr->force_refresh, AL_ATOMIC_ACQUIRE);
+ do_render |= force_refresh > 0;
+ *force = force_refresh > 0;
+ if (force_refresh > 0) {
+ al_atomic_store(u32)(&scr->force_refresh, 0, AL_ATOMIC_RELEASE);
+ }
return do_render;
}
diff --git a/src/screen/screen.h b/src/screen/screen.h
index e0d1fc3..0b8ed33 100644
--- a/src/screen/screen.h
+++ b/src/screen/screen.h
@@ -18,9 +18,10 @@
#define CAMU_SCREEN_HEIGHT 700
enum {
- CAMU_SCREEN_MODIFIER = 1,
- CAMU_SCREEN_DRAGGING = 1 << 1,
- CAMU_SCREEN_ZOOM_PAN_SIMPLE = 1 << 2
+ CAMU_SCREEN_MOD_SHIFT = 1,
+ CAMU_SCREEN_MOD_CONTROL = 1 << 1,
+ CAMU_SCREEN_DRAGGING = 1 << 2,
+ CAMU_SCREEN_ZOOM_PAN_SIMPLE = 1 << 3
};
enum {
@@ -55,11 +56,10 @@ struct camu_screen {
struct nn_thread thread;
#endif
struct camu_renderer *renderer;
- bool force_refresh;
+ atomic(u32) force_refresh;
u32 flags;
- s32 width;
- s32 height;
- bool last_fullscreen;
+ u32 width;
+ u32 height;
u64 last_click_ts;
f64 last_mouse_y;
f64 last_mouse_x;
diff --git a/src/screen/view.c b/src/screen/view.c
index 0ca5f4c..b5fade2 100644
--- a/src/screen/view.c
+++ b/src/screen/view.c
@@ -15,34 +15,33 @@
#define VIEW_ON_ITS_SIDE(view) \
(view->rotation % CAMU_VIEW_ROTATION_180 == CAMU_VIEW_ROTATION_90)
-void camu_view_calculate(struct camu_view *view, s32 window_width, s32 window_height)
+void camu_view_calculate(struct camu_view *view, u32 window_width, u32 window_height)
{
- f64 width, height;
+ u32 width, height;
if (VIEW_ON_ITS_SIDE(view)) {
- width = (f64)view->height;
- height = (f64)view->width;
- view->stretch = height / width;
+ width = view->height;
+ height = view->width;
+ view->stretch = height / (f64)width;
} else {
- width = (f64)view->width;
- height = (f64)view->height;
+ width = view->width;
+ height = view->height;
view->stretch = 1.0;
}
- f64 frame_ratio = width / height;
+ f64 frame_ratio = width / (f64)height;
f64 ratio = window_width / (f64)window_height;
- view->ratio = ratio / frame_ratio;
switch (view->mode) {
case CAMU_VIEW_AUTOFIT:
if (frame_ratio > ratio) {
- view->zoom = window_width / width;
+ view->zoom = window_width / (f64)width;
} else {
- view->zoom = window_height / height;
+ view->zoom = window_height / (f64)height;
}
break;
case CAMU_VIEW_ZOOM:
if (frame_ratio < ratio) {
- view->zoom = window_width / width;
+ view->zoom = window_width / (f64)width;
} else {
- view->zoom = window_height / height;
+ view->zoom = window_height / (f64)height;
}
break;
default:
@@ -52,7 +51,7 @@ void camu_view_calculate(struct camu_view *view, s32 window_width, s32 window_he
view->y_offset = (window_height - (height * view->zoom)) / 2.0;
}
-bool camu_view_pan_simple(struct camu_view *view, s32 window_width, s32 window_height, f64 dx, f64 dy)
+bool camu_view_pan_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 dx, f64 dy)
{
f64 nx = view->x_offset + dx;
f64 ny = view->y_offset + dy;
@@ -68,7 +67,7 @@ bool camu_view_pan_simple(struct camu_view *view, s32 window_width, s32 window_h
return view_changed;
}
-bool camu_view_zoom_simple(struct camu_view *view, s32 window_width, s32 window_height, f64 x, f64 y, f64 v)
+bool camu_view_zoom_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 x, f64 y, f64 v)
{
x -= view->x_offset;
y -= view->y_offset;
diff --git a/src/screen/view.h b/src/screen/view.h
index 8cd3d28..30b6f64 100644
--- a/src/screen/view.h
+++ b/src/screen/view.h
@@ -21,17 +21,16 @@ enum {
struct camu_view {
u8 mode;
- s32 width;
- s32 height;
- f64 ratio;
+ u8 rotation;
+ u32 width;
+ u32 height;
+ s32 zindex;
f64 zoom;
f64 x_offset;
f64 y_offset;
f64 stretch;
- s32 rotation;
- s32 zindex;
};
-void camu_view_calculate(struct camu_view *view, s32 window_width, s32 window_height);
-bool camu_view_pan_simple(struct camu_view *view, s32 window_width, s32 window_height, f64 dx, f64 dy);
-bool camu_view_zoom_simple(struct camu_view *view, s32 window_width, s32 window_height, f64 x, f64 y, f64 v);
+void camu_view_calculate(struct camu_view *view, u32 window_width, u32 window_height);
+bool camu_view_pan_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 dx, f64 dy);
+bool camu_view_zoom_simple(struct camu_view *view, u32 window_width, u32 window_height, f64 x, f64 y, f64 v);
diff --git a/src/server/db.c b/src/server/db.c
index e2ad808..3d44e11 100644
--- a/src/server/db.c
+++ b/src/server/db.c
@@ -1,10 +1,11 @@
#include <al/log.h>
#include <nnwt/file.h>
-#include <jansson.h>
#include "server.h"
+#if 0
// This is still nothing.
+#include <jansson.h>
static void open_user(struct camu_server *server, struct nn_dir *camu_db, struct nn_dir_entry *dir)
{
@@ -33,6 +34,13 @@ static void open_user(struct camu_server *server, struct nn_dir *camu_db, struct
out:
al_str_free(&path);
}
+#endif
+static void open_user(struct camu_server *server, struct nn_dir *camu_db, struct nn_dir_entry *dir)
+{
+ (void)server;
+ (void)camu_db;
+ (void)dir;
+}
bool camu_db_open(struct camu_server *server, str *path)
{
diff --git a/src/server/server.c b/src/server/server.c
index fcacf1d..e22cf88 100644
--- a/src/server/server.c
+++ b/src/server/server.c
@@ -268,7 +268,6 @@ static void send_clients_entry_seeked(struct camu_server *server, struct lia_lis
static void list_callback(void *userdata, u8 op, struct lia_list_entry *entry, void *opaque)
{
struct camu_server *server = (struct camu_server *)userdata;
- (void)server;
struct camu_resource *resource = (struct camu_resource *)entry->opaque;
switch (op) {
case LIANA_LOAD_ENTRY:
@@ -295,6 +294,9 @@ static void list_callback(void *userdata, u8 op, struct lia_list_entry *entry, v
lia_node_close(resource->node);
break;
case LIANA_LIST_META:
+ if (server->meta_callback) {
+ server->meta_callback(server->userdata, *(u8 *)opaque, entry);
+ }
switch (*(u8 *)opaque) {
case LIANA_META_CURRENT_CHANGED:
al_log_info("server", "Now playing: %ls.", entry->name.data);
diff --git a/src/server/server.h b/src/server/server.h
index 210f8a4..db7d30c 100644
--- a/src/server/server.h
+++ b/src/server/server.h
@@ -45,6 +45,8 @@ struct camu_server {
struct camu_portal_bridge bridge;
struct camu_post_cache cache;
#endif
+ void (*meta_callback)(void *, u8, struct lia_list_entry *entry); // @TEMP
+ void *userdata;
};
void camu_server_init(struct camu_server *server, struct nn_event_loop *loop);
diff --git a/subprojects/ffmpeg.wrap b/subprojects/ffmpeg.wrap
index 779104a..602ebad 100644
--- a/subprojects/ffmpeg.wrap
+++ b/subprojects/ffmpeg.wrap
@@ -1,23 +1,27 @@
[wrap-file]
directory = ffmpeg-7.1
+#directory = ffmpeg-5.1.6
#directory = ffmpeg-7.0.2
#directory = ffmpeg-7.0.1
#directory = ffmpeg-7.0
#directory = ffmpeg-6.1.1
#directory = ffmpeg-5.1.4
source_url = https://ffmpeg.org/releases/ffmpeg-7.1.tar.gz
+#source_url = https://www.ffmpeg.org/releases/ffmpeg-5.1.6.tar.gz
#source_url = https://ffmpeg.org/releases/ffmpeg-7.0.2.tar.gz
#source_url = https://ffmpeg.org/releases/ffmpeg-7.0.1.tar.gz
#source_url = https://www.ffmpeg.org/releases/ffmpeg-7.0.tar.gz
#source_url = https://www.ffmpeg.org/releases/ffmpeg-6.1.1.tar.gz
#source_url = https://www.ffmpeg.org/releases/ffmpeg-5.1.4.tar.gz
source_filename = ffmpeg-7.1.tar.gz
+#source_filename = ffmpeg-5.1.6.tar.gz
#source_filename = ffmpeg-7.0.2.tar.gz
#source_filename = ffmpeg-7.0.1.tar.gz
#source_filename = ffmpeg-7.0.tar.gz
#source_filename = ffmpeg-6.1.1.tar.gz
#source_filename = ffmpeg-5.1.4.tar.gz
source_hash = 42a7dc0d1583885d1b8f6559fa7ce28f97acafea6803de6a8f73e3ba229348bd
+#source_hash = 2a963f9f627dc55cac7ce4d2bb2e063ce52c8ab57185d2b592a631c36be56fda
#source_hash = 1233b3a93dd7517cc3c56b72a67f64041c044848d981e3deff4bebffa25f1054
#source_hash = e273eae00b4f833595654c1ee2b2349240e0332f9dbf3c4cadd39df9819ff052
#source_hash = 943a2a28044947c17a905c39075494b0da46ec0795224c2c61eff986518321eb
diff --git a/subprojects/libalabaster.wrap b/subprojects/libalabaster.wrap
index 6ecfc88..5977202 100644
--- a/subprojects/libalabaster.wrap
+++ b/subprojects/libalabaster.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/libalabaster
-revision = d8141e0ca0c53a3e2d89f20e3c89cdec5a4593e4
+revision = 5f80381cc8d2875d27d14288b6f11c31a72fb3cf
depth = 1
diff --git a/subprojects/stela.wrap b/subprojects/stela.wrap
index 26184ba..dcbade6 100644
--- a/subprojects/stela.wrap
+++ b/subprojects/stela.wrap
@@ -1,4 +1,4 @@
[wrap-git]
url = https://git.akon.city/stela
-revision = a55bfe4c21858eef0eb1bddf4085fcfde1b7e07d
+revision = dda9ba19bb94947cf7d2f176f0d6760078598b69
depth = 1