convert-to-h264/convert.go
2026-08-05 15:28:43 +03:00

109 lines
3.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bytes"
"context"
"io"
"log"
"net/http"
"os"
"os/exec"
"time"
)
func compressRAMHandler(w http.ResponseWriter, r *http.Request) {
start := time.Now()
log.Printf("[+] Новый запрос от %s", r.RemoteAddr)
if r.Method != http.MethodPost {
http.Error(w, "Только POST метод", http.StatusMethodNotAllowed)
return
}
// 1. Создаем временный файл в RAM (/tmp работает на tmpfs)
tmpFile, err := os.CreateTemp("", "input-video-*.mp4")
if err != nil {
log.Printf("[-] Ошибка создания tmp-файла: %v", err)
http.Error(w, "Ошибка выpoint буфера RAM", http.StatusInternalServerError)
return
}
tmpFilePath := tmpFile.Name()
defer os.Remove(tmpFilePath) // Удаляем файл после завершения работы
// 2. Копируем тело запроса во временный файл
copiedBytes, err := io.Copy(tmpFile, r.Body)
_ = tmpFile.Close() // Обязательно закрываем файл перед чтением в FFmpeg!
if err != nil {
log.Printf("[-] Ошибка вычитывания Body: %v (получено %d байт)", err, copiedBytes)
http.Error(w, "Ошибка загрузки файла", http.StatusBadRequest)
return
}
log.Printf("[+] Файл полностью загружен в RAM: %d байт (%.2f MB) за %v",
copiedBytes, float64(copiedBytes)/(1024*1024), time.Since(start))
if copiedBytes == 0 {
log.Printf("[-] Ошибка: Прилетело 0 байт!")
http.Error(w, "Пустой файл", http.StatusBadRequest)
return
}
// 3. Заголовки ответа
w.Header().Set("Content-Type", "video/mp4")
w.Header().Set("Transfer-Encoding", "chunked")
ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
defer cancel()
// 4. Запуск FFmpeg
args := []string{
"-y",
"-i", tmpFilePath,
"-c:v", "libx264",
"-preset", "veryfast",
"-crf", "29",
"-tune", "zerolatency",
"-threads", "5",
"-movflags", "frag_keyframe+empty_moov",
"-f", "mp4",
"pipe:1",
}
cmd := exec.CommandContext(ctx, "ffmpeg", args...)
var stderrBuf bytes.Buffer
cmd.Stderr = &stderrBuf
stdoutPipe, err := cmd.StdoutPipe()
if err != nil {
log.Printf("[-] Ошибка stdout pipe: %v", err)
http.Error(w, "Ошибка FFmpeg", http.StatusInternalServerError)
return
}
if err := cmd.Start(); err != nil {
log.Printf("[-] Ошибка старта FFmpeg: %v", err)
http.Error(w, "Ошибка старта FFmpeg", http.StatusInternalServerError)
return
}
log.Printf("[+] FFmpeg успешно запущен, начинаем стримить ответ...")
// 5. Потоково отдаем сжатый ответ
written, _ := io.Copy(w, stdoutPipe)
if waitErr := cmd.Wait(); waitErr != nil {
log.Printf("[-] FFmpeg завершился с ошибкой: %v\nЛог FFmpeg:\n%s", waitErr, stderrBuf.String())
} else {
log.Printf("[+] Обработка завершена успешно! Отправлено клиенту: %d байт (%.2f MB). Общее время: %v",
written, float64(written)/(1024*1024), time.Since(start))
}
}
func main() {
http.HandleFunc("/compress", compressRAMHandler)
log.Println("RAM Converter запущен на :1554...")
log.Fatal(http.ListenAndServe("127.0.0.1:1554", nil))
}