Subject | Hash | Author | Date (UTC) |
---|---|---|---|
npv: media pipeline | 30efa7c8f2574c676d9d41b8bf5678d5725b8721 | Sylvain BERTRAND | 2020-05-24 17:40:23 |
alsa-lib warning | 54b6dc9b912bb9509ba35405f15a623632adf784 | Sylvain BERTRAND | 2020-05-19 14:32:04 |
npv: proper handling of snd_pcm_drain() | db7959bdd5f7ed921773aef42984b1c982575ad4 | Sylvain BERTRAND | 2020-05-15 15:00:13 |
proper handling of snd_pcm_drain() | ae2c6458f893e77cd352fe9225e123bdf077e33f | Sylvain BERTRAND | 2020-05-15 14:46:30 |
after network testing: need an input thread | 0c26bffd3c2268cfb9f7d8e43c7aba2fbf7fec16 | Sylvain BERTRAND | 2020-05-13 23:11:20 |
spin on futex waiting in threaded scaler | e90420f459c608cbcbae423b3c487598a83de359 | Sylvain BERTRAND | 2020-05-13 21:05:48 |
npa tidying and npv addition, usable on our system | 443ff39347c8b599dfaafff612caed84287f1a70 | Sylvain BERTRAND | 2020-05-13 15:12:15 |
here we go | d5a2b11f4d73ec5365a340163f451fa8a123d728 | Sylvain BERTRAND | 2020-02-12 18:50:57 |
File npv/README changed (mode: 100644) (index 50c6ffc..32436e0) | |||
1 | lightweight, vulkan, x11, linux, alsa, audio driven video file player | ||
2 | using ffmpeg. | ||
1 | lightweight, vulkan, x11, linux, alsa, audio driven and self-limiting | ||
2 | media pipeline video file player using ffmpeg. | ||
3 | 3 | BUILDING: | BUILDING: |
4 | 4 | this is a "one compilation unit" project. just compile main.c and link | this is a "one compilation unit" project. just compile main.c and link |
5 | 5 | the compiler output file to your ffmpeg libs, your alsa lib (we should | the compiler output file to your ffmpeg libs, your alsa lib (we should |
File npv/TODO changed (mode: 100644) (index 27a5aad..d6dcb23) | |||
1 | next: | ||
2 | an input thread is needed to "receive" the packets since it is blocking while pulling data | ||
3 | from internet. will need a mutex for the format and a mutex for each packet queue. | ||
4 | |||
5 | 1 | not ordered: | not ordered: |
2 | - better/smoother/less drop/(correct?) video frame display strategy | ||
6 | 3 | - xcb(resize)/vulkan swapchain loss or "not optimal" ? (state re-factoring) | - xcb(resize)/vulkan swapchain loss or "not optimal" ? (state re-factoring) |
7 | 4 | - x11 map/unmap handling to: | - x11 map/unmap handling to: |
8 | + restore the paused image (aka keep the image in cpu ram) | ||
5 | + restore the paused image (aka keep the image currently displayed in cpu ram) | ||
9 | 6 | + switch on/off video decoding? | + switch on/off video decoding? |
10 | - better/smoother/(correct?) video frame display strategy | ||
11 | - power management event? it implies: | ||
7 | - the exit condition is audio drained *AND* format eof was raised. | ||
8 | dynamic formats: if eof was not raised and audio is drained, reprobe | ||
9 | for best streams? Wait for user streams selection? | ||
10 | - "power management event"? it implies: | ||
12 | 11 | + if alsa notify of a "power management" event we should | + if alsa notify of a "power management" event we should |
13 | 12 | (invalidate/update/something) the clock | (invalidate/update/something) the clock |
14 | 13 | + vulkan surface loss? (it should be the case) | + vulkan surface loss? (it should be the case) |
14 | + is this should be handle or it is overkill? | ||
15 | 15 | - vulkan device loss? (dynamically change device?) | - vulkan device loss? (dynamically change device?) |
16 | 16 | - alsa-lib dynamic loading | - alsa-lib dynamic loading |
17 | 17 | - "buffering" indicator | - "buffering" indicator |
18 | 18 | - subtitles | - subtitles |
19 | 19 | - osd (On Screen Display) | - osd (On Screen Display) |
20 | - use vulkan shaders, compute or not, in order to perform some yuvX pixel | ||
20 | - use vulkan shaders (no glsl or hlsl), compute or not, in order to perform some yuvX pixel | ||
21 | 21 | formats to srgb format conversions. do NOT use the vulkan yuvX samplers as | formats to srgb format conversions. do NOT use the vulkan yuvX samplers as |
22 | 22 | they are mostly dirty hack tricks built into spirv translaters. | they are mostly dirty hack tricks built into spirv translaters. |
23 | - vulkan thingy for better quality down/up scaling? | ||
24 | - actually off-loading alsa output and vulkan output to their thread? |
File npv/audio/filt/main.c changed (mode: 100644) (index 488e1c9..ba5aa67) | |||
6 | 6 | */ | */ |
7 | 7 | #include <stdbool.h> | #include <stdbool.h> |
8 | 8 | #include <stdio.h> | #include <stdio.h> |
9 | #include <pthread.h> | ||
9 | 10 | #include <libavfilter/avfilter.h> | #include <libavfilter/avfilter.h> |
10 | 11 | #include <libavfilter/buffersrc.h> | #include <libavfilter/buffersrc.h> |
11 | 12 | #include <libavfilter/buffersink.h> | #include <libavfilter/buffersink.h> |
File npv/audio/filt/public/code.frag.c changed (mode: 100644) (index 400f5d4..45e2b1d) | |||
4 | 4 | #define FILT_SWITCHED_TO_DRAINING 3 | #define FILT_SWITCHED_TO_DRAINING 3 |
5 | 5 | static u8 filt_push_dec_sets(void) | static u8 filt_push_dec_sets(void) |
6 | 6 | { | { |
7 | int r; | ||
7 | u8 r8; | ||
8 | int ri; | ||
8 | 9 | avutil_audio_set_ref_t **a; | avutil_audio_set_ref_t **a; |
9 | 10 | ||
11 | audio_dec_sets_lock(); | ||
10 | 12 | if (audio_dec_sets_p.n == 0) { | if (audio_dec_sets_p.n == 0) { |
11 | 13 | if (audio_dec_sets_p.eof_receive) { | if (audio_dec_sets_p.eof_receive) { |
12 | r = avfilter_bufsrc_add_audio_set_flags(abufsrc_ctx_l, | ||
14 | ri = avfilter_bufsrc_add_audio_set_flags(abufsrc_ctx_l, | ||
13 | 15 | 0, AVFILTER_BUFSRC_FLAG_PUSH | 0, AVFILTER_BUFSRC_FLAG_PUSH |
14 | 16 | | AVFILTER_BUFSRC_FLAG_KEEP_REF); | | AVFILTER_BUFSRC_FLAG_KEEP_REF); |
15 | if (r < 0) | ||
17 | if (ri < 0) | ||
16 | 18 | FATALAF("ffmpeg:unable to notify the end of data to the filter source audio buffer context\n"); | FATALAF("ffmpeg:unable to notify the end of data to the filter source audio buffer context\n"); |
17 | 19 | POUTAF("ffmpeg:interactive filter switched to draining\n"); | POUTAF("ffmpeg:interactive filter switched to draining\n"); |
18 | return FILT_SWITCHED_TO_DRAINING; | ||
20 | r8 = FILT_SWITCHED_TO_DRAINING; | ||
21 | goto unlock; | ||
19 | 22 | } | } |
20 | return NO_DEC_SET; | ||
23 | r8 = NO_DEC_SET; | ||
24 | goto unlock; | ||
21 | 25 | } | } |
22 | 26 | a = audio_dec_sets_p.a; | a = audio_dec_sets_p.a; |
23 | 27 | /* the dec_sets_p bufs will be unref in avcodec_audio_receive_set */ | /* the dec_sets_p bufs will be unref in avcodec_audio_receive_set */ |
24 | r = avfilter_bufsrc_add_audio_set_flags(abufsrc_ctx_l, a[0], | ||
28 | ri = avfilter_bufsrc_add_audio_set_flags(abufsrc_ctx_l, a[0], | ||
25 | 29 | AVFILTER_BUFSRC_FLAG_PUSH | AVFILTER_BUFSRC_FLAG_KEEP_REF); | AVFILTER_BUFSRC_FLAG_PUSH | AVFILTER_BUFSRC_FLAG_KEEP_REF); |
26 | if (r >= 0) { | ||
30 | if (ri >= 0) { | ||
27 | 31 | /* rotate the ptrs if needed */ | /* rotate the ptrs if needed */ |
28 | 32 | if (audio_dec_sets_p.n > 1) { | if (audio_dec_sets_p.n > 1) { |
29 | 33 | avutil_audio_set_ref_t *save; | avutil_audio_set_ref_t *save; |
... | ... | static u8 filt_push_dec_sets(void) | |
34 | 38 | a[audio_dec_sets_p.n - 1] = save; | a[audio_dec_sets_p.n - 1] = save; |
35 | 39 | } | } |
36 | 40 | audio_dec_sets_p.n--; | audio_dec_sets_p.n--; |
37 | return PUSHED_ONE_SET; | ||
38 | } else if (r == AVERROR(EAGAIN)) | ||
39 | return AGAIN; | ||
41 | r8 = PUSHED_ONE_SET; | ||
42 | goto unlock; | ||
43 | } else if (ri == AVERROR(EAGAIN)) { | ||
44 | r8 = AGAIN; | ||
45 | goto unlock; | ||
46 | } | ||
40 | 47 | FATALAF("ffmpeg:unable to submit a decoder set of frames to the filter source audio buffer context\n"); | FATALAF("ffmpeg:unable to submit a decoder set of frames to the filter source audio buffer context\n"); |
48 | unlock: | ||
49 | audio_dec_sets_unlock(); | ||
50 | return r8; | ||
41 | 51 | } | } |
42 | 52 | #undef AGAIN | #undef AGAIN |
43 | 53 | #undef PUSHED_ONE_SET | #undef PUSHED_ONE_SET |
File npv/audio/local/code.frag.c changed (mode: 100644) (index 6c0aa29..be9ecb6) | |||
... | ... | static void pcm_filt_frs_write(snd_pcm_ufrs_t ufrs_n) { loop | |
599 | 599 | if (audio_filt_p.set->frs_n == 0) loop { | if (audio_filt_p.set->frs_n == 0) loop { |
600 | 600 | u8 r; | u8 r; |
601 | 601 | ||
602 | r = filt_push_dec_sets(); | ||
603 | if (r == NO_DEC_SET || r == AGAIN) | ||
604 | return; /* not enough data for 1 set */ | ||
605 | /* r == PUSHED_ONE_SET || r == FILT_SWITCHED_TO_DRAINING */ | ||
602 | /* we _really_ want audio data */ | ||
603 | (void)filt_push_dec_sets(); | ||
606 | 604 | r = audio_filt_set_try_get(); | r = audio_filt_set_try_get(); |
607 | 605 | if (r == EOF_FILT) { | if (r == EOF_FILT) { |
608 | 606 | draining_state_switch(); | draining_state_switch(); |
... | ... | static void pcm_filt_frs_write(snd_pcm_ufrs_t ufrs_n) { loop | |
660 | 658 | * XXX: getting the "right" ts from ff is convoluted | * XXX: getting the "right" ts from ff is convoluted |
661 | 659 | */ | */ |
662 | 660 | if (audio_filt_p.pcm_written_ufrs_n == 0) | if (audio_filt_p.pcm_written_ufrs_n == 0) |
663 | clk_ref_time_point_update(audio_filt_p.set->pts, written_ufrs_n); | ||
661 | clk_ref_time_point_update(audio_filt_p.set->pts, | ||
662 | written_ufrs_n); | ||
664 | 663 | audio_filt_p.pcm_written_ufrs_n += written_ufrs_n; | audio_filt_p.pcm_written_ufrs_n += written_ufrs_n; |
665 | 664 | ufrs_n -= written_ufrs_n; | ufrs_n -= written_ufrs_n; |
666 | 665 | ||
... | ... | static void init_pcm_once_public(u8 *pcm_str) | |
771 | 770 | } | } |
772 | 771 | static void init_once_public(u8 *pcm_str) | static void init_once_public(u8 *pcm_str) |
773 | 772 | { | { |
774 | st_idx_p = -1; | ||
773 | int r; | ||
774 | |||
775 | memset(&st_p, 0, sizeof(st_p)); | ||
775 | 776 | pkt_q_p = pkt_q_new("audio"); | pkt_q_p = pkt_q_new("audio"); |
776 | 777 | dec_ctx_p = 0; | dec_ctx_p = 0; |
777 | 778 | dec_sets_p.eof_receive = false; | dec_sets_p.eof_receive = false; |
... | ... | static void init_once_public(u8 *pcm_str) | |
786 | 787 | if (draining_timer_fd_p == -1) | if (draining_timer_fd_p == -1) |
787 | 788 | FATALA("unable to get a draining timer file descriptor:%s\n", strerror(errno)); | FATALA("unable to get a draining timer file descriptor:%s\n", strerror(errno)); |
788 | 789 | draining_p = false; | draining_p = false; |
790 | r = pthread_mutex_init(&dec_ctx_mutex_l, 0); | ||
791 | if (r != 0) | ||
792 | FATALA("%d:unable to init the decoder mutex\n", r); | ||
793 | r = pthread_mutex_init(&dec_sets_p.mutex, 0); | ||
794 | if (r != 0) | ||
795 | FATALA("%d:unable to init the mutex for the array of decoder sets\n", r); | ||
789 | 796 | } | } |
790 | 797 | static void init_once_local(void) | static void init_once_local(void) |
791 | 798 | { | { |
File npv/audio/local/state.frag.c changed (mode: 100644) (index 66eeb98..cef7b74) | |||
1 | 1 | /* ff dec */ | /* ff dec */ |
2 | static avcodec_codec_t *dec_l; | ||
2 | static avcodec_codec_t *dec_l; /* same mutex than dec_ctx_p */ | ||
3 | static pthread_mutex_t dec_ctx_mutex_l; | ||
3 | 4 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
4 | 5 | /* alsa */ | /* alsa */ |
5 | 6 | static snd_output_t *pcm_pout_l; | static snd_output_t *pcm_pout_l; |
File npv/audio/main.c changed (mode: 100644) (index bad69ff..e320148) | |||
10 | 10 | #include <stdio.h> | #include <stdio.h> |
11 | 11 | #include <unistd.h> | #include <unistd.h> |
12 | 12 | #include <signal.h> | #include <signal.h> |
13 | #include <pthread.h> | ||
13 | 14 | #include <sys/timerfd.h> | #include <sys/timerfd.h> |
14 | 15 | #include <sys/epoll.h> | #include <sys/epoll.h> |
15 | 16 | #include <libavcodec/avcodec.h> | #include <libavcodec/avcodec.h> |
File npv/audio/namespace/main.c changed (mode: 100644) (index 1276ce0..d038c5a) | |||
3 | 3 | #define chans_buf_inc audio_chans_buf_inc | #define chans_buf_inc audio_chans_buf_inc |
4 | 4 | #define dec_a_grow audio_dec_a_grow | #define dec_a_grow audio_dec_a_grow |
5 | 5 | #define dec_a_unref_all audio_dec_a_unref_all | #define dec_a_unref_all audio_dec_a_unref_all |
6 | #define dec_ctx_mutex_l audio_dec_ctx_mutex_l | ||
6 | 7 | #define dec_flush audio_dec_flush | #define dec_flush audio_dec_flush |
7 | 8 | #define dec_l audio_dec_l | #define dec_l audio_dec_l |
8 | 9 | #define draining_state_handle audio_draining_state_handle | #define draining_state_handle audio_draining_state_handle |
37 | 38 | #undef chans_buf_inc | #undef chans_buf_inc |
38 | 39 | #undef dec_a_grow | #undef dec_a_grow |
39 | 40 | #undef dec_a_unref_all | #undef dec_a_unref_all |
41 | #undef dec_ctx_mutex_l | ||
40 | 42 | #undef dec_l | #undef dec_l |
41 | 43 | #undef dec_flush | #undef dec_flush |
42 | 44 | #undef draining_state_handle | #undef draining_state_handle |
File npv/audio/namespace/public.h changed (mode: 100644) (index f5c2960..816c928) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | 2 | #define dec_ctx_cfg audio_dec_ctx_cfg | #define dec_ctx_cfg audio_dec_ctx_cfg |
3 | #define dec_ctx_lock audio_dec_ctx_lock | ||
3 | 4 | #define dec_ctx_p audio_dec_ctx_p | #define dec_ctx_p audio_dec_ctx_p |
5 | #define dec_ctx_unlock audio_dec_ctx_unlock | ||
4 | 6 | #define dec_flush audio_dec_flush | #define dec_flush audio_dec_flush |
7 | #define dec_sets_lock audio_dec_sets_lock | ||
5 | 8 | #define dec_sets_p audio_dec_sets_p | #define dec_sets_p audio_dec_sets_p |
6 | #define dec_sets_get_avail audio_dec_sets_get_avail | ||
7 | #define dec_set_try_get audio_dec_set_try_get | ||
9 | #define dec_sets_unlock audio_dec_sets_unlock | ||
10 | #define dec_sets_receive_avail audio_dec_sets_receive_avail | ||
11 | #define dec_set_try_receive audio_dec_set_try_receive | ||
8 | 12 | #define draining_p audio_draining_p | #define draining_p audio_draining_p |
9 | 13 | #define draining_state_evt audio_draining_state_evt | #define draining_state_evt audio_draining_state_evt |
10 | 14 | #define draining_timer_fd_p audio_draining_timer_fd_p | #define draining_timer_fd_p audio_draining_timer_fd_p |
18 | 22 | #define pcm_pollfds_n_max audio_pcm_pollfds_n_max | #define pcm_pollfds_n_max audio_pcm_pollfds_n_max |
19 | 23 | #define pcm_silence_bufs_cfg audio_pcm_silence_bufs_cfg | #define pcm_silence_bufs_cfg audio_pcm_silence_bufs_cfg |
20 | 24 | #define pkt_q_p audio_pkt_q_p | #define pkt_q_p audio_pkt_q_p |
25 | #define pkts_send audio_pkts_send | ||
21 | 26 | #define selected_ts_type_p audio_selected_ts_type_p | #define selected_ts_type_p audio_selected_ts_type_p |
22 | #define st_idx_p audio_st_idx_p | ||
27 | #define st_p audio_st_p | ||
23 | 28 | /*============================================================================*/ | /*============================================================================*/ |
24 | 29 | #else | #else |
25 | 30 | #undef dec_ctx_cfg | #undef dec_ctx_cfg |
31 | #undef dec_ctx_lock | ||
26 | 32 | #undef dec_ctx_p | #undef dec_ctx_p |
33 | #undef dec_ctx_unlock | ||
27 | 34 | #undef dec_flush | #undef dec_flush |
35 | #undef dec_sets_lock | ||
28 | 36 | #undef dec_sets_p | #undef dec_sets_p |
29 | #undef dec_sets_get_avail | ||
30 | #undef dec_set_try_get | ||
37 | #undef dec_sets_unlock | ||
38 | #undef dec_sets_receive_avail | ||
39 | #undef dec_set_try_receive | ||
31 | 40 | #undef draining_p | #undef draining_p |
32 | 41 | #undef draining_state_evt | #undef draining_state_evt |
33 | 42 | #undef draining_timer_fd_p | #undef draining_timer_fd_p |
41 | 50 | #undef pcm_pollfds_n_max | #undef pcm_pollfds_n_max |
42 | 51 | #undef pcm_silence_bufs_cfg | #undef pcm_silence_bufs_cfg |
43 | 52 | #undef pkt_q_p | #undef pkt_q_p |
53 | #undef pkts_send | ||
44 | 54 | #undef selected_ts_type | #undef selected_ts_type |
45 | #undef st_idx_p | ||
55 | #undef st_p | ||
46 | 56 | #endif | #endif |
File npv/audio/public.h changed (mode: 100644) (index 1e553d3..3529c4f) | |||
6 | 6 | */ | */ |
7 | 7 | #include <stdbool.h> | #include <stdbool.h> |
8 | 8 | #include <poll.h> | #include <poll.h> |
9 | #include <pthread.h> | ||
9 | 10 | #include <libavformat/avformat.h> | #include <libavformat/avformat.h> |
10 | 11 | #include <libavcodec/avcodec.h> | #include <libavcodec/avcodec.h> |
11 | 12 | #include <libavutil/samplefmt.h> | #include <libavutil/samplefmt.h> |
22 | 23 | #include "npv/audio/public/state.frag.h" | #include "npv/audio/public/state.frag.h" |
23 | 24 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
24 | 25 | static void dec_ctx_cfg(avcodec_params_t *params); | static void dec_ctx_cfg(avcodec_params_t *params); |
26 | static void dec_ctx_lock(void); | ||
27 | static void dec_ctx_unlock(void); | ||
28 | static void dec_sets_lock(void); | ||
29 | static void dec_sets_unlock(void); | ||
25 | 30 | static void dec_flush(void); | static void dec_flush(void); |
26 | static u8 dec_set_try_get(void); | ||
27 | static void dec_sets_get_avail(void); | ||
31 | static u8 dec_set_try_receive(void); | ||
32 | static void dec_sets_receive_avail(void); | ||
28 | 33 | static void draining_state_evt(void); | static void draining_state_evt(void); |
29 | 34 | static void evt_pcm_write(void); | static void evt_pcm_write(void); |
30 | 35 | static void pcm_silence_bufs_cfg(bool print_info); | static void pcm_silence_bufs_cfg(bool print_info); |
... | ... | static void pcm_cfg(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate, | |
34 | 39 | static void pcm2ff(snd_pcm_t *pcm, enum avutil_audio_fr_fmt_t *ff_fmt, | static void pcm2ff(snd_pcm_t *pcm, enum avutil_audio_fr_fmt_t *ff_fmt, |
35 | 40 | int *ff_rate, int *ff_chans_n, uint64_t *ff_chans_layout, | int *ff_rate, int *ff_chans_n, uint64_t *ff_chans_layout, |
36 | 41 | bool print_info); | bool print_info); |
42 | static void pkts_send(void); | ||
37 | 43 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
38 | 44 | #define CLEANUP | #define CLEANUP |
39 | 45 | #include "npv/namespace/ffmpeg.h" | #include "npv/namespace/ffmpeg.h" |
File npv/audio/public/code.frag.c changed (mode: 100644) (index 2eca537..370e80f) | |||
... | ... | static void dec_ctx_cfg(avcodec_params_t *params) | |
22 | 22 | #define AGAIN 0 | #define AGAIN 0 |
23 | 23 | #define HAVE_DEC_SET 1 | #define HAVE_DEC_SET 1 |
24 | 24 | #define EOF_DEC 2 | #define EOF_DEC 2 |
25 | static u8 dec_set_try_get(void) | ||
25 | static u8 dec_set_try_receive(void) | ||
26 | 26 | { | { |
27 | 27 | int r; | int r; |
28 | 28 | u32 last; | u32 last; |
... | ... | static u8 dec_set_try_get(void) | |
31 | 31 | return EOF_DEC; | return EOF_DEC; |
32 | 32 | if (dec_sets_p.n == dec_sets_p.n_max) | if (dec_sets_p.n == dec_sets_p.n_max) |
33 | 33 | dec_a_grow(); | dec_a_grow(); |
34 | /* will unref any previous dec_sets_p.a[x] bufs for us */ | ||
35 | 34 | last = dec_sets_p.n; | last = dec_sets_p.n; |
35 | /* will unref any previous dec_sets_p.a[x] bufs for us */ | ||
36 | 36 | r = avcodec_receive_audio_set(dec_ctx_p, dec_sets_p.a[last]); | r = avcodec_receive_audio_set(dec_ctx_p, dec_sets_p.a[last]); |
37 | if (r == AVUTIL_AVERROR(EAGAIN)) { | ||
37 | if (r == AVUTIL_AVERROR(EAGAIN)) | ||
38 | 38 | return AGAIN; | return AGAIN; |
39 | } else if (r == 0) { | ||
39 | else if (r == 0) { | ||
40 | 40 | ++dec_sets_p.n; | ++dec_sets_p.n; |
41 | 41 | return HAVE_DEC_SET; | return HAVE_DEC_SET; |
42 | 42 | } else if (r == AVUTIL_AVERROR_EOF) { | } else if (r == AVUTIL_AVERROR_EOF) { |
... | ... | static u8 dec_set_try_get(void) | |
52 | 52 | #define AGAIN 0 | #define AGAIN 0 |
53 | 53 | #define HAVE_DEC_SET 1 | #define HAVE_DEC_SET 1 |
54 | 54 | #define EOF_DEC 2 | #define EOF_DEC 2 |
55 | static void dec_sets_get_avail(void) { loop | ||
55 | /* | ||
56 | * this can be long and we don't want to lock that long the q of audio frs | ||
57 | * for the alsa writer, then do finer-grained locking here | ||
58 | */ | ||
59 | static void dec_sets_receive_avail(void) { loop | ||
56 | 60 | { | { |
57 | 61 | u8 r; | u8 r; |
58 | 62 | ||
59 | r = dec_set_try_get(); | ||
63 | dec_ctx_lock(); | ||
64 | dec_sets_lock(); | ||
65 | r = dec_set_try_receive(); | ||
66 | dec_sets_unlock(); | ||
67 | dec_ctx_unlock(); | ||
60 | 68 | if (r == HAVE_DEC_SET) | if (r == HAVE_DEC_SET) |
61 | 69 | continue; | continue; |
62 | 70 | else if (r == AGAIN || r == EOF_DEC) | else if (r == AGAIN || r == EOF_DEC) |
... | ... | static void dec_flush(void) | |
197 | 205 | dec_sets_p.eof_receive = false; | dec_sets_p.eof_receive = false; |
198 | 206 | avcodec_flush_bufs(dec_ctx_p); | avcodec_flush_bufs(dec_ctx_p); |
199 | 207 | } | } |
208 | static void dec_ctx_lock(void) | ||
209 | { | ||
210 | int r; | ||
211 | |||
212 | r = pthread_mutex_lock(&dec_ctx_mutex_l); | ||
213 | if (r != 0) | ||
214 | FATALA("%d:unable to lock the decoder context\n", r); | ||
215 | } | ||
216 | static void dec_ctx_unlock(void) | ||
217 | { | ||
218 | int r; | ||
219 | |||
220 | r = pthread_mutex_unlock(&dec_ctx_mutex_l); | ||
221 | if (r != 0) | ||
222 | FATALA("%d:unable to unlock the decoder context\n", r); | ||
223 | } | ||
224 | static void dec_sets_lock(void) | ||
225 | { | ||
226 | int r; | ||
227 | |||
228 | r = pthread_mutex_lock(&dec_sets_p.mutex); | ||
229 | if (r != 0) | ||
230 | FATALA("%d:unable to lock the array of decoder sets\n", r); | ||
231 | } | ||
232 | static void dec_sets_unlock(void) | ||
233 | { | ||
234 | int r; | ||
235 | |||
236 | r = pthread_mutex_unlock(&dec_sets_p.mutex); | ||
237 | if (r != 0) | ||
238 | FATALA("%d:unable to unlock the array of decoder sets\n", r); | ||
239 | } | ||
200 | 240 | static void pcm_cfg(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate, | static void pcm_cfg(snd_pcm_t *pcm, unsigned int chans_n, unsigned int rate, |
201 | 241 | enum avutil_audio_fr_fmt_t ff_fmt) | enum avutil_audio_fr_fmt_t ff_fmt) |
202 | 242 | { | { |
... | ... | static void pcm2ff(snd_pcm_t *pcm, enum avutil_audio_fr_fmt_t *ff_fmt, | |
247 | 287 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
248 | 288 | snd_pcm_hw_params_free(hw_params); | snd_pcm_hw_params_free(hw_params); |
249 | 289 | } | } |
290 | /* we do per-loop fine-grained locking */ | ||
291 | #define sz size | ||
292 | static void pkts_send(void) { loop | ||
293 | { | ||
294 | int r; | ||
295 | avcodec_pkt_ref_t *pr; | ||
296 | |||
297 | pkt_q_lock(pkt_q_p); | ||
298 | if (pkt_q_p->n == 0) | ||
299 | goto unlock_and_return; | ||
300 | pr = pkt_q_p->q[0]; | ||
301 | dec_ctx_lock(); | ||
302 | r = avcodec_send_pkt(dec_ctx_p, pr); | ||
303 | dec_ctx_unlock(); | ||
304 | if (r == AVERROR(EAGAIN)) /* dec is full and the pkt is rejected */ | ||
305 | goto unlock_and_return; | ||
306 | else if (r == AVUTIL_AVERROR_EOF) /* the dec is in draining mode */ | ||
307 | goto unlock_and_return; | ||
308 | else if (r != 0) | ||
309 | FATALA("error while sending a packet to the decoder\n"); | ||
310 | /* r == 0 */ | ||
311 | pipeline_limits_lock(); | ||
312 | pipeline_limits_p.pkts.audio_bytes_n -= pr->sz; | ||
313 | pipeline_limits_unlock(); | ||
314 | |||
315 | pkt_q_deq(pkt_q_p); | ||
316 | avcodec_pkt_unref(pr); | ||
317 | pkt_q_unlock(pkt_q_p); | ||
318 | continue; | ||
319 | |||
320 | unlock_and_return: | ||
321 | pkt_q_unlock(pkt_q_p); | ||
322 | return; | ||
323 | }} | ||
324 | #undef sz |
File npv/audio/public/state.frag.h changed (mode: 100644) (index 7a9cbb0..4a009ad) | |||
1 | 1 | static avcodec_codec_ctx_t *dec_ctx_p; | static avcodec_codec_ctx_t *dec_ctx_p; |
2 | 2 | static struct { | static struct { |
3 | bool eof_receive; /* "receiving" from the dec returned eof */ | ||
3 | pthread_mutex_t mutex; | ||
4 | 4 | ||
5 | bool eof_receive; /* "receiving" from the dec returned eof */ | ||
5 | 6 | u32 n_max; | u32 n_max; |
6 | 7 | u32 n; | u32 n; |
7 | 8 | avutil_audio_set_ref_t **a; | avutil_audio_set_ref_t **a; |
8 | 9 | } dec_sets_p; | } dec_sets_p; |
9 | 10 | static struct pkt_q_t *pkt_q_p; | static struct pkt_q_t *pkt_q_p; |
10 | static int st_idx_p; | ||
11 | /* | ||
12 | * we copy some stream data in the case the stream does vanish or is replaced | ||
13 | * (don't know how ffmpeg does handle this) | ||
14 | */ | ||
15 | static struct { | ||
16 | int idx; | ||
17 | int id; | ||
18 | avutil_rational_t tb; | ||
19 | int64_t start_time; | ||
20 | } st_p; | ||
11 | 21 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
12 | 22 | /* alsa -- start */ | /* alsa -- start */ |
13 | 23 | static snd_pcm_t *pcm_p; | static snd_pcm_t *pcm_p; |
File npv/c_fixing.h changed (mode: 100644) (index 117136d..9516575) | |||
28 | 28 | #if UCHAR_WIDTH == 8 | #if UCHAR_WIDTH == 8 |
29 | 29 | #define atomic_u8 atomic_uchar | #define atomic_u8 atomic_uchar |
30 | 30 | #else | #else |
31 | #error "unable to find the right atomic for a 8 bits byte needed for futex, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
31 | #error "unable to find the right atomic for a 8 bits byte, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
32 | #endif | ||
33 | #if SHRT_WIDTH == 16 | ||
34 | #define atomic_u16 atomic_ushort | ||
35 | #else | ||
36 | #error "unable to find the right atomic for an unsigned 16 bits word, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
32 | 37 | #endif | #endif |
33 | 38 | #if UINT_WIDTH == 32 | #if UINT_WIDTH == 32 |
34 | 39 | #define atomic_u32 atomic_uint | #define atomic_u32 atomic_uint |
35 | 40 | #elif ULONG_WIDTH == 32 | #elif ULONG_WIDTH == 32 |
36 | 41 | #define atomic_u32 atomic_ulong | #define atomic_u32 atomic_ulong |
37 | 42 | #else | #else |
38 | #error "unable to select the right atomic for a 32 bits word needed for futex, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
43 | #error "unable to select the right atomic for an unsigned 32 bits word, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
44 | #endif | ||
45 | #if LONG_WIDTH == 64 | ||
46 | #define atomic_s64 atomic_long | ||
47 | #else | ||
48 | #error "unable to select the right atomic for a 64 bits signed word, be sure to have __STDC_WANT_IEC_60559_BFP_EXT__ defined" | ||
39 | 49 | #endif | #endif |
40 | 50 | ||
41 | 51 | #define loop for(;;) | #define loop for(;;) |
File npv/clk/main.c changed (mode: 100644) (index ec4db14..115012d) | |||
6 | 6 | */ | */ |
7 | 7 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
8 | 8 | #include <string.h> | #include <string.h> |
9 | #include <pthread.h> | ||
9 | 10 | #include <alsa/asoundlib.h> | #include <alsa/asoundlib.h> |
10 | 11 | #include <libavformat/avformat.h> | #include <libavformat/avformat.h> |
11 | 12 | #include "npv/c_fixing.h" | #include "npv/c_fixing.h" |
12 | #include "npv/fmt/public.h" | ||
13 | 13 | #include "npv/audio/public.h" | #include "npv/audio/public.h" |
14 | 14 | #include "npv/video/public.h" | #include "npv/video/public.h" |
15 | 15 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
File npv/clk/public/code.frag.c changed (mode: 100644) (index 3585313..1795d99) | |||
... | ... | static u8 clk_get_audio_st_ts(s64 *ts) | |
51 | 51 | ||
52 | 52 | ref_audio_ts = (f64)clk_l.ref.audio_st_ts; | ref_audio_ts = (f64)clk_l.ref.audio_st_ts; |
53 | 53 | ref_delay_frs_n = (f64)snd_pcm_status_get_delay(clk_l.ref.status); | ref_delay_frs_n = (f64)snd_pcm_status_get_delay(clk_l.ref.status); |
54 | audio_tb_num = (f64)fmt_ctx_p->sts[audio_st_idx_p]->tb.num; | ||
55 | audio_tb_den = (f64)fmt_ctx_p->sts[audio_st_idx_p]->tb.den; | ||
54 | audio_tb_num = (f64)audio_st_p.tb.num; | ||
55 | audio_tb_den = (f64)audio_st_p.tb.den; | ||
56 | 56 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
57 | 57 | r = snd_pcm_hw_params_current(audio_pcm_p, clk_pcm_hw_params_l); | r = snd_pcm_hw_params_current(audio_pcm_p, clk_pcm_hw_params_l); |
58 | 58 | if (r != 0) | if (r != 0) |
... | ... | static u8 clk_get_video_st_ts(s64 *ts) | |
87 | 87 | f64 now_video_ts; | f64 now_video_ts; |
88 | 88 | u8 r; | u8 r; |
89 | 89 | ||
90 | audio_tb_num = (f64)fmt_ctx_p->sts[audio_st_idx_p]->tb.num; | ||
91 | audio_tb_den = (f64)fmt_ctx_p->sts[audio_st_idx_p]->tb.den; | ||
92 | video_tb_num = (f64)fmt_ctx_p->sts[video_st_idx_p]->tb.num; | ||
93 | video_tb_den = (f64)fmt_ctx_p->sts[video_st_idx_p]->tb.den; | ||
90 | audio_tb_num = (f64)audio_st_p.tb.num; | ||
91 | audio_tb_den = (f64)audio_st_p.tb.den; | ||
92 | video_tb_num = (f64)video_st_p.tb.num; | ||
93 | video_tb_den = (f64)video_st_p.tb.den; | ||
94 | 94 | ||
95 | 95 | r = clk_get_audio_st_ts(&now_audio_ts_s64); | r = clk_get_audio_st_ts(&now_audio_ts_s64); |
96 | 96 | if (r != TS_RETURNED) | if (r != TS_RETURNED) |
File npv/fmt/local/code.frag.c changed (mode: 100644) (index 044d315..9b8a611) | |||
... | ... | static void init_once_public(u8 *url) | |
6 | 6 | r = avformat_open_input(&ctx_p, url, NULL, NULL); | r = avformat_open_input(&ctx_p, url, NULL, NULL); |
7 | 7 | if (r < 0) | if (r < 0) |
8 | 8 | FATALF("ffmpeg:unable to open \"%s\"\n", url); | FATALF("ffmpeg:unable to open \"%s\"\n", url); |
9 | r = pthread_mutex_init(&ctx_mutex_l, 0); | ||
10 | if (r != 0) | ||
11 | FATALF("unable to init the format mutex\n"); | ||
9 | 12 | } | } |
10 | 13 | static void init_once_local(void) | static void init_once_local(void) |
11 | 14 | { | { |
... | ... | static void init_once_local(void) | |
13 | 16 | if (pkt_l == 0) | if (pkt_l == 0) |
14 | 17 | FATALF("ffmpeg:unable to allocate a reference on a packet for encoded/compressed audio/video\n"); | FATALF("ffmpeg:unable to allocate a reference on a packet for encoded/compressed audio/video\n"); |
15 | 18 | } | } |
19 | static void ctx_lock(void) | ||
20 | { | ||
21 | int r; | ||
22 | |||
23 | r = pthread_mutex_lock(&ctx_mutex_l); | ||
24 | if (r != 0) | ||
25 | FATALF("%d:unable to lock the format context\n", r); | ||
26 | } | ||
27 | static void ctx_unlock(void) | ||
28 | { | ||
29 | int r; | ||
30 | |||
31 | r = pthread_mutex_unlock(&ctx_mutex_l); | ||
32 | if (r != 0) | ||
33 | FATALF("%d:unable to unlock the format context\n", r); | ||
34 | } |
File npv/fmt/local/state.frag.c changed (mode: 100644) (index a1cad7b..15fd6ab) | |||
1 | 1 | static avcodec_pkt_ref_t *pkt_l; | static avcodec_pkt_ref_t *pkt_l; |
2 | static pthread_mutex_t ctx_mutex_l; |
File npv/fmt/main.c changed (mode: 100644) (index 8757761..e01fc75) | |||
4 | 4 | * code protected with a GNU affero GPLv3 license | * code protected with a GNU affero GPLv3 license |
5 | 5 | * copyright (C) 2020 Sylvain BERTRAND | * copyright (C) 2020 Sylvain BERTRAND |
6 | 6 | */ | */ |
7 | #include <pthread.h> | ||
7 | 8 | #include <libavformat/avformat.h> | #include <libavformat/avformat.h> |
8 | 9 | #include <libavcodec/avcodec.h> | #include <libavcodec/avcodec.h> |
9 | 10 | #include "npv/c_fixing.h" | #include "npv/c_fixing.h" |
File npv/fmt/namespace/main.c changed (mode: 100644) (index 26d01d9..1399a6c) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | 2 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
3 | /* misc */ | ||
3 | 4 | #define avformat_read_pkt av_read_frame | #define avformat_read_pkt av_read_frame |
4 | 5 | #define programs_n nb_programs | #define programs_n nb_programs |
5 | 6 | #define st stream | #define st stream |
6 | 7 | #define st_idx stream_index | #define st_idx stream_index |
7 | 8 | #define sts_n nb_streams | #define sts_n nb_streams |
9 | #define sz size | ||
8 | 10 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
9 | 11 | #define audio fmt_break_on_audio | #define audio fmt_break_on_audio |
12 | #define ctx_mutex_l fmt_ctx_mutex_l | ||
10 | 13 | #define pkt_l fmt_pkt_l | #define pkt_l fmt_pkt_l |
11 | 14 | #define video fmt_break_on_video | #define video fmt_break_on_video |
12 | 15 | /*============================================================================*/ | /*============================================================================*/ |
17 | 20 | #undef st | #undef st |
18 | 21 | #undef st_idx | #undef st_idx |
19 | 22 | #undef sts_n | #undef sts_n |
23 | #undef sz | ||
20 | 24 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
21 | 25 | #undef audio | #undef audio |
26 | #undef ctx_mutex_l | ||
22 | 27 | #undef pkt_l | #undef pkt_l |
23 | 28 | #undef video | #undef video |
24 | 29 | #endif | #endif |
File npv/fmt/namespace/public.h changed (mode: 100644) (index a5d8336..83e463a) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | 2 | #define break_on_audio fmt_break_on_audio | #define break_on_audio fmt_break_on_audio |
3 | 3 | #define break_on_video fmt_break_on_video | #define break_on_video fmt_break_on_video |
4 | #define ctx_lock fmt_ctx_lock | ||
4 | 5 | #define ctx_p fmt_ctx_p | #define ctx_p fmt_ctx_p |
6 | #define ctx_unlock fmt_ctx_unlock | ||
5 | 7 | #define duration_estimate_to_str fmt_duration_estimate_to_str | #define duration_estimate_to_str fmt_duration_estimate_to_str |
6 | 8 | #define flush fmt_flush | #define flush fmt_flush |
7 | 9 | #define init_once fmt_init_once | #define init_once fmt_init_once |
13 | 15 | #else | #else |
14 | 16 | #undef break_on_audio | #undef break_on_audio |
15 | 17 | #undef break_on_video | #undef break_on_video |
18 | #undef ctx_lock | ||
16 | 19 | #undef ctx_p | #undef ctx_p |
20 | #undef ctx_unlock | ||
17 | 21 | #undef duration_estimate_to_str | #undef duration_estimate_to_str |
18 | 22 | #undef flush | #undef flush |
19 | 23 | #undef init_once | #undef init_once |
File npv/fmt/public.h changed (mode: 100644) (index 7a96b2a..24b6b9d) | |||
6 | 6 | */ | */ |
7 | 7 | #include <libavformat/avformat.h> | #include <libavformat/avformat.h> |
8 | 8 | #include "npv/c_fixing.h" | #include "npv/c_fixing.h" |
9 | #include "npv/pipeline/public.h" | ||
9 | 10 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
10 | 11 | #include "npv/namespace/ffmpeg.h" | #include "npv/namespace/ffmpeg.h" |
11 | 12 | #include "npv/fmt/namespace/public.h" | #include "npv/fmt/namespace/public.h" |
14 | 15 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
15 | 16 | static u8 *duration_estimate_to_str( | static u8 *duration_estimate_to_str( |
16 | 17 | enum avformat_duration_estimation_method_t m); | enum avformat_duration_estimation_method_t m); |
17 | static void probe_best_sts(int *best_v, int *best_a); | ||
18 | static void probe_best_sts( | ||
19 | int *best_v_idx, int *best_v_id, avutil_rational_t **best_v_tb, | ||
20 | int64_t *best_v_start_time, avcodec_params_t **best_v_codec_params, | ||
21 | int *best_a_idx, int *best_a_id, avutil_rational_t **best_a_tb, | ||
22 | int64_t *best_a_start_time, avcodec_params_t **best_a_codec_params); | ||
18 | 23 | static void init_once(u8 *url); | static void init_once(u8 *url); |
19 | constant_u32 { | ||
20 | break_on_video = 0x01, | ||
21 | break_on_audio = 0x02 | ||
22 | }; | ||
23 | static u8 pkts_read_and_q(u8 break_on); | ||
24 | static u8 pkts_read_and_q(void); | ||
24 | 25 | static void flush(void); | static void flush(void); |
26 | static void fmt_ctx_lock(void); | ||
27 | static void fmt_ctx_unlock(void); | ||
25 | 28 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
26 | 29 | #define CLEANUP | #define CLEANUP |
27 | 30 | #include "npv/namespace/ffmpeg.h" | #include "npv/namespace/ffmpeg.h" |
File npv/fmt/public/code.frag.c changed (mode: 100644) (index 9ae7123..8370517) | |||
... | ... | static u8 *duration_estimate_to_str(enum avformat_duration_estimation_method_t | |
12 | 12 | return "unkwown"; | return "unkwown"; |
13 | 13 | } | } |
14 | 14 | } | } |
15 | /*NSPC*/ | ||
16 | static bool did_reached_limits(void) | ||
17 | { | ||
18 | bool r; | ||
19 | |||
20 | pipeline_limits_lock(); | ||
21 | if (pipeline_limits_p.pkts.audio_bytes_n | ||
22 | >= pipeline_limits_p.pkts.limit.audio_bytes_n | ||
23 | && pipeline_limits_p.pkts.video_bytes_n | ||
24 | >= pipeline_limits_p.pkts.limit.video_bytes_n) | ||
25 | r = true; | ||
26 | else | ||
27 | r = false; | ||
28 | pipeline_limits_unlock(); | ||
29 | return false; | ||
30 | } | ||
15 | 31 | #define AGAIN 0 | #define AGAIN 0 |
16 | #define HAVE_PKT 1 | ||
32 | #define LIMITS_REACHED 1 | ||
17 | 33 | #define EOF_FMT 2 | #define EOF_FMT 2 |
18 | static u8 pkts_read_and_q(u8 break_on) { loop | ||
34 | /* | ||
35 | * we don't want to lock any pkt q for too long, then we do finer-grained | ||
36 | * locking here | ||
37 | */ | ||
38 | static u8 pkts_read_and_q(void) { loop | ||
19 | 39 | { | { |
20 | 40 | int r; | int r; |
21 | 41 | ||
42 | if (did_reached_limits()) | ||
43 | return LIMITS_REACHED; | ||
44 | /* XXX: there, new streams can appear (could some disappear?) */ | ||
45 | ctx_lock(); | ||
22 | 46 | r = avformat_read_pkt(ctx_p, pkt_l); | r = avformat_read_pkt(ctx_p, pkt_l); |
23 | if (r == 0) { | ||
24 | if (pkt_l->st_idx == audio_st_idx_p) { | ||
25 | /* will "steal" the pkt */ | ||
26 | pkt_q_enq(audio_pkt_q_p, pkt_l); | ||
27 | if ((break_on & audio) != 0) | ||
28 | return HAVE_PKT; | ||
29 | } else if (pkt_l->st_idx == video_st_idx_p) { | ||
30 | /* will "steal" the pkt */ | ||
31 | pkt_q_enq(video_pkt_q_p, pkt_l); | ||
32 | if ((break_on & video) != 0) | ||
33 | return HAVE_PKT; | ||
34 | } | ||
35 | /* other types of pkt: data, subtitles, etc */ | ||
36 | avcodec_pkt_unref(pkt_l); | ||
37 | continue; | ||
38 | } else if (r == AVERROR(EAGAIN)) | ||
47 | ctx_unlock(); | ||
48 | if (r == AVERROR(EAGAIN)) | ||
39 | 49 | return AGAIN; | return AGAIN; |
40 | 50 | else if (r == AVERROR_EOF) | else if (r == AVERROR_EOF) |
41 | 51 | return EOF_FMT; | return EOF_FMT; |
42 | FATALF("ffmpeg:error while reading coded/compressed data into packets\n"); | ||
52 | else if (r != 0) | ||
53 | FATALF("ffmpeg:error while reading coded/compressed data into packets\n"); | ||
54 | /* r == 0 */ | ||
55 | if (pkt_l->st_idx == audio_st_p.idx) { | ||
56 | pipeline_limits_lock(); | ||
57 | pipeline_limits_p.pkts.audio_bytes_n += (u64)pkt_l->sz; | ||
58 | if (pipeline_limits_p.pkts.prefill.audio_bytes_rem > 0) | ||
59 | pipeline_limits_p.pkts.prefill.audio_bytes_rem -= | ||
60 | (s64)pkt_l->sz; | ||
61 | pipeline_limits_unlock(); | ||
62 | |||
63 | pkt_q_lock(audio_pkt_q_p); | ||
64 | /* will "steal" the pkt */ | ||
65 | pkt_q_enq(audio_pkt_q_p, pkt_l); | ||
66 | pkt_q_unlock(audio_pkt_q_p); | ||
67 | } else if (pkt_l->st_idx == video_st_p.idx) { | ||
68 | pipeline_limits_lock(); | ||
69 | pipeline_limits_p.pkts.video_bytes_n += (u64)pkt_l->sz; | ||
70 | if (pipeline_limits_p.pkts.prefill.video_bytes_rem > 0) | ||
71 | pipeline_limits_p.pkts.prefill.video_bytes_rem -= | ||
72 | (s64)pkt_l->sz; | ||
73 | pipeline_limits_unlock(); | ||
74 | |||
75 | pkt_q_lock(video_pkt_q_p); | ||
76 | /* will "steal" the pkt */ | ||
77 | pkt_q_enq(video_pkt_q_p, pkt_l); | ||
78 | pkt_q_unlock(video_pkt_q_p); | ||
79 | } else /* data, subtitles, non active sts, etc */ | ||
80 | avcodec_pkt_unref(pkt_l); | ||
43 | 81 | }} | }} |
44 | 82 | #undef AGAIN | #undef AGAIN |
45 | #undef HAVE_PKT | ||
83 | #undef LIMITS_REACHED | ||
46 | 84 | #undef EOF_FMT | #undef EOF_FMT |
47 | static void probe_best_sts(int *best_v, int *best_a) | ||
85 | static void probe_best_sts( | ||
86 | int *best_v_idx, int *best_v_id, avutil_rational_t **best_v_tb, | ||
87 | int64_t *best_v_start_time, avcodec_params_t **best_v_codec_params, | ||
88 | int *best_a_idx, int *best_a_id, avutil_rational_t **best_a_tb, | ||
89 | int64_t *best_a_start_time, avcodec_params_t **best_a_codec_params) | ||
48 | 90 | { | { |
49 | 91 | int r; | int r; |
50 | 92 | ||
... | ... | static void probe_best_sts(int *best_v, int *best_a) | |
56 | 98 | 0); | 0); |
57 | 99 | if (r < 0) | if (r < 0) |
58 | 100 | FATALF("ffmpeg:no audio stream found\n"); | FATALF("ffmpeg:no audio stream found\n"); |
59 | *best_a = r; | ||
101 | /* we copy what we need */ | ||
102 | *best_a_idx = r; | ||
103 | *best_a_id = ctx_p->sts[r]->id; | ||
104 | *best_a_tb = &ctx_p->sts[r]->tb; | ||
105 | *best_a_start_time = ctx_p->sts[r]->start_time; | ||
106 | *best_a_codec_params = ctx_p->sts[r]->codecpar; /* used once for init */ | ||
60 | 107 | ||
61 | 108 | r = avformat_find_best_st(ctx_p, AVUTIL_AVMEDIA_TYPE_VIDEO, -1, -1, 0, | r = avformat_find_best_st(ctx_p, AVUTIL_AVMEDIA_TYPE_VIDEO, -1, -1, 0, |
62 | 109 | 0); | 0); |
63 | 110 | if (r < 0) | if (r < 0) |
64 | 111 | FATALF("ffmpeg:no video stream found\n"); | FATALF("ffmpeg:no video stream found\n"); |
65 | *best_v = r; | ||
112 | /* we copy what we need */ | ||
113 | *best_v_idx = r; | ||
114 | *best_v_id = ctx_p->sts[r]->id; | ||
115 | *best_v_tb = &ctx_p->sts[r]->tb; | ||
116 | *best_v_start_time = ctx_p->sts[r]->start_time; | ||
117 | *best_v_codec_params = ctx_p->sts[r]->codecpar; /* used once for init */ | ||
66 | 118 | POUTF("####%s#### probing: %u streams and %u programs\n", ctx_p->url, ctx_p->sts_n, ctx_p->programs_n); | POUTF("####%s#### probing: %u streams and %u programs\n", ctx_p->url, ctx_p->sts_n, ctx_p->programs_n); |
67 | POUTF("####%s#### probing: best video stream id=%d, best audio stream id=%d\n", ctx_p->url, ctx_p->sts[video_st_idx_p]->id, ctx_p->sts[audio_st_idx_p]->id); | ||
119 | POUTF("####%s#### probing: best video stream index=%d/id=%d, best audio stream index=%d/id=%d\n", ctx_p->url, *best_v_idx, *best_v_id, *best_a_idx, *best_a_id); | ||
68 | 120 | } | } |
69 | 121 | static void flush(void) | static void flush(void) |
70 | 122 | { | { |
File npv/input/local/code.frag.c deleted (index 3e78dbf..0000000) | |||
1 | static void timer_init_once(void) | ||
2 | { | ||
3 | /* linux bug: still no CLOCK_MONOTONIC_RAW for timerfd */ | ||
4 | errno = 0; | ||
5 | timer_fd_p = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); | ||
6 | if (timer_fd_p == -1) | ||
7 | FATALI("unable to get a timer file descriptor:%s\n", strerror(errno)); | ||
8 | } | ||
9 | static void timer_ack(void) | ||
10 | { | ||
11 | int r; | ||
12 | uint64_t exps_n; | ||
13 | |||
14 | exps_n = 0; | ||
15 | r = read(timer_fd_p, &exps_n, sizeof(exps_n)); | ||
16 | if (r == -1) | ||
17 | FATALI("unable to read the number of timer expirations\n"); | ||
18 | } |
File npv/input/main.c deleted (index c183ca3..0000000) | |||
1 | #ifndef NPV_INPUT_MAIN_C | ||
2 | #define NPV_INPUT_MAIN_C | ||
3 | /* | ||
4 | * code protected with a GNU affero GPLv3 license | ||
5 | * copyright (C) 2020 Sylvain BERTRAND | ||
6 | */ | ||
7 | #include <string.h> | ||
8 | #include <stdint.h> | ||
9 | #include <sys/timerfd.h> | ||
10 | #include "npv/c_fixing.h" | ||
11 | #include "npv/global.h" | ||
12 | #include "npv/pkt_q/public.h" | ||
13 | #include "npv/input/public.h" | ||
14 | #include "npv/fmt/public.h" | ||
15 | #include "npv/audio/public.h" | ||
16 | #include "npv/video/public.h" | ||
17 | /*----------------------------------------------------------------------------*/ | ||
18 | #include "npv/namespace/ffmpeg.h" | ||
19 | #include "npv/input/namespace/public.h" | ||
20 | #include "npv/input/namespace/main.c" | ||
21 | /*----------------------------------------------------------------------------*/ | ||
22 | #define FATALI(fmt, ...) FATAL("input:" fmt, ##__VA_ARGS__) | ||
23 | #define WARNINGI(fmt, ...) WARNING("input:" fmt, ##__VA_ARGS__) | ||
24 | #define POUTI(fmt, ...) POUT("input:" fmt, ##__VA_ARGS__) | ||
25 | /*----------------------------------------------------------------------------*/ | ||
26 | #define TIMER_INTERVAL_NSECS_N 1000000 /* 1 ms */ | ||
27 | #define BOOTSTRAP_VIDEO_FRS_N_LIMIT 5 /* usually expensive */ | ||
28 | #define BOOTSTRAP_AUDIO_SETS_N_LIMIT 20 /* usually cheap */ | ||
29 | #define VIDEO_FRS_N_LIMIT 10 /* usually expensive */ | ||
30 | #define AUDIO_SETS_N_LIMIT 20 /* usually cheap */ | ||
31 | /*----------------------------------------------------------------------------*/ | ||
32 | #include "npv/input/local/state.frag.c" | ||
33 | /*----------------------------------------------------------------------------*/ | ||
34 | #include "npv/input/local/code.frag.c" | ||
35 | #include "npv/input/public/code.frag.c" | ||
36 | /*----------------------------------------------------------------------------*/ | ||
37 | #undef TIMER_INTERVAL_NSECS_N | ||
38 | #undef VIDEO_FRS_N_LIMIT | ||
39 | #undef AUDIO_SETS_N_LIMIT | ||
40 | #undef FATALI | ||
41 | #undef WARNINGI | ||
42 | #undef POUTI | ||
43 | #undef BOOTSTRAP_VIDEO_FRS_N_LIMIT | ||
44 | #undef BOOTSTRAP_AUDIO_SETS_N_LIMIT | ||
45 | /*---------------------------------------------------------------------------*/ | ||
46 | #define CLEANUP | ||
47 | #include "npv/namespace/ffmpeg.h" | ||
48 | #include "npv/input/namespace/public.h" | ||
49 | #include "npv/input/namespace/main.c" | ||
50 | #undef CLEANUP | ||
51 | #endif |
File npv/input/namespace/main.c deleted (index e21e819..0000000) | |||
1 | #ifndef CLEANUP | ||
2 | /*----------------------------------------------------------------------------*/ | ||
3 | /* some struct fields */ | ||
4 | #define sz size | ||
5 | /*----------------------------------------------------------------------------*/ | ||
6 | #define timer_ack input_timer_ack | ||
7 | #define timer_init_once input_timer_init_once | ||
8 | /*============================================================================*/ | ||
9 | #else | ||
10 | #undef timer_ack | ||
11 | #undef timer_init_once | ||
12 | /*----------------------------------------------------------------------------*/ | ||
13 | #undef sz | ||
14 | /*----------------------------------------------------------------------------*/ | ||
15 | #endif | ||
16 |
File npv/input/namespace/public.h deleted (index ee12f6f..0000000) | |||
1 | #ifndef CLEANUP | ||
2 | #define bootstrap_audio_video input_bootstrap_audio_video | ||
3 | #define init_once input_init_once | ||
4 | #define timer_evt input_timer_evt | ||
5 | #define timer_fd_p input_timer_fd_p | ||
6 | #define timer_start input_timer_start | ||
7 | /*============================================================================*/ | ||
8 | #else | ||
9 | #undef bootstrap_audio_video | ||
10 | #undef init_once | ||
11 | #undef timer_evt | ||
12 | #undef timer_fd_p | ||
13 | #undef timer_start | ||
14 | #endif |
File npv/input/public.h deleted (index 9fa5f46..0000000) | |||
1 | #ifndef NPV_INPUT_PUBLIC_H | ||
2 | #define NPV_INPUT_PUBLIC_H | ||
3 | /* | ||
4 | * code protected with a GNU affero GPLv3 license | ||
5 | * copyright (C) 2020 Sylvain BERTRAND | ||
6 | */ | ||
7 | /*---------------------------------------------------------------------------*/ | ||
8 | #include "npv/input/namespace/public.h" | ||
9 | /*---------------------------------------------------------------------------*/ | ||
10 | static int timer_fd_p; | ||
11 | static void init_once(void); | ||
12 | static void timer_start(void); | ||
13 | static void timer_evt(void); | ||
14 | static void bootstrap_audio_video(void); | ||
15 | /*---------------------------------------------------------------------------*/ | ||
16 | #define CLEANUP | ||
17 | #include "npv/input/namespace/public.h" | ||
18 | #undef CLEANUP | ||
19 | #endif |
File npv/input/public/code.frag.c deleted (index 68e5be3..0000000) | |||
1 | static void init_once(void) | ||
2 | { | ||
3 | timer_init_once(); | ||
4 | eof_pkt_l = avcodec_pkt_ref_alloc(); | ||
5 | if (eof_pkt_l == 0) | ||
6 | FATALI("ffmpeg:unable to allocate a null/eof reference on a packet\n"); | ||
7 | eof_pkt_l->data = 0; | ||
8 | eof_pkt_l->sz = 0; | ||
9 | } | ||
10 | static void timer_start(void) | ||
11 | { | ||
12 | struct itimerspec t; | ||
13 | int r; | ||
14 | |||
15 | memset(&t, 0, sizeof(t)); | ||
16 | /* initial and interval */ | ||
17 | t.it_value.tv_nsec = TIMER_INTERVAL_NSECS_N; | ||
18 | t.it_interval.tv_nsec = TIMER_INTERVAL_NSECS_N; | ||
19 | r = timerfd_settime(timer_fd_p, 0, &t, 0); | ||
20 | if (r == -1) | ||
21 | FATALI("unable to arm the timer\n"); | ||
22 | } | ||
23 | #define AGAIN 0 | ||
24 | #define HAVE_AUDIO_DEC_SET 1 | ||
25 | #define HAVE_VIDEO_FR 1 | ||
26 | #define EOF_FMT 2 | ||
27 | #define EOF_VIDEO_DEC 2 | ||
28 | #define EOF_AUDIO_DEC 2 | ||
29 | #define STOP 4 | ||
30 | /* | ||
31 | * audio and video are decoupled | ||
32 | * 2 steps: | ||
33 | * 1 - one set of audio frs, one video fr | ||
34 | * 2 - buffering of sets of audio frs, buffering of video frs | ||
35 | */ | ||
36 | static void bootstrap_audio_video(void) | ||
37 | { | ||
38 | u8 fmt; | ||
39 | u8 audio; | ||
40 | u8 video; | ||
41 | u8 break_on; | ||
42 | |||
43 | audio = AGAIN; | ||
44 | video = AGAIN; | ||
45 | break_on = fmt_break_on_audio | fmt_break_on_video; | ||
46 | loop { | ||
47 | fmt = fmt_pkts_read_and_q(break_on); | ||
48 | if (fmt == EOF_FMT) { | ||
49 | if (!pkt_q_has_eof(audio_pkt_q_p)) | ||
50 | pkt_q_enq(audio_pkt_q_p, eof_pkt_l); | ||
51 | if (!pkt_q_has_eof(video_pkt_q_p)) | ||
52 | pkt_q_enq(video_pkt_q_p, eof_pkt_l); | ||
53 | } | ||
54 | if (audio == AGAIN) { /* first step */ | ||
55 | (void)pkt_q_send(audio_pkt_q_p, audio_dec_ctx_p); | ||
56 | audio = audio_dec_set_try_get(); | ||
57 | if (audio == EOF_AUDIO_DEC) { | ||
58 | audio = STOP; | ||
59 | WARNING("bootstrap:end of file reached before one set of audio frames could be available for playing, %u sets of audio frames\n", BOOTSTRAP_AUDIO_SETS_N_LIMIT); | ||
60 | } | ||
61 | } else if (audio == HAVE_AUDIO_DEC_SET) { /* 2nd step */ | ||
62 | if (audio_dec_sets_p.n | ||
63 | <= BOOTSTRAP_AUDIO_SETS_N_LIMIT) { | ||
64 | u8 r; | ||
65 | |||
66 | r = pkt_q_send(audio_pkt_q_p, audio_dec_ctx_p); | ||
67 | if (r == EOF_AUDIO_DEC || r == AGAIN) { | ||
68 | audio = STOP; | ||
69 | break_on &= ~fmt_break_on_audio; | ||
70 | } | ||
71 | audio_dec_sets_get_avail(); | ||
72 | } else { | ||
73 | audio = STOP; | ||
74 | break_on &= ~fmt_break_on_audio; | ||
75 | } | ||
76 | } | ||
77 | if (video == AGAIN) { /* first step */ | ||
78 | (void)pkt_q_send(video_pkt_q_p, video_dec_ctx_p); | ||
79 | video = video_dec_fr_try_get(); | ||
80 | if (video == EOF_VIDEO_DEC) { | ||
81 | video = STOP; | ||
82 | WARNING("bootstrap:end of file reached before one video frame could be available for presentation, %u video frames\n", BOOTSTRAP_VIDEO_FRS_N_LIMIT); | ||
83 | } | ||
84 | } else if (video == HAVE_VIDEO_FR) { /* 2nd step: buffering */ | ||
85 | if (video_dec_frs_p.n | ||
86 | <= BOOTSTRAP_VIDEO_FRS_N_LIMIT) { | ||
87 | u8 r; | ||
88 | r = pkt_q_send(video_pkt_q_p, video_dec_ctx_p); | ||
89 | if (r == EOF_VIDEO_DEC || r == AGAIN) { | ||
90 | video = STOP; | ||
91 | break_on &= ~fmt_break_on_video; | ||
92 | } | ||
93 | video_dec_frs_get_avail(); | ||
94 | } else { | ||
95 | video = STOP; | ||
96 | break_on &= ~fmt_break_on_video; | ||
97 | } | ||
98 | } | ||
99 | if (video == STOP && audio == STOP) | ||
100 | break; | ||
101 | } | ||
102 | } | ||
103 | #undef AGAIN | ||
104 | #undef HAVE_AUDIO_DEC_SET | ||
105 | #undef HAVE_VIDEO_FR | ||
106 | #undef EOF_FMT | ||
107 | #undef EOF_VIDEO_DEC | ||
108 | #undef EOF_AUDIO_DEC | ||
109 | #undef STOP | ||
110 | #define AGAIN 0 | ||
111 | #define SEND 0 | ||
112 | #define HAVE_PKT 1 | ||
113 | #define EOF_FMT 2 | ||
114 | #define EOF_DEC 2 | ||
115 | #define STOP 4 | ||
116 | /* | ||
117 | * TODO: bad, should consider unlimited streams in the 2 following cases: | ||
118 | * - very fast | ||
119 | * - very slow | ||
120 | */ | ||
121 | static void timer_evt(void) | ||
122 | { | ||
123 | u8 audio; | ||
124 | u8 video; | ||
125 | u8 break_on; | ||
126 | |||
127 | timer_ack(); | ||
128 | /* 1 - empty the decs as much as we can */ | ||
129 | audio_dec_sets_get_avail(); | ||
130 | video_dec_frs_get_avail(); | ||
131 | /* | ||
132 | * 2 - send pkts as much as we can in decs unless we did bump on our | ||
133 | * reasonable limits | ||
134 | */ | ||
135 | break_on = 0; | ||
136 | if (audio_dec_sets_p.n > AUDIO_SETS_N_LIMIT) | ||
137 | audio = STOP; | ||
138 | else { | ||
139 | break_on |= fmt_break_on_audio; | ||
140 | audio = SEND; | ||
141 | } | ||
142 | if (video_dec_frs_p.n > VIDEO_FRS_N_LIMIT) | ||
143 | video = STOP; | ||
144 | else { | ||
145 | break_on |= fmt_break_on_video; | ||
146 | video = SEND; | ||
147 | } | ||
148 | loop { | ||
149 | u8 r; | ||
150 | |||
151 | if (video == STOP && audio == STOP) | ||
152 | break; | ||
153 | r = fmt_pkts_read_and_q(break_on); | ||
154 | if (r == EOF_FMT) { | ||
155 | if (!pkt_q_has_eof(audio_pkt_q_p)) | ||
156 | pkt_q_enq(audio_pkt_q_p, eof_pkt_l); | ||
157 | if (!pkt_q_has_eof(video_pkt_q_p)) | ||
158 | pkt_q_enq(video_pkt_q_p, eof_pkt_l); | ||
159 | (void)pkt_q_send(audio_pkt_q_p, audio_dec_ctx_p); | ||
160 | (void)pkt_q_send(video_pkt_q_p, video_dec_ctx_p); | ||
161 | break; | ||
162 | } | ||
163 | if (r == AGAIN) | ||
164 | break; | ||
165 | /* r == HAVE_PKT */ | ||
166 | if (audio != STOP) { | ||
167 | r = pkt_q_send(audio_pkt_q_p, audio_dec_ctx_p); | ||
168 | if (r == AGAIN || r == EOF_DEC) { | ||
169 | audio = STOP; | ||
170 | break_on &= ~fmt_break_on_audio; | ||
171 | } else /* r == EMPTY */ | ||
172 | audio = SEND; | ||
173 | } | ||
174 | if (video != STOP) { | ||
175 | r = pkt_q_send(video_pkt_q_p, video_dec_ctx_p); | ||
176 | if (r == AGAIN || r == EOF_DEC) { | ||
177 | video = STOP; | ||
178 | break_on &= ~fmt_break_on_video; | ||
179 | } else /* r == EMPTY */ | ||
180 | video = SEND; | ||
181 | } | ||
182 | } | ||
183 | } | ||
184 | #undef AGAIN | ||
185 | #undef SEND | ||
186 | #undef HAVE_PKT | ||
187 | #undef EOF_FMT | ||
188 | #undef EOF_DEC | ||
189 | #undef STOP |
File npv/local/code.frag.c changed (mode: 100644) (index 4fd08bd..215d044) | |||
... | ... | static void evt_add_all_fds(void) | |
117 | 117 | if (r == -1) | if (r == -1) |
118 | 118 | FATAL("unable to add the signalfd file descriptior to the epoll file descriptor\n"); | FATAL("unable to add the signalfd file descriptior to the epoll file descriptor\n"); |
119 | 119 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
120 | /* the input timer */ | ||
121 | evt.events = EPOLLIN; | ||
122 | evt.data.fd = input_timer_fd_p; | ||
123 | r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, input_timer_fd_p, &evt); | ||
124 | if (r == -1) | ||
125 | FATAL("unable to add the input timer file descriptor\n"); | ||
120 | ///* the input timer */ | ||
121 | //evt.events = EPOLLIN; | ||
122 | //evt.data.fd = input_timer_fd_p; | ||
123 | //r = epoll_ctl(ep_fd_p, EPOLL_CTL_ADD, input_timer_fd_p, &evt); | ||
124 | //if (r == -1) | ||
125 | // FATAL("unable to add the input timer file descriptor\n"); | ||
126 | 126 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
127 | 127 | /* the video timer */ | /* the video timer */ |
128 | 128 | evt.events = EPOLLIN; | evt.events = EPOLLIN; |
... | ... | static void evt_sigs(void) | |
182 | 182 | /*NSPC*/ | /*NSPC*/ |
183 | 183 | static void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs, | static void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs, |
184 | 184 | bool *have_evt_pcm, bool *have_evt_video, bool *have_evt_pcm_draining, | bool *have_evt_pcm, bool *have_evt_video, bool *have_evt_pcm_draining, |
185 | bool *have_evt_x11, bool *have_evt_input) | ||
185 | bool *have_evt_x11) //bool *have_evt_input) | ||
186 | 186 | { | { |
187 | 187 | u8 i; | u8 i; |
188 | 188 | ||
... | ... | static void evt_accumulate(struct epoll_event *evt, bool *have_evt_sigs, | |
221 | 221 | FATAL("event loop wait:video:unexpected event\n"); | FATAL("event loop wait:video:unexpected event\n"); |
222 | 222 | } | } |
223 | 223 | /*-------------------------------------------------------------------*/ | /*-------------------------------------------------------------------*/ |
224 | if (evt->data.fd == input_timer_fd_p) { | ||
225 | if ((evt->events & EPOLLIN) != 0) { | ||
226 | *have_evt_input = true; | ||
227 | return; | ||
228 | } | ||
229 | FATAL("event loop wait:input:unexpected event\n"); | ||
230 | } | ||
224 | //if (evt->data.fd == input_timer_fd_p) { | ||
225 | // if ((evt->events & EPOLLIN) != 0) { | ||
226 | // *have_evt_input = true; | ||
227 | // return; | ||
228 | // } | ||
229 | // FATAL("event loop wait:input:unexpected event\n"); | ||
230 | //} | ||
231 | 231 | /*-------------------------------------------------------------------*/ | /*-------------------------------------------------------------------*/ |
232 | 232 | if (evt->data.fd == audio_draining_timer_fd_p) { | if (evt->data.fd == audio_draining_timer_fd_p) { |
233 | 233 | if ((evt->events & EPOLLIN) != 0) { | if ((evt->events & EPOLLIN) != 0) { |
... | ... | static void evts_loop(void) | |
254 | 254 | bool have_evt_video; | bool have_evt_video; |
255 | 255 | bool have_evt_pcm_draining; | bool have_evt_pcm_draining; |
256 | 256 | bool have_evt_x11; | bool have_evt_x11; |
257 | bool have_evt_input; | ||
257 | //bool have_evt_input; | ||
258 | 258 | int r; | int r; |
259 | 259 | short pcm_evts; | short pcm_evts; |
260 | 260 | ||
... | ... | static void evts_loop(void) | |
274 | 274 | have_evt_video = false; | have_evt_video = false; |
275 | 275 | have_evt_pcm_draining = false; | have_evt_pcm_draining = false; |
276 | 276 | have_evt_x11 = false; | have_evt_x11 = false; |
277 | have_evt_input = false; | ||
277 | //have_evt_input = false; | ||
278 | 278 | ||
279 | 279 | fd_idx = 0; | fd_idx = 0; |
280 | 280 | loop { | loop { |
... | ... | static void evts_loop(void) | |
282 | 282 | break; | break; |
283 | 283 | evt_accumulate(&evts[fd_idx], &have_evt_sigs, &have_evt_pcm, | evt_accumulate(&evts[fd_idx], &have_evt_sigs, &have_evt_pcm, |
284 | 284 | &have_evt_video, &have_evt_pcm_draining, | &have_evt_video, &have_evt_pcm_draining, |
285 | &have_evt_x11, &have_evt_input); | ||
285 | &have_evt_x11); | ||
286 | //&have_evt_input); | ||
286 | 287 | ++fd_idx; | ++fd_idx; |
287 | 288 | } | } |
288 | 289 | ||
... | ... | static void evts_loop(void) | |
319 | 320 | } | } |
320 | 321 | if (have_evt_video) | if (have_evt_video) |
321 | 322 | video_timer_evt(); | video_timer_evt(); |
322 | if (have_evt_input) | ||
323 | input_timer_evt(); | ||
323 | //if (have_evt_input) | ||
324 | // input_timer_evt(); | ||
324 | 325 | /* while audio is draining, video fr may need to be displayed */ | /* while audio is draining, video fr may need to be displayed */ |
325 | 326 | if (have_evt_pcm_draining) | if (have_evt_pcm_draining) |
326 | 327 | audio_draining_state_evt(); | audio_draining_state_evt(); |
... | ... | static void ff_log_stdout(void *a, int b, const char *fmt, va_list ap) | |
334 | 335 | /*NSPC*/ | /*NSPC*/ |
335 | 336 | static void usage(void) | static void usage(void) |
336 | 337 | { | { |
337 | POUT("npv [-p alsa pcm] [-v volume(0..100)] [-h height in pixels] [-w width in pixels] [-help] url\n"); | ||
338 | POUT("\ | ||
339 | npv [-p alsa pcm] [-v volume(0..100)] [-h height in pixels] [-w width in pixels]\ | ||
340 | [-b packet buffer prefill wait(0..100)] [-help] url\n"); | ||
338 | 341 | } | } |
339 | 342 | /*NSPC*/ | /*NSPC*/ |
340 | 343 | static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h, | static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h, |
341 | u8 **pcm_str, double *vol) | ||
344 | u8 **pcm_str, double *vol, u8 *pkts_prefill_percent) | ||
342 | 345 | { | { |
343 | 346 | int i; | int i; |
344 | 347 | int url_idx; | int url_idx; |
... | ... | static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h, | |
362 | 365 | unsigned long vol_ul; | unsigned long vol_ul; |
363 | 366 | ||
364 | 367 | if ((i + 1) == argc) | if ((i + 1) == argc) |
365 | FATAL("-v:initial volume option is missing\n"); | ||
368 | FATAL("-v:initial volume is missing\n"); | ||
366 | 369 | ||
367 | 370 | vol_ul = strtoul(args[i + 1], 0, 10); | vol_ul = strtoul(args[i + 1], 0, 10); |
368 | 371 | if (vol_ul < 0 || 100 < vol_ul) | if (vol_ul < 0 || 100 < vol_ul) |
... | ... | static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h, | |
395 | 398 | *w = (u16)w_ul; | *w = (u16)w_ul; |
396 | 399 | POUT("-w:using initial window width %lu pixels\n", w_ul); | POUT("-w:using initial window width %lu pixels\n", w_ul); |
397 | 400 | i += 2; | i += 2; |
401 | } else if (strcmp("-b", args[i]) == 0) { | ||
402 | if ((i + 1) == argc) | ||
403 | FATAL("-b:percent value for prefill of buffer of packet queues is missing\n"); | ||
404 | |||
405 | *pkts_prefill_percent = (u8)strtoul(args[i + 1], 0, 10); | ||
406 | POUT("-v:using a %u prefilled buffer of packet queues\n", *pkts_prefill_percent); | ||
407 | i += 2; | ||
398 | 408 | } else if (strcmp("-help", args[i]) == 0) { | } else if (strcmp("-help", args[i]) == 0) { |
399 | 409 | usage(); | usage(); |
400 | 410 | exit(0); | exit(0); |
... | ... | static void opts_parse(int argc, u8 **args, u8 **url, u16 *w, u16 *h, | |
411 | 421 | /*NSPC*/ | /*NSPC*/ |
412 | 422 | #define WIDTH_NOT_DEFINED 0 | #define WIDTH_NOT_DEFINED 0 |
413 | 423 | #define HEIGHT_NOT_DEFINED 0 | #define HEIGHT_NOT_DEFINED 0 |
414 | static void init_once(u8 *url, u16 win_width, u16 win_height, u8 *pcm_str) | ||
424 | static void init_once(u8 *url, u16 win_width, u16 win_height, u8 *pcm_str, | ||
425 | u8 pkts_prefill_percent, avcodec_params_t **audio_codec_params, | ||
426 | avcodec_params_t **video_codec_params) | ||
415 | 427 | { | { |
428 | avutil_rational_t *audio_st_tb; | ||
429 | avutil_rational_t *video_st_tb; | ||
430 | |||
416 | 431 | evt_init_once(); | evt_init_once(); |
417 | 432 | sigs_init_once(); | sigs_init_once(); |
418 | 433 | npv_vk_init_once(); /* generic plumbing */ | npv_vk_init_once(); /* generic plumbing */ |
... | ... | static void init_once(u8 *url, u16 win_width, u16 win_height, u8 *pcm_str) | |
420 | 435 | audio_init_once(pcm_str); /* before audio_st_idx_p is actually used */ | audio_init_once(pcm_str); /* before audio_st_idx_p is actually used */ |
421 | 436 | video_init_once(); /* before video_st_idx_p is actually used */ | video_init_once(); /* before video_st_idx_p is actually used */ |
422 | 437 | clk_init_once(); | clk_init_once(); |
423 | input_init_once(); | ||
438 | pipeline_init_once(pkts_prefill_percent); | ||
424 | 439 | ||
425 | 440 | fmt_init_once(url); | fmt_init_once(url); |
426 | 441 | /* we need something to start with */ | /* we need something to start with */ |
427 | fmt_probe_best_sts(&video_st_idx_p, &audio_st_idx_p); | ||
442 | fmt_probe_best_sts( | ||
443 | &video_st_p.idx, &video_st_p.id, &video_st_tb, | ||
444 | &video_st_p.start_time, video_codec_params, | ||
445 | &audio_st_p.idx, &audio_st_p.id, &audio_st_tb, | ||
446 | &audio_st_p.start_time, audio_codec_params); | ||
447 | memcpy(&audio_st_p.tb, audio_st_tb, sizeof(*audio_st_tb)); | ||
448 | memcpy(&video_st_p.tb, video_st_tb, sizeof(*video_st_tb)); | ||
428 | 449 | if (win_width == WIDTH_NOT_DEFINED) | if (win_width == WIDTH_NOT_DEFINED) |
429 | win_width = fmt_ctx_p->sts[video_st_idx_p]->codecpar->width; | ||
450 | win_width = (*video_codec_params)->width; | ||
430 | 451 | if (win_height == HEIGHT_NOT_DEFINED) | if (win_height == HEIGHT_NOT_DEFINED) |
431 | win_height = fmt_ctx_p->sts[video_st_idx_p]->codecpar->height; | ||
452 | win_height = (*video_codec_params)->height; | ||
432 | 453 | npv_xcb_init_once(win_width, win_height); | npv_xcb_init_once(win_width, win_height); |
433 | 454 | npv_vk_surf_init_once(npv_xcb_p.c, npv_xcb_p.win_id); | npv_vk_surf_init_once(npv_xcb_p.c, npv_xcb_p.win_id); |
434 | 455 | } | } |
... | ... | static void init_once(u8 *url, u16 win_width, u16 win_height, u8 *pcm_str) | |
436 | 457 | #undef HEIGHT_NOT_DEFINED | #undef HEIGHT_NOT_DEFINED |
437 | 458 | /*NSPC*/ | /*NSPC*/ |
438 | 459 | #define PRINT_INFO true | #define PRINT_INFO true |
439 | static void prepare(double initial_vol) | ||
460 | static void prepare(double initial_vol, avcodec_params_t *audio_codec_params, | ||
461 | avcodec_params_t *video_codec_params) | ||
440 | 462 | { | { |
441 | 463 | enum avutil_audio_fr_fmt_t dst_fmt; | enum avutil_audio_fr_fmt_t dst_fmt; |
442 | 464 | int dst_rate; | int dst_rate; |
443 | 465 | int dst_chans_n; | int dst_chans_n; |
444 | 466 | uint64_t dst_chans_layout; | uint64_t dst_chans_layout; |
445 | 467 | ||
446 | audio_dec_ctx_cfg(fmt_ctx_p->sts[audio_st_idx_p]->codecpar); | ||
447 | video_dec_ctx_cfg(fmt_ctx_p->sts[video_st_idx_p]->codecpar); | ||
468 | audio_dec_ctx_cfg(audio_codec_params); | ||
469 | video_dec_ctx_cfg(video_codec_params); | ||
448 | 470 | /* | /* |
449 | 471 | * do our best to match the pcm cfg to audio ff dec output, BUT we don't | * do our best to match the pcm cfg to audio ff dec output, BUT we don't |
450 | 472 | * expect to match it "exactly": see right below why | * expect to match it "exactly": see right below why |
... | ... | static void prepare(double initial_vol) | |
462 | 484 | PRINT_INFO); | PRINT_INFO); |
463 | 485 | audio_pcm_silence_bufs_cfg(PRINT_INFO); | audio_pcm_silence_bufs_cfg(PRINT_INFO); |
464 | 486 | ||
465 | input_bootstrap_audio_video(); | ||
466 | |||
467 | 487 | evt_add_all_fds(); | evt_add_all_fds(); |
468 | 488 | } | } |
469 | 489 | #undef PRINT_INFO | #undef PRINT_INFO |
... | ... | static void cmd_quit(void) | |
477 | 497 | EXIT("quit command received\n"); | EXIT("quit command received\n"); |
478 | 498 | } | } |
479 | 499 | /*NSPC*/ | /*NSPC*/ |
500 | static void seek_lock(void) | ||
501 | { | ||
502 | /* fmt */ | ||
503 | fmt_ctx_lock(); | ||
504 | /* video */ | ||
505 | pkt_q_lock(video_pkt_q_p); | ||
506 | video_dec_frs_lock(); | ||
507 | video_dec_ctx_lock(); | ||
508 | /* audio */ | ||
509 | pkt_q_lock(audio_pkt_q_p); | ||
510 | audio_dec_sets_lock(); | ||
511 | audio_dec_ctx_lock(); | ||
512 | } | ||
513 | /*NSPC*/ | ||
514 | static void seek_unlock(void) | ||
515 | { | ||
516 | /* audio */ | ||
517 | audio_dec_ctx_unlock(); | ||
518 | audio_dec_sets_unlock(); | ||
519 | pkt_q_unlock(audio_pkt_q_p); | ||
520 | /* video */ | ||
521 | video_dec_ctx_unlock(); | ||
522 | video_dec_frs_unlock(); | ||
523 | pkt_q_unlock(video_pkt_q_p); | ||
524 | /* fmt */ | ||
525 | fmt_ctx_unlock(); | ||
526 | } | ||
527 | /*NSPC*/ | ||
480 | 528 | #define TS_FROM_CLK_OK 0 | #define TS_FROM_CLK_OK 0 |
481 | 529 | static void seek_x(s64 delta) | static void seek_x(s64 delta) |
482 | 530 | { | { |
483 | int ri; | ||
484 | u8 r8; | ||
485 | avformat_st_t *audio_st; | ||
486 | avformat_st_t *video_st; | ||
531 | int a; | ||
532 | u8 r; | ||
487 | 533 | s64 new_audio_ts; | s64 new_audio_ts; |
488 | 534 | s64 new_video_ts; | s64 new_video_ts; |
489 | 535 | s64 audio_now; | s64 audio_now; |
... | ... | static void seek_x(s64 delta) | |
493 | 539 | WARNING("seek:audio is draining, seeking disable\n"); | WARNING("seek:audio is draining, seeking disable\n"); |
494 | 540 | return; | return; |
495 | 541 | } | } |
496 | if ((fmt_ctx_p->ctx_flags & AVFMTCTX_UNSEEKABLE) != 0) { | ||
497 | WARNING("seek:format is flagged unseekable\n"); | ||
498 | return; | ||
499 | } | ||
500 | 542 | ||
501 | 543 | thdsws_wait_for_idle(video_scaler_p.ctx); | thdsws_wait_for_idle(video_scaler_p.ctx); |
502 | 544 | ||
503 | r8 = clk_get_audio_st_ts(&audio_now); | ||
504 | if (r8 != TS_FROM_CLK_OK) { | ||
545 | seek_lock(); | ||
546 | |||
547 | r = clk_get_audio_st_ts(&audio_now); | ||
548 | if (r != TS_FROM_CLK_OK) { | ||
505 | 549 | WARNING("seek:audio:clock timestamp unavailable, ignoring command\n"); | WARNING("seek:audio:clock timestamp unavailable, ignoring command\n"); |
550 | seek_unlock(); | ||
506 | 551 | return; | return; |
507 | 552 | } | } |
508 | r8 = clk_get_video_st_ts(&video_now); | ||
509 | if (r8 != TS_FROM_CLK_OK) { | ||
553 | r = clk_get_video_st_ts(&video_now); | ||
554 | if (r != TS_FROM_CLK_OK) { | ||
510 | 555 | WARNING("seek:video:clock timestamp unavailable, ignoring command\n"); | WARNING("seek:video:clock timestamp unavailable, ignoring command\n"); |
556 | seek_unlock(); | ||
511 | 557 | return; | return; |
512 | 558 | } | } |
559 | /* | ||
560 | * XXX: a set of sts can share the same id for seeking. if they share | ||
561 | * the same id then the tbs should be the same. | ||
562 | */ | ||
513 | 563 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
514 | audio_st = fmt_ctx_p->sts[audio_st_idx_p]; | ||
515 | video_st = fmt_ctx_p->sts[video_st_idx_p]; | ||
516 | |||
517 | new_audio_ts = audio_now + delta | ||
518 | * audio_st->time_base.den / audio_st->time_base.num; | ||
564 | new_audio_ts = audio_now + delta * audio_st_p.tb.den | ||
565 | / audio_st_p.tb.num; | ||
519 | 566 | /* rewind capping if possible */ | /* rewind capping if possible */ |
520 | if (audio_st->start_time != AV_NOPTS_VALUE) | ||
521 | if (new_audio_ts < audio_st->start_time) | ||
522 | new_audio_ts = audio_st->start_time; | ||
567 | if (audio_st_p.start_time != AV_NOPTS_VALUE) | ||
568 | if (new_audio_ts < audio_st_p.start_time) | ||
569 | new_audio_ts = audio_st_p.start_time; | ||
523 | 570 | POUT("trying to seek to %"PRId64" audio stream time base units\n", new_audio_ts); | POUT("trying to seek to %"PRId64" audio stream time base units\n", new_audio_ts); |
524 | |||
525 | ri = avformat_seek_pkt(fmt_ctx_p, audio_st->id, new_audio_ts, 0); | ||
526 | if (ri < 0) { | ||
571 | a = avformat_seek_pkt(fmt_ctx_p, audio_st_p.id, new_audio_ts, 0); | ||
572 | if (a < 0) { | ||
527 | 573 | WARNING("unable to seek to %"PRId64" audio stream time base units\n", new_audio_ts); | WARNING("unable to seek to %"PRId64" audio stream time base units\n", new_audio_ts); |
528 | 574 | goto try_restore_audio; | goto try_restore_audio; |
529 | 575 | } | } |
530 | 576 | POUT("audio seek to %"PRId64" audio stream time base units\n", new_audio_ts); | POUT("audio seek to %"PRId64" audio stream time base units\n", new_audio_ts); |
531 | 577 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
532 | new_video_ts = video_now + delta | ||
533 | * video_st->time_base.den / video_st->time_base.num; | ||
578 | new_video_ts = video_now + delta * video_st_p.tb.den | ||
579 | / video_st_p.tb.num; | ||
534 | 580 | /* rewind capping if possible */ | /* rewind capping if possible */ |
535 | if (video_st->start_time != AV_NOPTS_VALUE) | ||
536 | if (new_video_ts < video_st->start_time) | ||
537 | new_video_ts = video_st->start_time; | ||
538 | ri = avformat_seek_pkt(fmt_ctx_p, video_st->id, new_video_ts, 0); | ||
539 | if (ri < 0) { | ||
581 | if (video_st_p.start_time != AV_NOPTS_VALUE) | ||
582 | if (new_video_ts < video_st_p.start_time) | ||
583 | new_video_ts = video_st_p.start_time; | ||
584 | POUT("trying to seek to %"PRId64" video stream time base units\n", new_video_ts); | ||
585 | a = avformat_seek_pkt(fmt_ctx_p, video_st_p.id, new_video_ts, 0); | ||
586 | if (a < 0) { | ||
540 | 587 | WARNING("unable to seek to %"PRId64" video stream time base units but audio was seeked)\n", new_video_ts); | WARNING("unable to seek to %"PRId64" video stream time base units but audio was seeked)\n", new_video_ts); |
541 | 588 | goto try_restore_video; | goto try_restore_video; |
542 | 589 | } | } |
... | ... | flush: | |
547 | 594 | audio_filt_flush(); | audio_filt_flush(); |
548 | 595 | fmt_flush(); | fmt_flush(); |
549 | 596 | clk_invalidate(); | clk_invalidate(); |
550 | input_bootstrap_audio_video(); | ||
597 | //input_bootstrap_audio_video(); | ||
598 | seek_unlock(); | ||
551 | 599 | return; | return; |
552 | 600 | ||
553 | 601 | try_restore_video: | try_restore_video: |
554 | ri = avformat_seek_pkt(fmt_ctx_p, video_st->id, video_now, 0); | ||
555 | if (ri < 0) /* we don't send an application error */ | ||
602 | a = avformat_seek_pkt(fmt_ctx_p, video_st_p.id, video_now, 0); | ||
603 | if (a < 0) /* we don't send an application error */ | ||
556 | 604 | EXIT("unable to restore video to %"PRId64" video stream time base units\n", video_now); | EXIT("unable to restore video to %"PRId64" video stream time base units\n", video_now); |
557 | 605 | try_restore_audio: | try_restore_audio: |
558 | ri = avformat_seek_pkt(fmt_ctx_p, audio_st->id, audio_now, 0); | ||
559 | if (ri < 0) /* we don't send an application error */ | ||
606 | a = avformat_seek_pkt(fmt_ctx_p, audio_st_p.id, audio_now, 0); | ||
607 | if (a < 0) /* we don't send an application error */ | ||
560 | 608 | EXIT("unable to restore audio to %"PRId64" audio stream time base units\n", audio_now); | EXIT("unable to restore audio to %"PRId64" audio stream time base units\n", audio_now); |
561 | 609 | goto flush; | goto flush; |
562 | 610 | } | } |
... | ... | static void cmd_pause(void) | |
592 | 640 | return; | return; |
593 | 641 | } | } |
594 | 642 | if (paused_p) { | if (paused_p) { |
643 | int r; | ||
644 | |||
595 | 645 | POUT("COMMAND:unpause\n"); | POUT("COMMAND:unpause\n"); |
596 | 646 | paused_p = false; | paused_p = false; |
647 | fmt_ctx_lock(); | ||
648 | avformat_read_play(fmt_ctx_p); | ||
649 | fmt_ctx_unlock(); | ||
597 | 650 | } else { | } else { |
651 | int r; | ||
652 | |||
598 | 653 | POUT("COMMAND:pause\n"); | POUT("COMMAND:pause\n"); |
599 | 654 | paused_p = true; | paused_p = true; |
655 | fmt_ctx_lock(); | ||
656 | avformat_read_pause(fmt_ctx_p); | ||
657 | fmt_ctx_unlock(); | ||
600 | 658 | } | } |
601 | 659 | } | } |
602 | 660 | ||
... | ... | static void cmd_mute(void) | |
614 | 672 | { | { |
615 | 673 | audio_filt_cmd_mute(); | audio_filt_cmd_mute(); |
616 | 674 | } | } |
675 | /*NSPC*/ | ||
676 | static void prefill_wait(void) { loop | ||
677 | { | ||
678 | struct timespec wanted; | ||
679 | struct timespec rem; | ||
680 | s64 prefill_audio; | ||
681 | s64 prefill_video; | ||
682 | |||
683 | pipeline_limits_lock(); | ||
684 | prefill_audio = pipeline_limits_p.pkts.prefill.audio_bytes_rem; | ||
685 | prefill_video = pipeline_limits_p.pkts.prefill.video_bytes_rem; | ||
686 | pipeline_limits_unlock(); | ||
687 | if (prefill_audio <= 0 && prefill_video <= 0) | ||
688 | break; | ||
689 | memset(&wanted, 0, sizeof(wanted)); | ||
690 | memset(&rem, 0, sizeof(rem)); | ||
691 | wanted.tv_nsec = 100000000; /* 100 ms */ | ||
692 | loop { | ||
693 | int r; | ||
694 | |||
695 | /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */ | ||
696 | r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem); | ||
697 | if (r == 0) | ||
698 | break; | ||
699 | if (r != EINTR) | ||
700 | FATAL("prefill wait timer failed:%d\n", r); | ||
701 | /* r == EINTR */ | ||
702 | memcpy(&wanted, &rem, sizeof(wanted)); | ||
703 | memset(&rem, 0, sizeof(rem)); | ||
704 | } | ||
705 | }} | ||
706 | /*NSPC*/ | ||
707 | static void predecoding_wait(void) | ||
708 | { | ||
709 | struct timespec wanted; | ||
710 | struct timespec rem; | ||
711 | |||
712 | memset(&wanted, 0, sizeof(wanted)); | ||
713 | memset(&rem, 0, sizeof(rem)); | ||
714 | /* we target ~double audio buf, namely (0.25s * 2 ~ 500ms) */ | ||
715 | wanted.tv_nsec = 500000000; | ||
716 | loop { | ||
717 | int r; | ||
718 | |||
719 | /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */ | ||
720 | r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem); | ||
721 | if (r == 0) | ||
722 | break; | ||
723 | if (r != EINTR) | ||
724 | FATAL("predecoding wait timer failed:%d\n", r); | ||
725 | /* r == EINTR */ | ||
726 | memcpy(&wanted, &rem, sizeof(wanted)); | ||
727 | memset(&rem, 0, sizeof(rem)); | ||
728 | } | ||
729 | } | ||
617 | 730 | #define WIDTH_NOT_DEFINED 0 | #define WIDTH_NOT_DEFINED 0 |
618 | 731 | #define HEIGHT_NOT_DEFINED 0 | #define HEIGHT_NOT_DEFINED 0 |
619 | 732 | int main(int argc, u8 **args) | int main(int argc, u8 **args) |
... | ... | int main(int argc, u8 **args) | |
623 | 736 | u8 *pcm_str; | u8 *pcm_str; |
624 | 737 | u8 *url; | u8 *url; |
625 | 738 | double initial_vol; | double initial_vol; |
739 | u8 pkts_prefill_percent; | ||
740 | avcodec_params_t *audio_codec_params; | ||
741 | avcodec_params_t *video_codec_params; | ||
626 | 742 | ||
627 | 743 | /* "turn on utf8" processing in used libs if any *AND* locale system */ | /* "turn on utf8" processing in used libs if any *AND* locale system */ |
628 | 744 | setlocale(LC_ALL, ""); | setlocale(LC_ALL, ""); |
... | ... | int main(int argc, u8 **args) | |
635 | 751 | pcm_str = "default"; | pcm_str = "default"; |
636 | 752 | url = 0; | url = 0; |
637 | 753 | initial_vol = 1.; | initial_vol = 1.; |
638 | opts_parse(argc, args, &url, &win_width, &win_height, &pcm_str, &initial_vol); | ||
639 | |||
640 | init_once(url, win_width, win_height, pcm_str); | ||
641 | prepare(initial_vol); | ||
754 | pkts_prefill_percent = 0; | ||
755 | opts_parse(argc, args, &url, &win_width, &win_height, &pcm_str, | ||
756 | &initial_vol, &pkts_prefill_percent); | ||
757 | init_once(url, win_width, win_height, pcm_str, pkts_prefill_percent, | ||
758 | &audio_codec_params, &video_codec_params); | ||
759 | prepare(initial_vol, audio_codec_params, video_codec_params); | ||
642 | 760 | ||
643 | 761 | /* switch the ffmpeg log to stdout for metadata/etc dump */ | /* switch the ffmpeg log to stdout for metadata/etc dump */ |
644 | 762 | avutil_log_set_callback(ff_log_stdout); | avutil_log_set_callback(ff_log_stdout); |
645 | 763 | avformat_dump_fmt(fmt_ctx_p, 0, url, 0); | avformat_dump_fmt(fmt_ctx_p, 0, url, 0); |
646 | 764 | avutil_log_set_callback(avutil_log_default_callback); | avutil_log_set_callback(avutil_log_default_callback); |
647 | 765 | ||
766 | pipeline_read_thd_start(); | ||
767 | POUT("prefilling audio and video buffers..."); | ||
768 | prefill_wait(); | ||
769 | POUT("done\n"); | ||
770 | pipeline_audio_thd_start(); | ||
771 | pipeline_video_thd_start(); | ||
772 | POUT("predecoding audio and video..."); | ||
773 | predecoding_wait(); | ||
774 | POUT("done\n"); | ||
648 | 775 | video_timer_start(); | video_timer_start(); |
649 | input_timer_start(); | ||
650 | 776 | ||
651 | 777 | loop evts_loop(); | loop evts_loop(); |
652 | 778 | /* unreachable */ | /* unreachable */ |
File npv/main.c changed (mode: 100644) (index 3e50878..033910c) | |||
32 | 32 | #include "npv/global.h" | #include "npv/global.h" |
33 | 33 | #include "npv/public.h" | #include "npv/public.h" |
34 | 34 | #include "npv/fmt/public.h" | #include "npv/fmt/public.h" |
35 | #include "npv/input/public.h" | ||
35 | #include "npv/pipeline/public.h" | ||
36 | 36 | #include "npv/audio/filt/public.h" | #include "npv/audio/filt/public.h" |
37 | 37 | #include "npv/audio/public.h" | #include "npv/audio/public.h" |
38 | 38 | #include "npv/video/public.h" | #include "npv/video/public.h" |
64 | 64 | #include "npv/xcb/main.c" | #include "npv/xcb/main.c" |
65 | 65 | #include "npv/vk/main.c" | #include "npv/vk/main.c" |
66 | 66 | #include "npv/clk/main.c" | #include "npv/clk/main.c" |
67 | #include "npv/input/main.c" | ||
67 | #include "npv/pipeline/main.c" | ||
68 | 68 | #include "npv/thdsws/main.c" | #include "npv/thdsws/main.c" |
69 | 69 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
70 | 70 | #endif | #endif |
File npv/namespace/ffmpeg.h changed (mode: 100644) (index 01f31e5..6c2e055) | |||
35 | 35 | #define avformat_duration_from_st AVFMT_DURATION_FROM_STREAM | #define avformat_duration_from_st AVFMT_DURATION_FROM_STREAM |
36 | 36 | #define avformat_find_best_st av_find_best_stream | #define avformat_find_best_st av_find_best_stream |
37 | 37 | #define avformat_find_st_info avformat_find_stream_info | #define avformat_find_st_info avformat_find_stream_info |
38 | #define avformat_read_pause av_read_pause | ||
39 | #define avformat_read_play av_read_play | ||
38 | 40 | #define avformat_seek_pkt av_seek_frame | #define avformat_seek_pkt av_seek_frame |
39 | 41 | #define avformat_st_t AVStream | #define avformat_st_t AVStream |
40 | 42 | #define avutil_cpus_n av_cpu_count | #define avutil_cpus_n av_cpu_count |
100 | 102 | #undef avformat_duration_from_st | #undef avformat_duration_from_st |
101 | 103 | #undef avformat_find_best_st | #undef avformat_find_best_st |
102 | 104 | #undef avformat_find_st_info | #undef avformat_find_st_info |
105 | #undef avformat_read_pause | ||
106 | #undef avformat_read_play | ||
103 | 107 | #undef avformat_seek_pkt | #undef avformat_seek_pkt |
104 | 108 | #undef avformat_st_t | #undef avformat_st_t |
105 | 109 | #undef avutil_cpus_n | #undef avutil_cpus_n |
File npv/pipeline/local/code.frag.c added (mode: 100644) (index 0000000..7c58498) | |||
1 | static void wait(long ns) | ||
2 | { | ||
3 | struct timespec wanted; | ||
4 | struct timespec rem; | ||
5 | |||
6 | memset(&wanted, 0, sizeof(wanted)); | ||
7 | memset(&rem, 0, sizeof(rem)); | ||
8 | wanted.tv_nsec = ns; | ||
9 | loop { | ||
10 | int r; | ||
11 | |||
12 | /* linux bug: cannot specify CLOCK_MONOTONIC_RAW */ | ||
13 | r = clock_nanosleep(CLOCK_MONOTONIC, 0, &wanted, &rem); | ||
14 | if (r == 0) | ||
15 | break; | ||
16 | if (r != EINTR) | ||
17 | FATALP("data wait timer failed:%d\n", r); | ||
18 | /* r == EINTR */ | ||
19 | memcpy(&wanted, &rem, sizeof(wanted)); | ||
20 | memset(&rem, 0, sizeof(rem)); | ||
21 | } | ||
22 | } | ||
23 | #define EOF_FMT 2 | ||
24 | static void read(void) { loop /* infinite loop */ | ||
25 | { | ||
26 | u8 r; | ||
27 | |||
28 | /* | ||
29 | * we do finer-grained locking in there, since we do not want to lock | ||
30 | * the pkt qs during slow access | ||
31 | */ | ||
32 | r = fmt_pkts_read_and_q(); | ||
33 | if (r == EOF_FMT) { | ||
34 | pkt_q_lock(audio_pkt_q_p); | ||
35 | if (!pkt_q_has_eof(audio_pkt_q_p)) | ||
36 | pkt_q_enq(audio_pkt_q_p, eof_pkt_l); | ||
37 | pkt_q_unlock(audio_pkt_q_p); | ||
38 | |||
39 | pkt_q_lock(video_pkt_q_p); | ||
40 | if (!pkt_q_has_eof(video_pkt_q_p)) | ||
41 | pkt_q_enq(video_pkt_q_p, eof_pkt_l); | ||
42 | pkt_q_unlock(video_pkt_q_p); | ||
43 | } | ||
44 | /*--------------------------------------------------------------------*/ | ||
45 | /* should be enough to avoid non kept-alive network connexion */ | ||
46 | wait(100000000); /* 100ms */ | ||
47 | }} | ||
48 | #undef EOF_FMT | ||
49 | static void *read_thd_entry(void *arg) | ||
50 | { | ||
51 | int r; | ||
52 | sigset_t sset; | ||
53 | |||
54 | r = sigfillset(&sset); | ||
55 | if (r == -1) | ||
56 | FATALP("read thread:unable to get a full signal mask\n"); | ||
57 | r = pthread_sigmask(SIG_SETMASK, &sset, 0); | ||
58 | if (r != 0) | ||
59 | FATALP("read thread:unable to \"block\" \"all\" signals\n"); | ||
60 | read(); | ||
61 | /* unreachable */ | ||
62 | } | ||
63 | /* | ||
64 | * our alsa audio buf is (rate/4)~0.25s (interactivity like vol processing). | ||
65 | * then we would like to have ~2 times this buf (~double buf) of decoded audio | ||
66 | * frs. namely we try to have ~(2 *0.25s) of decoded audio frames. we presume | ||
67 | * the audio dec is fine grained enough in order to base our calculation on the | ||
68 | * pts-es of 2 sets of audio frs. | ||
69 | */ | ||
70 | /*NPSC*/ | ||
71 | static bool have_enough_predecoded_audio_frs(void) | ||
72 | { | ||
73 | int64_t pts_min; | ||
74 | int64_t pts_max; | ||
75 | int64_t pts_delta; | ||
76 | int64_t pts_delta_limit; | ||
77 | |||
78 | audio_dec_sets_lock(); | ||
79 | if (audio_dec_sets_p.eof_receive) { | ||
80 | audio_dec_sets_unlock(); | ||
81 | return true; | ||
82 | } else if (audio_dec_sets_p.n == 0) { | ||
83 | audio_dec_sets_unlock(); | ||
84 | return false; | ||
85 | } | ||
86 | /* from here we have at least 1 sets of audio frs */ | ||
87 | /* we presume the audio sets are in pts order */ | ||
88 | pts_min = audio_dec_sets_p.a[0]->pts; | ||
89 | pts_max = audio_dec_sets_p.a[audio_dec_sets_p.n - 1]->pts; | ||
90 | audio_dec_sets_unlock(); | ||
91 | |||
92 | pts_delta = pts_max - pts_min; | ||
93 | /* 2 * 0.25s = 500 ms */ | ||
94 | pts_delta_limit = 500 * audio_st_p.tb.den / audio_st_p.tb.num / 1000; | ||
95 | if (pts_delta >= pts_delta_limit) | ||
96 | return true; | ||
97 | return false; | ||
98 | } | ||
99 | static void audio(void) { loop /* infinite loop */ | ||
100 | { | ||
101 | if (have_enough_predecoded_audio_frs()) { | ||
102 | wait(250000000); /* (rate/4)~0.25s */ | ||
103 | continue; | ||
104 | } | ||
105 | /* can be long, finer-grained locking is done in there */ | ||
106 | audio_pkts_send(); | ||
107 | audio_dec_sets_receive_avail(); | ||
108 | |||
109 | }} | ||
110 | static void *audio_thd_entry(void *arg) | ||
111 | { | ||
112 | int r; | ||
113 | sigset_t sset; | ||
114 | |||
115 | r = sigfillset(&sset); | ||
116 | if (r == -1) | ||
117 | FATALP("send thread:unable to get a full signal mask\n"); | ||
118 | r = pthread_sigmask(SIG_SETMASK, &sset, 0); | ||
119 | if (r != 0) | ||
120 | FATALP("send thread:unable to \"block\" \"all\" signals\n"); | ||
121 | audio(); | ||
122 | /* unreachable */ | ||
123 | } | ||
124 | /* | ||
125 | * same heuristics than for audio. there is some sort of sync due to video fr | ||
126 | * dropping based on "audio now" in the main thd | ||
127 | */ | ||
128 | /*NSPC*/ | ||
129 | static bool have_enough_predecoded_video_frs(void) | ||
130 | { | ||
131 | int64_t pts_min; | ||
132 | int64_t pts_max; | ||
133 | int64_t pts_delta; | ||
134 | int64_t pts_delta_limit; | ||
135 | |||
136 | video_dec_frs_lock(); | ||
137 | if (video_dec_frs_p.eof_receive) { | ||
138 | video_dec_frs_unlock(); | ||
139 | return true; | ||
140 | } else if (video_dec_frs_p.n == 0) { | ||
141 | video_dec_frs_unlock(); | ||
142 | return false; | ||
143 | } | ||
144 | /* from here we have at least 1 video fr */ | ||
145 | /* we presume the video frs are in pts order */ | ||
146 | pts_min = video_dec_frs_p.a[0]->pts; | ||
147 | pts_max = video_dec_frs_p.a[video_dec_frs_p.n - 1]->pts; | ||
148 | video_dec_frs_unlock(); | ||
149 | |||
150 | pts_delta = pts_max - pts_min; | ||
151 | /* 2 * 0.25s = 500 ms */ | ||
152 | pts_delta_limit = 500 * video_st_p.tb.den / video_st_p.tb.num / 1000; | ||
153 | if (pts_delta >= pts_delta_limit) | ||
154 | return true; | ||
155 | return false; | ||
156 | } | ||
157 | static void video(void) { loop /* infinite loop */ | ||
158 | { | ||
159 | if (have_enough_predecoded_video_frs()) { | ||
160 | wait(250000000); /* (audio rate/4)~0.25s */ | ||
161 | continue; | ||
162 | } | ||
163 | /* can be long, finer-grained locking is done in there */ | ||
164 | video_pkts_send(); | ||
165 | video_dec_frs_receive_avail(); | ||
166 | }} | ||
167 | static void *video_thd_entry(void *arg) | ||
168 | { | ||
169 | int r; | ||
170 | sigset_t sset; | ||
171 | |||
172 | r = sigfillset(&sset); | ||
173 | if (r == -1) | ||
174 | FATALP("send thread:unable to get a full signal mask\n"); | ||
175 | r = pthread_sigmask(SIG_SETMASK, &sset, 0); | ||
176 | if (r != 0) | ||
177 | FATALP("send thread:unable to \"block\" \"all\" signals\n"); | ||
178 | video(); | ||
179 | /* unreachable */ | ||
180 | } |
File npv/pipeline/local/state.frag.c renamed from npv/input/local/state.frag.c (similarity 100%) |
File npv/pipeline/main.c added (mode: 100644) (index 0000000..5679b81) | |||
1 | #ifndef NPV_PIPELINE_MAIN_C | ||
2 | #define NPV_PIPELINE_MAIN_C | ||
3 | /* | ||
4 | * code protected with a GNU affero GPLv3 license | ||
5 | * copyright (C) 2020 Sylvain BERTRAND | ||
6 | */ | ||
7 | #include <stdbool.h> | ||
8 | #include <string.h> | ||
9 | #include <time.h> | ||
10 | #include <signal.h> | ||
11 | #include <stdint.h> | ||
12 | #include <pthread.h> | ||
13 | #include "npv/c_fixing.h" | ||
14 | #include "npv/global.h" | ||
15 | #include "npv/pkt_q/public.h" | ||
16 | #include "npv/pipeline/public.h" | ||
17 | #include "npv/fmt/public.h" | ||
18 | #include "npv/audio/public.h" | ||
19 | #include "npv/video/public.h" | ||
20 | /*----------------------------------------------------------------------------*/ | ||
21 | #include "npv/namespace/ffmpeg.h" | ||
22 | #include "npv/pipeline/namespace/public.h" | ||
23 | #include "npv/pipeline/namespace/main.c" | ||
24 | /*----------------------------------------------------------------------------*/ | ||
25 | #define FATALP(fmt, ...) FATAL("pipeline:" fmt, ##__VA_ARGS__) | ||
26 | #define WARNINGP(fmt, ...) WARNING("pipeline:" fmt, ##__VA_ARGS__) | ||
27 | #define POUTP(fmt, ...) POUT("pipeline:" fmt, ##__VA_ARGS__) | ||
28 | /*----------------------------------------------------------------------------*/ | ||
29 | #include "npv/pipeline/local/state.frag.c" | ||
30 | /*----------------------------------------------------------------------------*/ | ||
31 | #include "npv/pipeline/local/code.frag.c" | ||
32 | #include "npv/pipeline/public/code.frag.c" | ||
33 | /*----------------------------------------------------------------------------*/ | ||
34 | #undef FATALP | ||
35 | #undef WARNINGP | ||
36 | #undef POUTP | ||
37 | /*---------------------------------------------------------------------------*/ | ||
38 | #define CLEANUP | ||
39 | #include "npv/namespace/ffmpeg.h" | ||
40 | #include "npv/pipeline/namespace/public.h" | ||
41 | #include "npv/pipeline/namespace/main.c" | ||
42 | #undef CLEANUP | ||
43 | #endif |
File npv/pipeline/namespace/main.c added (mode: 100644) (index 0000000..60ff3b0) | |||
1 | #ifndef CLEANUP | ||
2 | /*----------------------------------------------------------------------------*/ | ||
3 | /* some struct fields */ | ||
4 | #define sz size | ||
5 | /*----------------------------------------------------------------------------*/ | ||
6 | #define audio pipeline_audio | ||
7 | #define audio_thd_entry pipeline_audio_thd_entry | ||
8 | #define eof_pkt_l pipeline_eof_pkt_l | ||
9 | #define read pipeline_read | ||
10 | #define read_thd_entry pipeline_read_thd_entry | ||
11 | #define timer_ack pipeline_timer_ack | ||
12 | #define timer_init_once pipeline_timer_init_once | ||
13 | #define video pipeline_video | ||
14 | #define video_thd_entry pipeline_video_thd_entry | ||
15 | #define wait pipeline_wait | ||
16 | /*============================================================================*/ | ||
17 | #else | ||
18 | #undef eof_pkt_l | ||
19 | #undef read | ||
20 | #undef read_thd_entry | ||
21 | #undef audio | ||
22 | #undef audio_thd_entry | ||
23 | #undef timer_ack | ||
24 | #undef timer_init_once | ||
25 | #undef video | ||
26 | #undef video_thd_entry | ||
27 | #undef wait | ||
28 | /*----------------------------------------------------------------------------*/ | ||
29 | #undef sz | ||
30 | /*----------------------------------------------------------------------------*/ | ||
31 | #endif | ||
32 |
File npv/pipeline/namespace/public.h added (mode: 100644) (index 0000000..5e6b268) | |||
1 | #ifndef CLEANUP | ||
2 | #define audio_thd_start pipeline_audio_thd_start | ||
3 | #define init_once pipeline_init_once | ||
4 | #define limits_lock pipeline_limits_lock | ||
5 | #define limits_p pipeline_limits_p | ||
6 | #define limits_t pipeline_limits_t | ||
7 | #define limits_unlock pipeline_limits_unlock | ||
8 | #define read_thd_start pipeline_read_thd_start | ||
9 | #define video_thd_start pipeline_video_thd_start | ||
10 | /*============================================================================*/ | ||
11 | #else | ||
12 | #undef audio_thd_start | ||
13 | #undef init_once | ||
14 | #undef limits_lock | ||
15 | #undef limits_p | ||
16 | #undef limits_t | ||
17 | #undef limits_unlock | ||
18 | #undef read_thd_start | ||
19 | #undef video_thd_start | ||
20 | #endif |
File npv/pipeline/public.h added (mode: 100644) (index 0000000..9938412) | |||
1 | #ifndef NPV_PIPELINE_PUBLIC_H | ||
2 | #define NPV_PIPELINE_PUBLIC_H | ||
3 | /* | ||
4 | * code protected with a GNU affero GPLv3 license | ||
5 | * copyright (C) 2020 Sylvain BERTRAND | ||
6 | */ | ||
7 | #include <pthread.h> | ||
8 | #include "npv/c_fixing.h" | ||
9 | /*----------------------------------------------------------------------------*/ | ||
10 | #include "npv/pipeline/namespace/public.h" | ||
11 | /*----------------------------------------------------------------------------*/ | ||
12 | struct limits_t { | ||
13 | pthread_mutex_t mutex; | ||
14 | struct { | ||
15 | u64 audio_bytes_n; | ||
16 | u64 video_bytes_n; | ||
17 | struct { | ||
18 | u64 audio_bytes_n; /* arbitrary, init-ed once */ | ||
19 | u64 video_bytes_n; /* arbitrary, init-ed once */ | ||
20 | } limit; | ||
21 | struct { | ||
22 | s64 audio_bytes_rem; | ||
23 | s64 video_bytes_rem; | ||
24 | } prefill; | ||
25 | } pkts; | ||
26 | }; | ||
27 | /*----------------------------------------------------------------------------*/ | ||
28 | #include "npv/pipeline/public/state.frag.h" | ||
29 | /*----------------------------------------------------------------------------*/ | ||
30 | static void init_once(u8 pkts_prefill_percent); | ||
31 | static void limits_lock(void); | ||
32 | static void limits_unlock(void); | ||
33 | static void read_thd_start(void); | ||
34 | static void audio_thd_start(void); | ||
35 | static void video_thd_start(void); | ||
36 | /*----------------------------------------------------------------------------*/ | ||
37 | #define CLEANUP | ||
38 | #include "npv/pipeline/namespace/public.h" | ||
39 | #undef CLEANUP | ||
40 | #endif |
File npv/pipeline/public/code.frag.c added (mode: 100644) (index 0000000..8857e49) | |||
1 | static void init_once(u8 pkts_prefill_percent) | ||
2 | { | ||
3 | int r; | ||
4 | |||
5 | eof_pkt_l = avcodec_pkt_ref_alloc(); | ||
6 | if (eof_pkt_l == 0) | ||
7 | FATALP("ffmpeg:unable to allocate a null/eof reference on a packet\n"); | ||
8 | eof_pkt_l->data = 0; | ||
9 | eof_pkt_l->sz = 0; | ||
10 | |||
11 | r = pthread_mutex_init(&limits_p.mutex, 0); | ||
12 | if (r != 0) | ||
13 | FATALP("unable to initialize the mutex to guard the accounting of limits\n"); | ||
14 | limits_p.pkts.audio_bytes_n = 0; | ||
15 | limits_p.pkts.video_bytes_n = 0; | ||
16 | /* hardcoded for now: arbitrary rough estimates */ | ||
17 | /* ~8s of 400kb audio */ | ||
18 | limits_p.pkts.limit.audio_bytes_n = 400000 / 8 * 8; | ||
19 | /* ~8s of 10Mb video */ | ||
20 | limits_p.pkts.limit.video_bytes_n = 10000000 / 8 * 8; | ||
21 | if (pkts_prefill_percent > 100) | ||
22 | FATALP("invalid prefill of %u%% for the buffer of packet queues\n", pkts_prefill_percent); | ||
23 | if (pkts_prefill_percent != 0) { | ||
24 | limits_p.pkts.prefill.audio_bytes_rem = | ||
25 | limits_p.pkts.limit.audio_bytes_n * pkts_prefill_percent | ||
26 | / 100; | ||
27 | POUTP("prefill size for the audio packet queue buffer is %u%%/%"PRId64" bytes\n", pkts_prefill_percent, limits_p.pkts.prefill.audio_bytes_rem); | ||
28 | limits_p.pkts.prefill.video_bytes_rem = | ||
29 | limits_p.pkts.limit.video_bytes_n * pkts_prefill_percent | ||
30 | / 100; | ||
31 | POUTP("prefill size for the video packet queue buffer is %u%%/%"PRId64" bytes\n", pkts_prefill_percent, limits_p.pkts.prefill.video_bytes_rem); | ||
32 | } else { | ||
33 | limits_p.pkts.prefill.audio_bytes_rem = 0; | ||
34 | limits_p.pkts.prefill.video_bytes_rem = 0; | ||
35 | POUTP("prefill for the packet queue buffers is disabled\n"); | ||
36 | } | ||
37 | } | ||
38 | static void limits_lock(void) | ||
39 | { | ||
40 | int r; | ||
41 | |||
42 | r = pthread_mutex_lock(&limits_p.mutex); | ||
43 | if (r != 0) | ||
44 | FATALP("unable to lock the limits\n"); | ||
45 | } | ||
46 | static void limits_unlock(void) | ||
47 | { | ||
48 | int r; | ||
49 | |||
50 | r = pthread_mutex_unlock(&limits_p.mutex); | ||
51 | if (r != 0) | ||
52 | FATALP("unable to unlock the limits\n"); | ||
53 | } | ||
54 | static void read_thd_start(void) | ||
55 | { | ||
56 | int r; | ||
57 | pthread_t id; | ||
58 | pthread_attr_t attr; | ||
59 | |||
60 | r = pthread_attr_init(&attr); | ||
61 | if (r != 0) | ||
62 | FATALP("unable to initialize read thread attribute\n"); | ||
63 | r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | ||
64 | if (r != 0) | ||
65 | FATALP("unable to set the read thread attribute to detach mode\n"); | ||
66 | r = pthread_create(&id, &attr, &read_thd_entry, 0); | ||
67 | if (r != 0) | ||
68 | FATALP("unable to create the read thread\n"); | ||
69 | POUTP("read thread %lu\n", (unsigned long)id); | ||
70 | pthread_attr_destroy(&attr); | ||
71 | } | ||
72 | static void audio_thd_start(void) | ||
73 | { | ||
74 | int r; | ||
75 | pthread_t id; | ||
76 | pthread_attr_t attr; | ||
77 | |||
78 | r = pthread_attr_init(&attr); | ||
79 | if (r != 0) | ||
80 | FATALP("unable to initialize audio thread attribute\n"); | ||
81 | r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | ||
82 | if (r != 0) | ||
83 | FATALP("unable to set the audio thread attribute to detach mode\n"); | ||
84 | r = pthread_create(&id, &attr, &audio_thd_entry, 0); | ||
85 | if (r != 0) | ||
86 | FATALP("unable to create the audio thread\n"); | ||
87 | POUTP("audio thread %lu\n", (unsigned long)id); | ||
88 | pthread_attr_destroy(&attr); | ||
89 | } | ||
90 | static void video_thd_start(void) | ||
91 | { | ||
92 | int r; | ||
93 | pthread_t id; | ||
94 | pthread_attr_t attr; | ||
95 | |||
96 | r = pthread_attr_init(&attr); | ||
97 | if (r != 0) | ||
98 | FATALP("unable to initialize video thread attribute\n"); | ||
99 | r = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); | ||
100 | if (r != 0) | ||
101 | FATALP("unable to set the video thread attribute to detach mode\n"); | ||
102 | r = pthread_create(&id, &attr, &video_thd_entry, 0); | ||
103 | if (r != 0) | ||
104 | FATALP("unable to create the video thread\n"); | ||
105 | POUTP("video thread %lu\n", (unsigned long)id); | ||
106 | pthread_attr_destroy(&attr); | ||
107 | } |
File npv/pipeline/public/state.frag.h added (mode: 100644) (index 0000000..658187e) | |||
1 | static struct pipeline_limits_t limits_p; |
File npv/pkt_q/local/code.frag.c changed (mode: 100644) (index 8bbfcf4..9d70250) | |||
1 | struct pkt_q_t { | ||
2 | u32 n_max; | ||
3 | u32 n; | ||
4 | avcodec_pkt_ref_t **q; | ||
5 | u8 *msg_hdr; | ||
6 | }; | ||
7 | 1 | static void grow(struct pkt_q_t *this) | static void grow(struct pkt_q_t *this) |
8 | 2 | { | { |
9 | 3 | u32 p; | u32 p; |
... | ... | static void grow(struct pkt_q_t *this) | |
18 | 12 | FATALXPQ("ffmpeg:unable to allocate a new packet reference\n"); | FATALXPQ("ffmpeg:unable to allocate a new packet reference\n"); |
19 | 13 | ++this->n_max; | ++this->n_max; |
20 | 14 | } | } |
21 | /* actually rotate the pkt ref ptrs */ | ||
22 | static void deq(struct pkt_q_t *this) | ||
23 | { | ||
24 | if (this->n > 1) { | ||
25 | avcodec_pkt_ref_t *save; | ||
26 | |||
27 | save = this->q[0]; | ||
28 | memmove(&this->q[0], &this->q[1], (this->n - 1) | ||
29 | * sizeof(this->q[0])); | ||
30 | this->q[this->n - 1] = save; | ||
31 | } | ||
32 | this->n--; | ||
33 | } |
File npv/pkt_q/main.c changed (mode: 100644) (index 4650d8c..df4253a) | |||
2 | 2 | #define NPV_PKT_Q_MAIN_C | #define NPV_PKT_Q_MAIN_C |
3 | 3 | #include <stdlib.h> | #include <stdlib.h> |
4 | 4 | #include <string.h> | #include <string.h> |
5 | #include <pthread.h> | ||
5 | 6 | #include <libavcodec/avcodec.h> | #include <libavcodec/avcodec.h> |
6 | 7 | #include "npv/c_fixing.h" | #include "npv/c_fixing.h" |
7 | 8 | #include "npv/global.h" | #include "npv/global.h" |
8 | 9 | #include "npv/pkt_q/public.h" | #include "npv/pkt_q/public.h" |
10 | #include "npv/pipeline/public.h" | ||
9 | 11 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
10 | 12 | #define FATALPQ(fmt, ...) FATAL("packet queue:" fmt, ##__VA_ARGS__) | #define FATALPQ(fmt, ...) FATAL("packet queue:" fmt, ##__VA_ARGS__) |
11 | 13 | #define FATALXPQ(fmt, ...) FATAL("%s:packet queue:" fmt, this->msg_hdr, ##__VA_ARGS__) | #define FATALXPQ(fmt, ...) FATAL("%s:packet queue:" fmt, this->msg_hdr, ##__VA_ARGS__) |
File npv/pkt_q/namespace/main.c changed (mode: 100644) (index e62f1e3..4199cba) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | #define deq pkt_q_deq | ||
3 | 2 | #define grow pkt_q_grow | #define grow pkt_q_grow |
4 | 3 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
5 | 4 | /* some struct field names */ | /* some struct field names */ |
6 | 5 | #define sz size | #define sz size |
6 | #define st_idx stream_index | ||
7 | 7 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
8 | 8 | /*============================================================================*/ | /*============================================================================*/ |
9 | 9 | #else | #else |
10 | #undef deq | ||
11 | 10 | #undef grow | #undef grow |
12 | 11 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
13 | 12 | #undef sz | #undef sz |
13 | #undef st_idx | ||
14 | 14 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
15 | 15 | #endif | #endif |
File npv/pkt_q/namespace/public.h changed (mode: 100644) (index 8aa31df..f9f0b15) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | #define deq pkt_q_deq | ||
2 | 3 | #define enq pkt_q_enq | #define enq pkt_q_enq |
3 | 4 | #define has_eof pkt_q_has_eof | #define has_eof pkt_q_has_eof |
5 | #define lock pkt_q_lock | ||
4 | 6 | #define new pkt_q_new | #define new pkt_q_new |
5 | 7 | #define pkt_q_t pkt_q_t | #define pkt_q_t pkt_q_t |
6 | #define send pkt_q_send | ||
8 | #define unlock pkt_q_unlock | ||
7 | 9 | #define unref_all pkt_q_unref_all | #define unref_all pkt_q_unref_all |
8 | 10 | /*============================================================================*/ | /*============================================================================*/ |
9 | 11 | #else | #else |
12 | #undef lock | ||
13 | #undef deq | ||
10 | 14 | #undef enq | #undef enq |
11 | 15 | #undef has_eof | #undef has_eof |
12 | 16 | #undef new | #undef new |
13 | 17 | #undef pkt_q_t | #undef pkt_q_t |
14 | #undef send | ||
18 | #undef unlock | ||
15 | 19 | #undef unref_all | #undef unref_all |
16 | 20 | #endif | #endif |
File npv/pkt_q/public.h changed (mode: 100644) (index 76d96d6..0eea2d0) | |||
3 | 3 | #include <stdbool.h> | #include <stdbool.h> |
4 | 4 | #include <libavcodec/avcodec.h> | #include <libavcodec/avcodec.h> |
5 | 5 | #include "npv/c_fixing.h" | #include "npv/c_fixing.h" |
6 | #include "npv/pipeline/public.h" | ||
6 | 7 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
7 | 8 | #include "npv/namespace/ffmpeg.h" | #include "npv/namespace/ffmpeg.h" |
8 | 9 | #include "npv/pkt_q/namespace/public.h" | #include "npv/pkt_q/namespace/public.h" |
9 | 10 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
10 | struct pkt_q_t; | ||
11 | struct pkt_q_t { | ||
12 | pthread_mutex_t mutex; | ||
13 | |||
14 | u32 n_max; | ||
15 | u32 n; | ||
16 | avcodec_pkt_ref_t **q; | ||
17 | u8 *msg_hdr; | ||
18 | }; | ||
19 | static void lock(struct pkt_q_t *this); | ||
20 | static void unlock(struct pkt_q_t *this); | ||
11 | 21 | static void enq(struct pkt_q_t *this, avcodec_pkt_ref_t *pr); | static void enq(struct pkt_q_t *this, avcodec_pkt_ref_t *pr); |
22 | static void deq(struct pkt_q_t *this); | ||
12 | 23 | static void unref_all(struct pkt_q_t *this); | static void unref_all(struct pkt_q_t *this); |
13 | static u8 send(struct pkt_q_t *this, avcodec_codec_ctx_t *dec_ctx); | ||
14 | 24 | static struct pkt_q_t *new(u8 *msg_hdr); | static struct pkt_q_t *new(u8 *msg_hdr); |
15 | 25 | static bool has_eof(struct pkt_q_t *this); | static bool has_eof(struct pkt_q_t *this); |
16 | 26 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
File npv/pkt_q/public/code.frag.c changed (mode: 100644) (index 01213ad..6a94aa0) | |||
1 | static void lock(struct pkt_q_t *this) | ||
2 | { | ||
3 | int r; | ||
4 | |||
5 | r = pthread_mutex_lock(&this->mutex); | ||
6 | if (r != 0) | ||
7 | FATALXPQ("%d:unable to lock packet queue\n", r); | ||
8 | } | ||
9 | static void unlock(struct pkt_q_t *this) | ||
10 | { | ||
11 | int r; | ||
12 | |||
13 | r = pthread_mutex_unlock(&this->mutex); | ||
14 | if (r != 0) | ||
15 | FATALXPQ("%d:unable to unlock packet queue\n", r); | ||
16 | } | ||
17 | /* actually rotate the pkt ref ptrs */ | ||
18 | static void deq(struct pkt_q_t *this) | ||
19 | { | ||
20 | if (this->n > 1) { | ||
21 | avcodec_pkt_ref_t *save; | ||
22 | |||
23 | save = this->q[0]; | ||
24 | memmove(&this->q[0], &this->q[1], (this->n - 1) | ||
25 | * sizeof(this->q[0])); | ||
26 | this->q[this->n - 1] = save; | ||
27 | } | ||
28 | this->n--; | ||
29 | } | ||
1 | 30 | /* steal the pkt reference */ | /* steal the pkt reference */ |
2 | 31 | static void enq(struct pkt_q_t *this, avcodec_pkt_ref_t *pr) | static void enq(struct pkt_q_t *this, avcodec_pkt_ref_t *pr) |
3 | 32 | { | { |
... | ... | static void unref_all(struct pkt_q_t *this) | |
14 | 43 | loop { | loop { |
15 | 44 | if (pr == this->n) | if (pr == this->n) |
16 | 45 | break; | break; |
46 | //TODO: account for the limits bytes removal | ||
17 | 47 | avcodec_pkt_unref(this->q[pr]); | avcodec_pkt_unref(this->q[pr]); |
18 | 48 | ++pr; | ++pr; |
19 | 49 | } | } |
20 | 50 | this->n = 0; | this->n = 0; |
21 | 51 | } | } |
22 | #define AGAIN 0 | ||
23 | #define EMPTY 1 | ||
24 | #define EOF_DEC 2 | ||
25 | static u8 send(struct pkt_q_t *this, avcodec_codec_ctx_t *dec_ctx) { loop | ||
26 | { | ||
27 | int r; | ||
28 | avcodec_pkt_ref_t *pr; | ||
29 | |||
30 | if (this->n == 0) | ||
31 | return EMPTY; | ||
32 | pr = this->q[0]; | ||
33 | r = avcodec_send_pkt(dec_ctx, pr); | ||
34 | if (r == AVERROR(EAGAIN)) /* dec is full and the pkt is rejected */ | ||
35 | return AGAIN; | ||
36 | else if (r == 0) { | ||
37 | deq(this); | ||
38 | avcodec_pkt_unref(pr); | ||
39 | continue; | ||
40 | } else if (r == AVUTIL_AVERROR_EOF) | ||
41 | return EOF_DEC; /* the dec is in draining mode */ | ||
42 | FATALXPQ("error while sending a packet to the decoder\n"); | ||
43 | }} | ||
44 | #undef AGAIN | ||
45 | #undef EMPTY | ||
46 | #undef EOF_DEC | ||
47 | 52 | static struct pkt_q_t *new(u8 *msg_hdr) | static struct pkt_q_t *new(u8 *msg_hdr) |
48 | 53 | { | { |
54 | int r; | ||
49 | 55 | struct pkt_q_t *this; | struct pkt_q_t *this; |
50 | 56 | ||
51 | 57 | this = malloc(sizeof(*this)); | this = malloc(sizeof(*this)); |
... | ... | static struct pkt_q_t *new(u8 *msg_hdr) | |
55 | 61 | this->n = 0; | this->n = 0; |
56 | 62 | this->n_max = 0; | this->n_max = 0; |
57 | 63 | this->msg_hdr = strdup(msg_hdr); | this->msg_hdr = strdup(msg_hdr); |
64 | r = pthread_mutex_init(&this->mutex, 0); | ||
65 | if (r != 0) | ||
66 | FATALPQ("unable to init the mutex for the %s packet queue\n", msg_hdr); | ||
58 | 67 | return this; | return this; |
59 | 68 | } | } |
60 | 69 | static bool has_eof(struct pkt_q_t *this) | static bool has_eof(struct pkt_q_t *this) |
File npv/video/local/code.frag.c changed (mode: 100644) (index a827fe4..5b7ef06) | |||
... | ... | static void init_once_local(void) | |
13 | 13 | ++i; | ++i; |
14 | 14 | } | } |
15 | 15 | } | } |
16 | static void scaler_img_create(u8 fr) | ||
16 | static void scaler_img_create(avutil_video_fr_ref_t *fr) | ||
17 | 17 | { | { |
18 | 18 | struct vk_img_create_info_t info; | struct vk_img_create_info_t info; |
19 | 19 | s32 r; | s32 r; |
... | ... | static void scaler_img_create(u8 fr) | |
23 | 23 | info.flags = vk_img_create_flag_2d_array_compatible_bit; | info.flags = vk_img_create_flag_2d_array_compatible_bit; |
24 | 24 | info.img_type = vk_img_type_2d; | info.img_type = vk_img_type_2d; |
25 | 25 | info.texel_mem_blk_fmt = vk_texel_mem_blk_fmt_b8g8r8a8_srgb; | info.texel_mem_blk_fmt = vk_texel_mem_blk_fmt_b8g8r8a8_srgb; |
26 | info.extent.width = (u32)dec_frs_p.a[fr]->width; | ||
27 | info.extent.height = (u32)dec_frs_p.a[fr]->height; | ||
26 | info.extent.width = (u32)fr->width; | ||
27 | info.extent.height = (u32)fr->height; | ||
28 | 28 | info.extent.depth = 1; | info.extent.depth = 1; |
29 | 29 | info.mip_lvls_n = 1; | info.mip_lvls_n = 1; |
30 | 30 | info.samples_n = vk_samples_n_1_bit; | info.samples_n = vk_samples_n_1_bit; |
... | ... | static void dec_a_grow(void) | |
238 | 238 | /* extract a fr ref, shift the a, push it back at the e, and unref its bufs */ | /* extract a fr ref, shift the a, push it back at the e, and unref its bufs */ |
239 | 239 | static void fr_drop(u16 fr) | static void fr_drop(u16 fr) |
240 | 240 | { | { |
241 | struct dec_fr_priv_t *fr_priv; | ||
241 | 242 | avutil_video_fr_ref_t *save; | avutil_video_fr_ref_t *save; |
242 | 243 | ||
244 | fr_priv = dec_frs_p.priv_a + fr; | ||
245 | if (!fr_priv->was_qed_to_pe) | ||
246 | WARNINGV("dropping undisplayed frame\n"); | ||
243 | 247 | save = dec_frs_p.a[fr]; | save = dec_frs_p.a[fr]; |
244 | 248 | avutil_video_fr_unref(save); | avutil_video_fr_unref(save); |
245 | 249 | if (dec_frs_p.n > 1) { | if (dec_frs_p.n > 1) { |
... | ... | static void fr_drop(u16 fr) | |
254 | 258 | sizeof(dec_frs_p.priv_a[fr]) * (e - (fr + 1))); | sizeof(dec_frs_p.priv_a[fr]) * (e - (fr + 1))); |
255 | 259 | memset(&dec_frs_p.priv_a[e - 1], 0, | memset(&dec_frs_p.priv_a[e - 1], 0, |
256 | 260 | sizeof(dec_frs_p.priv_a[e - 1])); | sizeof(dec_frs_p.priv_a[e - 1])); |
257 | } | ||
261 | } else | ||
262 | memset(&dec_frs_p.priv_a[0], 0, sizeof(dec_frs_p.priv_a[0])); | ||
258 | 263 | dec_frs_p.n--; | dec_frs_p.n--; |
259 | 264 | } | } |
260 | 265 | static void frs_drop(s64 now) | static void frs_drop(s64 now) |
... | ... | static void frs_drop(s64 now) | |
262 | 267 | s64 low; | s64 low; |
263 | 268 | s64 threshold; | s64 threshold; |
264 | 269 | u16 fr; | u16 fr; |
265 | avformat_st_t *st; | ||
266 | 270 | ||
267 | st = fmt_ctx_p->sts[st_idx_p]; | ||
268 | |||
269 | /* audio can be late of 0.25s, and audio is 'now' */ | ||
270 | threshold = (300 * 1000 * st->tb.num) / st->tb.den; | ||
271 | /* audio can be late up to 0.25s, and audio is 'now' */ | ||
272 | threshold = (250 * st_p.tb.den) / (st_p.tb.num * 1000); | ||
271 | 273 | low = now - threshold; | low = now - threshold; |
272 | 274 | fr = 0; | fr = 0; |
273 | 275 | loop { | loop { |
... | ... | static void frs_drop(s64 now) | |
280 | 282 | pts = dec_frs_p.a[fr]->pts; | pts = dec_frs_p.a[fr]->pts; |
281 | 283 | fr_priv = dec_frs_p.priv_a + fr; | fr_priv = dec_frs_p.priv_a + fr; |
282 | 284 | ||
283 | if ((fr != dec_frs_p.fr_being_scaled) && (pts < low)) | ||
285 | if ((dec_frs_p.a[fr] != dec_frs_p.being_scaled.fr) | ||
286 | && (pts < low)) | ||
284 | 287 | fr_drop(fr); /* do not advance */ | fr_drop(fr); /* do not advance */ |
285 | 288 | else | else |
286 | 289 | ++fr; | ++fr; |
287 | 290 | } | } |
288 | 291 | } | } |
289 | #define NO_FR U16_MAX | ||
290 | static u16 select_fr(s64 now) | ||
292 | #define NO_FR 0 | ||
293 | static void select_fr(s64 now, avutil_video_fr_ref_t **selected_fr, | ||
294 | struct dec_fr_priv_t **selected_fr_priv) | ||
291 | 295 | { | { |
292 | u16 fr; | ||
293 | u16 selected_fr; | ||
296 | u16 fr_idx; | ||
294 | 297 | u64 selected_fr_delta; | u64 selected_fr_delta; |
295 | 298 | ||
296 | fr = 0; | ||
297 | selected_fr = NO_FR; | ||
299 | fr_idx = 0; | ||
300 | *selected_fr = NO_FR; | ||
298 | 301 | selected_fr_delta = S64_MAX; | selected_fr_delta = S64_MAX; |
299 | 302 | loop { | loop { |
300 | 303 | u64 delta; | u64 delta; |
301 | 304 | ||
302 | if (fr == dec_frs_p.n) | ||
305 | if (fr_idx == dec_frs_p.n) | ||
303 | 306 | break; | break; |
304 | delta = s64_abs(now - (s64)dec_frs_p.a[fr]->pts); | ||
307 | delta = s64_abs(now - (s64)dec_frs_p.a[fr_idx]->pts); | ||
305 | 308 | if (delta < selected_fr_delta) { | if (delta < selected_fr_delta) { |
306 | selected_fr = fr; | ||
309 | *selected_fr = dec_frs_p.a[fr_idx]; | ||
310 | *selected_fr_priv = dec_frs_p.priv_a + fr_idx; | ||
307 | 311 | selected_fr_delta = delta; | selected_fr_delta = delta; |
308 | 312 | } | } |
309 | ++fr; | ||
313 | ++fr_idx; | ||
310 | 314 | } | } |
311 | return selected_fr; | ||
312 | 315 | } | } |
313 | 316 | #undef NO_FR | #undef NO_FR |
314 | 317 | static void frs_clear_last_qed_to_pe(void) | static void frs_clear_last_qed_to_pe(void) |
... | ... | static void blit_setup(u8 swpchn_img, bool scaler_dims_changed) | |
404 | 407 | IF_FATALVVK("%d:swapchain img:%u:command buffer:%p:unable to end recording\n", r, swpchn_img, npv_vk_surf_p.dev.cbs[swpchn_img]); | IF_FATALVVK("%d:swapchain img:%u:command buffer:%p:unable to end recording\n", r, swpchn_img, npv_vk_surf_p.dev.cbs[swpchn_img]); |
405 | 408 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
406 | 409 | /* keep track in order to detect change */ | /* keep track in order to detect change */ |
407 | blit_l[npv_vk_swpchn_imgs_n_max].viewport.width = npv_xcb_p.width; | ||
408 | blit_l[npv_vk_swpchn_imgs_n_max].viewport.height = npv_xcb_p.height; | ||
410 | blit_l[swpchn_img].viewport.width = npv_xcb_p.width; | ||
411 | blit_l[swpchn_img].viewport.height = npv_xcb_p.height; | ||
409 | 412 | } | } |
410 | 413 | #define READY 0 | #define READY 0 |
411 | 414 | #define NOT_READY 1 | #define NOT_READY 1 |
... | ... | static void send_to_pe(u32 swpchn_img) | |
464 | 467 | IF_FATALVVK("%d:queue:%p:unable to submit the image %u to the presentation engine\n", r, npv_vk_surf_p.dev.q, swpchn_img); | IF_FATALVVK("%d:queue:%p:unable to submit the image %u to the presentation engine\n", r, npv_vk_surf_p.dev.q, swpchn_img); |
465 | 468 | /*--------------------------------------------------------------------*/ | /*--------------------------------------------------------------------*/ |
466 | 469 | } | } |
467 | static void start_scaling(u8 fr, bool *scaler_dims_changed) | ||
470 | static void start_scaling(avutil_video_fr_ref_t *fr, | ||
471 | struct dec_fr_priv_t *fr_priv, bool *scaler_dims_changed) | ||
468 | 472 | { | { |
469 | 473 | u32 scaled_line_bytes_n; | u32 scaled_line_bytes_n; |
470 | 474 | ||
471 | if (scaler_p.ctx->cfg.width != dec_frs_p.a[fr]->width | ||
472 | || scaler_p.ctx->cfg.height != dec_frs_p.a[fr]->height) { | ||
475 | if (scaler_p.ctx->cfg.width != fr->width | ||
476 | || scaler_p.ctx->cfg.height != fr->height) { | ||
473 | 477 | if (scaler_p.img.vk != 0) | if (scaler_p.img.vk != 0) |
474 | 478 | scaler_img_destroy(); | scaler_img_destroy(); |
475 | 479 | scaler_img_create(fr); | scaler_img_create(fr); |
... | ... | static void start_scaling(u8 fr, bool *scaler_dims_changed) | |
481 | 485 | scaler_img_dev_mem_map(); | scaler_img_dev_mem_map(); |
482 | 486 | ||
483 | 487 | *scaler_dims_changed = true; | *scaler_dims_changed = true; |
484 | scaler_p.ctx->cfg.width = dec_frs_p.a[fr]->width; | ||
485 | scaler_p.ctx->cfg.height = dec_frs_p.a[fr]->height; | ||
488 | scaler_p.ctx->cfg.width = fr->width; | ||
489 | scaler_p.ctx->cfg.height = fr->height; | ||
486 | 490 | } else | } else |
487 | 491 | *scaler_dims_changed = false; | *scaler_dims_changed = false; |
488 | scaler_p.ctx->cfg.src_fmt = dec_frs_p.a[fr]->fmt; | ||
492 | scaler_p.ctx->cfg.src_fmt = fr->fmt; | ||
489 | 493 | scaler_p.ctx->cfg.dst_fmt = AVUTIL_PIX_FMT_RGB32; | scaler_p.ctx->cfg.dst_fmt = AVUTIL_PIX_FMT_RGB32; |
490 | 494 | scaler_p.ctx->cfg.flags = SWS_POINT; /* | SWS_PRINT_INFO */ | scaler_p.ctx->cfg.flags = SWS_POINT; /* | SWS_PRINT_INFO */ |
491 | 495 | ||
492 | 496 | scaled_line_bytes_n = (u32)scaler_p.img.layout.row_pitch; | scaled_line_bytes_n = (u32)scaler_p.img.layout.row_pitch; |
493 | scaler_p.ctx->scale.src_slices = dec_frs_p.a[fr]->data; | ||
494 | scaler_p.ctx->scale.src_strides = dec_frs_p.a[fr]->linesize; | ||
497 | scaler_p.ctx->scale.src_slices = fr->data; | ||
498 | scaler_p.ctx->scale.src_strides = fr->linesize; | ||
495 | 499 | scaler_p.ctx->scale.dst_slice = scaler_p.img.data; | scaler_p.ctx->scale.dst_slice = scaler_p.img.data; |
496 | 500 | scaler_p.ctx->scale.dst_stride = scaled_line_bytes_n; | scaler_p.ctx->scale.dst_stride = scaled_line_bytes_n; |
497 | 501 | thdsws_run(scaler_p.ctx); | thdsws_run(scaler_p.ctx); |
498 | dec_frs_p.fr_being_scaled = fr; | ||
502 | dec_frs_p.being_scaled.fr = fr; | ||
503 | dec_frs_p.being_scaled.fr_priv = fr_priv; | ||
499 | 504 | } | } |
500 | 505 | static void timer_ack(void) | static void timer_ack(void) |
501 | 506 | { | { |
File npv/video/local/state.frag.c changed (mode: 100644) (index 549bafc..90ad001) | |||
... | ... | struct dec_fr_priv_t { | |
2 | 2 | bool is_being_scaled; | bool is_being_scaled; |
3 | 3 | bool is_scaled; | bool is_scaled; |
4 | 4 | bool is_last_qed_to_pe; | bool is_last_qed_to_pe; |
5 | bool was_qed_to_pe; | ||
5 | 6 | }; | }; |
6 | 7 | /*===========================================================================*/ | /*===========================================================================*/ |
8 | static pthread_mutex_t dec_ctx_mutex_l; | ||
7 | 9 | static avcodec_codec_t *dec_l; | static avcodec_codec_t *dec_l; |
8 | 10 | static struct vk_mem_rqmts_t tmp_mem_rqmts_l; | static struct vk_mem_rqmts_t tmp_mem_rqmts_l; |
9 | 11 | static struct { | static struct { |
File npv/video/main.c changed (mode: 100644) (index 99d9347..0cbd7e2) | |||
46 | 46 | #define WARNINGVVK(fmt, ...) WARNINGVK("video:" fmt, ##__VA_ARGS__) | #define WARNINGVVK(fmt, ...) WARNINGVK("video:" fmt, ##__VA_ARGS__) |
47 | 47 | #define IF_FATALVVK(fmt, ...) IF_FATALVK("video:" fmt, ##__VA_ARGS__) | #define IF_FATALVVK(fmt, ...) IF_FATALVK("video:" fmt, ##__VA_ARGS__) |
48 | 48 | ||
49 | #define TIMER_INTERVAL_NSECS_N 2000000 /* 2 ms->4 ms ? */ | ||
49 | /* 4ms + ~5ms "scaling" ~ 100 frs per sec */ | ||
50 | #define TIMER_INTERVAL_NSECS_N 4000000 | ||
50 | 51 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
51 | 52 | #include "npv/video/local/state.frag.c" | #include "npv/video/local/state.frag.c" |
52 | 53 | /*----------------------------------------------------------------------------*/ | /*----------------------------------------------------------------------------*/ |
File npv/video/namespace/main.c changed (mode: 100644) (index d3f5559..dd8729b) | |||
7 | 7 | #define blit_l video_blit_l | #define blit_l video_blit_l |
8 | 8 | #define blit_setup video_blit_setup | #define blit_setup video_blit_setup |
9 | 9 | #define dec_a_grow video_dec_a_grow | #define dec_a_grow video_dec_a_grow |
10 | #define dec_ctx_mutex_l video_dec_ctx_mutex_l | ||
10 | 11 | #define dec_l video_dec_l | #define dec_l video_dec_l |
11 | 12 | #define fr_drop video_fr_drop | #define fr_drop video_fr_drop |
12 | 13 | #define frs_drop video_frs_drop | #define frs_drop video_frs_drop |
41 | 42 | #undef blit_l | #undef blit_l |
42 | 43 | #undef blit_setup | #undef blit_setup |
43 | 44 | #undef dec_a_grow | #undef dec_a_grow |
45 | #undef dec_ctx_mutex_l | ||
44 | 46 | #undef dec_l | #undef dec_l |
45 | 47 | #undef fr_drop | #undef fr_drop |
46 | 48 | #undef frs_clear_last_qed_to_pe | #undef frs_clear_last_qed_to_pe |
File npv/video/namespace/public.h changed (mode: 100644) (index 515c8ab..59efc9b) | |||
1 | 1 | #ifndef CLEANUP | #ifndef CLEANUP |
2 | 2 | #define dec_ctx_cfg video_dec_ctx_cfg | #define dec_ctx_cfg video_dec_ctx_cfg |
3 | #define dec_ctx_lock video_dec_ctx_lock | ||
3 | 4 | #define dec_ctx_p video_dec_ctx_p | #define dec_ctx_p video_dec_ctx_p |
5 | #define dec_ctx_unlock video_dec_ctx_unlock | ||
4 | 6 | #define dec_flush video_dec_flush | #define dec_flush video_dec_flush |
5 | 7 | #define dec_fr_priv_t video_dec_fr_priv | #define dec_fr_priv_t video_dec_fr_priv |
6 | #define dec_fr_try_get video_dec_fr_try_get | ||
7 | #define dec_frs_get_avail video_dec_frs_get_avail | ||
8 | #define dec_fr_try_receive video_dec_fr_try_receive | ||
9 | #define dec_frs_receive_avail video_dec_frs_receive_avail | ||
10 | #define dec_frs_lock video_dec_frs_lock | ||
8 | 11 | #define dec_frs_p video_dec_frs_p | #define dec_frs_p video_dec_frs_p |
12 | #define dec_frs_unlock video_dec_frs_unlock | ||
9 | 13 | #define init_once video_init_once | #define init_once video_init_once |
10 | 14 | #define pkt_q_p video_pkt_q_p | #define pkt_q_p video_pkt_q_p |
15 | #define pkts_send video_pkts_send | ||
11 | 16 | #define scaler_p video_scaler_p | #define scaler_p video_scaler_p |
12 | #define st_idx_p video_st_idx_p | ||
17 | #define st_p video_st_p | ||
13 | 18 | #define timer_fd_p video_timer_fd_p | #define timer_fd_p video_timer_fd_p |
14 | 19 | #define timer_start video_timer_start | #define timer_start video_timer_start |
15 | 20 | #define timer_evt video_timer_evt | #define timer_evt video_timer_evt |
16 | 21 | /*============================================================================*/ | /*============================================================================*/ |
17 | 22 | #else | #else |
18 | 23 | #undef dec_ctx_cfg | #undef dec_ctx_cfg |
24 | #undef dec_ctx_lock | ||
19 | 25 | #undef dec_ctx_p | #undef dec_ctx_p |
26 | #undef dec_ctx_unlock | ||
20 | 27 | #undef dec_flush | #undef dec_flush |
21 | 28 | #undef dec_fr_priv_t | #undef dec_fr_priv_t |
22 | #undef dec_fr_try_get | ||
23 | #undef dec_frs_get_avail | ||
29 | #undef dec_fr_try_receive | ||
30 | #undef dec_frs_receive_avail | ||
31 | #undef dec_frs_lock | ||
24 | 32 | #undef dec_frs_p | #undef dec_frs_p |
33 | #undef dec_frs_unlock | ||
25 | 34 | #undef init_once | #undef init_once |
26 | 35 | #undef pkt_q_p | #undef pkt_q_p |
36 | #undef pkts_send | ||
27 | 37 | #undef scaler_p | #undef scaler_p |
28 | #undef st_idx_p | ||
38 | #undef st_p | ||
29 | 39 | #undef timer_fd_p | #undef timer_fd_p |
30 | 40 | #undef timer_start | #undef timer_start |
31 | 41 | #undef timer_evt | #undef timer_evt |
File npv/video/public.h changed (mode: 100644) (index e5e3b99..820867c) | |||
20 | 20 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
21 | 21 | static void dec_ctx_cfg(avcodec_params_t *params); | static void dec_ctx_cfg(avcodec_params_t *params); |
22 | 22 | static void dec_flush(void); | static void dec_flush(void); |
23 | static u8 dec_fr_try_get(void); | ||
24 | static void dec_frs_get_avail(void); | ||
23 | static u8 dec_fr_try_receive(void); | ||
24 | static void dec_frs_receive_avail(void); | ||
25 | 25 | static void init_once(void); | static void init_once(void); |
26 | 26 | static void timer_evt(void); | static void timer_evt(void); |
27 | 27 | static void timer_start(void); | static void timer_start(void); |
28 | static void dec_ctx_lock(void); | ||
29 | static void dec_ctx_unlock(void); | ||
30 | static void dec_frs_lock(void); | ||
31 | static void dec_frs_unlock(void); | ||
32 | static void pkts_send(void); | ||
28 | 33 | /*---------------------------------------------------------------------------*/ | /*---------------------------------------------------------------------------*/ |
29 | 34 | #define CLEANUP | #define CLEANUP |
30 | 35 | #include "npv/namespace/ffmpeg.h" | #include "npv/namespace/ffmpeg.h" |
File npv/video/public/code.frag.c changed (mode: 100644) (index eae91e9..8cd0380) | |||
1 | #define NO_FR U16_MAX | ||
1 | #define NO_FR 0 | ||
2 | 2 | static void init_once_public(void) | static void init_once_public(void) |
3 | 3 | { | { |
4 | int r; | ||
5 | |||
4 | 6 | /* linux bug: still no CLOCK_MONOTONIC_RAW for timerfd */ | /* linux bug: still no CLOCK_MONOTONIC_RAW for timerfd */ |
5 | 7 | errno = 0; | errno = 0; |
6 | 8 | timer_fd_p = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); | timer_fd_p = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); |
7 | 9 | if (timer_fd_p == -1) | if (timer_fd_p == -1) |
8 | 10 | FATALV("unable to get a timer file descriptor:%s\n", strerror(errno)); | FATALV("unable to get a timer file descriptor:%s\n", strerror(errno)); |
9 | |||
10 | st_idx_p = -1; | ||
11 | memset(&st_p, 0, sizeof(st_p)); | ||
11 | 12 | pkt_q_p = pkt_q_new("video"); | pkt_q_p = pkt_q_new("video"); |
12 | 13 | dec_ctx_p = 0; | dec_ctx_p = 0; |
14 | r = pthread_mutex_init(&dec_ctx_mutex_l, 0); | ||
15 | if (r != 0) | ||
16 | FATALV("unable to create the mutex for the decoder context\n"); | ||
13 | 17 | ||
14 | 18 | dec_frs_p.eof_receive = false; | dec_frs_p.eof_receive = false; |
15 | 19 | dec_frs_p.n_max = 0; | dec_frs_p.n_max = 0; |
16 | 20 | dec_frs_p.n = 0; | dec_frs_p.n = 0; |
17 | 21 | dec_frs_p.a = 0; | dec_frs_p.a = 0; |
18 | 22 | dec_frs_p.priv_a = 0; | dec_frs_p.priv_a = 0; |
19 | dec_frs_p.fr_being_scaled = NO_FR; | ||
23 | dec_frs_p.being_scaled.fr = NO_FR; | ||
24 | dec_frs_p.being_scaled.fr_priv = 0; | ||
25 | r = pthread_mutex_init(&dec_frs_p.mutex, 0); | ||
26 | if (r != 0) | ||
27 | FATALV("unable to create the mutex for the array of frames\n"); | ||
20 | 28 | ||
21 | 29 | scaler_p.img.vk = 0; | scaler_p.img.vk = 0; |
22 | 30 | memset(&scaler_p.img.layout, 0, sizeof(scaler_p.img.layout)); | memset(&scaler_p.img.layout, 0, sizeof(scaler_p.img.layout)); |
... | ... | static void timer_start(void) | |
71 | 79 | #define AGAIN 0 | #define AGAIN 0 |
72 | 80 | #define HAVE_FR 1 | #define HAVE_FR 1 |
73 | 81 | #define EOF_DEC 2 | #define EOF_DEC 2 |
74 | static u8 dec_fr_try_get(void) | ||
82 | static u8 dec_fr_try_receive(void) | ||
75 | 83 | { | { |
76 | 84 | int r; | int r; |
77 | 85 | u16 fr; | u16 fr; |
78 | 86 | ||
79 | 87 | if (dec_frs_p.eof_receive) | if (dec_frs_p.eof_receive) |
80 | 88 | return EOF_DEC; | return EOF_DEC; |
81 | |||
82 | 89 | if (dec_frs_p.n == dec_frs_p.n_max) | if (dec_frs_p.n == dec_frs_p.n_max) |
83 | 90 | dec_a_grow(); | dec_a_grow(); |
84 | |||
85 | 91 | fr = dec_frs_p.n; | fr = dec_frs_p.n; |
86 | 92 | r = avcodec_receive_video_fr(dec_ctx_p, dec_frs_p.a[fr]); | r = avcodec_receive_video_fr(dec_ctx_p, dec_frs_p.a[fr]); |
87 | 93 | if (r == AVUTIL_AVERROR(EAGAIN)) | if (r == AVUTIL_AVERROR(EAGAIN)) |
... | ... | static u8 dec_fr_try_get(void) | |
103 | 109 | #define AGAIN 0 | #define AGAIN 0 |
104 | 110 | #define HAVE_FR 1 | #define HAVE_FR 1 |
105 | 111 | #define EOF_DEC 2 | #define EOF_DEC 2 |
106 | static void dec_frs_get_avail(void) { loop | ||
112 | static void dec_frs_receive_avail(void) { loop | ||
107 | 113 | { | { |
108 | 114 | u8 r; | u8 r; |
109 | 115 | ||
110 | r = dec_fr_try_get(); | ||
116 | dec_frs_lock(); | ||
117 | dec_ctx_lock(); | ||
118 | r = dec_fr_try_receive(); | ||
119 | dec_ctx_unlock(); | ||
120 | dec_frs_unlock(); | ||
111 | 121 | if (r == HAVE_FR) | if (r == HAVE_FR) |
112 | 122 | continue; | continue; |
113 | 123 | else if (r == AGAIN || r == EOF_DEC) | else if (r == AGAIN || r == EOF_DEC) |
... | ... | static void dec_frs_get_avail(void) { loop | |
116 | 126 | #undef AGAIN | #undef AGAIN |
117 | 127 | #undef HAVE_FR | #undef HAVE_FR |
118 | 128 | #undef EOF_DEC | #undef EOF_DEC |
119 | #define NO_FR U16_MAX | ||
129 | #define NO_FR 0 | ||
120 | 130 | static void dec_flush(void) | static void dec_flush(void) |
121 | 131 | { | { |
122 | 132 | pkt_q_unref_all(pkt_q_p); | pkt_q_unref_all(pkt_q_p); |
123 | 133 | frs_reset(); | frs_reset(); |
124 | 134 | dec_frs_p.eof_receive = false; | dec_frs_p.eof_receive = false; |
125 | dec_frs_p.fr_being_scaled = NO_FR; | ||
135 | dec_frs_p.being_scaled.fr = NO_FR; | ||
136 | dec_frs_p.being_scaled.fr_priv = 0; | ||
126 | 137 | avcodec_flush_bufs(dec_ctx_p); | avcodec_flush_bufs(dec_ctx_p); |
127 | 138 | } | } |
128 | 139 | #undef NO_FR | #undef NO_FR |
140 | static void dec_ctx_lock(void) | ||
141 | { | ||
142 | int r; | ||
143 | |||
144 | r = pthread_mutex_lock(&dec_ctx_mutex_l); | ||
145 | if (r != 0) | ||
146 | FATALV("%d:unable to lock the video decoder context\n", r); | ||
147 | } | ||
148 | static void dec_ctx_unlock(void) | ||
149 | { | ||
150 | int r; | ||
151 | |||
152 | r = pthread_mutex_unlock(&dec_ctx_mutex_l); | ||
153 | if (r != 0) | ||
154 | FATALV("%d:unable to unlock the video decoder context\n", r); | ||
155 | } | ||
156 | static void dec_frs_lock(void) | ||
157 | { | ||
158 | int r; | ||
159 | |||
160 | r = pthread_mutex_lock(&dec_frs_p.mutex); | ||
161 | if (r != 0) | ||
162 | FATALV("%d:unable to lock the array of decoder frames\n", r); | ||
163 | } | ||
164 | static void dec_frs_unlock(void) | ||
165 | { | ||
166 | int r; | ||
167 | |||
168 | r = pthread_mutex_unlock(&dec_frs_p.mutex); | ||
169 | if (r != 0) | ||
170 | FATALV("%d:unable to unlock the array of decoder frames\n", r); | ||
171 | } | ||
129 | 172 | /* go non-blocking or a worker thread is needed */ | /* go non-blocking or a worker thread is needed */ |
130 | #define NO_FR U16_MAX | ||
173 | #define NO_FR 0 | ||
131 | 174 | #define TS_FROM_CLK_OK 0 | #define TS_FROM_CLK_OK 0 |
132 | 175 | #define READY 0 | #define READY 0 |
133 | 176 | #define NOT_READY 1 | #define NOT_READY 1 |
177 | /* XXX: we do want to lock the frs q the least amount of time as possible */ | ||
134 | 178 | static void timer_evt(void) | static void timer_evt(void) |
135 | 179 | { | { |
136 | 180 | u8 r; | u8 r; |
137 | 181 | s64 now; | s64 now; |
138 | u16 fr; | ||
182 | avutil_video_fr_ref_t *fr; | ||
139 | 183 | struct dec_fr_priv_t *fr_priv; | struct dec_fr_priv_t *fr_priv; |
140 | 184 | bool scaler_is_busy; | bool scaler_is_busy; |
141 | 185 | bool scaler_dims_changed; | bool scaler_dims_changed; |
... | ... | static void timer_evt(void) | |
144 | 188 | timer_ack(); | timer_ack(); |
145 | 189 | if (npv_paused_p) | if (npv_paused_p) |
146 | 190 | return; | return; |
147 | dec_frs_get_avail(); | ||
148 | if (dec_frs_p.n == 0) | ||
149 | return; | ||
150 | 191 | r = clk_get_video_st_ts(&now); | r = clk_get_video_st_ts(&now); |
151 | 192 | if (r != TS_FROM_CLK_OK) | if (r != TS_FROM_CLK_OK) |
152 | 193 | return; | return; |
194 | /* lock --------------------------------------------------------------*/ | ||
195 | dec_frs_lock(); | ||
196 | if (dec_frs_p.n == 0) { | ||
197 | dec_frs_unlock(); | ||
198 | return; | ||
199 | } | ||
153 | 200 | frs_drop(now); | frs_drop(now); |
154 | fr = select_fr(now); | ||
201 | select_fr(now, &fr, &fr_priv); | ||
202 | dec_frs_unlock(); | ||
203 | /* unlock ------------------------------------------------------------*/ | ||
155 | 204 | if (fr == NO_FR) | if (fr == NO_FR) |
156 | 205 | return; | return; |
157 | fr_priv = dec_frs_p.priv_a + fr; | ||
158 | 206 | if (fr_priv->is_last_qed_to_pe) | if (fr_priv->is_last_qed_to_pe) |
159 | 207 | return; | return; |
160 | 208 | scaler_is_busy = thdsws_is_busy(scaler_p.ctx); | scaler_is_busy = thdsws_is_busy(scaler_p.ctx); |
161 | 209 | if (!scaler_is_busy) { | if (!scaler_is_busy) { |
162 | if (dec_frs_p.fr_being_scaled != NO_FR) { | ||
163 | dec_frs_p.priv_a[dec_frs_p.fr_being_scaled].is_scaled | ||
164 | = true; | ||
165 | dec_frs_p.fr_being_scaled = NO_FR; | ||
210 | if (dec_frs_p.being_scaled.fr != NO_FR) { | ||
211 | dec_frs_p.being_scaled.fr_priv->is_scaled = true; | ||
212 | dec_frs_p.being_scaled.fr = NO_FR; | ||
166 | 213 | } | } |
167 | 214 | } | } |
168 | 215 | if (!fr_priv->is_scaled) { | if (!fr_priv->is_scaled) { |
169 | 216 | if (scaler_is_busy) | if (scaler_is_busy) |
170 | 217 | return; | return; |
171 | start_scaling(fr, &scaler_dims_changed); | ||
218 | start_scaling(fr, fr_priv, &scaler_dims_changed); | ||
172 | 219 | return; | return; |
173 | 220 | } | } |
174 | 221 | r = swpchn_next_img(&swpchn_img); | r = swpchn_next_img(&swpchn_img); |
... | ... | static void timer_evt(void) | |
176 | 223 | return; | return; |
177 | 224 | blit_setup(swpchn_img, scaler_dims_changed); | blit_setup(swpchn_img, scaler_dims_changed); |
178 | 225 | send_to_pe(swpchn_img); | send_to_pe(swpchn_img); |
226 | /* lock --------------------------------------------------------------*/ | ||
227 | dec_frs_lock(); | ||
179 | 228 | frs_clear_last_qed_to_pe(); | frs_clear_last_qed_to_pe(); |
229 | dec_frs_unlock(); | ||
230 | /* unlock ------------------------------------------------------------*/ | ||
180 | 231 | fr_priv->is_last_qed_to_pe = true; | fr_priv->is_last_qed_to_pe = true; |
232 | fr_priv->was_qed_to_pe = true; /* drop detection */ | ||
181 | 233 | } | } |
182 | 234 | #undef NO_FR | #undef NO_FR |
183 | 235 | #undef TS_FROM_CLK_OK | #undef TS_FROM_CLK_OK |
184 | 236 | #undef READY | #undef READY |
185 | 237 | #undef NOT_READY | #undef NOT_READY |
238 | /* we do per-loop fine-grained locking */ | ||
239 | #define sz size | ||
240 | static void pkts_send(void) { loop | ||
241 | { | ||
242 | int r; | ||
243 | avcodec_pkt_ref_t *pr; | ||
244 | |||
245 | pkt_q_lock(pkt_q_p); | ||
246 | if (pkt_q_p->n == 0) | ||
247 | goto unlock_and_return; | ||
248 | pr = pkt_q_p->q[0]; | ||
249 | dec_ctx_lock(); | ||
250 | r = avcodec_send_pkt(dec_ctx_p, pr); | ||
251 | dec_ctx_unlock(); | ||
252 | if (r == AVERROR(EAGAIN)) /* dec is full and the pkt is rejected */ | ||
253 | goto unlock_and_return; | ||
254 | else if (r == AVUTIL_AVERROR_EOF) /* the dec is in draining mode */ | ||
255 | goto unlock_and_return; | ||
256 | else if (r != 0) | ||
257 | FATALV("error while sending a packet to the decoder\n"); | ||
258 | /* r == 0 */ | ||
259 | pipeline_limits_lock(); | ||
260 | pipeline_limits_p.pkts.video_bytes_n -= pr->sz; | ||
261 | pipeline_limits_unlock(); | ||
262 | |||
263 | pkt_q_deq(pkt_q_p); | ||
264 | avcodec_pkt_unref(pr); | ||
265 | pkt_q_unlock(pkt_q_p); | ||
266 | continue; | ||
267 | |||
268 | unlock_and_return: | ||
269 | pkt_q_unlock(pkt_q_p); | ||
270 | return; | ||
271 | }} | ||
272 | #undef sz |
File npv/video/public/state.frag.h changed (mode: 100644) (index 3b548ce..8fde273) | |||
1 | static avcodec_codec_ctx_t* dec_ctx_p; | ||
1 | static avcodec_codec_ctx_t *dec_ctx_p; | ||
2 | 2 | static struct pkt_q_t *pkt_q_p; | static struct pkt_q_t *pkt_q_p; |
3 | static int st_idx_p; | ||
3 | /* | ||
4 | * we copy some stream data in the case the stream does vanish or is replaced | ||
5 | * (don't know how ffmpeg does handle this) | ||
6 | */ | ||
7 | static struct { | ||
8 | int idx; | ||
9 | int id; | ||
10 | avutil_rational_t tb; | ||
11 | int64_t start_time; | ||
12 | } st_p; | ||
4 | 13 | static int timer_fd_p; | static int timer_fd_p; |
5 | /*---------------------------------------------------------------------------*/ | ||
14 | /*----------------------------------------------------------------------------*/ | ||
6 | 15 | struct dec_fr_priv_t; | struct dec_fr_priv_t; |
7 | 16 | static struct { | static struct { |
17 | pthread_mutex_t mutex; | ||
18 | |||
8 | 19 | bool eof_receive; /* "receiving" from the dec returned eof */ | bool eof_receive; /* "receiving" from the dec returned eof */ |
9 | 20 | ||
10 | 21 | u16 n_max; | u16 n_max; |
... | ... | static struct { | |
12 | 23 | /* we did not factor the 2 following fields on purpose */ | /* we did not factor the 2 following fields on purpose */ |
13 | 24 | avutil_video_fr_ref_t **a; | avutil_video_fr_ref_t **a; |
14 | 25 | struct dec_fr_priv_t *priv_a; | struct dec_fr_priv_t *priv_a; |
15 | u16 fr_being_scaled; | ||
26 | struct { | ||
27 | avutil_video_fr_ref_t *fr; | ||
28 | struct dec_fr_priv_t *fr_priv; | ||
29 | } being_scaled; | ||
16 | 30 | } dec_frs_p; | } dec_frs_p; |
17 | /*---------------------------------------------------------------------------*/ | ||
31 | /*----------------------------------------------------------------------------*/ | ||
18 | 32 | static struct { | static struct { |
19 | 33 | struct { | struct { |
20 | 34 | struct vk_img_t *vk; | struct vk_img_t *vk; |