golang调用ffmpeg根据帧率截取视频中的图片和调用ffmpeg获取视频时长

编程入门 行业动态 更新时间:2024-10-26 14:28:08

golang调用ffmpeg根据帧率截取<a href=https://www.elefans.com/category/jswz/34/1771437.html style=视频中的图片和调用ffmpeg获取视频时长"/>

golang调用ffmpeg根据帧率截取视频中的图片和调用ffmpeg获取视频时长

ffmpeg二进制地址:

根据下面代码可以测试三种case:

1:通过视频地址,在线边解码,截取图片信息

2:通过视频地址,下载视频到本地,使用本地视频文件,截取图片信息

3:通过视频地址,下载视频到本地,使用本地视频文件,使用ss参数获取视频截帧文件。

三种case的测试结果为:

780s视频:5秒截取一帧

视频地址:=207&fhash=f225917356e4ea8f0e551428699f0d716f2f029f&sz=126346680&e=1583723435&uid=323340911&validity=0&keyspace=1&s=3976267b36cf73b886c626d5bbb2f3e2
方案1:73163ms 截取出来158帧

方案2:先下载,再处理。新方案耗时:133671ms 下载时间:67160ms  处理时间:66511ms  截取出来156帧
方案3:先下载,ss再处理。新方案2耗时:68591ms 下载时间:67188ms  处理时间:1388ms   截取出来43帧

视频地址:=qie&outId=RFjFgL4-b_Nak5jHVQNmW-FWpzk_1ECtx4LDq1fh0JSrtKtZgrkxSG3C8WceTjLQn1TvKQ89op-qEdfHKm56lF0J7nVhKwTzub1
209s视频:5秒截取一帧
893
方案1:7293ms 截取出来44帧

方案2:先下载,再处理。  新方案1耗时:8724ms 下载时间:1587ms  处理时间:7136ms   截取出来43帧
方案3:先下载,ss再处理。新方案2耗时:2103ms 下载时间:1871ms  处理时间:218ms   截取出来43帧

视频地址:.mp4_bd.mp4
205s视频:5秒截取一帧
893
方案1:3779ms 截取出来43帧

方案2:先下载,再处理。新方案耗时:4899ms 下载时间:1212ms  处理时间:3686ms   截取出来43帧
方案3:先下载,ss再处理。新方案2耗时:637ms 下载时间:377ms  处理时间:205ms   截取出来43帧

结论:第三种情况处理速度会有提升

package main

import (
    "errors"
    "fmt"
    "io/ioutil"
    "net"
    "net/http"
    "net/url"
    "os"
    "strings"
    "time"
    "path/filepath"
    "context"
    "syscall"
    "regexp"
    "os/exec"
    "strconv"
    "bytes"
)

type HttpClient struct {
    Client http.Client
    AddrIp string
}

func NewHttpClientImage(connTimeout time.Duration, readTimeout time.Duration, newhttpclient *HttpClient) {
    client := http.Client{
        Transport: &http.Transport{
        Dial: func(netw, addr string) (net.Conn, error) {
            c, err := net.DialTimeout(netw, addr, connTimeout)
                if err != nil {
                    return nil, err
                }
                newhttpclient.AddrIp = c.RemoteAddr().String()
                c.SetDeadline(time.Now().Add(readTimeout))
                return c, nil
            },
        },
    }
    newhttpclient.Client = client
 }

func (this *HttpClient) Get(httpUrl string, postParams map[string]string, referer bool) ([]byte, error) {
    u, err := url.Parse(httpUrl)
    if err != nil {
        return nil, err
    }
    q := u.Query()
    for key, value := range postParams {
        q.Set(key, value)
    }
    u.RawQuery = q.Encode()
    url := ""
    if postParams != nil {
        url = u.String()
    } else {
        url = httpUrl
    }
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("user-agent", "shumei")
    if referer {
        req.Header.Set("referer", "")//vipkid使用
    }
    resp, reqErr := this.Client.Do(req)

    if reqErr != nil {
        return nil, reqErr
    }
    defer resp.Body.Close()
    var Codeerr error
    switch resp.StatusCode {
            case 200:
                break
        default:
            Codeerr = errors.New(fmt.Sprintf("Server get request error,statuscode: %v",resp.StatusCode))
            break
       }
    if Codeerr != nil {
        return nil,Codeerr
    }

    data, respErr := ioutil.ReadAll(resp.Body)
    if respErr != nil {
        return nil, respErr
    }
    return data, nil
}
 

func (this *HttpClient) Post(httpUrl string, headers map[string]string, body string) (string, error) {
    req, _ := http.NewRequest("POST", httpUrl, strings.NewReader(body))
    for key, value := range headers {
        req.Header.Set(key, value)
    }
    resp, reqErr := this.Client.Do(req)
    if reqErr != nil {
        return "", reqErr
    }
    defer resp.Body.Close()
    data, respErr := ioutil.ReadAll(resp.Body)
    if respErr != nil {
        return "", respErr
    }
    return string(data), nil
}

//README:在可执行文件当前文件夹下建立images文件夹用于保存截取出来的图片

//参数1:图片地址

func main() {

    dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
    fmt.Println("dir:", dir)
    var ffmpegPath string = dir + "/ffmpeg"
    urlpath:=os.Args[1]
    savefilename := dir+"/video.mp4"
    st := time.Now().UnixNano()
    //var freq float64 = 60

    //根据视频地址,下载视频到本地
    if true {
        client := HttpClient{}
        ConnectTimeout := time.Duration(1000)*time.Nanosecond*1e9
        ReadTimeout := time.Duration(1000)*time.Nanosecond*1e9
        NewHttpClientImage(ConnectTimeout, ReadTimeout, &client)
        imgbytes, err := client.Get(urlpath, nil, false)
        urlpath = savefilename
        ioutil.WriteFile(savefilename, imgbytes, 0777)
        fmt.Println("savefilename:", savefilename)
        fmt.Println("download err:", err)
    }

    cost := float64((time.Now().UnixNano()-st)/1000000)
    fmt.Println("download timecost:", cost)

    if true {
        path := "./images/"
        if err := os.MkdirAll(path, os.ModePerm); err != nil {
            fmt.Println("mkdir failed!!!")
        }
        //ffmpeg使用命令: ffmpeg -i .mp4 -r 1 -t 4 -f image2 image-%05d.jpeg
        /*
            -t 代表持续时间,单位为秒
            -f 指定保存图片使用的格式,可忽略。
            -r 指定抽取的帧率,即从视频中每秒钟抽取图片的数量。1代表每秒抽取一帧。
            -ss 指定起始时间
            -vframes 指定抽取的帧数
        */
        videoLen, _ := GenerateLength(ffmpegPath, urlpath, "1111111111")
        testFfmpegParams(urlpath, path, ffmpegPath, 60, videoLen)
        //invokeFfmpeg(urlpath, path, ffmpegPath, freq)
        //getLastFrame(urlpath, path, ffmpegPath)
    }
    cost = float64((time.Now().UnixNano()-st)/1000000)
    fmt.Println("download and process timecost:", cost)
}

//通过-ss参数 获取视频中的图片帧
func testFfmpegParams(url string, path string, ffmpegPath string, freq int, videoLen int) string {
            st := time.Now().UnixNano()
            var outputerror string
            for i:=0; i<videoLen; i=i+freq {
                sec := strconv.Itoa(i)
                ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50000)*time.Millisecond)
                cmd := exec.CommandContext(ctx, ffmpegPath,
                    "-loglevel", "error",
                    "-y",
                    "-ss", sec,
                    "-t", "1",
                    "-i", url,
                    "-vframes","1",
                    path+"/"+ sec +".jpg")
                defer cancel()
                var stderr bytes.Buffer
                cmd.Stderr = &stderr
                err := cmd.Run()
                if err != nil {
                    outputerror += fmt.Sprintf("lastframecmderr:%v;", err)
                }
                if stderr.Len() != 0 {
                    outputerror += fmt.Sprintf("lastframestderr:%v;", stderr.String())
                }
                if ctx.Err() != nil {
                    outputerror += fmt.Sprintf("lastframectxerr:%v;", ctx.Err())
                }
            }
            cost := float64((time.Now().UnixNano()-st)/1000000)
            fmt.Println("jiezhencost:", cost)
            return outputerror
}

//获取视频时长,通过正则解析日志获取

func GenerateLength(ffmpegPath string, url string, reqId string) (int, error) {
    st := time.Now().UnixNano()
    var length int
    for i := 0; i < 2; i++ {
        //ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Conf.ConfigMap.VideoC.CalcLengthTimeout)*time.Millisecond)
        //视频处理使用,延长超时时间
        ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
        cmd := exec.CommandContext(ctx, ffmpegPath,
            "-i", url)
        defer cancel()
        var stdout bytes.Buffer
        var stderr bytes.Buffer
        cmd.Stdout = &stdout
        cmd.Stderr = &stderr
        // always return err exit 1; do not catch
        cmd.Run()
        str := video_length_regexp.FindString(stderr.String())
        str = "2006-01-02" + strings.TrimPrefix(str, "Duration:")
        if videotime, err := time.Parse("2006-01-02 15:04:05", str); err != nil {
            if ctx.Err() != nil {
                fmt.Println("GenerateLength Err:", ctx.Err())
            }
            length = 0
        } else {
            length = videotime.Hour()*3600 + videotime.Minute()*60 + videotime.Second()
            break
        }
    }
    cost := float64((time.Now().UnixNano()-st)/1000000)
    fmt.Println("videolengthcost:", cost)
    fmt.Println("---------->>>videolength:", length)
    return length, nil
}

//获取视频中最后一帧的图片
func getLastFrame(url string, path string, ffmpegPath string) string {
            fmt.Println("url:", url)
            fmt.Println("ffmpeg:", ffmpegPath)
            ctx, cancel := context.WithTimeout(context.Background(), time.Duration(50000)*time.Millisecond)
            cmd := exec.CommandContext(ctx, ffmpegPath,
                "-timeout", "60000000",
                "-loglevel", "error",
                "-y",
                "-ss", "13",
                "-t", "1",
                "-i", url,
                "-vframes","1",
                path+"/"+"lastfram.jpg")
            defer cancel()
            var stderr bytes.Buffer
            cmd.Stderr = &stderr
            var outputerror string
            fmt.Println("lastframpath:", path+"/"+"lastfram.jpg")
            err := cmd.Run()
            fmt.Println("zuihouyzihenerr:", err)
            if err != nil {
                outputerror += fmt.Sprintf("lastframecmderr:%v;", err)
            }
            if stderr.Len() != 0 {
                outputerror += fmt.Sprintf("lastframestderr:%v;", stderr.String())
            }
            if ctx.Err() != nil {
                outputerror += fmt.Sprintf("lastframectxerr:%v;", ctx.Err())
            }
            fmt.Println("outputerror:", outputerror)
            return outputerror
}

//通过帧率获取视频中的图片和testFfmpegParams函数一样功能.

func invokeFfmpeg(urlpath string, path string, ffmpegPath string, Freq float64) {
fmt.Println("urlpath:", urlpath)
        currentTime := time.Now().UnixNano()
        //ffmpeg -i '.m3u8' -r 1 -t 200 -f image2 images/image-%05d.jpeg  //中央电视台视频流中的图片
        //ffmpeg -i '.m3u8' -r 10 -vcodec copy video/aaaaa.mp4    //copy中央电视台的视频流中的视频
        //var Freq float64 = 5 //设置多少秒截一帧
        ctx, cancel := context.WithTimeout(context.Background(), time.Duration(14400000)*time.Millisecond)
        //cmd := exec.CommandContext(ctx, ffmpegPath,
        //    "-i", urlpath,
        //  "-vcodec", "copy",
        //  path+"/"+"tttt.mp4")
        cmd := exec.CommandContext(ctx, ffmpegPath,
            "-loglevel", "error",
            "-i", urlpath,
            "-f", "image2",
            "-r", strconv.FormatFloat(float64(1/Freq), 'e', -1, 64),
            path+"/" + "%04d.jpg")
        cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
        defer cancel()
        var stderr bytes.Buffer
        cmd.Stderr = &stderr
        var outputerror string
        err := cmd.Run()
        if err != nil {
            outputerror += fmt.Sprintf("cmderr:%v;", err)
        }
        if stderr.Len() != 0 {
            outputerror += fmt.Sprintf("stderr:%v;", stderr.String())
        }
        if ctx.Err() != nil {
            outputerror += fmt.Sprintf("ctxerr:%v;", ctx.Err())
        }
        cost := float64((time.Now().UnixNano()-currentTime)/1000000)
fmt.Println("invokeFfmpeg err:", outputerror)
        fmt.Println("invokeFfmpeg videolengthcost:", cost)
}
 

 

 

 

更多推荐

golang调用ffmpeg根据帧率截取视频中的图片和调用ffmpeg获取视频时长

本文发布于:2024-03-06 07:54:24,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1714777.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:视频   时长   图片   golang   ffmpeg

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!