summaryrefslogtreecommitdiffstats
path: root/DOCS/tech-overview.txt
diff options
context:
space:
mode:
Diffstat (limited to 'DOCS/tech-overview.txt')
-rw-r--r--DOCS/tech-overview.txt128
1 files changed, 64 insertions, 64 deletions
diff --git a/DOCS/tech-overview.txt b/DOCS/tech-overview.txt
index fba894606a..ca565c77e7 100644
--- a/DOCS/tech-overview.txt
+++ b/DOCS/tech-overview.txt
@@ -11,7 +11,7 @@ player/*.c:
* main():
* basic initializations (e.g. init_libav() and more)
* pre-parse command line (verbosity level, config file locations)
- * load config files (parse_cfgfiles())
+ * load config files (mp_parse_cfgfiles())
* parse command line, add files from the command line to playlist
(m_config_parse_mp_command_line())
* check help options etc. (call handle_help_options()), possibly exit
@@ -45,7 +45,7 @@ player/*.c:
Things worth saying about the playback core:
- most state is in MPContext (core.h), which is not available to the
subsystems (and should not be made available)
- - the currently played tracks are in mpctx->current_tracks, and decoder
+ - the currently played tracks are in mpctx->current_track, and decoder
state in track.dec/d_sub
- the other subsystems rarely call back into the frontend, and the frontend
polls them instead (probably a good thing)
@@ -113,7 +113,7 @@ options/options.h, options/options.c
link them to the option parser. For example, an entry like this may be
typical:
- OPT_SUBSTRUCT("", demux_opts, demux_conf, 0),
+ {"", OPT_SUBSTRUCT(demux_opts, demux_conf)},
This directs the option access code to include all options in demux_conf
into the global option list, with no prefix (""), and as part of the
@@ -126,15 +126,16 @@ options/options.h, options/options.c
... to get a copy of its options.
- See m_config.h (below) how to access options.
+ See m_config_core.h (below) how to access options.
- The actual option parser is spread over m_option.c, m_config.c, and
- parse_commandline.c, and uses the option table in options.c.
+ The actual option parser is spread over m_option.c, m_config_frontend.c,
+ and parse_commandline.c, and uses the option table in options.c.
-options/m_config.h & m_config.c:
- Code for querying and managing options. This (unfortunately) contains both
- declarations for the "legacy-ish" global m_config struct, and ways to access
- options in a threads-safe way anywhere, like m_config_cache_alloc().
+options/m_config_*.h & m_config_*.c:
+ Code for querying and managing options. m_config_frontend.h contains
+ declarations for the "legacy-ish" global m_config struct, while
+ m_config_core.h provides ways to access options in a threads-safe way
+ anywhere, like m_config_cache_alloc().
m_config_cache_alloc() lets anyone read, observe, and write options in any
thread. The only state it needs is struct mpv_global, which is an opaque
@@ -165,16 +166,15 @@ stream/*:
E.g. if mpv sees "http://something" on the command line, it will pick
stream_lavf.c based on the prefix, and pass the rest of the filename to it.
- Some stream inputs are quite special: stream_dvd.c turns DVDs into mpeg
+ Some stream inputs are quite special: stream_dvdnav.c turns DVDs into mpeg
streams (DVDs are actually a bunch of vob files etc. on a filesystem),
- stream_tv.c provides TV input including channel switching.
Some stream inputs are just there to invoke special demuxers, like
stream_mf.c. (Basically to make the prefix "mf://" do something special.)
demux/:
Demuxers split data streams into audio/video/sub streams, which in turn
- are split in packets. Packets (see demux_packet.h) are mostly byte chunks
+ are split in packets. Packets (see packet.h) are mostly byte chunks
tagged with a playback time (PTS). These packets are passed to the decoders.
Most demuxers have been removed from this fork, and the only important and
@@ -189,6 +189,14 @@ demux/:
cache, which is implemented as a list of packets. The cache is complex
because it support seeking, multiple ranges, prefetching, and so on.
+filters/:
+ Filter related code. filter.c contains the generic filtering framework
+ which converts input frames to output frames (audio, video, or demux
+ packet data). f_decoder_wrapper.c is a source filter which connects the
+ frontend with the actual audio and video decoders. f_output_chain.c handles
+ VO/AO output conversions. f_autoconvert.c automatically inserts the
+ appropriate conversion filters if format conversion is needed.
+
video/:
This contains several things related to audio/video decoding, as well as
video filters.
@@ -197,22 +205,18 @@ video/:
internally.
video/decode/:
- vd_*.c are video decoders. (There's only vd_lavc.c left.) dec_video.c
- handles most of connecting the frontend with the actual decoder.
+ vd_*.c are video decoders. (There's only vd_lavc.c left.)
video/filter/:
- vf_*.c and vf.c form the video filter chain. They are fed by the video
- decoder, and output the filtered images to the VOs though vf_vo.c. By
- default, no video filters (except vf_vo) are used. vf_scale is automatically
- inserted if the video output can't handle the video format used by the
- decoder.
+ vf_*.c are video filters. They are fed by the video decoder, and output the
+ filtered images to the VOs. By default, no video filters are used.
video/out/:
Video output. They also create GUI windows and handle user input. In most
cases, the windowing code is shared among VOs, like x11_common.c for X11 and
w32_common.c for Windows. The VOs stand between frontend and windowing code.
- vo_gpu can pick a windowing system at runtime, e.g. the same binary can
- provide both X11 and Cocoa support on OSX.
+ vo_gpu and vo_gpu_next can pick a windowing system at runtime, e.g. the same
+ binary can provide both X11 and Cocoa support on macOS.
VOs can be reconfigured at runtime. A vo_reconfig() call can change the video
resolution and format, without destroying the window.
@@ -224,13 +228,13 @@ audio/:
compressed formats used for spdif.)
audio/decode/:
- ad_*.c and dec_audio.c handle audio decoding. ad_lavc.c is the
- decoder using ffmpeg. ad_spdif.c is not really a decoder, but is used for
- compressed audio passthrough.
+ ad_*.c handle audio decoding. ad_lavc.c is the decoder using ffmpeg.
+ ad_spdif.c is not really a decoder, but is used for compressed audio
+ passthrough.
audio/filter/:
- Audio filter chain. af_lavrresample is inserted if any form of conversion
- between audio formats is needed.
+ Audio filters. af_scaletempo2 is inserted by default if playback is different
+ from normal speed.
audio/out/:
Audio outputs.
@@ -238,9 +242,9 @@ audio/out/:
Unlike VOs, AOs can't be reconfigured on a format change. On audio format
changes, the AO will simply be closed and re-opened.
- There are wrappers to support for two types of audio APIs: push.c and
- pull.c. ao.c calls into one of these. They contain generic code to deal
- with the data flow these APIs impose.
+ buffer.c is the wrapper to support for two types of audio APIs: push and
+ pull. ao.c calls into that. It contains generic code to deal with the data
+ flow these APIs impose.
Note that mpv synchronizes the video to the audio. That's the reason
why buggy audio drivers can have a bad influence on playback quality.
@@ -257,10 +261,7 @@ sub/:
in turn asks dec_sub.c for subtitle overlay bitmaps, which relays the
request to one of the sd_*.c subtitle decoders/renderers.
- Subtitle loading is in demux/. The MPlayer subreader.c is mostly gone - parts
- of it survive in demux_subreader.c. It's used as last fallback, or to handle
- some text subtitle types on Libav. It should go away eventually. Normally,
- subtitles are loaded via demux_lavf.c.
+ Subtitle loading is in demux/. Normally, subtitles are loaded via demux_lavf.c.
The subtitles are passed to dec_sub.c and the subtitle decoders in sd_*.c
as they are demuxed. All text subtitles are rendered by sd_ass.c. If text
@@ -272,8 +273,8 @@ sub/:
sd_ass.c's internal state.
etc/:
- The file input.conf is actually integrated into the mpv binary by the
- build system. It contains the default keybindings.
+ The files input.conf and builtin.conf are actually integrated into the mpv
+ binary by the build system. They contain the default configs and keybindings.
Best practices and Concepts within mpv
======================================
@@ -377,7 +378,7 @@ without causing conflicts with other library users in the same process. To any
piece of code, a "safe" library's API can simply be used, without having to
worry about other API users that may be around somewhere.
-Libraries are often not library safe, because they they use global mutable state
+Libraries are often not library safe, because they use global mutable state
or other "global" resources. Typical examples include use of signals, simple
global variables (like hsearch() in libc), or internal caches not protected by
locks.
@@ -430,12 +431,11 @@ like a log file or the internal console.lua script.
Locking
-------
-See generally available literature. In mpv, we use pthread for this.
+See generally available literature. In mpv, we use mp_thread for this.
Always keep locking clean. Don't skip locking just because it will work "in
practice". (See undefined behavior section.) If your use case is simple, you may
-use C11 atomics (osdep/atomic.h for partial C99 support), but most likely you
-will only hurt yourself and others.
+use C11 atomics, but most likely you will only hurt yourself and others.
Always make clear which fields in a struct are protected by which lock. If a
field is immutable, or simply not thread-safe (e.g. state for a single worker
@@ -481,7 +481,7 @@ VOs, AOs, demuxers, and more. The frontend usually calls "down" the usage
hierarchy: mpctx almost on top, then things like vo/ao, and utility code on the
very bottom.
-"Callback hell" is when when components call both up and down the hierarchy,
+"Callback hell" is when components call both up and down the hierarchy,
which for example leads to accidentally recursion, reentrancy problems, or
locking nightmares. This is avoided by (mostly) calling only down the hierarchy.
Basically the call graph forms a DAG. The other direction is handled by event
@@ -556,13 +556,13 @@ Condition variables
-------------------
They're used whenever a thread needs to wait for something, without nonsense
-like sleep calls or busy waiting. mpv uses the standard pthread API for this.
-There's a lot of literature on it. Read it.
+like sleep calls or busy waiting. mpv uses the mp_thread API for this.
+There's a lot of literature on condition variables, threading in general. Read it.
For initial understanding, it may be helpful to know that condition variables
-are not variables that signal a condition. pthread_cond_t does not have any
-state per-se. Maybe pthread_cond_t would better be named pthread_interrupt_t,
-because its sole purpose is to interrupt a thread waiting via pthread_cond_wait()
+are not variables that signal a condition. mp_cond does not have any
+state per-se. Maybe mp_cond would better be named mp_interrupt,
+because its sole purpose is to interrupt a thread waiting via mp_cond_wait()
(or similar). The "something" in "waiting for something" can be called
predicate (to avoid confusing it with "condition"). Consult literature for the
proper terms.
@@ -571,24 +571,24 @@ The very short version is...
Shared declarations:
- pthread_mutex_t lock;
- pthread_cond_t cond_var;
+ mp_mutex lock;
+ mp_cond cond_var;
struct something state_var; // protected by lock, changes signaled by cond_var
Waiter thread:
- pthread_mutex_lock(&lock);
+ mp_mutex_lock(&lock);
// Wait for a change in state_var. We want to wait until predicate_fulfilled()
// returns true.
// Must be a loop for 2 reasons:
// 1. cond_var may be associated with other conditions too
- // 2. pthread_cond_wait() can have sporadic wakeups
+ // 2. mp_cond_wait() can have sporadic wakeups
while (!predicate_fulfilled(&state_var)) {
// This unlocks, waits for cond_var to be signaled, and then locks again.
// The _whole_ point of cond_var is that unlocking and waiting for the
// signal happens atomically.
- pthread_cond_wait(&cond_var, &lock);
+ mp_cond_wait(&cond_var, &lock);
}
// Here you may react to the state change. The state cannot change
@@ -596,43 +596,43 @@ Waiter thread:
// and reacquire it).
// ...
- pthread_mutex_unlock(&lock);
+ mp_mutex_unlock(&lock);
Signaler thread:
- pthread_mutex_lock(&lock);
+ mp_mutex_lock(&lock);
// Something changed. Update the shared variable with the new state.
update_state(&state_var);
// Notify that something changed. This will wake up the waiter thread if
- // it's blocked in pthread_cond_wait(). If not, nothing happens.
- pthread_cond_broadcast(&cond_var);
+ // it's blocked in mp_cond_wait(). If not, nothing happens.
+ mp_cond_broadcast(&cond_var);
// Fun fact: good implementations wake up the waiter only when the lock is
// released, to reduce kernel scheduling overhead.
- pthread_mutex_unlock(&lock);
+ mp_mutex_unlock(&lock);
Some basic rules:
1. Always access your state under proper locking
- 2. Always check your predicate before every call to pthread_cond_wait()
- (And don't call pthread_cond_wait() if the predicate is fulfilled.)
- 3. Always call pthread_cond_wait() in a loop
+ 2. Always check your predicate before every call to mp_cond_wait()
+ (And don't call mp_cond_wait() if the predicate is fulfilled.)
+ 3. Always call mp_cond_wait() in a loop
(And only if your predicate failed without releasing the lock..)
- 4. Always call pthread_cond_broadcast()/_signal() inside of its associated
+ 4. Always call mp_cond_broadcast()/_signal() inside of its associated
lock
mpv sometimes violates rule 3, and leaves "retrying" (i.e. looping) to the
caller.
Common pitfalls:
- - Thinking that pthread_cond_t is some kind of semaphore, or holds any
+ - Thinking that mp_cond is some kind of semaphore, or holds any
application state or the user predicate (it _only_ wakes up threads
- that are at the same time blocking on pthread_cond_wait() and friends,
+ that are at the same time blocking on mp_cond_wait() and friends,
nothing else)
- - Changing the predicate, but not updating all pthread_cond_broadcast()/
+ - Changing the predicate, but not updating all mp_cond_broadcast()/
_signal() calls correctly
- - Forgetting that pthread_cond_wait() unlocks the lock (other threads can
+ - Forgetting that mp_cond_wait() unlocks the lock (other threads can
and must acquire the lock)
- Holding multiple nested locks while trying to wait (=> deadlock, violates
the lock order anyway)