Skip to main content

Cricket Streaming MAUI App: Complete Solution

This document outlines a comprehensive solution for building a cricket streaming MAUI app with subscription plans, geo-restrictions, and preview functionality. Let's break this down into manageable steps.

System Architecture Overview

text
Frontend: .NET MAUI App
Backend: ASP.NET Core Web API
Database: SQL Server (or PostgreSQL)
Streaming: HLS/DASH via Wowza/FFmpeg
Auth: JWT Tokens
Geo-Restriction: IP Geolocation
Payment: Stripe/Razorpay integration

Step 1: Setup Development Environment

Prerequisites

  • Visual Studio 2022 (with .NET MAUI workload)

  • .NET 7/8 SDK

  • SQL Server (or PostgreSQL)

  • FFmpeg (for streaming processing)

Step 2: Database Design

sql
-- Users table
CREATE TABLE Users (
    Id INT PRIMARY KEY IDENTITY,
    Username NVARCHAR(50) UNIQUE NOT NULL,
    Email NVARCHAR(100) UNIQUE NOT NULL,
    PasswordHash NVARCHAR(255) NOT NULL,
    Salt NVARCHAR(100) NOT NULL,
    CreatedAt DATETIME DEFAULT GETDATE(),
    LastLogin DATETIME,
    DeviceId NVARCHAR(255),
    IPAddress NVARCHAR(50)
);

-- Subscription Plans
CREATE TABLE SubscriptionPlans (
    Id INT PRIMARY KEY IDENTITY,
    Name NVARCHAR(50) NOT NULL,
    Description NVARCHAR(255),
    Price DECIMAL(10,2) NOT NULL,
    VideoQuality NVARCHAR(20) NOT NULL, -- 480p, 720p, 1080p
    IsActive BIT DEFAULT 1
);

-- User Subscriptions
CREATE TABLE UserSubscriptions (
    Id INT PRIMARY KEY IDENTITY,
    UserId INT FOREIGN KEY REFERENCES Users(Id),
    PlanId INT FOREIGN KEY REFERENCES SubscriptionPlans(Id),
    StartDate DATETIME NOT NULL,
    EndDate DATETIME NOT NULL,
    PaymentTransactionId NVARCHAR(255),
    IsActive BIT DEFAULT 1
);

-- Preview Access
CREATE TABLE PreviewAccess (
    Id INT PRIMARY KEY IDENTITY,
    DeviceId NVARCHAR(255) NOT NULL,
    IPAddress NVARCHAR(50) NOT NULL,
    LastAccess DATETIME NOT NULL,
    AccessCount INT DEFAULT 1
);

-- Live Matches
CREATE TABLE LiveMatches (
    Id INT PRIMARY KEY IDENTITY,
    Title NVARCHAR(100) NOT NULL,
    Description NVARCHAR(255),
    StartTime DATETIME NOT NULL,
    EndTime DATETIME,
    StreamUrl NVARCHAR(255) NOT NULL,
    ThumbnailUrl NVARCHAR(255),
    IsActive BIT DEFAULT 1
);

Step 3: Backend API Development

Create ASP.NET Core Web API Project

  1. Set up controllers:

    • AuthController (Login/Register)

    • SubscriptionController

    • StreamingController

    • MatchController

AuthController.cs

csharp
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IAuthService _authService;
    
    public AuthController(IAuthService authService)
    {
        _authService = authService;
    }
    
    [HttpPost("register")]
    public async Task<IActionResult> Register(RegisterDto registerDto)
    {
        var result = await _authService.Register(registerDto);
        if (!result.Success)
            return BadRequest(result);
            
        return Ok(result);
    }
    
    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginDto loginDto)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
        var deviceId = Request.Headers["Device-Id"].ToString();
        
        var result = await _authService.Login(loginDto, ipAddress, deviceId);
        if (!result.Success)
            return Unauthorized(result);
            
        return Ok(result);
    }
}

StreamingController.cs (with Geo-Restriction)

csharp
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class StreamingController : ControllerBase
{
    private readonly IStreamingService _streamingService;
    private readonly IGeoRestrictionService _geoService;
    
    public StreamingController(IStreamingService streamingService, IGeoRestrictionService geoService)
    {
        _streamingService = streamingService;
        _geoService = geoService;
    }
    
    [HttpGet("stream/{matchId}")]
    public async Task<IActionResult> GetStream(int matchId)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
        
        // Check if IP is from India
        if (!await _geoService.IsAccessAllowed(ipAddress))
        {
            return Forbid("Streaming is only available in India");
        }
        
        var userId = int.Parse(User.FindFirst(ClaimTypes.NameIdentifier).Value);
        var streamInfo = await _streamingService.GetStreamUrl(matchId, userId);
        
        if (!streamInfo.Success)
            return BadRequest(streamInfo);
            
        return Ok(streamInfo);
    }
    
    [HttpGet("preview/{matchId}")]
    public async Task<IActionResult> GetPreview(int matchId)
    {
        var ipAddress = HttpContext.Connection.RemoteIpAddress?.ToString();
        var deviceId = Request.Headers["Device-Id"].ToString();
        
        // Check if IP is from India
        if (!await _geoService.IsAccessAllowed(ipAddress))
        {
            return Forbid("Streaming is only available in India");
        }
        
        var previewInfo = await _streamingService.GetPreviewStream(matchId, ipAddress, deviceId);
        
        if (!previewInfo.Success)
            return BadRequest(previewInfo);
            
        return Ok(previewInfo);
    }
}

GeoRestrictionService.cs

csharp
public class GeoRestrictionService : IGeoRestrictionService
{
    private readonly HttpClient _httpClient;
    private readonly IConfiguration _config;
    
    public GeoRestrictionService(HttpClient httpClient, IConfiguration config)
    {
        _httpClient = httpClient;
        _config = config;
    }
    
    public async Task<bool> IsAccessAllowed(string ipAddress)
    {
        try
        {
            // Use IP geolocation service (e.g., ipapi.co, ipgeolocation.io)
            var apiKey = _config["GeoLocation:ApiKey"];
            var response = await _httpClient.GetFromJsonAsync<IpGeoLocation>($"http://api.ipapi.com/{ipAddress}?access_key={apiKey}");
            
            return response?.CountryCode == "IN";
        }
        catch
        {
            // Fallback - deny access if geolocation fails
            return false;
        }
    }
}

Step 4: MAUI Frontend Development

App Structure

  1. Views:

    • LoginPage.xaml

    • RegisterPage.xaml

    • DashboardPage.xaml

    • MatchDetailPage.xaml

    • VideoPlayerPage.xaml

    • SubscriptionPage.xaml

    • ProfilePage.xaml

  2. ViewModels for each page

  3. Services:

    • AuthService

    • StreamingService

    • SubscriptionService

AuthService.cs (MAUI)

csharp
public class AuthService : IAuthService
{
    private readonly HttpClient _httpClient;
    private readonly IDeviceInfo _deviceInfo;
    
    public AuthService(HttpClient httpClient, IDeviceInfo deviceInfo)
    {
        _httpClient = httpClient;
        _deviceInfo = deviceInfo;
    }
    
    public async Task<AuthResponse> Login(string username, string password)
    {
        try
        {
            var deviceId = _deviceInfo.Idiom.ToString() + _deviceInfo.Model;
            var request = new LoginRequest 
            { 
                Username = username, 
                Password = password,
                DeviceId = deviceId
            };
            
            var response = await _httpClient.PostAsJsonAsync("api/auth/login", request);
            
            if (response.IsSuccessStatusCode)
            {
                var authResponse = await response.Content.ReadFromJsonAsync<AuthResponse>();
                // Store token securely
                await SecureStorage.SetAsync("jwt_token", authResponse.Token);
                return authResponse;
            }
            
            var error = await response.Content.ReadFromJsonAsync<AuthResponse>();
            return error;
        }
        catch (Exception ex)
        {
            return new AuthResponse { Success = false, Message = ex.Message };
        }
    }
    
    // Similar implementation for Register
}

VideoPlayerPage.xaml

xml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CricketStreaming.VideoPlayerPage"
             Title="Live Streaming">
    
    <Grid>
        <MediaElement x:Name="videoPlayer"
                      ShouldShowPlaybackControls="True"
                      ShouldAutoPlay="True"
                      Aspect="AspectFit" />
        
        <ActivityIndicator x:Name="loadingIndicator"
                          IsRunning="True"
                          IsVisible="False"
                          VerticalOptions="Center"
                          HorizontalOptions="Center" />
        
        <Label x:Name="errorLabel"
               Text="Error loading stream"
               IsVisible="False"
               HorizontalOptions="Center"
               VerticalOptions="Center" />
    </Grid>
</ContentPage>

VideoPlayerPage.xaml.cs

csharp
public partial class VideoPlayerPage : ContentPage
{
    private readonly IStreamingService _streamingService;
    private int _matchId;
    
    public VideoPlayerPage(IStreamingService streamingService, int matchId)
    {
        InitializeComponent();
        _streamingService = streamingService;
        _matchId = matchId;
        
        LoadStream();
    }
    
    private async void LoadStream()
    {
        loadingIndicator.IsVisible = true;
        
        var result = await _streamingService.GetStreamUrl(_matchId);
        
        if (result.Success)
        {
            videoPlayer.Source = result.StreamUrl;
            videoPlayer.IsVisible = true;
        }
        else
        {
            errorLabel.Text = result.Message;
            errorLabel.IsVisible = true;
        }
        
        loadingIndicator.IsVisible = false;
    }
}

Step 5: Streaming Infrastructure

Option 1: Using Wowza Streaming Engine

  1. Set up Wowza Streaming Engine on a cloud server

  2. Configure live streams for cricket matches

  3. Create different renditions (480p, 720p, 1080p)

  4. Set up HLS/DASH output

Option 2: FFmpeg-based Solution (Open Source)

bash
# Sample FFmpeg command to create multi-bitrate HLS streams
ffmpeg -i input_stream -map 0:v:0 -map 0:a:0 \
-c:v libx264 -crf 22 -preset veryfast -g 60 -sc_threshold 0 \
-b:v:0 800k -maxrate:0 856k -bufsize:0 1200k -filter:v:0 "scale=-2:480" \
-b:v:1 1200k -maxrate:1 1280k -bufsize:1 1600k -filter:v:1 "scale=-2:720" \
-b:v:2 2400k -maxrate:2 2560k -bufsize:2 3200k -filter:v:2 "scale=-2:1080" \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
-f hls -hls_time 6 -hls_list_size 0 -hls_playlist_type event \
-hls_segment_filename "stream_%v/segment%d.ts" \
-master_pl_name master.m3u8 \
"stream_%v/index.m3u8"

Sample Streaming Service

csharp
public class StreamingService : IStreamingService
{
    private readonly HttpClient _httpClient;
    private readonly ISubscriptionService _subscriptionService;
    
    public StreamingService(HttpClient httpClient, ISubscriptionService subscriptionService)
    {
        _httpClient = httpClient;
        _subscriptionService = subscriptionService;
    }
    
    public async Task<StreamResponse> GetStreamUrl(int matchId)
    {
        try
        {
            // Check user's subscription
            var subscription = await _subscriptionService.GetCurrentSubscription();
            
            if (subscription == null || !subscription.IsActive)
            {
                return new StreamResponse 
                { 
                    Success = false, 
                    Message = "You need an active subscription to watch this stream" 
                };
            }
            
            // Get match stream info from API
            var response = await _httpClient.GetAsync($"api/streaming/stream/{matchId}");
            
            if (response.IsSuccessStatusCode)
            {
                var streamInfo = await response.Content.ReadFromJsonAsync<StreamInfo>();
                
                // Select appropriate stream based on subscription
                string streamUrl = subscription.Plan.VideoQuality switch
                {
                    "480p" => streamInfo.Stream480pUrl,
                    "720p" => streamInfo.Stream720pUrl,
                    "1080p" => streamInfo.Stream1080pUrl,
                    _ => streamInfo.Stream480pUrl
                };
                
                return new StreamResponse 
                { 
                    Success = true, 
                    StreamUrl = streamUrl 
                };
            }
            
            var error = await response.Content.ReadFromJsonAsync<StreamResponse>();
            return error;
        }
        catch (Exception ex)
        {
            return new StreamResponse { Success = false, Message = ex.Message };
        }
    }
    
    public async Task<StreamResponse> GetPreviewStream(int matchId)
    {
        // Similar implementation with 5-minute preview logic
    }
}

Step 6: Subscription and Payment Integration

SubscriptionPage.xaml

xml
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CricketStreaming.SubscriptionPage"
             Title="Subscription Plans">
    
    <ScrollView>
        <StackLayout Padding="20" Spacing="20">
            <Label Text="Choose Your Plan" FontSize="Title" HorizontalOptions="Center" />
            
            <!-- Plan A -->
            <Frame BorderColor="LightGray" CornerRadius="10" Padding="15">
                <StackLayout Spacing="10">
                    <Label Text="Plan A" FontSize="Subtitle" FontAttributes="Bold" />
                    <Label Text="480p Streaming Quality" />
                    <Label Text="₹199/month" FontAttributes="Bold" />
                    <Button Text="Subscribe" 
                            BackgroundColor="#4CAF50"
                            TextColor="White"
                            Command="{Binding SubscribeCommand}"
                            CommandParameter="1" />
                </StackLayout>
            </Frame>
            
            <!-- Plan B -->
            <Frame BorderColor="LightGray" CornerRadius="10" Padding="15">
                <StackLayout Spacing="10">
                    <Label Text="Plan B" FontSize="Subtitle" FontAttributes="Bold" />
                    <Label Text="720p Streaming Quality" />
                    <Label Text="₹399/month" FontAttributes="Bold" />
                    <Button Text="Subscribe" 
                            BackgroundColor="#2196F3"
                            TextColor="White"
                            Command="{Binding SubscribeCommand}"
                            CommandParameter="2" />
                </StackLayout>
            </Frame>
            
            <!-- Plan C -->
            <Frame BorderColor="LightGray" CornerRadius="10" Padding="15">
                <StackLayout Spacing="10">
                    <Label Text="Plan C" FontSize="Subtitle" FontAttributes="Bold" />
                    <Label Text="1080p Streaming Quality" />
                    <Label Text="₹699/month" FontAttributes="Bold" />
                    <Button Text="Subscribe" 
                            BackgroundColor="#9C27B0"
                            TextColor="White"
                            Command="{Binding SubscribeCommand}"
                            CommandParameter="3" />
                </StackLayout>
            </Frame>
        </StackLayout>
    </ScrollView>
</ContentPage>

Payment Integration (Razorpay for India)

csharp
public class PaymentService : IPaymentService
{
    private readonly HttpClient _httpClient;
    
    public PaymentService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }
    
    public async Task<PaymentResponse> CreateOrder(decimal amount, string currency, string receipt)
    {
        try
        {
            var request = new 
            {
                amount = amount * 100, // Razorpay expects amount in paise
                currency,
                receipt,
                payment_capture = 1
            };
            
            var response = await _httpClient.PostAsJsonAsync("api/payment/createorder", request);
            
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadFromJsonAsync<PaymentResponse>();
            }
            
            var error = await response.Content.ReadFromJsonAsync<PaymentResponse>();
            return error;
        }
        catch (Exception ex)
        {
            return new PaymentResponse { Success = false, Message = ex.Message };
        }
    }
    
    public async Task<PaymentResponse> VerifyPayment(string paymentId, string orderId, string signature)
    {
        // Implementation to verify payment with backend
    }
}

Step 7: Deployment

Backend Deployment (Azure/AWS)

  1. Set up Azure App Service or AWS Elastic Beanstalk

  2. Configure SQL Database

  3. Set up Wowza/FFmpeg streaming server

  4. Configure CDN for streaming content

MAUI App Deployment

  1. Android:

    • Generate signed APK/AAB

    • Upload to Google Play Store

  2. iOS:

    • Archive in Visual Studio

    • Distribute via App Store Connect

Step 8: Testing

  1. Test all user flows:

    • Registration/Login

    • Subscription purchase

    • Geo-restriction (use VPN to test)

    • Preview functionality

    • Different quality streams based on plan

  2. Test on multiple devices (Android/iOS)

Final Notes

  1. For a production app, consider:

    • DRM protection for streams

    • More robust geo-blocking

    • Better error handling and logging

    • Analytics integration

    • Push notifications for match alerts

  2. For the sample project, you can use:

    • Mock streaming URLs (public HLS streams)

    • Simulated payment gateway

    • Local database for testing

This comprehensive solution provides all the components needed to build a cricket streaming app with the specified requirements. The implementation can be adjusted based on specific needs and available resources.

Comments

Popular posts from this blog

Play Audio in Xamarin Forms using Xam.Plugin.SimpleAudioPlayer

A light-weight and easy to use cross-platform audio player for Windows UWP, Xamarin.iOS, Xamarin.Android, Xamarin.Mac, Xamarin.tvOS and Xamarin.Forms. Load wav and mp3 files from any location including a shared library. Works well for sound effects or music. Multiple instances can be instantiated to play multiple sources simultaniously. Install-Package Xam.Plugin.SimpleAudioPlayer -Version 1.1.0 Dependencies ·          .NETPlatform 5.0 No dependencies. ·          .NETStandard 1.0 NETStandard.Library (>= 1.6.1) ·          MonoAndroid 1.0 No dependencies. ·          Tizen 0.0 No dependencies. ·          UAP 0.0 No dependencies. ·          Xamarin.iOS 1.0 No dependencie...

How To Check Network Connectivity In Xamarin.Forms

Introduction Network connectivity refers to the internet connectivity of a device,  which can be either mobile data or Wi-Fi. In Xamarin.Forms, we are creating cross platform apps, so the different platforms have different implementations. In this blog, we will learn how to check internet connectivity in Xamarin.Forms apps. Solution To check the internet connection in Xamarin.Forms app, we need to follow the steps given below. Step 1 Go to your solution and on your Portable project, right click and select Manage NuGet Packages. Step 2 Search and install Xam.Plugin.Connectivity NuGet Package. Step 3 Write the lines of code given below to check the internet connectivity. if  (CrossConnectivity.Current.IsConnected) {        // your logic...    }  else  {        // write your code if there is no Internet...

All About .NET MAUI

  What’s .NET MAUI? .NET MAUI (.NET Multi-platform App UI) is a framework for building modern, multi-platform, natively compiled iOS, Android, macOS, Windows and apps using C# and XAML in a single codebase. With  .NET MAUI , you can build native applications from a single codebase for Android, iOS, macOS, Windows and Tizen which is backed by Samsung. If you’ve used Xamarin Forms,  MAUI will be very familiar  to you. Instead of different projects for desktop, mobile, and each OS, all your code is in a single project. .NET MAUI also provides  hosting Blazor in MAUI , with embedded web view controls to run Razor components natively on your target device. The decoupled UI and single project enables you to stay focused on one application instead of juggling the unique needs of multiple platforms. It’s the new baby of Microsoft and I know there will be more libraries and the broader ecosystem come alongside .NET MAUI in the following months. Rich Components UI compone...