Add some thoughts about decoder structures.
[dcpomatic.git] / doc / design / decoder_structures.tex
1 \documentclass{article}
2 \usepackage[usenames]{xcolor}
3 \usepackage{listings}
4 \title{Decoder structures}
5 \author{}
6 \date{}
7 \begin{document}
8 \maketitle
9
10 At the time of writing we have a get-stuff-at-this-time API which
11 hides a decode-some-and-see-what-comes-out approach.
12
13 \section{Easy and hard extraction of particular pieces of content}
14
15 With most decoders it is quick, easy and reliable to get a particular
16 piece of content from a particular timecode.  This applies to the DCP,
17 DCP subtitle, Sndfile and Image decoders.  With FFmpeg, however, this is not easy.
18
19 This suggests that it would make more sense to keep the
20 decode-and-see-what-comes-out code within the FFmpeg decoder and not
21 use it anywhere else.
22
23 However resampling screws this up, as it means all audio requires
24 decode-and-see.  I don't think you can't resample in neat blocks as
25 there are fractional samples other complications.  You can't postpone
26 resampling to the end of the player since different audio may be
27 coming in at different rates.
28
29 This suggests that decode-and-see is a better match, even if it feels
30 a bit ridiculous when most of the decoders have slightly clunky seek
31 and pass methods.
32
33
34 \section{Multiple streams}
35
36 Another thing unique to FFmpeg is multiple audio streams, possibly at
37 different sample rates.
38
39 There seem to be two approaches to handling this:
40
41 \begin{enumerate}
42 \item Every audio decoder has one or more `streams'.  The player loops
43   content and streams within content, and the audio decoder resamples
44   each stream individually.
45 \item Every audio decoder just returns audio data, and the FFmpeg
46   decoder returns all its streams' data in one block.
47 \end{enumerate}
48
49 The second approach has the disadvantage that the FFmpeg decoder must
50 resample and merge its audio streams into one block.  This is in
51 addition to the resampling that must be done for the other decoders,
52 and the merging of all audio content inside the player.
53
54 These disadvantages suggest that the first approach is better.
55
56 One might think that the logical conclusion is to take streams all the
57 way back to the player and resample them there, but the resampling
58 must occur on the other side of the get-stuff-at-time API.
59
60
61 \section{Going back}
62
63 Thinking about this again in October 2016 it feels like the
64 get-stuff-at-this-time API is causing problems.  It especially seems
65 to be a bad fit for previewing audio.  The API is nice for callers,
66 but there is a lot of dancing around behind it to make it work, and it
67 seems that it is more `flexible' than necessary; all callers ever do
68 is seek or run.
69
70 Hence there is a temptation to go back to see-what-comes-out.
71
72 There are two operations: make DCP and preview.  Make DCP seems to be
73
74 \lstset{language=C++}
75 \lstset{basicstyle=\footnotesize\ttfamily,
76         breaklines=true,
77         keywordstyle=\color{blue}\ttfamily,
78         stringstyle=\color{red}\ttfamily,
79         commentstyle=\color{olive}\ttfamily}
80
81 \begin{lstlisting}
82   while (!done) {
83     done = player->pass();
84     // pass() causes things to appear which are
85     // sent to encoders / disk
86   }
87 \end{lstlisting}
88
89 And preview seems to be
90
91 \begin{lstlisting}
92   // Thread 1
93   while (!done) {
94     done = player->pass();
95     // pass() causes things to appear which are buffered
96     sleep_until_buffers_empty();
97   }
98
99   // Thread 2
100   while (true) {
101     get_video_and_audio_from_buffers();
102     push_to_output();
103     sleep();
104   }
105 \end{lstlisting}
106
107 \texttt{Player::pass} must call \texttt{pass()} on its decoders.  They
108 will emit stuff which \texttt{Player} must adjust (mixing sound etc.).
109 Player then emits the `final cut', which must have properties like no
110 gaps in video/audio.
111
112 One problem I remember is which decoder to pass() at any given time:
113 it must be the one with the earliest last output, presumably.
114 Resampling also looks fiddly in the v1 code.
115
116 Possible steps:
117 \begin{enumerate}
118 \item Add signals to \texttt{Player}; remove \texttt{get\_*}
119 \item Give player a \texttt{pass()} which calls decoders and sanitises
120   output.
121 \item Make transcoder attach to \texttt{Player} and pass output through to encoding.
122 \item Make preview attach to \texttt{Player}, buffer the output and then fetch it from a UI thread.
123 \end{enumerate}
124
125 \end{document}