package transcoder /* Transcoder.go This module handle real-time transcoding of media files that is not supported by playing on web. */ import ( "io" "log" "net/http" "os/exec" ) type TranscodeOutputResolution string const ( TranscodeResolution_360p TranscodeOutputResolution = "360p" TranscodeResolution_720p TranscodeOutputResolution = "720p" TranscodeResolution_1080p TranscodeOutputResolution = "1280p" TranscodeResolution_original TranscodeOutputResolution = "" ) // Transcode and stream the given file. Make sure ffmpeg is installed before calling to transcoder. func TranscodeAndStream(w http.ResponseWriter, r *http.Request, inputFile string, resolution TranscodeOutputResolution) { // Build the FFmpeg command based on the resolution parameter var cmd *exec.Cmd switch resolution { case "360p": cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:360", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1") case "720p": cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:720", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1") case "1080p": cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:1080", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1") case "": // Original resolution cmd = exec.Command("ffmpeg", "-i", inputFile, "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1") default: http.Error(w, "Invalid resolution parameter", http.StatusBadRequest) return } // Set response headers for streaming MP4 video w.Header().Set("Content-Type", "video/mp4") w.Header().Set("Transfer-Encoding", "chunked") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Accept-Ranges", "bytes") // Get the command output pipe stdout, err := cmd.StdoutPipe() if err != nil { http.Error(w, "Failed to create output pipe", http.StatusInternalServerError) return } // Get the command error pipe to capture standard error stderr, err := cmd.StderrPipe() if err != nil { http.Error(w, "Failed to create error pipe", http.StatusInternalServerError) log.Printf("Failed to create error pipe: %v", err) return } // Start the command if err := cmd.Start(); err != nil { http.Error(w, "Failed to start FFmpeg", http.StatusInternalServerError) return } // Copy the command output to the HTTP response in a separate goroutine go func() { if _, err := io.Copy(w, stdout); err != nil { //End of video } }() // Read and log the command standard error go func() { errOutput, _ := io.ReadAll(stderr) if len(errOutput) > 0 { log.Printf("FFmpeg error output: %s", string(errOutput)) } }() // Wait for the command to finish if err := cmd.Wait(); err != nil { http.Error(w, "FFmpeg process failed", http.StatusInternalServerError) log.Printf("FFmpeg process failed: %v", err) } }