Recherche avancée

Médias (91)

Autres articles (103)

  • Script d’installation automatique de MediaSPIP

    25 avril 2011, par

    Afin de palier aux difficultés d’installation dues principalement aux dépendances logicielles coté serveur, un script d’installation "tout en un" en bash a été créé afin de faciliter cette étape sur un serveur doté d’une distribution Linux compatible.
    Vous devez bénéficier d’un accès SSH à votre serveur et d’un compte "root" afin de l’utiliser, ce qui permettra d’installer les dépendances. Contactez votre hébergeur si vous ne disposez pas de cela.
    La documentation de l’utilisation du script d’installation (...)

  • La gestion des forums

    3 novembre 2011, par

    Si les forums sont activés sur le site, les administrateurs ont la possibilité de les gérer depuis l’interface d’administration ou depuis l’article même dans le bloc de modification de l’article qui se trouve dans la navigation de la page.
    Accès à l’interface de modération des messages
    Lorsqu’il est identifié sur le site, l’administrateur peut procéder de deux manières pour gérer les forums.
    S’il souhaite modifier (modérer, déclarer comme SPAM un message) les forums d’un article particulier, il a à sa (...)

  • Les autorisations surchargées par les plugins

    27 avril 2010, par

    Mediaspip core
    autoriser_auteur_modifier() afin que les visiteurs soient capables de modifier leurs informations sur la page d’auteurs

Sur d’autres sites (9036)

  • How to install ffmpeg on a Windows Dockerhub image ?

    18 janvier, par Youssef Kharoufi

    I have a program that executes a ffmpeg command to a video input, copies this video and pastes is in an output directory.

    


    Here is my code in case you would want to duplicate it :

    


        using Renderer.Models;&#xA;using System;&#xA;using System.IO;&#xA;using System.Text.Json;&#xA;using System.Threading.Tasks;&#xA;&#xA;public class Program&#xA;{&#xA;    public static async Task Main(string[] args)&#xA;    {&#xA;        var result = new DockerTaskResult();&#xA;        try&#xA;        {&#xA;            // Path to the JSON input file&#xA;            string jsonInputPath = @"C:\Users\ykharoufi\source\repos\Renderer\Renderer\Json\task.json";&#xA;&#xA;            // Check if the JSON file exists&#xA;            if (!File.Exists(jsonInputPath))&#xA;            {&#xA;                throw new FileNotFoundException($"JSON input file not found at path: {jsonInputPath}");&#xA;            }&#xA;&#xA;            // Read the JSON content&#xA;            string jsonContent = File.ReadAllText(jsonInputPath);&#xA;&#xA;            try&#xA;            {&#xA;                // Deserialize the JSON into a DockerTask object&#xA;                DockerTask task = JsonSerializer.Deserialize<dockertask>(jsonContent);&#xA;                if (task == null)&#xA;                {&#xA;                    throw new Exception("Failed to deserialize the task from JSON.");&#xA;                }&#xA;&#xA;                // Validate the input paths&#xA;                if (string.IsNullOrEmpty(task.InputFileRepoPath) || !File.Exists(task.InputFileRepoPath))&#xA;                {&#xA;                    throw new Exception($"Input file path is invalid or does not exist: {task.InputFileRepoPath}");&#xA;                }&#xA;&#xA;                if (string.IsNullOrEmpty(task.OutputFileRepoPath) || !Directory.Exists(task.OutputFileRepoPath))&#xA;                {&#xA;                    throw new Exception($"Output directory path is invalid or does not exist: {task.OutputFileRepoPath}");&#xA;                }&#xA;&#xA;                // Initialize the Docker worker and run the task&#xA;                var worker = new DockerWorker();&#xA;                var success = await worker.RunDockerTaskAsync(task);&#xA;&#xA;                if (success.Success)&#xA;                {&#xA;                    result.Success = true;&#xA;                    result.Message = "Command executed successfully!";&#xA;&#xA;                    // Check the output directory for files&#xA;                    if (Directory.Exists(task.OutputFileRepoPath))&#xA;                    {&#xA;                        result.OutputFiles = Directory.GetFiles(task.OutputFileRepoPath);&#xA;                    }&#xA;                }&#xA;                else&#xA;                {&#xA;                    result.Success = false;&#xA;                    result.Message = "Failed to execute the command.";&#xA;                    result.ErrorDetails = success.ErrorDetails;&#xA;                }&#xA;            }&#xA;            catch (JsonException)&#xA;            {&#xA;                // Handle invalid JSON format&#xA;                result.Success = false;&#xA;                result.Message = "Invalid JSON format.";&#xA;                result.ErrorDetails = "Invalid data entry";&#xA;            }&#xA;        }&#xA;        catch (Exception ex)&#xA;        {&#xA;            result.Success = false;&#xA;            result.Message = "An error occurred during execution.";&#xA;            result.ErrorDetails = ex.Message;&#xA;        }&#xA;        finally&#xA;        {&#xA;            // Serialize the result to JSON and write to console&#xA;            string outputJson = JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true });&#xA;            Console.WriteLine(outputJson);&#xA;        }&#xA;    }&#xA;}&#xA;&#xA;    using System;&#xA;using System.Collections.Generic;&#xA;using System.IO;&#xA;using System.Text.Json;&#xA;using System.Threading.Tasks;&#xA;using Docker.DotNet;&#xA;using Docker.DotNet.Models;&#xA;using Renderer.Models;&#xA;&#xA;public class DockerWorker&#xA;{&#xA;    private readonly DockerClient _dockerClient;&#xA;&#xA;    public DockerWorker()&#xA;    {&#xA;        Console.WriteLine("Initializing Docker client...");&#xA;        _dockerClient = new DockerClientConfiguration(&#xA;            new Uri("npipe://./pipe/docker_engine")) // Windows Docker URI&#xA;            .CreateClient();&#xA;        Console.WriteLine("Docker client initialized.");&#xA;    }&#xA;&#xA;    public async Task<dockertaskresult> RunDockerTaskAsync(DockerTask task)&#xA;    {&#xA;        var result = new DockerTaskResult();&#xA;&#xA;        try&#xA;        {&#xA;            Console.WriteLine("Starting Docker task...");&#xA;            Console.WriteLine($"Image: {task.ImageNaming}");&#xA;            Console.WriteLine($"Container: {task.ContainerNaming}");&#xA;            Console.WriteLine($"Input Path: {task.InputFileRepoPath}");&#xA;            Console.WriteLine($"Output Path: {task.OutputFileRepoPath}");&#xA;            Console.WriteLine($"Command: {task.CommandDef}");&#xA;&#xA;            // Ensure the Docker image exists&#xA;            Console.WriteLine("Checking if Docker image exists...");&#xA;            var imageCheckResult = await EnsureImageExists(task.ImageNaming);&#xA;            if (!imageCheckResult.Success)&#xA;            {&#xA;                result.Success = false;&#xA;                result.Message = imageCheckResult.Message;&#xA;                result.ErrorDetails = imageCheckResult.ErrorDetails;&#xA;                return result;&#xA;            }&#xA;            Console.WriteLine("Docker image verified.");&#xA;&#xA;            // Determine platform&#xA;            var platform = await GetImagePlatform(task.ImageNaming);&#xA;            Console.WriteLine($"Detected image platform: {platform}");&#xA;&#xA;            // Translate paths&#xA;            string inputVolume, outputVolume;&#xA;            if (platform == "linux")&#xA;            {&#xA;                inputVolume = $"{task.InputFileRepoPath.Replace("C:\\", "/mnt/c/").Replace("\\", "/")}:/app/input";&#xA;                outputVolume = $"{task.OutputFileRepoPath.Replace("C:\\", "/mnt/c/").Replace("\\", "/")}:/app/output";&#xA;            }&#xA;            else if (platform == "windows")&#xA;            {&#xA;                string inputDir = Path.GetFullPath(Path.GetDirectoryName(task.InputFileRepoPath)).TrimEnd(&#x27;\\&#x27;);&#xA;                string outputDir = Path.GetFullPath(task.OutputFileRepoPath).TrimEnd(&#x27;\\&#x27;);&#xA;&#xA;                if (!Directory.Exists(inputDir))&#xA;                {&#xA;                    throw new Exception($"Input directory does not exist: {inputDir}");&#xA;                }&#xA;                if (!Directory.Exists(outputDir))&#xA;                {&#xA;                    throw new Exception($"Output directory does not exist: {outputDir}");&#xA;                }&#xA;&#xA;                inputVolume = $"{inputDir}:C:\\app\\input";&#xA;                outputVolume = $"{outputDir}:C:\\app\\output";&#xA;&#xA;                Console.WriteLine($"Input volume: {inputVolume}");&#xA;                Console.WriteLine($"Output volume: {outputVolume}");&#xA;            }&#xA;            else&#xA;            {&#xA;                throw new Exception($"Unsupported platform: {platform}");&#xA;            }&#xA;&#xA;            // Create container&#xA;            Console.WriteLine("Creating Docker container...");&#xA;            var containerResponse = await _dockerClient.Containers.CreateContainerAsync(new CreateContainerParameters&#xA;            {&#xA;                Image = task.ImageNaming,&#xA;                Name = task.ContainerNaming,&#xA;                HostConfig = new HostConfig&#xA;                {&#xA;                    Binds = new List<string> { inputVolume, outputVolume },&#xA;                    AutoRemove = true&#xA;                },&#xA;                Cmd = new[] { platform == "linux" ? "bash" : "cmd.exe", "/c", task.CommandDef }&#xA;            });&#xA;&#xA;            if (string.IsNullOrEmpty(containerResponse.ID))&#xA;            {&#xA;                throw new Exception("Failed to create Docker container.");&#xA;            }&#xA;            Console.WriteLine($"Container created with ID: {containerResponse.ID}");&#xA;&#xA;            // Start container&#xA;            Console.WriteLine("Starting container...");&#xA;            bool started = await _dockerClient.Containers.StartContainerAsync(containerResponse.ID, new ContainerStartParameters());&#xA;            if (!started)&#xA;            {&#xA;                throw new Exception($"Failed to start container: {task.ContainerNaming}");&#xA;            }&#xA;&#xA;            // Wait for container to finish&#xA;            Console.WriteLine("Waiting for container to finish...");&#xA;            var waitResponse = await _dockerClient.Containers.WaitContainerAsync(containerResponse.ID);&#xA;&#xA;            if (waitResponse.StatusCode != 0)&#xA;            {&#xA;                Console.WriteLine($"Container exited with error code: {waitResponse.StatusCode}");&#xA;                await FetchContainerLogs(containerResponse.ID);&#xA;                throw new Exception($"Container exited with non-zero status code: {waitResponse.StatusCode}");&#xA;            }&#xA;&#xA;            // Fetch logs&#xA;            Console.WriteLine("Fetching container logs...");&#xA;            await FetchContainerLogs(containerResponse.ID);&#xA;&#xA;            result.Success = true;&#xA;            result.Message = "Docker task completed successfully.";&#xA;            return result;&#xA;        }&#xA;        catch (Exception ex)&#xA;        {&#xA;            Console.WriteLine($"Error: {ex.Message}");&#xA;            result.Success = false;&#xA;            result.Message = "An error occurred during execution.";&#xA;            result.ErrorDetails = ex.Message;&#xA;            return result;&#xA;        }&#xA;    }&#xA;&#xA;    private async Task<string> GetImagePlatform(string imageName)&#xA;    {&#xA;        try&#xA;        {&#xA;            Console.WriteLine($"Inspecting Docker image: {imageName}...");&#xA;            var imageDetails = await _dockerClient.Images.InspectImageAsync(imageName);&#xA;            Console.WriteLine($"Image platform: {imageDetails.Os}");&#xA;            return imageDetails.Os.ToLower();&#xA;        }&#xA;        catch (Exception ex)&#xA;        {&#xA;            throw new Exception($"Failed to inspect image: {ex.Message}");&#xA;        }&#xA;    }&#xA;&#xA;    private async Task<dockertaskresult> EnsureImageExists(string imageName)&#xA;    {&#xA;        var result = new DockerTaskResult();&#xA;        try&#xA;        {&#xA;            Console.WriteLine($"Pulling Docker image: {imageName}...");&#xA;            await _dockerClient.Images.CreateImageAsync(&#xA;                new ImagesCreateParameters { FromImage = imageName, Tag = "latest" },&#xA;                null,&#xA;                new Progress<jsonmessage>()&#xA;            );&#xA;            result.Success = true;&#xA;            result.Message = "Docker image is available.";&#xA;        }&#xA;        catch (Exception ex)&#xA;        {&#xA;            result.Success = false;&#xA;            result.Message = $"Failed to pull Docker image: {imageName}";&#xA;            result.ErrorDetails = ex.Message;&#xA;        }&#xA;        return result;&#xA;    }&#xA;&#xA;    private async Task FetchContainerLogs(string containerId)&#xA;    {&#xA;        try&#xA;        {&#xA;            Console.WriteLine($"Fetching logs for container: {containerId}...");&#xA;            var logs = await _dockerClient.Containers.GetContainerLogsAsync(&#xA;                containerId,&#xA;                new ContainerLogsParameters { ShowStdout = true, ShowStderr = true });&#xA;&#xA;            using (var reader = new StreamReader(logs))&#xA;            {&#xA;                string logLine;&#xA;                while ((logLine = await reader.ReadLineAsync()) != null)&#xA;                {&#xA;                    Console.WriteLine(logLine);&#xA;                }&#xA;            }&#xA;        }&#xA;        catch (Exception ex)&#xA;        {&#xA;            Console.WriteLine($"Failed to fetch logs: {ex.Message}");&#xA;        }&#xA;    }&#xA;}&#xA;&#xA;    {&#xA;  "ImageNaming": "khuser/windowsimage:latest",&#xA;  "ContainerNaming": "custom-worker-container",&#xA;  "InputFileRepoPath": "C:/Users/user/source/repos/Renderer/Renderer/wwwroot/audio.mp4",&#xA;  "OutputFileRepoPath": "C:/Users/user/Desktop/output/",&#xA;  "CommandDef": "ffmpeg -i C:\\app\\input\\audio.mp4 -c copy C:\\app\\output\\copiedAudio.mp4"&#xA;</jsonmessage></dockertaskresult></string></string></dockertaskresult></dockertask>

    &#xA;

    }. My problem is what I get in the output of my program : Initializing Docker client... Docker client initialized. Starting Docker task... Image: khyoussef/windowsimage:latest Container: custom-worker-container Input Path: C:/Users/ykharoufi/source/repos/Renderer/Renderer/wwwroot/audio.mp4 Output Path: C:/Users/ykharoufi/Desktop/output/ Command: ffmpeg -i C:\app\input\audio.mp4 -c copy C:\app\output\copiedAudio.mp4 Checking if Docker image exists... Pulling Docker image: khyoussef/windowsimage:latest... Docker image verified. Inspecting Docker image: khyoussef/windowsimage:latest... Image platform: windows Detected image platform: windows Input volume: C:\Users\ykharoufi\source\repos\Renderer\Renderer\wwwroot:C:\app\input Output volume: C:\Users\ykharoufi\Desktop\output:C:\app\output Creating Docker container... Container created with ID: 1daca99b3c76bc8c99c1aed7d2c546ae75aedd9aa1feb0f5002e54769390248e Starting container... Waiting for container to finish... Container exited with error code: 1 Fetching logs for container: 1daca99b3c76bc8c99c1aed7d2c546ae75aedd9aa1feb0f5002e54769390248e... @&#x27;ffmpeg&#x27; is not recognized as an internal or external command, !operable program or batch file. Error: Container exited with non-zero status code: 1 { "Success": false, "Message": "Failed to execute the command.", "ErrorDetails": "Container exited with non-zero status code: 1", "OutputFiles": null }, This is the Dockerfile I am working with :

    &#xA;

    `# Use Windows Server Core as the base image&#xA;FROM mcr.microsoft.com/windows/server:ltsc2022&#xA;&#xA;# Set the working directory&#xA;WORKDIR /app&#xA;&#xA;# Install required tools (FFmpeg and Visual C&#x2B;&#x2B; Redistributable)&#xA;RUN powershell -Command \&#xA;    $ErrorActionPreference = &#x27;Stop&#x27;; \&#xA;    # Install Visual C&#x2B;&#x2B; Redistributable&#xA;    Invoke-WebRequest -Uri https://aka.ms/vs/16/release/vc_redist.x64.exe -OutFile vc_redist.x64.exe; \&#xA;    Start-Process -FilePath vc_redist.x64.exe -ArgumentList &#x27;/install&#x27;, &#x27;/quiet&#x27;, &#x27;/norestart&#x27; -NoNewWindow -Wait; \&#xA;    Remove-Item -Force vc_redist.x64.exe; \&#xA;    # Download FFmpeg&#xA;    Invoke-WebRequest -Uri https://github.com/BtbN/FFmpeg-Builds/releases/download/latest/ffmpeg-n7.1-latest-win64-gpl-7.1.zip -OutFile ffmpeg.zip; \&#xA;    # Extract FFmpeg&#xA;    Expand-Archive -Path ffmpeg.zip -DestinationPath C:\ffmpeg; \&#xA;    Remove-Item -Force ffmpeg.zip; \&#xA;    # Debug step: Verify FFmpeg extraction&#xA;    Write-Output "FFmpeg extracted to C:\\ffmpeg"; \&#xA;    dir C:\ffmpeg; \&#xA;    dir C:\ffmpeg\ffmpeg-n7.1-latest-win64-gpl-7.1\bin&#xA;&#xA;# Add FFmpeg to PATH permanently&#xA;ENV PATH="C:\\ffmpeg\\ffmpeg-n7.1-latest-win64-gpl-7.1\\bin;${PATH}"&#xA;&#xA;# Verify FFmpeg installation&#xA;RUN ["cmd", "/S", "/C", "ffmpeg -version"]&#xA;&#xA;# Copy required files to the container&#xA;COPY ./ /app/&#xA;&#xA;# Default to a PowerShell session&#xA;CMD ["C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe", "-NoExit"]&#xA;`&#xA;

    &#xA;

    . I did build it using this command : docker build -t khuser/windowsimage:latest -f Dockerfile .

    &#xA;

  • lavc/vvcdec : remove unneeded set_output_format

    26 janvier, par Nuo Mi
    lavc/vvcdec : remove unneeded set_output_format
    

    Downstream can determine the format from the output frame format

    Co-authored-by : Frank Plowman <post@frankplowman.com>

    • [DH] libavcodec/vvc/dec.c
  • How to stream synchronized video and audio in real-time from an Android smartphone using HLS while preserving orientation metadata ?

    6 mars, par Jérôme LAROSE
    Hello,  &#xA;I am working on an Android application where I need to stream video&#xA;from one or two cameras on my smartphone, along with audio from the&#xA;microphone, in real-time via a link or web page accessible to users.&#xA;The stream should be live, allow rewinding (DVR functionality), and be&#xA;recorded simultaneously. A latency of 1 to 2 minutes is acceptable,&#xA;and the streaming is one-way.  &#xA;&#xA;I have chosen HLS (HTTP Live Streaming) for its browser compatibility&#xA;and DVR support. However, I am encountering issues with audio-video&#xA;synchronization, managing camera orientation metadata, and format&#xA;conversions.&#xA;

    &#xA;

    Here are my attempts :

    &#xA;

      &#xA;
    1. MP4 segmentation with MediaRecorder

      &#xA;

        &#xA;
      • I used MediaRecorder with setNextOutputFile to generate short MP4 segments, then ffmpeg-kit to convert them to fMP4 for HLS.
      • &#xA;

      • Expected : Well-aligned segments for smooth HLS playback.
      • &#xA;

      • Result : Timestamp issues causing jumps or interruptions in playback.
      • &#xA;

      &#xA;

    2. &#xA;

    3. MPEG2-TS via local socket

      &#xA;

        &#xA;
      • I configured MediaRecorder to produce an MPEG2-TS stream sent via a local socket to ffmpeg-kit.
      • &#xA;

      • Expected : Stable streaming with preserved metadata.
      • &#xA;

      • Result : Streaming works, but orientation metadata is lost, leading to incorrectly oriented video (e.g., rotated 90°).
      • &#xA;

      &#xA;

    4. &#xA;

    5. Orientation correction with ffmpeg

      &#xA;

        &#xA;
      • I tested -vf transpose=1 in ffmpeg to correct the orientation.
      • &#xA;

      • Expected : Correctly oriented video without excessive latency.
      • &#xA;

      • Result : Re-encoding takes too long for real-time streaming, causing unacceptable latency.
      • &#xA;

      &#xA;

    6. &#xA;

    7. MPEG2-TS to fMP4 conversion

      &#xA;

        &#xA;
      • I converted the MPEG2-TS stream to fMP4 with ffmpeg to preserve orientation.
      • &#xA;

      • Expected : Perfect audio-video synchronization.
      • &#xA;

      • Result : Slight desynchronization between audio and video, affecting the user experience.
      • &#xA;

      &#xA;

    8. &#xA;

    &#xA;

    I am looking for a solution to :

    &#xA;

      &#xA;
    • Stream an HLS feed from Android with correctly timestamped segments.
    • &#xA;

    • Preserve orientation metadata without heavy re-encoding.
    • &#xA;

    • Ensure perfect audio-video synchronization.
    • &#xA;

    &#xA;

    UPDATE

    &#xA;

    package com.example.angegardien&#xA;&#xA;import android.Manifest&#xA;import android.content.Context&#xA;import android.content.pm.PackageManager&#xA;import android.graphics.SurfaceTexture&#xA;import android.hardware.camera2.*&#xA;import android.media.*&#xA;import android.os.*&#xA;import android.util.Log&#xA;import android.view.Surface&#xA;import android.view.TextureView&#xA;import android.view.WindowManager&#xA;import androidx.activity.ComponentActivity&#xA;import androidx.core.app.ActivityCompat&#xA;import com.arthenica.ffmpegkit.FFmpegKit&#xA;import fi.iki.elonen.NanoHTTPD&#xA;import kotlinx.coroutines.*&#xA;import java.io.File&#xA;import java.io.IOException&#xA;import java.net.ServerSocket&#xA;import android.view.OrientationEventListener&#xA;&#xA;/**&#xA; * MainActivity class:&#xA; * - Manages camera operations using the Camera2 API.&#xA; * - Records video using MediaRecorder.&#xA; * - Pipes data to FFmpeg to generate HLS segments.&#xA; * - Hosts a local HLS server using NanoHTTPD to serve the generated HLS content.&#xA; */&#xA;class MainActivity : ComponentActivity() {&#xA;&#xA;    // TextureView used for displaying the camera preview.&#xA;    private lateinit var textureView: TextureView&#xA;    // Camera device instance.&#xA;    private lateinit var cameraDevice: CameraDevice&#xA;    // Camera capture session for managing capture requests.&#xA;    private lateinit var cameraCaptureSession: CameraCaptureSession&#xA;    // CameraManager to access camera devices.&#xA;    private lateinit var cameraManager: CameraManager&#xA;    // Directory where HLS output files will be stored.&#xA;    private lateinit var hlsDir: File&#xA;    // Instance of the HLS server.&#xA;    private lateinit var hlsServer: HlsServer&#xA;&#xA;    // Camera id ("1" corresponds to the rear camera).&#xA;    private val cameraId = "1"&#xA;    // Flag indicating whether recording is currently active.&#xA;    private var isRecording = false&#xA;&#xA;    // MediaRecorder used for capturing audio and video.&#xA;    private lateinit var activeRecorder: MediaRecorder&#xA;    // Surface for the camera preview.&#xA;    private lateinit var previewSurface: Surface&#xA;    // Surface provided by MediaRecorder for recording.&#xA;    private lateinit var recorderSurface: Surface&#xA;&#xA;    // Port for the FFmpeg local socket connection.&#xA;    private val ffmpegPort = 8080&#xA;&#xA;    // Coroutine scope to manage asynchronous tasks.&#xA;    private val scope = CoroutineScope(Dispatchers.IO &#x2B; SupervisorJob())&#xA;&#xA;    // Variables to track current device rotation and listen for orientation changes.&#xA;    private var currentRotation = 0&#xA;    private lateinit var orientationListener: OrientationEventListener&#xA;&#xA;    override fun onCreate(savedInstanceState: Bundle?) {&#xA;        super.onCreate(savedInstanceState)&#xA;&#xA;        // Initialize the TextureView and set it as the content view.&#xA;        textureView = TextureView(this)&#xA;        setContentView(textureView)&#xA;&#xA;        // Get the CameraManager system service.&#xA;        cameraManager = getSystemService(CAMERA_SERVICE) as CameraManager&#xA;        // Setup the directory for HLS output.&#xA;        setupHLSDirectory()&#xA;&#xA;        // Start the local HLS server on port 8081.&#xA;        hlsServer = HlsServer(8081, hlsDir, this)&#xA;        try {&#xA;            hlsServer.start()&#xA;            Log.d("HLS_SERVER", "HLS Server started on port 8081")&#xA;        } catch (e: IOException) {&#xA;            Log.e("HLS_SERVER", "Error starting HLS Server", e)&#xA;        }&#xA;&#xA;        // Initialize the current rotation.&#xA;        currentRotation = getDeviceRotation()&#xA;&#xA;        // Add a listener to detect orientation changes.&#xA;        orientationListener = object : OrientationEventListener(this) {&#xA;            override fun onOrientationChanged(orientation: Int) {&#xA;                if (orientation == ORIENTATION_UNKNOWN) return // Skip unknown orientations.&#xA;                // Determine the new rotation angle.&#xA;                val newRotation = when {&#xA;                    orientation >= 315 || orientation &lt; 45 -> 0&#xA;                    orientation >= 45 &amp;&amp; orientation &lt; 135 -> 90&#xA;                    orientation >= 135 &amp;&amp; orientation &lt; 225 -> 180&#xA;                    orientation >= 225 &amp;&amp; orientation &lt; 315 -> 270&#xA;                    else -> 0&#xA;                }&#xA;                // If the rotation has changed and recording is active, update the rotation.&#xA;                if (newRotation != currentRotation &amp;&amp; isRecording) {&#xA;                    Log.d("ROTATION", "Orientation change detected: $newRotation")&#xA;                    currentRotation = newRotation&#xA;                }&#xA;            }&#xA;        }&#xA;        orientationListener.enable()&#xA;&#xA;        // Set up the TextureView listener to know when the surface is available.&#xA;        textureView.surfaceTextureListener = object : TextureView.SurfaceTextureListener {&#xA;            override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {&#xA;                // Open the camera when the texture becomes available.&#xA;                openCamera()&#xA;            }&#xA;            override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {}&#xA;            override fun onSurfaceTextureDestroyed(surface: SurfaceTexture) = false&#xA;            override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {}&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Sets up the HLS directory in the public Downloads folder.&#xA;     * If the directory exists, it deletes it recursively and creates a new one.&#xA;     */&#xA;    private fun setupHLSDirectory() {&#xA;        val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)&#xA;        hlsDir = File(downloadsDir, "HLS_Output")&#xA;&#xA;        if (hlsDir.exists()) {&#xA;            hlsDir.deleteRecursively()&#xA;        }&#xA;        hlsDir.mkdirs()&#xA;&#xA;        Log.d("HLS", "&#128194; HLS folder created: ${hlsDir.absolutePath}")&#xA;    }&#xA;&#xA;    /**&#xA;     * Opens the camera after checking for necessary permissions.&#xA;     */&#xA;    private fun openCamera() {&#xA;        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED ||&#xA;            ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {&#xA;            // Request permissions if they are not already granted.&#xA;            ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO), 101)&#xA;            return&#xA;        }&#xA;&#xA;        try {&#xA;            // Open the specified camera using its cameraId.&#xA;            cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {&#xA;                override fun onOpened(camera: CameraDevice) {&#xA;                    cameraDevice = camera&#xA;                    // Start the recording session once the camera is opened.&#xA;                    startNextRecording()&#xA;                }&#xA;                override fun onDisconnected(camera: CameraDevice) { camera.close() }&#xA;                override fun onError(camera: CameraDevice, error: Int) { camera.close() }&#xA;            }, null)&#xA;        } catch (e: CameraAccessException) {&#xA;            e.printStackTrace()&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Starts a new recording session:&#xA;     * - Sets up the preview and recorder surfaces.&#xA;     * - Creates a pipe for MediaRecorder output.&#xA;     * - Creates a capture session for simultaneous preview and recording.&#xA;     */&#xA;    private fun startNextRecording() {&#xA;        // Get the SurfaceTexture from the TextureView and set its default buffer size.&#xA;        val texture = textureView.surfaceTexture!!&#xA;        texture.setDefaultBufferSize(1920, 1080)&#xA;        // Create the preview surface.&#xA;        previewSurface = Surface(texture)&#xA;&#xA;        // Create and configure the MediaRecorder.&#xA;        activeRecorder = createMediaRecorder()&#xA;&#xA;        // Create a pipe to route MediaRecorder data.&#xA;        val pipe = ParcelFileDescriptor.createPipe()&#xA;        val pfdWrite = pipe[1] // Write end used by MediaRecorder.&#xA;        val pfdRead = pipe[0]  // Read end used by the local socket server.&#xA;&#xA;        // Set MediaRecorder output to the file descriptor of the write end.&#xA;        activeRecorder.setOutputFile(pfdWrite.fileDescriptor)&#xA;        setupMediaRecorder(activeRecorder)&#xA;        // Obtain the recorder surface from MediaRecorder.&#xA;        recorderSurface = activeRecorder.surface&#xA;&#xA;        // Create a capture request using the RECORD template.&#xA;        val captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD)&#xA;        captureRequestBuilder.addTarget(previewSurface)&#xA;        captureRequestBuilder.addTarget(recorderSurface)&#xA;&#xA;        // Create a capture session including both preview and recorder surfaces.&#xA;        cameraDevice.createCaptureSession(&#xA;            listOf(previewSurface, recorderSurface),&#xA;            object : CameraCaptureSession.StateCallback() {&#xA;                override fun onConfigured(session: CameraCaptureSession) {&#xA;                    cameraCaptureSession = session&#xA;                    captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO)&#xA;                    // Start a continuous capture request.&#xA;                    cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null)&#xA;&#xA;                    // Launch a coroutine to start FFmpeg and MediaRecorder with synchronization.&#xA;                    scope.launch {&#xA;                        startFFmpeg()&#xA;                        delay(500) // Wait for FFmpeg to be ready.&#xA;                        activeRecorder.start()&#xA;                        isRecording = true&#xA;                        Log.d("HLS", "&#127909; Recording started...")&#xA;                    }&#xA;&#xA;                    // Launch a coroutine to run the local socket server to forward data.&#xA;                    scope.launch {&#xA;                        startLocalSocketServer(pfdRead)&#xA;                    }&#xA;                }&#xA;                override fun onConfigureFailed(session: CameraCaptureSession) {&#xA;                    Log.e("Camera2", "❌ Configuration failed")&#xA;                }&#xA;            },&#xA;            null&#xA;        )&#xA;    }&#xA;&#xA;    /**&#xA;     * Coroutine to start a local socket server.&#xA;     * It reads from the MediaRecorder pipe and sends the data to FFmpeg.&#xA;     */&#xA;    private suspend fun startLocalSocketServer(pfdRead: ParcelFileDescriptor) {&#xA;        withContext(Dispatchers.IO) {&#xA;            val serverSocket = ServerSocket(ffmpegPort)&#xA;            Log.d("HLS", "Local socket server started on port $ffmpegPort")&#xA;&#xA;            // Accept connection from FFmpeg.&#xA;            val socket = serverSocket.accept()&#xA;            Log.d("HLS", "Connection accepted from FFmpeg")&#xA;&#xA;            // Read data from the pipe and forward it through the socket.&#xA;            val inputStream = ParcelFileDescriptor.AutoCloseInputStream(pfdRead)&#xA;            val outputStream = socket.getOutputStream()&#xA;            val buffer = ByteArray(8192)&#xA;            var bytesRead: Int&#xA;            while (inputStream.read(buffer).also { bytesRead = it } != -1) {&#xA;                outputStream.write(buffer, 0, bytesRead)&#xA;            }&#xA;            outputStream.close()&#xA;            inputStream.close()&#xA;            socket.close()&#xA;            serverSocket.close()&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Coroutine to start FFmpeg using a local TCP input.&#xA;     * Applies a video rotation filter based on device orientation and generates HLS segments.&#xA;     */&#xA;    private suspend fun startFFmpeg() {&#xA;        withContext(Dispatchers.IO) {&#xA;            // Retrieve the appropriate transpose filter based on current rotation.&#xA;            val transposeFilter = getTransposeFilter(currentRotation)&#xA;&#xA;            // FFmpeg command to read from the TCP socket and generate an HLS stream.&#xA;            // Two alternative commands are commented below.&#xA;            // val ffmpegCommand = "-fflags &#x2B;genpts -i tcp://localhost:$ffmpegPort -c copy -bsf:a aac_adtstoasc -movflags &#x2B;faststart -f dash -seg_duration 10 -hls_playlist 1 ${hlsDir.absolutePath}/manifest.mpd"&#xA;            // val ffmpegCommand = "-fflags &#x2B;genpts -i tcp://localhost:$ffmpegPort -c copy -bsf:a aac_adtstoasc -movflags &#x2B;faststart -f hls -hls_time 5 -hls_segment_type fmp4 -hls_flags split_by_time -hls_list_size 0 -hls_playlist_type event -hls_fmp4_init_filename init.mp4 -hls_segment_filename ${hlsDir.absolutePath}/segment_%03d.m4s ${hlsDir.absolutePath}/playlist.m3u8"&#xA;            val ffmpegCommand = "-fflags &#x2B;genpts -i tcp://localhost:$ffmpegPort -vf $transposeFilter -c:v libx264 -preset ultrafast -crf 23 -c:a copy -movflags &#x2B;faststart -f hls -hls_time 0.1 -hls_segment_type mpegts -hls_flags split_by_time -hls_list_size 0 -hls_playlist_type event -hls_segment_filename ${hlsDir.absolutePath}/segment_%03d.ts ${hlsDir.absolutePath}/playlist.m3u8"&#xA;&#xA;            FFmpegKit.executeAsync(ffmpegCommand) { session ->&#xA;                if (session.returnCode.isValueSuccess) {&#xA;                    Log.d("HLS", "✅ HLS generated successfully")&#xA;                } else {&#xA;                    Log.e("FFmpeg", "❌ Error generating HLS: ${session.allLogsAsString}")&#xA;                }&#xA;            }&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Gets the current device rotation using the WindowManager.&#xA;     */&#xA;    private fun getDeviceRotation(): Int {&#xA;        val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager&#xA;        return when (windowManager.defaultDisplay.rotation) {&#xA;            Surface.ROTATION_0 -> 0&#xA;            Surface.ROTATION_90 -> 90&#xA;            Surface.ROTATION_180 -> 180&#xA;            Surface.ROTATION_270 -> 270&#xA;            else -> 0&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Returns the FFmpeg transpose filter based on the rotation angle.&#xA;     * Used to rotate the video stream accordingly.&#xA;     */&#xA;    private fun getTransposeFilter(rotation: Int): String {&#xA;        return when (rotation) {&#xA;            90 -> "transpose=1" // 90&#xB0; clockwise&#xA;            180 -> "transpose=2,transpose=2" // 180&#xB0; rotation&#xA;            270 -> "transpose=2" // 90&#xB0; counter-clockwise&#xA;            else -> "transpose=0" // No rotation&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Creates and configures a MediaRecorder instance.&#xA;     * Sets up audio and video sources, formats, encoders, and bitrates.&#xA;     */&#xA;    private fun createMediaRecorder(): MediaRecorder {&#xA;        return MediaRecorder().apply {&#xA;            setAudioSource(MediaRecorder.AudioSource.MIC)&#xA;            setVideoSource(MediaRecorder.VideoSource.SURFACE)&#xA;            setOutputFormat(MediaRecorder.OutputFormat.MPEG_2_TS)&#xA;            setVideoEncodingBitRate(5000000)&#xA;            setVideoFrameRate(24)&#xA;            setVideoSize(1080, 720)&#xA;            setVideoEncoder(MediaRecorder.VideoEncoder.H264)&#xA;            setAudioEncoder(MediaRecorder.AudioEncoder.AAC)&#xA;            setAudioSamplingRate(16000)&#xA;            setAudioEncodingBitRate(96000) // 96 kbps&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Prepares the MediaRecorder and logs the outcome.&#xA;     */&#xA;    private fun setupMediaRecorder(recorder: MediaRecorder) {&#xA;        try {&#xA;            recorder.prepare()&#xA;            Log.d("HLS", "✅ MediaRecorder prepared")&#xA;        } catch (e: IOException) {&#xA;            Log.e("HLS", "❌ Error preparing MediaRecorder", e)&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Custom HLS server class extending NanoHTTPD.&#xA;     * Serves HLS segments and playlists from the designated HLS directory.&#xA;     */&#xA;    private inner class HlsServer(port: Int, private val hlsDir: File, private val context: Context) : NanoHTTPD(port) {&#xA;        override fun serve(session: IHTTPSession): Response {&#xA;            val uri = session.uri.trimStart(&#x27;/&#x27;)&#xA;&#xA;            // Intercept the request for `init.mp4` and serve it from assets.&#xA;            /*&#xA;            if (uri == "init.mp4") {&#xA;                Log.d("HLS Server", "&#128225; Intercepting init.mp4, sending file from assets...")&#xA;                return try {&#xA;                    val assetManager = context.assets&#xA;                    val inputStream = assetManager.open("init.mp4")&#xA;                    newFixedLengthResponse(Response.Status.OK, "video/mp4", inputStream, inputStream.available().toLong())&#xA;                } catch (e: Exception) {&#xA;                    Log.e("HLS Server", "❌ Error reading init.mp4 from assets: ${e.message}")&#xA;                    newFixedLengthResponse(Response.Status.INTERNAL_ERROR, MIME_PLAINTEXT, "Server error")&#xA;                }&#xA;            }&#xA;            */&#xA;&#xA;            // Serve all other HLS files normally from the hlsDir.&#xA;            val file = File(hlsDir, uri)&#xA;            return if (file.exists()) {&#xA;                newFixedLengthResponse(Response.Status.OK, getMimeTypeForFile(uri), file.inputStream(), file.length())&#xA;            } else {&#xA;                newFixedLengthResponse(Response.Status.NOT_FOUND, MIME_PLAINTEXT, "File not found")&#xA;            }&#xA;        }&#xA;    }&#xA;&#xA;    /**&#xA;     * Clean up resources when the activity is destroyed.&#xA;     * Stops recording, releases the camera, cancels coroutines, and stops the HLS server.&#xA;     */&#xA;    override fun onDestroy() {&#xA;        super.onDestroy()&#xA;        if (isRecording) {&#xA;            activeRecorder.stop()&#xA;            activeRecorder.release()&#xA;        }&#xA;        cameraDevice.close()&#xA;        scope.cancel()&#xA;        hlsServer.stop()&#xA;        orientationListener.disable()&#xA;        Log.d("HLS", "&#128721; Activity destroyed")&#xA;    }&#xA;}&#xA;

    &#xA;

    I have three examples of ffmpeg commands.

    &#xA;

      &#xA;
    • One command segments into DASH, but the camera does not have the correct rotation.
    • &#xA;

    • One command segments into HLS without re-encoding with 5-second segments ; it’s fast but does not have the correct rotation.
    • &#xA;

    • One command segments into HLS with re-encoding, which applies a rotation. It’s too slow for 5-second segments, so a 1-second segment was chosen.
    • &#xA;

    &#xA;

    Note :

    &#xA;

      &#xA;
    • In the second command ("One command segments into HLS without re-encoding with 5-second segments ; it’s fast but does not have the correct rotation."), it returns fMP4. To achieve the correct rotation, I provide a preconfigured init.mp4 file during the HTTP request to retrieve it (see comment).
    • &#xA;

    • In the third command ("One command segments into HLS with re-encoding, which applies a rotation. It’s too slow for 5-second segments, so a 1-second segment was chosen."), it returns TS.
    • &#xA;

    &#xA;