@@ -92,6 +92,9 @@ const pa_buffer_attr * bufattr = NULL;
92
92
93
93
static int verbose = 1 ;
94
94
95
+ static bool connect_disconnect_rec_stream (
96
+ struct userdata * u , bool state_callback , bool new_rec_allowed );
97
+
95
98
static void context_state_callback (pa_context * c , void * userdata );
96
99
97
100
static void playback_stream_drain (struct userdata * u );
@@ -354,12 +357,13 @@ static void send_rec_data(pa_stream *s, struct userdata *u, bool discard_overrun
354
357
size_t rec_buffer_length , rec_buffer_index = 0 ;
355
358
int l , vchan_buffer_space ;
356
359
357
- assert (s );
358
360
assert (u );
359
361
360
- if (!u -> rec_allowed )
362
+ if (!u -> rec_allowed || u -> rec_stream_connect_in_progress )
361
363
return ;
362
364
365
+ assert (s );
366
+
363
367
if (pa_stream_readable_size (s ) <= 0 )
364
368
return ;
365
369
@@ -448,8 +452,6 @@ static void vchan_rec_callback(pa_mainloop_api *UNUSED(a),
448
452
uint32_t cmd ;
449
453
while (libvchan_data_ready (u -> rec_ctrl ) >= (int )sizeof (cmd )) {
450
454
if (libvchan_read (u -> rec_ctrl , (char * )& cmd , sizeof (cmd )) != sizeof (cmd )) {
451
- if (!pa_stream_is_corked (u -> rec_stream ))
452
- pa_stream_cork (u -> rec_stream , 1 , NULL , u );
453
455
fprintf (stderr , "Failed to read from vchan\n" );
454
456
quit (u , 1 );
455
457
return ;
@@ -461,10 +463,10 @@ static void vchan_rec_callback(pa_mainloop_api *UNUSED(a),
461
463
if (!qdb_write (u -> qdb , u -> qdb_request_path , "1" , 1 )) {
462
464
pacat_log ("Failed to write QubesDB %s: %s" , u -> qdb_request_path , strerror (errno ));
463
465
}
464
- if (u -> rec_allowed ) {
466
+ if (u -> rec_allowed && ! u -> rec_stream_connect_in_progress ) {
465
467
pacat_log ("Recording start" );
466
468
pa_stream_cork (u -> rec_stream , 0 , NULL , u );
467
- } else
469
+ } else if (! u -> rec_allowed )
468
470
pacat_log ("Recording requested but not allowed" );
469
471
g_mutex_unlock (& u -> prop_mutex );
470
472
break ;
@@ -474,13 +476,17 @@ static void vchan_rec_callback(pa_mainloop_api *UNUSED(a),
474
476
if (!qdb_write (u -> qdb , u -> qdb_request_path , "0" , 1 )) {
475
477
pacat_log ("Failed to write QubesDB %s: %s" , u -> qdb_request_path , strerror (errno ));
476
478
}
477
- if (! pa_stream_is_corked ( u -> rec_stream ) ) {
479
+ if (u -> rec_allowed && ! u -> rec_stream_connect_in_progress ) {
478
480
pacat_log ("Recording stop" );
479
481
pa_stream_cork (u -> rec_stream , 1 , NULL , u );
480
482
}
481
483
g_mutex_unlock (& u -> prop_mutex );
482
484
break ;
483
485
case QUBES_PA_SINK_CORK_CMD :
486
+ if (pa_stream_is_corked (u -> play_stream )) {
487
+ pacat_log ("Stream already corked" );
488
+ break ;
489
+ }
484
490
u -> pending_play_cork = true;
485
491
if (libvchan_data_ready (u -> play_ctrl ) > 0 ) {
486
492
pacat_log ("Deferred stream drain" );
@@ -520,10 +526,11 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
520
526
assert (u -> mainloop_api );
521
527
u -> mainloop_api -> io_free (u -> play_stdio_event );
522
528
}
523
- if (u -> rec_stdio_event && u -> rec_stream == s ) {
529
+ if (u -> rec_stream == s ) {
524
530
pacat_log ("rec stream terminated" );
525
- assert (u -> mainloop_api );
526
- u -> mainloop_api -> io_free (u -> rec_stdio_event );
531
+ pa_stream_unref (u -> rec_stream );
532
+ u -> rec_stream = NULL ;
533
+ connect_disconnect_rec_stream (u , true, false);
527
534
}
528
535
break ;
529
536
@@ -559,11 +566,11 @@ static void stream_state_callback(pa_stream *s, void *userdata) {
559
566
}
560
567
}
561
568
if (u -> rec_stream == s ) {
562
- u -> rec_stdio_event = u -> mainloop_api -> io_new ( u -> mainloop_api ,
563
- libvchan_fd_for_select (u -> rec_ctrl ), PA_IO_EVENT_INPUT , vchan_rec_callback , u );
564
- if (! u -> rec_stdio_event ) {
565
- pacat_log ( "io_new rec failed" );
566
- quit ( u , 1 );
569
+ if ( connect_disconnect_rec_stream ( u , true, true)) {
570
+ if (u -> rec_requested ) {
571
+ pacat_log ( "Recording start" );
572
+ pa_stream_cork ( u -> rec_stream , 0 , NULL , NULL );
573
+ }
567
574
}
568
575
}
569
576
break ;
@@ -638,13 +645,84 @@ static void stream_event_callback(pa_stream *s, const char *name, pa_proplist *p
638
645
pa_xfree (t );
639
646
}
640
647
648
+ /*
649
+ * Connect/disconnect rec stream based on new_rec_allowed. This can be called
650
+ * from a state callback (if state_callback=true) to finalize
651
+ * connecting/disconnecting.
652
+ * Returns if stream is in the desired state.
653
+ */
654
+ static bool connect_disconnect_rec_stream_locked (
655
+ struct userdata * u , bool state_callback , bool new_rec_allowed )
656
+ {
657
+ if (state_callback ) {
658
+ if (new_rec_allowed == u -> rec_allowed ) {
659
+ if (!qdb_write (u -> qdb , u -> qdb_status_path , u -> rec_allowed ? "1" : "0" , 1 )) {
660
+ pacat_log ("Failed to write QubesDB %s: %s" , u -> qdb_status_path , strerror (errno ));
661
+ }
662
+ u -> rec_stream_connect_in_progress = false;
663
+ return true;
664
+ }
665
+ }
666
+ u -> rec_stream_connect_in_progress = true;
667
+ if (new_rec_allowed ) {
668
+ pa_stream_flags_t flags =
669
+ PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY ;
670
+
671
+ /* setup recording stream */
672
+ assert (!u -> rec_stream );
673
+
674
+ if (!(u -> rec_stream = pa_stream_new_with_proplist (u -> context , "record" , & sample_spec , & u -> channel_map , u -> proplist ))) {
675
+ pacat_log ("rec pa_stream_new() failed: %s" , pa_strerror (pa_context_errno (u -> context )));
676
+ quit (u , 1 );
677
+ }
678
+
679
+ pa_stream_set_state_callback (u -> rec_stream , stream_state_callback , u );
680
+ /* pa_stream_set_write_callback */
681
+ pa_stream_set_read_callback (u -> rec_stream , stream_read_callback , u );
682
+ pa_stream_set_suspended_callback (u -> rec_stream , stream_suspended_callback , u );
683
+ pa_stream_set_moved_callback (u -> rec_stream , stream_moved_callback , u );
684
+ pa_stream_set_underflow_callback (u -> rec_stream , stream_underflow_callback , u );
685
+ pa_stream_set_overflow_callback (u -> rec_stream , stream_overflow_callback , u );
686
+ pa_stream_set_started_callback (u -> rec_stream , stream_started_callback , u );
687
+ pa_stream_set_event_callback (u -> rec_stream , stream_event_callback , u );
688
+ pa_stream_set_buffer_attr_callback (u -> rec_stream , stream_buffer_attr_callback , u );
689
+
690
+ if (pa_stream_connect_record (u -> rec_stream ,
691
+ u -> rec_device ,
692
+ bufattr ,
693
+ flags ) < 0 ) {
694
+ pacat_log ("pa_stream_connect_record() failed: %s" ,
695
+ pa_strerror (pa_context_errno (u -> context )));
696
+ u -> rec_allowed = false;
697
+ u -> rec_stream_connect_in_progress = false;
698
+ }
699
+ } else {
700
+ if (pa_stream_disconnect (u -> rec_stream ) < 0 ) {
701
+ pacat_log ("pa_stream_disconnect() failed: %s" ,
702
+ pa_strerror (pa_context_errno (u -> context )));
703
+ u -> rec_stream_connect_in_progress = false;
704
+ }
705
+ }
706
+ return false;
707
+ }
708
+
709
+ static bool connect_disconnect_rec_stream (
710
+ struct userdata * u , bool state_callback , bool new_rec_allowed )
711
+ {
712
+ bool ret ;
713
+
714
+ g_mutex_lock (& u -> prop_mutex );
715
+ ret = connect_disconnect_rec_stream_locked (u , state_callback , new_rec_allowed );
716
+ g_mutex_unlock (& u -> prop_mutex );
717
+ return ret ;
718
+ }
719
+
641
720
642
721
643
722
/* This is called whenever the context status changes */
644
723
static void context_state_callback (pa_context * c , void * userdata ) {
645
724
struct userdata * u = userdata ;
646
725
pa_stream_flags_t flags = 0 ;
647
- pa_channel_map channel_map ;
648
726
649
727
assert (c );
650
728
@@ -656,9 +734,9 @@ static void context_state_callback(pa_context *c, void *userdata) {
656
734
657
735
case PA_CONTEXT_READY :
658
736
659
- pa_channel_map_init_extend (& channel_map , sample_spec .channels , PA_CHANNEL_MAP_DEFAULT );
737
+ pa_channel_map_init_extend (& u -> channel_map , sample_spec .channels , PA_CHANNEL_MAP_DEFAULT );
660
738
661
- if (!pa_channel_map_compatible (& channel_map , & sample_spec )) {
739
+ if (!pa_channel_map_compatible (& u -> channel_map , & sample_spec )) {
662
740
pacat_log ("Channel map doesn't match sample specification" );
663
741
goto fail ;
664
742
}
@@ -669,7 +747,7 @@ static void context_state_callback(pa_context *c, void *userdata) {
669
747
if (verbose )
670
748
pacat_log ("Connection established.%s" , CLEAR_LINE );
671
749
672
- if (!(u -> play_stream = pa_stream_new_with_proplist (c , "playback" , & sample_spec , & channel_map , u -> proplist ))) {
750
+ if (!(u -> play_stream = pa_stream_new_with_proplist (c , "playback" , & sample_spec , & u -> channel_map , u -> proplist ))) {
673
751
pacat_log ("play pa_stream_new() failed: %s" , pa_strerror (pa_context_errno (c )));
674
752
goto fail ;
675
753
}
@@ -693,37 +771,25 @@ static void context_state_callback(pa_context *c, void *userdata) {
693
771
goto fail ;
694
772
}
695
773
696
- /* setup recording stream */
697
- assert (!u -> rec_stream );
698
-
699
- if (!(u -> rec_stream = pa_stream_new_with_proplist (c , "record" , & sample_spec , & channel_map , u -> proplist ))) {
700
- pacat_log ("rec pa_stream_new() failed: %s" , pa_strerror (pa_context_errno (c )));
701
- goto fail ;
702
- }
703
-
704
- pa_stream_set_state_callback (u -> rec_stream , stream_state_callback , u );
705
- /* pa_stream_set_write_callback */
706
- pa_stream_set_read_callback (u -> rec_stream , stream_read_callback , u );
707
- pa_stream_set_suspended_callback (u -> rec_stream , stream_suspended_callback , u );
708
- pa_stream_set_moved_callback (u -> rec_stream , stream_moved_callback , u );
709
- pa_stream_set_underflow_callback (u -> rec_stream , stream_underflow_callback , u );
710
- pa_stream_set_overflow_callback (u -> rec_stream , stream_overflow_callback , u );
711
- pa_stream_set_started_callback (u -> rec_stream , stream_started_callback , u );
712
- pa_stream_set_event_callback (u -> rec_stream , stream_event_callback , u );
713
- pa_stream_set_buffer_attr_callback (u -> rec_stream , stream_buffer_attr_callback , u );
714
-
715
- flags = PA_STREAM_START_CORKED | PA_STREAM_ADJUST_LATENCY ;
716
774
u -> rec_requested = 0 ;
717
775
718
- if (pa_stream_connect_record (u -> rec_stream , u -> rec_device , bufattr , flags ) < 0 ) {
719
- pacat_log ("pa_stream_connect_record() failed: %s" , pa_strerror (pa_context_errno (c )));
720
- goto fail ;
776
+ /* and start watching for recording requests */
777
+ u -> rec_stdio_event = u -> mainloop_api -> io_new (u -> mainloop_api ,
778
+ libvchan_fd_for_select (u -> rec_ctrl ), PA_IO_EVENT_INPUT , vchan_rec_callback , u );
779
+ if (!u -> rec_stdio_event ) {
780
+ pacat_log ("io_new rec failed" );
781
+ quit (u , 1 );
721
782
}
722
-
783
+ if (u -> rec_allowed )
784
+ connect_disconnect_rec_stream (u , false, u -> rec_allowed );
723
785
break ;
724
786
725
787
case PA_CONTEXT_TERMINATED :
726
788
pacat_log ("pulseaudio connection terminated" );
789
+ if (u -> rec_stdio_event ) {
790
+ assert (u -> mainloop_api );
791
+ u -> mainloop_api -> io_free (u -> rec_stdio_event );
792
+ }
727
793
quit (u , 0 );
728
794
break ;
729
795
@@ -865,18 +931,9 @@ static void control_socket_callback(pa_mainloop_api *UNUSED(a),
865
931
g_mutex_lock (& u -> prop_mutex );
866
932
if (new_rec_allowed != u -> rec_allowed ) {
867
933
u -> rec_allowed = new_rec_allowed ;
934
+ if (!u -> rec_stream_connect_in_progress )
935
+ connect_disconnect_rec_stream_locked (u , false, new_rec_allowed );
868
936
pacat_log ("Setting audio-input to %s" , u -> rec_allowed ? "enabled" : "disabled" );
869
- if (u -> rec_allowed && u -> rec_requested ) {
870
- pacat_log ("Recording start" );
871
- pa_stream_cork (u -> rec_stream , 0 , NULL , NULL );
872
- } else if (!u -> rec_allowed && u -> rec_stream &&
873
- (u -> rec_requested || !pa_stream_is_corked (u -> rec_stream ))) {
874
- pacat_log ("Recording stop" );
875
- pa_stream_cork (u -> rec_stream , 1 , NULL , NULL );
876
- }
877
- if (!qdb_write (u -> qdb , u -> qdb_status_path , new_rec_allowed ? "1" : "0" , 1 )) {
878
- pacat_log ("Failed to write QubesDB %s: %s" , u -> qdb_status_path , strerror (errno ));
879
- }
880
937
}
881
938
g_mutex_unlock (& u -> prop_mutex );
882
939
}
@@ -929,7 +986,7 @@ static int setup_control(struct userdata *u) {
929
986
if (socket_fd < 0 )
930
987
goto fail ;
931
988
932
- rec_allowed = is_rec_allowed_from_qdb (u );
989
+ rec_allowed = is_rec_allowed_from_qdb (u );
933
990
if (rec_allowed >= 0 ) {
934
991
pacat_log ("mic allowed: initial value read from Qubes DB '%d'" , rec_allowed );
935
992
u -> rec_allowed = rec_allowed ;
0 commit comments