-
Notifications
You must be signed in to change notification settings - Fork 0
/
get_frame.go
executable file
·110 lines (106 loc) · 2.71 KB
/
get_frame.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package server
import (
"bytes"
"context"
"image/jpeg"
"io"
"net/http"
"strconv"
"time"
"github.com/pkg/errors"
"github.com/thavlik/t4vd/base/pkg/base"
"github.com/thavlik/t4vd/slideshow/pkg/imgcache"
"github.com/thavlik/t4vd/slideshow/pkg/slideshow"
"go.uber.org/zap"
)
func getFrame(
ctx context.Context,
imgCache imgcache.ImgCache,
bucket string,
videoID string,
t time.Duration,
log *zap.Logger,
) (data []byte, err error) {
log = log.With(
zap.String("id", videoID),
zap.Int64("t", int64(t)))
start := time.Now()
data, err = imgCache.GetImage(ctx, videoID, t)
if err == nil {
log.Debug("retrieved frame from imgcache", base.Elapsed(start))
return data, nil
} else if err == imgcache.ErrNotCached {
// Get frame from bucket
log.Debug("extracting frame from bucket")
frame, err := slideshow.GetSingleFrameFromBucket(
ctx,
bucket,
videoID+".webm",
t,
)
if err != nil {
return nil, errors.Wrap(err, "GetSingleFrameFromBucket")
}
var buf bytes.Buffer
if err := jpeg.Encode(&buf, frame, &jpeg.Options{
Quality: 100,
}); err != nil {
return nil, errors.Wrap(err, "jpeg.Encode")
}
data = buf.Bytes()
// Synchrously cache the image. Used by filter microservice
// to ensure the stack is good to go. This needs to sync
// so the frame can be discarded if imgcache fails.
if err := imgCache.SetImage(videoID, t, data); err != nil {
return nil, errors.Wrap(err, "imgcache.SetImage")
}
log.Debug("retrieved frame from bucket",
base.Elapsed(start))
return data, nil
}
return nil, errors.Wrap(err, "imgcache.GetImage")
}
func (s *Server) handleGetFrame() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if err := func() error {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusBadRequest)
return nil
}
w.Header().Set("Content-Type", "image/jpeg")
videoID := r.URL.Query().Get("v")
if videoID == "" {
return errors.New("missing videoID from query")
}
tv, err := strconv.ParseInt(r.URL.Query().Get("t"), 10, 64)
if err != nil {
return errors.Wrap(err, "parse time query")
}
noDownload := r.URL.Query().Get("nodownload") == "1"
t := time.Duration(tv)
s.log.Debug("handleGetFrame",
zap.String("videoID", videoID),
zap.String("t", t.String()),
zap.Bool("noDownload", noDownload))
data, err := getFrame(
r.Context(),
s.imgCache,
s.bucket,
videoID,
t,
s.log,
)
if err != nil {
return errors.Wrap(err, "getFrame")
}
if !noDownload {
if _, err := io.Copy(w, bytes.NewReader(data)); err != nil {
return errors.Wrap(err, "copy")
}
}
return nil
}(); err != nil {
s.log.Error("get frame handler error", zap.Error(err))
}
}
}