Saturday, 2 August 2025

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.

No comments:

Post a Comment

Complete Guide: Building a Live Cricket Streaming App for 100M Users

Comprehensive guide to building a scalable live cricket streaming platform for 100M users, covering backend infrastructure, streaming techno...