Age | Commit message (Collapse) | Author |
|
finishing up mechanical change started in previous commit
|
|
mechanical change to get filenames more accurate, thread function
will be renamed in subsequent commit.
|
|
finishing up mechanical change started in previous commit
|
|
mechanical change to get filenames more accurate, thread function
will be renamed in subsequent commit.
|
|
s/for(/for (/g
|
|
|
|
Mostly trailing space/tab removals, no functional changes.
|
|
Since avd is unconditionally copied just a few lines down,
this copy is pointless and harms readability.
I think when it was originally written there was going to be an
else branch where the unconditional copy is currently, but never
bothered.
The avd shared variable should really get changed to an atomic,
removing its lock. To anyone reading: PRs welcome.
|
|
The existing code would leave the internal handle non-NULL when
the underlying close failed. In the event of a transparent close
when chapters switch failing, a subsequent explicit close is
expected, and the handles must be NULLed out for that explicit
close to not blow up because fclose() leaves the handle undefined
after errors. It's basically a use after free bug.
|
|
|
|
--rescue uninitialized structs fix
uv_align bounds check fix affecting odd-sized windows
|
|
Various functions rely on this stuff at least being zeroed, rmd.c
already had been fixed but rmd_rescue.c has been neglected.
|
|
This would potentially exceed the clip w/h on odd inputs because
it was wrongly adding the cliprect offset while comparing against
coordinates already made relative within the cliprect
|
|
--rescue mode doesn't set pdata->capture_frameno to the total
The whole --rescue initialization path is a snowflake and should
get reworked to share more in common with the normal startup.
reported by @xloem, thanks!
|
|
|
|
Spurious artifacts in the chroma channels when the damage
regions weren't aligned showed up because this function
wasn't actually doing anything.
|
|
This only applies to ALSA, but the av-sync precision is entirely
dependent on the granularity of what ALSA refers to as "periods".
The pdata->avd delay is decremented by the audio capture in
period aligned steps, meaning the smallest decrement it can
possibly do is the period size. Between these period sized
intervals, rmdGetFrames kind of flies blind if the FPS is high
enough for frametime to be substantially smaller than the period
time.
A smaller period does increase the ALSA capture overhead,
basically increasing the number of times ALSA returns data to
userspace, kind of like a higher interrupt rate. But it seems
negligible for a modern system.
|
|
mechanical file rename to plural form
|
|
Mechanical rename to more accurate plural form
|
|
Not sure why the inherited code prohibited this, but it doesn't
jive with my expectations to ignore SIGINT just because the
recording is paused.
|
|
This removes the timer thread entirely in favor of a coordinated
delay loop directly within rmdGetFrame.
When there's an audio stream to synchronize with, avd is
maintained by the pcm buffer updates, and the fps-derived
frametimes synchronize with the audio that way.
When there's no audio stream (--no-sound), avd is now maintained
synthetically via clock_gettime(CLOCK_MONOTONIC) coordinating
with the clock instead.
There's been some reworking of frame sampling/reusing and cloning
logic, which may need some refinement. But for now the tests
seem to show promise.
The old timer approach just increased the non-determinism by
adding more scheduler latency and influence unnecessarily.
|
|
|
|
Make the names reflect their units of microseconds
|
|
This changes the syncer thread to always be created for writers,
and makes it posix_fallocate() the cache file to the maximum
size.
When the syncer is disabled w/ms=0, it simply returns after the
posix_fallocate.
posix_fallocate errors are ignored, this is simply an
opportunistic optimization. I have some concern about the glibc
emulation mentioned in the man page, but writers are opened
O_WRONLY which the notes say cause the emulation to fail with
EBADF which is preferable, and I'm using O_WRONLY for the
writers.
|
|
In preparation for posix_fallocate() pre-allocation of cache
files, expect zeroes for the header when running off the end of
valid data into the only allocated space, and handle gracefully
as an EOF equivalent.
|
|
This adds a new flag:
--periodic-datasync-ms
with a default of 100ms.
When a new CacheFile is created for writing, and the datasync
period is non-zero, a thread is created for the sole purpose of
calling fdatasync() on the underlying fd in a loop separated by
sleeps of the specified duration.
The purpose of this is to prevent a large backlog of dirty
buffers from accumulating until the operating system's normal
background dirty sync kicks in. Depending on how the underlying
filesystem and storage stack is configured, these bulk writebacks
can result in very long stalls on a subsequent write operation.
This is easily observed on ext4+lvm+dmcrypt setups, even using a
simple test like `dd if=/dev/urandom of=testfile bs=8M`. Despite
having plenty of available buffers, once ext4's internal journal
fills while a bulk writeback is underway, dd's progress will
completely stall until the entire writeback is completed, usually
it's in the jbd2 wchan of do_get_write_access().
When this occurs during a recording by recordMyDesktop, the
result is dropping frames for the entire duration of the stall.
One thing recordMyDesktop could do to insulate from this is
perform its own buffering of sampled frames at the YUV stage,
with the cache writer consuming from this pool of buffered
frames. Then when the cache writer gets stalled on jbd2/dmcrypt
holdups, the buffer pool just grows instead of frames being
dropped. I may explore this option in the future, but for now
simply syncing regularly has been sufficient in my usage, as it
keeps the storage subsystem more continuously utilized and
spreads out the writebacks so they don't completely back up the
journal.
|
|
hopefully this is is all of it, will need more testing
I noticed during some non-full-shots tests that the dirty areas
didn't seem to be getting rounded up properly so my single pixel
window border's color was sometimes omitted on the right edge and
bottom...
|
|
|
|
This minimally switches all the ad-hoc image cache files handling
over to using CacheFile.
I left the audio cache alone for now as it seems to not be
compressed. It might make sense in the future to switch that
over as well, especially if I start adding features to CacheFile
like preallocating and async periodic fdatasync.
|
|
Throughout the existing cache-related code there's constant checking
if compression is active or not.
This commit introduces a CacheFile ADT with the intention of
replacing all the open coded stuff with simple calls into
rmdCacheFile* functions operating on CacheFile instances.
The management of split up cache files has also been implemented
behind this API, so the reads and writes will transparently
handle the split files. These have been named "chapters" in new
code.
No callers have been changed, this only adds the new facility. A
subsequent commit will migrate things over.
|
|
No need for fflush(stderr) when it's already unbuffered by default.
According to setvbuf(3):
Normally all files are block buffered. If a stream refers
to a terminal (as stdout normally does), it is line
buffered. The standard error stream stderr is always
unbuffered by default.
|
|
Making things a bit more consistent
|
|
Despite this listing already being named rmd_cache_audio, it used
sound instead of audio for the api.
|
|
Largely mechanical cosmetic changes for more consistency
|
|
Cosmetic rename to make naming consistent, normalizing on audio.
This is a minimal rename and includes update, subsequent commit
will change naming as appropriate in rmd_encode_audio_buffer.[ch]
|
|
Largely mechanical change just finishing up cosmetic rename of
rmd_capture_sound->rmd_capture_audio
|
|
Make naming consistent with rmd_cache_audio.
This commit is minimal renaming of the files and tweaking
includes, a subsequent commit will make the various naming within
rmd_capture_audio.c consistent.
|
|
These don't need to be globals anymore since I've gotten rid of
the unnecessary macro insanity.
|
|
The AV-sync rework is significant enough for a release
|
|
Acquiring the new frame can take a potentially significant amount of
time, rather than letting any frames dropped during the acquire get
all taken by the next frame, update this one to include them.
It's both more accurate (the dropped frames occurred literally while
this was going on) and makes it more likely get_frame() will have
to wait on the upcoming cond_wait(time_cond) for the next tick.
If the upcoming cond_wait(time_cond) doesn't wait because a new
frame is already pending, it makes it more likely get_frame() will
snatch yuv_mutex before the encode/cache thread can wake up and
grab it. When that occurs it's effectively dropping frames because
the encode/cache thread gets blocked on yuv_mutex while the contents
get replaced, so the frames the previous contents were going to be
applied to will instead get the updated contents that belong to
the future sample's frames.
|
|
Since the frame timer implements a frame counter, and that frame count
is propagated through the get->encode pipeline for samples that get
through, any missed frames are noticed and dealt with making it not
lossy from the point of the timer down to the encoded stream in terms
of number of frames.
It's certainly lossy in terms of the contents of those frames, but
synchronization is all about the temporal domain and as long as the
frame counts all make it into the stream as frames, we can account
for them at the timer in terms of avd.
This in combination with the other commit moving the audio side of
avd maintenance immediately upon capture into the raw buffer, shrinks
the variable capacitance separating the audio timer and the frame
timer to virtually nil. As a consequence, the frame timer can now
be much more accurate in terms of how much longer/less to sleep or
if a frame should be dropped to get the timer caught up.
When avd was being maintained at points far removed from the actual
things they represented there was too much elastic capacitance in
there for sync_streams() to inform its adjustment accurately.
|
|
Since the sound capture buffers all sound in newly allocated memory,
the "stream time" represented by those buffers can be accounted for
immediately upon reading into the buffer. Doing it later in the
different threads on the other side of the queue, especially after
encoding, is an unnecessary pile of variable capacitance that just
makes things less synchronized for zero gain.
|
|
This requires a bit of adjustment in the get_frame time_cond wait
loop so it still services the event loop when woken without advance
At least now get_frame has no explicit pause code, but it does
require the timer keep firing while paused so it signals time_cond.
|
|
these concepts may return but not in this form
|
|
This brings the !--on-the-fly-encoding mode up to speed.
The cached file header loses the total_frames counter, as the
capture_frameno already represents this.
Dropped frames are detected by simply looking at the difference
between the previous capture_frameno and the current one. This
simply gets passed to the encoder as a n_frames count so theora
can duplicate the frames as needed.
This was being done manually before by looking at the frameno and
total frames in each header and maintaining separate counts for
"extra frames" "missed frames" etc, and resubmitting entire
frames multiply for encoding dropped frames.
So a chunk of code has been thrown out from rmd_load_cache.c, and
some general cleanups have occurred there as well.
I also needed to add more locking around pdata->avd accesses.
|
|
This is focused on keeping --on-the-fly-encoding in sync even
over long videos. The existing code inevitably would fall into a
permanently negative pdata->avd value letting things get
increasingly out of sync and never correcting.
Before removing the vestigial negative avd "don't wait" logic
from get_frame when this permanently negative avd state was
entered, get_frame would just start sampling at an unregulated
fps.
The timer thread which drives get_frame now consults avd on every
tick, Depending on which which half is ahead, the timer will
either cause get_frame to drop frames by advancing the frameno by
more than one, or it will adjust its sleep delay in proportion to
the delta.
See comments in rmd_timer.c for more details.
Note that in testing especially with a loaded system I observed
some surprisingly large deltas where multi-second sleeps occurred
to let the sound catch back up. I expect to revisit this issue
more in the future, but would just like to get things more
correct for now.
|
|
When the encoder finds the encoded - captured frameno delta > 1
it needs to fill the gap somehow.
With how things are currently architected, the old yuv countents
are gone so there's only the current frame available for filling.
The newer theoraenc.h API exposes a theora_control() parameter
for this purpose, so I've also added a theoraenc.h include
implicitly bumping the libtheora dependency. But by now it
shouldn't matter, and the rest of rmd should probably get updated
to use the new theora API eventually anyways.
I'm still uncertain what role pdata->avd will play in the
long-run, but leaving its maintenance for now.
|
|
avd accesses aren't serialized currently despite occurring from
concurrent threads. I'm reworking avd but this just introduces
and initializes a mutex for the existing variable.
|
|
Vestigial broadcast, only a single waiter on this now.
|
|
Maybe this made sense at some point in the original code, but the
way I have this setup currently get_frame() should strictly
capture a frame on every tick of the timer at the desired FPS to
the best of its ability.
The capture_frameno gets propagated to the encoder whenever a new
frame is acquired on that timer. When the encoder consumes it,
it should just dupe the frame to fill any gaps between the last
encoded frameno and the new one.
As-is, this avd value seems to drift permanently negative
eventually at which point get_frame() ceases ever waiting on the
timer. That's obviously broken, and devolves into a pinned CPU
with get_frame() attempting an infinitely high frame rate. Which
likely just makes things worse not better by starving the encoder
of CPU time.
I need to go check out the encoder now to make sure it fills
frameno gaps.
|