diff --git a/core/transcoder.go b/core/transcoder.go index 4cc0571328..f45b152b9a 100644 --- a/core/transcoder.go +++ b/core/transcoder.go @@ -36,7 +36,7 @@ func (lt *LocalTranscoder) Transcode(md *SegTranscodingMetadata) (*TranscodeData Accel: ffmpeg.Software, } profiles := md.Profiles - opts := profilesToTranscodeOptions(lt.workDir, ffmpeg.Software, profiles) + opts := profilesToTranscodeOptions(lt.workDir, ffmpeg.Software, profiles, md.CalcPerceptualHash) if md.DetectorEnabled { opts = append(opts, detectorsToTranscodeOptions(lt.workDir, ffmpeg.Software, md.DetectorProfiles)...) } @@ -77,7 +77,7 @@ func (nv *NvidiaTranscoder) Transcode(md *SegTranscodingMetadata) (*TranscodeDat Device: nv.device, } profiles := md.Profiles - out := profilesToTranscodeOptions(WorkDir, ffmpeg.Nvidia, profiles) + out := profilesToTranscodeOptions(WorkDir, ffmpeg.Nvidia, profiles, md.CalcPerceptualHash) if md.DetectorEnabled { out = append(out, detectorsToTranscodeOptions(WorkDir, ffmpeg.Nvidia, md.DetectorProfiles)...) } @@ -189,7 +189,18 @@ func resToTranscodeData(res *ffmpeg.TranscodeResults, opts []ffmpeg.TranscodeOpt glog.Error("Cannot read transcoded output for ", oname) return nil, err } - segments = append(segments, &TranscodedSegmentData{Data: o, Pixels: res.Encoded[i].Pixels}) + // Extract perceptual hash if calculated + var s []byte = nil + if opts[i].CalcSign { + sigfile := oname + ".bin" + s, err = ioutil.ReadFile(sigfile) + if err != nil { + glog.Error("Cannot read perceptual hash at ", sigfile) + return nil, err + } + os.Remove(sigfile) + } + segments = append(segments, &TranscodedSegmentData{Data: o, Pixels: res.Encoded[i].Pixels, PHash: s}) os.Remove(oname) } else { detections = append(detections, res.Encoded[i].DetectData) @@ -203,7 +214,7 @@ func resToTranscodeData(res *ffmpeg.TranscodeResults, opts []ffmpeg.TranscodeOpt }, nil } -func profilesToTranscodeOptions(workDir string, accel ffmpeg.Acceleration, profiles []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions { +func profilesToTranscodeOptions(workDir string, accel ffmpeg.Acceleration, profiles []ffmpeg.VideoProfile, calcPhash bool) []ffmpeg.TranscodeOptions { opts := make([]ffmpeg.TranscodeOptions, len(profiles), len(profiles)) for i := range profiles { o := ffmpeg.TranscodeOptions{ @@ -211,6 +222,7 @@ func profilesToTranscodeOptions(workDir string, accel ffmpeg.Acceleration, profi Profile: profiles[i], Accel: accel, AudioEncoder: ffmpeg.ComponentOptions{Name: "copy"}, + CalcSign: calcPhash, } opts[i] = o } diff --git a/core/transcoder_test.go b/core/transcoder_test.go index 191a77ab58..51cf79d8e5 100644 --- a/core/transcoder_test.go +++ b/core/transcoder_test.go @@ -151,6 +151,23 @@ func TestResToTranscodeData(t *testing.T) { assert.Equal(int64(300), tData.Segments[1].Pixels) assert.True(fileDNE(file1.Name())) assert.True(fileDNE(file2.Name())) + + // Test signature file + res = &ffmpeg.TranscodeResults{Encoded: make([]ffmpeg.MediaInfo, 1)} + pHash := []byte{4, 2, 0, 6, 9} + + file1, err = ioutil.TempFile(tempDir, "foo") + require.Nil(err) + ioutil.WriteFile(file1.Name()+".bin", pHash, 0664) + + opts = make([]ffmpeg.TranscodeOptions, 1) + opts[0].Oname = file1.Name() + opts[0].CalcSign = true + + tData, err = resToTranscodeData(res, opts) + assert.Nil(err) + assert.Equal(tData.Segments[0].PHash, pHash) + assert.True(fileDNE(file1.Name())) } func TestProfilesToTranscodeOptions(t *testing.T) { @@ -166,12 +183,12 @@ func TestProfilesToTranscodeOptions(t *testing.T) { // Test 0 profiles profiles := []ffmpeg.VideoProfile{} - opts := profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles) + opts := profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles, false) assert.Equal(0, len(opts)) // Test 1 profile profiles = []ffmpeg.VideoProfile{ffmpeg.P144p30fps16x9} - opts = profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles) + opts = profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles, false) assert.Equal(1, len(opts)) assert.Equal("foo/out_bar.tempfile", opts[0].Oname) assert.Equal(ffmpeg.Software, opts[0].Accel) @@ -180,7 +197,7 @@ func TestProfilesToTranscodeOptions(t *testing.T) { // Test > 1 profile profiles = []ffmpeg.VideoProfile{ffmpeg.P144p30fps16x9, ffmpeg.P240p30fps16x9} - opts = profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles) + opts = profilesToTranscodeOptions(workDir, ffmpeg.Software, profiles, false) assert.Equal(2, len(opts)) for i, p := range profiles { @@ -191,9 +208,14 @@ func TestProfilesToTranscodeOptions(t *testing.T) { } // Test different acceleration value - opts = profilesToTranscodeOptions(workDir, ffmpeg.Nvidia, profiles) + opts = profilesToTranscodeOptions(workDir, ffmpeg.Nvidia, profiles, false) assert.Equal(2, len(opts)) + // Test signature calculation + opts = profilesToTranscodeOptions(workDir, ffmpeg.Nvidia, profiles, true) + assert.True(opts[0].CalcSign) + assert.True(opts[1].CalcSign) + for i, p := range profiles { assert.Equal("foo/out_bar.tempfile", opts[i].Oname) assert.Equal(ffmpeg.Nvidia, opts[i].Accel) diff --git a/go.mod b/go.mod index 888301826c..98ef533b30 100644 --- a/go.mod +++ b/go.mod @@ -33,11 +33,11 @@ require ( github.com/jaypipes/ghw v0.7.0 github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 // indirect github.com/livepeer/livepeer-data v0.1.0 - github.com/livepeer/lpms v0.0.0-20210806125031-9fdbf80c8575 + github.com/livepeer/lpms v0.0.0-20210818144512-f639535652af github.com/livepeer/m3u8 v0.11.1 github.com/mattn/go-sqlite3 v1.11.0 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/olekukonko/tablewriter v0.0.1 + github.com/olekukonko/tablewriter v0.0.5 github.com/oschwald/maxminddb-golang v1.5.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pborman/uuid v1.2.0 // indirect diff --git a/go.sum b/go.sum index b9c8b4b755..2364a567f5 100644 --- a/go.sum +++ b/go.sum @@ -262,8 +262,8 @@ github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cO github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw= github.com/livepeer/livepeer-data v0.1.0 h1:TTVewBp0devrEpOwVKtgUAxYyjMjfTOQNTU4B04H2Ww= github.com/livepeer/livepeer-data v0.1.0/go.mod h1:vkW1PJ24gOJgx1hHUo07cXkR1b409n+dGpyWJsHKI3s= -github.com/livepeer/lpms v0.0.0-20210806125031-9fdbf80c8575 h1:nHz2JnaHXGt46frHtS62lCOTZHu2tZa7lPgemx81XNA= -github.com/livepeer/lpms v0.0.0-20210806125031-9fdbf80c8575/go.mod h1:uP4lJq1Mpz7ytsGP7lGiE3Pe//6AVlSETQ5X5rv8hp8= +github.com/livepeer/lpms v0.0.0-20210818144512-f639535652af h1:2zGphc7jU1RQUvkUJERpOaJtKkp34jdQ3VMER7qFbok= +github.com/livepeer/lpms v0.0.0-20210818144512-f639535652af/go.mod h1:iIvqFRRSIcouTWFheH2/TeDAq7xTVOJNR0eAS84o754= github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU= github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -273,8 +273,9 @@ github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.3 h1:a+kO+98RDGEfo6asOGMmpodZq4FNtnGP54yps8BzLR4= github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q= github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -301,8 +302,8 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= diff --git a/install_ffmpeg.sh b/install_ffmpeg.sh index ba415a9202..9af14c1375 100755 --- a/install_ffmpeg.sh +++ b/install_ffmpeg.sh @@ -126,7 +126,7 @@ fi if [ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]; then git clone https://github.com/livepeer/FFmpeg.git "$ROOT/ffmpeg" || echo "FFmpeg dir already exists" cd "$ROOT/ffmpeg" - git checkout b94707426b64d1c4fe24ec4e711dd0ccf9b20b74 + git checkout 33b062b3fe6ef3dce2320b3b5b218d29c366815c ./configure ${TARGET_OS:-} --fatal-warnings \ --disable-programs --disable-doc --disable-sdl2 --disable-iconv \ --disable-muxers --disable-demuxers --disable-parsers --disable-protocols \ @@ -138,7 +138,7 @@ if [ ! -e "$ROOT/ffmpeg/libavcodec/libavcodec.a" ]; then --enable-bsf=h264_mp4toannexb,aac_adtstoasc,h264_metadata,h264_redundant_pps,extract_extradata \ --enable-parser=aac,aac_latm,h264 \ --enable-filter=abuffer,buffer,abuffersink,buffersink,afifo,fifo,aformat,format \ - --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn \ + --enable-filter=aresample,asetnsamples,fps,scale,hwdownload,select,livepeer_dnn,signature \ --enable-encoder=aac,libx264 \ --enable-decoder=aac,h264 \ --extra-cflags="-I${ROOT}/compiled/include" \ diff --git a/server/segment_rpc.go b/server/segment_rpc.go index 00f45a8a1a..a9d60beffc 100644 --- a/server/segment_rpc.go +++ b/server/segment_rpc.go @@ -192,6 +192,16 @@ func (h *lphttp) ServeSegment(w http.ResponseWriter, r *http.Request) { Url: uri, Pixels: res.TranscodeData.Segments[i].Pixels, } + // Save perceptual hash if generated + if res.TranscodeData.Segments[i].PHash != nil { + pHashFile := name + ".phash" + pHashUri, err := res.OS.SaveData(pHashFile, res.TranscodeData.Segments[i].PHash, nil, 0) + if err != nil { + glog.Error("Could not upload segment perceptual hash for ", segData.Seq) + break + } + d.PerceptualHashUrl = pHashUri + } segments = append(segments, d) }