transcoder.go 3.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. package transcoder
  2. /*
  3. Transcoder.go
  4. This module handle real-time transcoding of media files
  5. that is not supported by playing on web.
  6. */
  7. import (
  8. "io"
  9. "log"
  10. "net/http"
  11. "os/exec"
  12. )
  13. type TranscodeOutputResolution string
  14. const (
  15. TranscodeResolution_360p TranscodeOutputResolution = "360p"
  16. TranscodeResolution_720p TranscodeOutputResolution = "720p"
  17. TranscodeResolution_1080p TranscodeOutputResolution = "1280p"
  18. TranscodeResolution_original TranscodeOutputResolution = ""
  19. )
  20. // Transcode and stream the given file. Make sure ffmpeg is installed before calling to transcoder.
  21. func TranscodeAndStream(w http.ResponseWriter, r *http.Request, inputFile string, resolution TranscodeOutputResolution) {
  22. // Build the FFmpeg command based on the resolution parameter
  23. var cmd *exec.Cmd
  24. switch resolution {
  25. case "360p":
  26. cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:360", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1")
  27. case "720p":
  28. cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:720", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1")
  29. case "1080p":
  30. cmd = exec.Command("ffmpeg", "-i", inputFile, "-vf", "scale=-1:1080", "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1")
  31. case "":
  32. // Original resolution
  33. cmd = exec.Command("ffmpeg", "-i", inputFile, "-f", "mp4", "-vcodec", "libx264", "-preset", "fast", "-movflags", "frag_keyframe+empty_moov", "pipe:1")
  34. default:
  35. http.Error(w, "Invalid resolution parameter", http.StatusBadRequest)
  36. return
  37. }
  38. // Set response headers for streaming MP4 video
  39. w.Header().Set("Content-Type", "video/mp4")
  40. w.Header().Set("Transfer-Encoding", "chunked")
  41. w.Header().Set("Cache-Control", "no-cache")
  42. w.Header().Set("Accept-Ranges", "bytes")
  43. // Get the command output pipe
  44. stdout, err := cmd.StdoutPipe()
  45. if err != nil {
  46. http.Error(w, "Failed to create output pipe", http.StatusInternalServerError)
  47. return
  48. }
  49. // Get the command error pipe to capture standard error
  50. stderr, err := cmd.StderrPipe()
  51. if err != nil {
  52. http.Error(w, "Failed to create error pipe", http.StatusInternalServerError)
  53. log.Printf("Failed to create error pipe: %v", err)
  54. return
  55. }
  56. // Start the command
  57. if err := cmd.Start(); err != nil {
  58. http.Error(w, "Failed to start FFmpeg", http.StatusInternalServerError)
  59. return
  60. }
  61. // Copy the command output to the HTTP response in a separate goroutine
  62. go func() {
  63. if _, err := io.Copy(w, stdout); err != nil {
  64. //End of video
  65. }
  66. }()
  67. // Read and log the command standard error
  68. go func() {
  69. errOutput, _ := io.ReadAll(stderr)
  70. if len(errOutput) > 0 {
  71. log.Printf("FFmpeg error output: %s", string(errOutput))
  72. }
  73. }()
  74. // Wait for the command to finish
  75. if err := cmd.Wait(); err != nil {
  76. http.Error(w, "FFmpeg process failed", http.StatusInternalServerError)
  77. log.Printf("FFmpeg process failed: %v", err)
  78. }
  79. }