Add: support customized ffmpeg container format
This commit is contained in:
40
config.json
40
config.json
@@ -6,16 +6,42 @@
|
||||
"token": "!! config your very strong token here !!",
|
||||
"ffmpeg_threads": 1,
|
||||
"ffmpeg_config_list": [
|
||||
{ "name": "OPUS 128k", "args": "-c:a libopus -ab 128k -vn" },
|
||||
{ "name": "OPUS 96k", "args": "-c:a libopus -ab 96k -vn" },
|
||||
{ "name": "OPUS 256k", "args": "-c:a libopus -ab 256k -vn" },
|
||||
{ "name": "OPUS 320k", "args": "-c:a libopus -ab 320k -vn" },
|
||||
{ "name": "OPUS 512k", "args": "-c:a libopus -ab 512k -vn" },
|
||||
{ "name": "AAC 128k", "args": "-c:a aac -ab 128k -vn" },
|
||||
{ "name": "AAC 256k", "args": "-c:a aac -ab 256k -vn" },
|
||||
{
|
||||
"name": "WEBM OPUS 128k",
|
||||
"args": "-c:a libopus -ab 128k -vn",
|
||||
"format": "webm"
|
||||
},
|
||||
{
|
||||
"name": "WEBM OPUS 96k",
|
||||
"args": "-c:a libopus -ab 96k -vn",
|
||||
"format": "webm"
|
||||
},
|
||||
{
|
||||
"name": "WEBM OPUS 256k",
|
||||
"args": "-c:a libopus -ab 256k -vn",
|
||||
"format": "webm"
|
||||
},
|
||||
{
|
||||
"name": "WEBM OPUS 512k",
|
||||
"args": "-c:a libopus -ab 512k -vn",
|
||||
"format": "webm"
|
||||
},
|
||||
{
|
||||
"name": "AAC 128k",
|
||||
"args": "-c:a aac -ab 128k -vn",
|
||||
"format": "adts"
|
||||
},
|
||||
{
|
||||
"name": "AAC 256k",
|
||||
"args": "-c:a aac -ab 256k -vn",
|
||||
"format": "adts"
|
||||
},
|
||||
{ "name": "MP3 128k", "args": "-c:a mp3 -ab 128k -vn", "format": "mp3" },
|
||||
{ "name": "MP3 320k", "args": "-c:a mp3 -ab 320k -vn", "format": "mp3" },
|
||||
{ "name": "全损音质 32k", "args": "-c:a libopus -ab 32k -vn" },
|
||||
{
|
||||
"name": "video test",
|
||||
"format": "webm",
|
||||
"args": "-c:a libopus -ab 128k -filter:v scale='min(640,iw)':min'(360,ih)':force_original_aspect_ratio=decrease -c:v libvpx -r 5"
|
||||
}
|
||||
]
|
||||
|
||||
4
main.go
4
main.go
@@ -5,6 +5,7 @@ import (
|
||||
"flag"
|
||||
"log"
|
||||
"msw-open-music/pkg/api"
|
||||
"msw-open-music/pkg/commonconfig"
|
||||
"os"
|
||||
)
|
||||
|
||||
@@ -14,12 +15,11 @@ func init() {
|
||||
flag.StringVar(&ConfigFilePath, "config", "config.json", "backend config file path")
|
||||
}
|
||||
|
||||
|
||||
func main() {
|
||||
var err error
|
||||
flag.Parse()
|
||||
|
||||
config := api.Config{}
|
||||
config := commonconfig.Config{}
|
||||
configFile, err := os.Open(ConfigFilePath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@@ -3,6 +3,7 @@ package api
|
||||
import (
|
||||
"github.com/gorilla/sessions"
|
||||
"msw-open-music/pkg/database"
|
||||
"msw-open-music/pkg/commonconfig"
|
||||
"msw-open-music/pkg/tmpfs"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -12,32 +13,13 @@ type API struct {
|
||||
Db *database.Database
|
||||
Server http.Server
|
||||
token string
|
||||
APIConfig APIConfig
|
||||
APIConfig commonconfig.APIConfig
|
||||
Tmpfs *tmpfs.Tmpfs
|
||||
store *sessions.CookieStore
|
||||
defaultSessionName string
|
||||
}
|
||||
|
||||
func NewAPIConfig() APIConfig {
|
||||
apiConfig := APIConfig{}
|
||||
return apiConfig
|
||||
}
|
||||
|
||||
type APIConfig struct {
|
||||
DatabaseName string `json:"database_name"`
|
||||
SingleThread bool `json:"single_thread,default=true"`
|
||||
Addr string `json:"addr"`
|
||||
Token string `json:"token"`
|
||||
FfmpegThreads int64 `json:"ffmpeg_threads"`
|
||||
FfmpegConfigList []FfmpegConfig `json:"ffmpeg_config_list"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
APIConfig APIConfig `json:"api"`
|
||||
TmpfsConfig tmpfs.TmpfsConfig `json:"tmpfs"`
|
||||
}
|
||||
|
||||
func NewAPI(config Config) (*API, error) {
|
||||
func NewAPI(config commonconfig.Config) (*API, error) {
|
||||
var err error
|
||||
|
||||
apiConfig := config.APIConfig
|
||||
@@ -117,7 +99,6 @@ func NewAPI(config Config) (*API, error) {
|
||||
// below needs token
|
||||
apiMux.HandleFunc("/walk", api.HandleWalk)
|
||||
apiMux.HandleFunc("/reset", api.HandleReset)
|
||||
apiMux.HandleFunc("/add_ffmpeg_config", api.HandleAddFfmpegConfig)
|
||||
|
||||
mux.Handle("/api/v1/", http.StripPrefix("/api/v1", apiMux))
|
||||
mux.Handle("/", http.StripPrefix("/", http.FileServer(http.Dir("web/build"))))
|
||||
|
||||
@@ -3,20 +3,12 @@ package api
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"msw-open-music/pkg/commonconfig"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type FfmpegConfig struct {
|
||||
Name string `json:"name"`
|
||||
Args string `json:"args"`
|
||||
}
|
||||
|
||||
type FfmpegConfigList struct {
|
||||
FfmpegConfigList []FfmpegConfig `json:"ffmpeg_config_list"`
|
||||
}
|
||||
|
||||
func (api *API) GetFfmpegConfig(configName string) (FfmpegConfig, bool) {
|
||||
ffmpegConfig := FfmpegConfig{}
|
||||
func (api *API) GetFfmpegConfig(configName string) (commonconfig.FfmpegConfig, bool) {
|
||||
ffmpegConfig := commonconfig.FfmpegConfig{}
|
||||
for _, f := range api.APIConfig.FfmpegConfigList {
|
||||
if f.Name == configName {
|
||||
ffmpegConfig = f
|
||||
@@ -30,45 +22,8 @@ func (api *API) GetFfmpegConfig(configName string) (FfmpegConfig, bool) {
|
||||
|
||||
func (api *API) HandleGetFfmpegConfigs(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("[api] Get ffmpeg config list")
|
||||
ffmpegConfigList := &FfmpegConfigList{
|
||||
ffmpegConfigList := &commonconfig.FfmpegConfigList{
|
||||
FfmpegConfigList: api.APIConfig.FfmpegConfigList,
|
||||
}
|
||||
json.NewEncoder(w).Encode(&ffmpegConfigList)
|
||||
}
|
||||
|
||||
type AddFfmpegConfigRequest struct {
|
||||
Token string `json:"token"`
|
||||
Name string `json:"name"`
|
||||
FfmpegConfig FfmpegConfig `json:"ffmpeg_config"`
|
||||
}
|
||||
|
||||
func (api *API) HandleAddFfmpegConfig(w http.ResponseWriter, r *http.Request) {
|
||||
addFfmpegConfigRequest := AddFfmpegConfigRequest{}
|
||||
err := json.NewDecoder(r.Body).Decode(&addFfmpegConfigRequest)
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
// check token
|
||||
err = api.CheckToken(w, r, addFfmpegConfigRequest.Token)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// check name and args not null
|
||||
if addFfmpegConfigRequest.Name == "" {
|
||||
api.HandleErrorString(w, r, `"ffmpeg_config.name" can't be empty`)
|
||||
return
|
||||
}
|
||||
if addFfmpegConfigRequest.FfmpegConfig.Args == "" {
|
||||
api.HandleErrorString(w, r, `"ffmpeg_config.args" can't be empty`)
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("[api] Add ffmpeg config")
|
||||
|
||||
api.APIConfig.FfmpegConfigList = append(api.APIConfig.FfmpegConfigList, addFfmpegConfigRequest.FfmpegConfig)
|
||||
|
||||
api.HandleOK(w, r)
|
||||
}
|
||||
|
||||
@@ -68,11 +68,12 @@ func (api *API) HandleGetFileStream(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
args := strings.Split(ffmpegConfig.Args, " ")
|
||||
startArgs := []string{"-threads", strconv.FormatInt(api.APIConfig.FfmpegThreads, 10), "-i", path}
|
||||
endArgs := []string{"-f", "webm", "-"}
|
||||
endArgs := []string{"-f", ffmpegConfig.Format, "-"}
|
||||
ffmpegArgs := append(startArgs, args...)
|
||||
ffmpegArgs = append(ffmpegArgs, endArgs...)
|
||||
cmd := exec.Command("ffmpeg", ffmpegArgs...)
|
||||
cmd.Stdout = w
|
||||
// cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
api.HandleError(w, r, err)
|
||||
@@ -127,7 +128,7 @@ func (api *API) HandlePrepareFileStreamDirect(w http.ResponseWriter, r *http.Req
|
||||
api.HandleErrorStringCode(w, r, `ffmpeg config not found`, 404)
|
||||
return
|
||||
}
|
||||
objPath := api.Tmpfs.GetObjFilePath(prepareFileStreamDirectRequst.ID, prepareFileStreamDirectRequst.ConfigName)
|
||||
objPath := api.Tmpfs.GetObjFilePath(prepareFileStreamDirectRequst.ID, ffmpegConfig)
|
||||
|
||||
// check obj file exists
|
||||
exists := api.Tmpfs.Exits(objPath)
|
||||
@@ -179,7 +180,13 @@ func (api *API) HandleGetFileStreamDirect(w http.ResponseWriter, r *http.Request
|
||||
configs := q["config"]
|
||||
configName := configs[0]
|
||||
|
||||
path := api.Tmpfs.GetObjFilePath(int64(id), configName)
|
||||
ffmpegConfig, ok := api.GetFfmpegConfig(configName)
|
||||
if !ok {
|
||||
api.HandleErrorStringCode(w, r, `ffmpeg config not found`, 404)
|
||||
return
|
||||
}
|
||||
|
||||
path := api.Tmpfs.GetObjFilePath(int64(id), ffmpegConfig)
|
||||
if api.Tmpfs.Exits(path) {
|
||||
api.Tmpfs.Record(path)
|
||||
}
|
||||
|
||||
43
pkg/commonconfig/config.go
Normal file
43
pkg/commonconfig/config.go
Normal file
@@ -0,0 +1,43 @@
|
||||
package commonconfig
|
||||
|
||||
type Config struct {
|
||||
APIConfig APIConfig `json:"api"`
|
||||
TmpfsConfig TmpfsConfig `json:"tmpfs"`
|
||||
}
|
||||
|
||||
type APIConfig struct {
|
||||
DatabaseName string `json:"database_name"`
|
||||
SingleThread bool `json:"single_thread,default=true"`
|
||||
Addr string `json:"addr"`
|
||||
Token string `json:"token"`
|
||||
FfmpegThreads int64 `json:"ffmpeg_threads"`
|
||||
FfmpegConfigList []FfmpegConfig `json:"ffmpeg_config_list"`
|
||||
}
|
||||
|
||||
type FfmpegConfigList struct {
|
||||
FfmpegConfigList []FfmpegConfig `json:"ffmpeg_config_list"`
|
||||
}
|
||||
|
||||
type FfmpegConfig struct {
|
||||
Name string `json:"name"`
|
||||
Args string `json:"args"`
|
||||
Format string `json:"format"`
|
||||
}
|
||||
|
||||
type TmpfsConfig struct {
|
||||
FileLifeTime int64 `json:"file_life_time"`
|
||||
CleanerInternal int64 `json:"cleaner_internal"`
|
||||
Root string `json:"root"`
|
||||
}
|
||||
|
||||
// Constructors for Config
|
||||
|
||||
func NewAPIConfig() APIConfig {
|
||||
apiConfig := APIConfig{}
|
||||
return apiConfig
|
||||
}
|
||||
|
||||
func NewTmpfsConfig() *TmpfsConfig {
|
||||
config := &TmpfsConfig{}
|
||||
return config
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package tmpfs
|
||||
|
||||
import (
|
||||
"log"
|
||||
"msw-open-music/pkg/commonconfig"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
@@ -10,14 +11,14 @@ import (
|
||||
)
|
||||
|
||||
type Tmpfs struct {
|
||||
record map[string]int64
|
||||
Config TmpfsConfig
|
||||
wg sync.WaitGroup
|
||||
record map[string]int64
|
||||
Config commonconfig.TmpfsConfig
|
||||
wg sync.WaitGroup
|
||||
recordLocks map[string]*sync.Mutex
|
||||
}
|
||||
|
||||
func (tmpfs *Tmpfs) GetObjFilePath(id int64, configName string) (string) {
|
||||
return filepath.Join(tmpfs.Config.Root, strconv.FormatInt(id, 10) + "." + configName + ".webm")
|
||||
func (tmpfs *Tmpfs) GetObjFilePath(id int64, ffmpegConfig commonconfig.FfmpegConfig) string {
|
||||
return filepath.Join(tmpfs.Config.Root, strconv.FormatInt(id, 10)+"."+ffmpegConfig.Name+"."+ffmpegConfig.Format)
|
||||
}
|
||||
|
||||
func (tmpfs *Tmpfs) GetLock(filename string) *sync.Mutex {
|
||||
@@ -35,21 +36,10 @@ func (tmpfs *Tmpfs) Unlock(filename string) {
|
||||
tmpfs.GetLock(filename).Unlock()
|
||||
}
|
||||
|
||||
type TmpfsConfig struct {
|
||||
FileLifeTime int64 `json:"file_life_time"`
|
||||
CleanerInternal int64 `json:"cleaner_internal"`
|
||||
Root string `json:"root"`
|
||||
}
|
||||
|
||||
func NewTmpfsConfig() (*TmpfsConfig) {
|
||||
config := &TmpfsConfig{}
|
||||
return config
|
||||
}
|
||||
|
||||
func NewTmpfs(config TmpfsConfig) *Tmpfs {
|
||||
func NewTmpfs(config commonconfig.TmpfsConfig) *Tmpfs {
|
||||
tmpfs := &Tmpfs{
|
||||
record: make(map[string]int64),
|
||||
Config: config,
|
||||
record: make(map[string]int64),
|
||||
Config: config,
|
||||
recordLocks: make(map[string]*sync.Mutex),
|
||||
}
|
||||
tmpfs.wg.Add(1)
|
||||
@@ -61,7 +51,7 @@ func (tmpfs *Tmpfs) Record(filename string) {
|
||||
tmpfs.record[filename] = time.Now().Unix()
|
||||
}
|
||||
|
||||
func (tmpfs *Tmpfs) Exits(filename string) (bool) {
|
||||
func (tmpfs *Tmpfs) Exits(filename string) bool {
|
||||
_, ok := tmpfs.record[filename]
|
||||
return ok
|
||||
}
|
||||
@@ -77,7 +67,7 @@ func (tmpfs *Tmpfs) Cleaner() {
|
||||
lock.Unlock()
|
||||
continue
|
||||
}
|
||||
if now - recordTime > tmpfs.Config.FileLifeTime {
|
||||
if now-recordTime > tmpfs.Config.FileLifeTime {
|
||||
err = os.Remove(path)
|
||||
if err != nil {
|
||||
log.Println("[tmpfs] Failed to remove file", err)
|
||||
|
||||
Reference in New Issue
Block a user