// Package probe 负责媒体信息探测:图片尺寸读取、ffprobe 视频/音频解析与内嵌解码器释放。 package probe import ( "encoding/json" "fmt" "os/exec" "strconv" ) // ProbeResult 媒体探测结果(图片/视频/音频通用) type ProbeResult struct { HasVideo bool HasAudio bool Width int Height int Duration float64 VideoCodec string AudioCodec string Format string RawJSON string } type streamInfo struct { Index int `json:"index"` CodecType string `json:"codec_type"` CodecName string `json:"codec_name"` Width int `json:"width"` Height int `json:"height"` } type formatInfo struct { Duration string `json:"duration"` Format string `json:"format_name"` } type probeOutput struct { Streams []streamInfo `json:"streams"` Format formatInfo `json:"format"` } // ProbeMediaFile 用 ffprobe 探测媒体文件,一次调用同时获取视频/音频流与时长 func ProbeMediaFile(ffprobePath, filePath string) (*ProbeResult, error) { cmd := exec.Command(ffprobePath, "-v", "error", "-show_entries", "stream=index,codec_type,codec_name,width,height:format=duration,format_name", "-of", "json", filePath) output, err := cmd.Output() if err != nil { return nil, fmt.Errorf("ffprobe failed: %w", err) } var out probeOutput if err := json.Unmarshal(output, &out); err != nil { return nil, fmt.Errorf("parse ffprobe output: %w", err) } res := &ProbeResult{ Format: out.Format.Format, RawJSON: string(output), } if d, err := strconv.ParseFloat(out.Format.Duration, 64); err == nil { res.Duration = d } for _, s := range out.Streams { switch s.CodecType { case "video": res.HasVideo = true if res.VideoCodec == "" { res.VideoCodec = s.CodecName } if res.Width == 0 && s.Width > 0 && s.Height > 0 { res.Width, res.Height = s.Width, s.Height } case "audio": res.HasAudio = true if res.AudioCodec == "" { res.AudioCodec = s.CodecName } } } return res, nil }