From 041fceca56093ba327e45e3c302728edaf7240d7 Mon Sep 17 00:00:00 2001 From: JPasterkampRotec Date: Fri, 14 Feb 2025 11:36:47 +0100 Subject: [PATCH 1/3] Created editorconfig with spaces as indent style Most visual studio installations default to tabs, while the original creator of this solution seems to use spaces. By adding this editor config everyone will automatically use spaces in this solution. --- src/.editorconfig | 321 ++++++++++++++++++++++++++++++++++++++ src/Nager.VideoStream.sln | 9 +- 2 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 src/.editorconfig diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 0000000..c8fe7df --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,321 @@ +# Remove the line below if you want to inherit .editorconfig settings from higher directories +root = true + +# C# files +[*.{cs,vb}] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Code Actions #### + +# Type members +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = at_the_end +dotnet_property_generation_behavior = prefer_auto_properties + +# Symbol search +dotnet_search_reference_assemblies = true + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = false + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = when_multiline:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_top_level_statements = true:silent + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace:silent + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = warning +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = warning +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.struct_should_be_pascal_case.severity = warning +dotnet_naming_rule.struct_should_be_pascal_case.symbols = struct +dotnet_naming_rule.struct_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.enum_should_be_pascal_case.severity = warning +dotnet_naming_rule.enum_should_be_pascal_case.symbols = enum +dotnet_naming_rule.enum_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.const_field_should_be_uppercase_and_underscores.severity = suggestion +dotnet_naming_rule.const_field_should_be_uppercase_and_underscores.symbols = const_field +dotnet_naming_rule.const_field_should_be_uppercase_and_underscores.style = uppercase_and_underscores + +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be__fieldname.style = _fieldname + +dotnet_naming_rule.private_or_internal_static_field_should_be__fieldname.severity = suggestion +dotnet_naming_rule.private_or_internal_static_field_should_be__fieldname.symbols = private_or_internal_static_field +dotnet_naming_rule.private_or_internal_static_field_should_be__fieldname.style = _fieldname + +dotnet_naming_rule.private_or_internal_static_readonly_field_should_be_uppercase_and_underscores.severity = suggestion +dotnet_naming_rule.private_or_internal_static_readonly_field_should_be_uppercase_and_underscores.symbols = private_or_internal_static_readonly_field +dotnet_naming_rule.private_or_internal_static_readonly_field_should_be_uppercase_and_underscores.style = uppercase_and_underscores + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.struct.applicable_kinds = struct +dotnet_naming_symbols.struct.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.struct.required_modifiers = + +dotnet_naming_symbols.enum.applicable_kinds = enum +dotnet_naming_symbols.enum.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.enum.required_modifiers = + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = + +dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = static + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +dotnet_naming_symbols.const_field.applicable_kinds = field +dotnet_naming_symbols.const_field.applicable_accessibilities = * +dotnet_naming_symbols.const_field.required_modifiers = const + +dotnet_naming_symbols.private_or_internal_static_readonly_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_static_readonly_field.applicable_accessibilities = internal, private +dotnet_naming_symbols.private_or_internal_static_readonly_field.required_modifiers = readonly, static + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +dotnet_naming_style._fieldname.required_prefix = _ +dotnet_naming_style._fieldname.required_suffix = +dotnet_naming_style._fieldname.word_separator = +dotnet_naming_style._fieldname.capitalization = camel_case + +dotnet_naming_style.uppercase_and_underscores.required_prefix = +dotnet_naming_style.uppercase_and_underscores.required_suffix = +dotnet_naming_style.uppercase_and_underscores.word_separator = _ +dotnet_naming_style.uppercase_and_underscores.capitalization = all_upper + +[*.cs] +csharp_indent_labels = one_less_than_current +csharp_using_directive_placement = outside_namespace:silent +csharp_prefer_simple_using_statement = true:suggestion +csharp_prefer_braces = when_multiline:silent +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent \ No newline at end of file diff --git a/src/Nager.VideoStream.sln b/src/Nager.VideoStream.sln index e233b44..942ffdd 100644 --- a/src/Nager.VideoStream.sln +++ b/src/Nager.VideoStream.sln @@ -1,12 +1,17 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29728.190 +# Visual Studio Version 17 +VisualStudioVersion = 17.12.35728.132 d17.12 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nager.VideoStream", "Nager.VideoStream\Nager.VideoStream.csproj", "{D6D2E338-0FAE-4B15-98D7-6B70BD4B91A0}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nager.VideoStream.TestConsole", "Nager.VideoStream.TestConsole\Nager.VideoStream.TestConsole.csproj", "{F02C8810-0CDB-401B-A701-2686F0EBB1E0}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{60CF3E99-1F18-437F-9FFB-9CEFDC37824A}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 385024eb29ffa3e9ba059e5de052866e77e02763 Mon Sep 17 00:00:00 2001 From: JPasterkampRotec Date: Fri, 14 Feb 2025 11:39:59 +0100 Subject: [PATCH 2/3] Passing cancellation token into Stream.ReadAsync This does not cancel the reading in most cases sadly, but it is good practice to always pass in cancellation tokens. --- src/Nager.VideoStream/VideoStreamClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Nager.VideoStream/VideoStreamClient.cs b/src/Nager.VideoStream/VideoStreamClient.cs index da29c77..577c654 100644 --- a/src/Nager.VideoStream/VideoStreamClient.cs +++ b/src/Nager.VideoStream/VideoStreamClient.cs @@ -69,7 +69,7 @@ public async Task StartFrameReaderAsync( while (!cancellationToken.IsCancellationRequested) { - var length = await frameOutputStream.ReadAsync(buffer, 0, buffer.Length); + var length = await frameOutputStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken); if (length == 0) { break; From 1cdfaf36fed3832d500c91ceaac35c385b348018 Mon Sep 17 00:00:00 2001 From: JPasterkampRotec Date: Fri, 14 Feb 2025 11:50:38 +0100 Subject: [PATCH 3/3] Killing the ffmpeg process if it will not cancel within 1 second Since the cancellation token does not stop the Stream.ReadAsync when the inputsource is offline, we manually kill the ffmpeg process after 1 second of lenience. This causes the ReadAsync method to return 0 bytes, allowing the stream to end. --- src/Nager.VideoStream/VideoStreamClient.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Nager.VideoStream/VideoStreamClient.cs b/src/Nager.VideoStream/VideoStreamClient.cs index 577c654..71631be 100644 --- a/src/Nager.VideoStream/VideoStreamClient.cs +++ b/src/Nager.VideoStream/VideoStreamClient.cs @@ -54,6 +54,7 @@ public async Task StartFrameReaderAsync( }; using (var ffmpegProcess = new Process { StartInfo = startInfo }) + using (cancellationToken.Register(() => WaitBeforeKill(ffmpegProcess))) { ffmpegProcess.ErrorDataReceived += this.ProcessDataReceived; @@ -99,12 +100,7 @@ public async Task StartFrameReaderAsync( ffmpegProcess.ErrorDataReceived -= this.ProcessDataReceived; - ffmpegProcess.WaitForExit(1000); - - if (!ffmpegProcess.HasExited) - { - ffmpegProcess.Kill(); - } + WaitBeforeKill(ffmpegProcess); } } @@ -112,5 +108,15 @@ private void ProcessDataReceived(object sender, DataReceivedEventArgs e) { this.FFmpegInfoReceived?.Invoke(e.Data); } + + private static void WaitBeforeKill(Process process, int waitForExitMilliseconds = 1000) + { + process.WaitForExit(waitForExitMilliseconds); + + if (!process.HasExited) + { + process.Kill(); + } + } } }