1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192 |
- 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)
- }
- }
|