ffmpeg二进制地址:

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

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

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

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

三种case的测试结果为:

780s视频:5秒截取一帧

视频地址:http://dl-shanghai.oss.yunpan.360.cn/oss/yvideo/dWZpUDduQ00zaVkubXA0?scid=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帧

视频地址:https://content.cc.heytapmobi.com/resource/interact/playurl302?source=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帧

视频地址:https://voppo1.go2yd.com/user_upload/15829702606628cb8e1dc708bec91f7320db323803fdc.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", "https://www.fengkongcloud.com")//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 http://video.pearvideo.com/head/20180301/cont-1288289-11630613.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 'http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8' -r 1 -t 200 -f image2 images/image-%05d.jpeg  //中央电视台视频流中的图片
         //ffmpeg -i 'http://ivi.bupt.edu.cn/hls/cctv1hd.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)
 }