mirror of
https://github.com/livepeer/lpms
synced 2025-11-01 12:03:00 +08:00
add from/to parametrs support for transcoding function
from/to specified in ms from segment start
This commit is contained in:
@@ -18,8 +18,9 @@ func validRenditions() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
var err error
|
||||||
if len(os.Args) <= 3 {
|
if len(os.Args) <= 3 {
|
||||||
panic("Usage: <input file> <output renditions, comma separated> <sw/nv>")
|
panic("Usage: <input file> <output renditions, comma separated> <sw/nv> <from> <to>")
|
||||||
}
|
}
|
||||||
str2accel := func(inp string) (ffmpeg.Acceleration, string) {
|
str2accel := func(inp string) (ffmpeg.Acceleration, string) {
|
||||||
if inp == "nv" {
|
if inp == "nv" {
|
||||||
@@ -42,17 +43,32 @@ func main() {
|
|||||||
fname := os.Args[1]
|
fname := os.Args[1]
|
||||||
profiles := str2profs(os.Args[2])
|
profiles := str2profs(os.Args[2])
|
||||||
accel, lbl := str2accel(os.Args[3])
|
accel, lbl := str2accel(os.Args[3])
|
||||||
|
var from, to time.Duration
|
||||||
|
if len(os.Args) > 4 {
|
||||||
|
from, err = time.ParseDuration(os.Args[4])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(os.Args) > 5 {
|
||||||
|
to, err = time.ParseDuration(os.Args[5])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
profs2opts := func(profs []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions {
|
profs2opts := func(profs []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions {
|
||||||
opts := []ffmpeg.TranscodeOptions{}
|
opts := []ffmpeg.TranscodeOptions{}
|
||||||
for i := range profs {
|
for i := range profs {
|
||||||
o := ffmpeg.TranscodeOptions{
|
o := ffmpeg.TranscodeOptions{
|
||||||
Oname: fmt.Sprintf("out_%s_%d_out.mkv", lbl, i),
|
Oname: fmt.Sprintf("out_%s_%d_out.mp4", lbl, i),
|
||||||
Profile: profs[i],
|
Profile: profs[i],
|
||||||
// Uncomment the following to test scene classifier
|
// Uncomment the following to test scene classifier
|
||||||
// Detector: &ffmpeg.DSceneAdultSoccer,
|
// Detector: &ffmpeg.DSceneAdultSoccer,
|
||||||
Accel: accel,
|
Accel: accel,
|
||||||
}
|
}
|
||||||
|
o.From = from
|
||||||
|
o.To = to
|
||||||
opts = append(opts, o)
|
opts = append(opts, o)
|
||||||
}
|
}
|
||||||
return opts
|
return opts
|
||||||
|
|||||||
@@ -1288,6 +1288,40 @@ func TestTranscoder_OutputFPS(t *testing.T) {
|
|||||||
outputFPS(t, Software)
|
outputFPS(t, Software)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTranscoderAPI_ClipInvalidConfig(t *testing.T) {
|
||||||
|
tc := NewTranscoder()
|
||||||
|
defer tc.StopTranscoder()
|
||||||
|
in := &TranscodeOptionsIn{}
|
||||||
|
out := []TranscodeOptions{{
|
||||||
|
Oname: "-",
|
||||||
|
VideoEncoder: ComponentOptions{Name: "drop"},
|
||||||
|
From: time.Second,
|
||||||
|
}}
|
||||||
|
|
||||||
|
_, err := tc.Transcode(in, out)
|
||||||
|
if err == nil || err != ErrTranscoderClipConfig {
|
||||||
|
t.Errorf("Expected '%s', got %v", ErrTranscoderClipConfig, err)
|
||||||
|
}
|
||||||
|
out[0].VideoEncoder.Name = "copy"
|
||||||
|
_, err = tc.Transcode(in, out)
|
||||||
|
if err == nil || err != ErrTranscoderClipConfig {
|
||||||
|
t.Errorf("Expected '%s', got %v", ErrTranscoderClipConfig, err)
|
||||||
|
}
|
||||||
|
out[0].From = 0
|
||||||
|
out[0].To = time.Second
|
||||||
|
_, err = tc.Transcode(in, out)
|
||||||
|
if err == nil || err != ErrTranscoderClipConfig {
|
||||||
|
t.Errorf("Expected '%s', got %v", ErrTranscoderClipConfig, err)
|
||||||
|
}
|
||||||
|
out[0].VideoEncoder.Name = ""
|
||||||
|
out[0].From = 10 * time.Second
|
||||||
|
out[0].To = time.Second
|
||||||
|
_, err = tc.Transcode(in, out)
|
||||||
|
if err == nil || err != ErrTranscoderClipConfig {
|
||||||
|
t.Errorf("Expected '%s', got %v", ErrTranscoderClipConfig, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
func detectionFreq(t *testing.T, accel Acceleration) {
|
func detectionFreq(t *testing.T, accel Acceleration) {
|
||||||
run, dir := setupTest(t)
|
run, dir := setupTest(t)
|
||||||
|
|||||||
@@ -26,17 +26,23 @@ static int add_video_stream(struct output_ctx *octx, struct input_ctx *ictx)
|
|||||||
} else if (octx->vc) {
|
} else if (octx->vc) {
|
||||||
st->time_base = octx->vc->time_base;
|
st->time_base = octx->vc->time_base;
|
||||||
ret = avcodec_parameters_from_context(st->codecpar, octx->vc);
|
ret = avcodec_parameters_from_context(st->codecpar, octx->vc);
|
||||||
if (octx->gop_time) {
|
// Rescale the gop/clip time to the expected timebase after filtering.
|
||||||
// Rescale the gop time to the expected timebase after filtering.
|
|
||||||
// The FPS filter outputs pts incrementing by 1 at a rate of 1/framerate
|
// The FPS filter outputs pts incrementing by 1 at a rate of 1/framerate
|
||||||
// while non-fps will retain the input timebase.
|
// while non-fps will retain the input timebase.
|
||||||
AVRational gop_tb = {1, 1000};
|
AVRational ms_tb = {1, 1000};
|
||||||
AVRational dest_tb;
|
AVRational dest_tb;
|
||||||
if (octx->fps.den) dest_tb = av_inv_q(octx->fps);
|
if (octx->fps.den) dest_tb = av_inv_q(octx->fps);
|
||||||
else dest_tb = ictx->ic->streams[ictx->vi]->time_base;
|
else dest_tb = ictx->ic->streams[ictx->vi]->time_base;
|
||||||
octx->gop_pts_len = av_rescale_q(octx->gop_time, gop_tb, dest_tb);
|
if (octx->gop_time) {
|
||||||
|
octx->gop_pts_len = av_rescale_q(octx->gop_time, ms_tb, dest_tb);
|
||||||
octx->next_kf_pts = 0; // force for first frame
|
octx->next_kf_pts = 0; // force for first frame
|
||||||
}
|
}
|
||||||
|
if (octx->clip_from) {
|
||||||
|
octx->clip_from_pts = av_rescale_q(octx->clip_from, ms_tb, dest_tb);
|
||||||
|
}
|
||||||
|
if (octx->clip_to) {
|
||||||
|
octx->clip_to_pts = av_rescale_q(octx->clip_to, ms_tb, dest_tb);
|
||||||
|
}
|
||||||
if (ret < 0) LPMS_ERR(add_video_err, "Error setting video params from encoder");
|
if (ret < 0) LPMS_ERR(add_video_err, "Error setting video params from encoder");
|
||||||
} else LPMS_ERR(add_video_err, "No video encoder, not a copy; what is this?");
|
} else LPMS_ERR(add_video_err, "No video encoder, not a copy; what is this?");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -444,6 +450,29 @@ int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext
|
|||||||
frame = NULL;
|
frame = NULL;
|
||||||
} else if (ret < 0) goto proc_cleanup;
|
} else if (ret < 0) goto proc_cleanup;
|
||||||
|
|
||||||
|
if (is_video && !octx->clip_start_pts_found && frame) {
|
||||||
|
octx->clip_start_pts = frame->pts;
|
||||||
|
octx->clip_start_pts_found = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (octx->clip_to && octx->clip_start_pts_found && frame && frame->pts > octx->clip_to_pts + octx->clip_start_pts) goto skip;
|
||||||
|
|
||||||
|
if (is_video) {
|
||||||
|
if (octx->clip_from && frame) {
|
||||||
|
if (frame->pts < octx->clip_from_pts + octx->clip_start_pts) goto skip;
|
||||||
|
if (!octx->clip_started) {
|
||||||
|
octx->clip_started = 1;
|
||||||
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
||||||
|
if (octx->gop_pts_len) {
|
||||||
|
octx->next_kf_pts = frame->pts + octx->gop_pts_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (octx->clip_from_pts && !octx->clip_started) {
|
||||||
|
// we want first frame to be video frame
|
||||||
|
goto skip;
|
||||||
|
}
|
||||||
|
|
||||||
// Set GOP interval if necessary
|
// Set GOP interval if necessary
|
||||||
if (is_video && octx->gop_pts_len && frame && frame->pts >= octx->next_kf_pts) {
|
if (is_video && octx->gop_pts_len && frame && frame->pts >= octx->next_kf_pts) {
|
||||||
frame->pict_type = AV_PICTURE_TYPE_I;
|
frame->pict_type = AV_PICTURE_TYPE_I;
|
||||||
@@ -458,6 +487,7 @@ int process_out(struct input_ctx *ictx, struct output_ctx *octx, AVCodecContext
|
|||||||
}
|
}
|
||||||
ret = encode(encoder, frame, octx, ost);
|
ret = encode(encoder, frame, octx, ost);
|
||||||
}
|
}
|
||||||
|
skip:
|
||||||
av_frame_unref(frame);
|
av_frame_unref(frame);
|
||||||
// For HW we keep the encoder open so will only get EAGAIN.
|
// For HW we keep the encoder open so will only get EAGAIN.
|
||||||
// Return EOF in place of EAGAIN for to terminate the flush
|
// Return EOF in place of EAGAIN for to terminate the flush
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
@@ -28,6 +29,7 @@ import "C"
|
|||||||
var ErrTranscoderRes = errors.New("TranscoderInvalidResolution")
|
var ErrTranscoderRes = errors.New("TranscoderInvalidResolution")
|
||||||
var ErrTranscoderHw = errors.New("TranscoderInvalidHardware")
|
var ErrTranscoderHw = errors.New("TranscoderInvalidHardware")
|
||||||
var ErrTranscoderInp = errors.New("TranscoderInvalidInput")
|
var ErrTranscoderInp = errors.New("TranscoderInvalidInput")
|
||||||
|
var ErrTranscoderClipConfig = errors.New("TranscoderInvalidClipConfig")
|
||||||
var ErrTranscoderVid = errors.New("TranscoderInvalidVideo")
|
var ErrTranscoderVid = errors.New("TranscoderInvalidVideo")
|
||||||
var ErrTranscoderStp = errors.New("TranscoderStopped")
|
var ErrTranscoderStp = errors.New("TranscoderStopped")
|
||||||
var ErrTranscoderFmt = errors.New("TranscoderUnrecognizedFormat")
|
var ErrTranscoderFmt = errors.New("TranscoderUnrecognizedFormat")
|
||||||
@@ -72,6 +74,8 @@ type TranscodeOptions struct {
|
|||||||
Accel Acceleration
|
Accel Acceleration
|
||||||
Device string
|
Device string
|
||||||
CalcSign bool
|
CalcSign bool
|
||||||
|
From time.Duration
|
||||||
|
To time.Duration
|
||||||
|
|
||||||
Muxer ComponentOptions
|
Muxer ComponentOptions
|
||||||
VideoEncoder ComponentOptions
|
VideoEncoder ComponentOptions
|
||||||
@@ -269,6 +273,16 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions)
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
for _, p := range ps {
|
||||||
|
if (p.From > 0 || p.To > 0) && (p.VideoEncoder.Name == "drop" || p.VideoEncoder.Name == "copy") {
|
||||||
|
glog.Warning("Could clip only when transcoding video")
|
||||||
|
return nil, ErrTranscoderClipConfig
|
||||||
|
}
|
||||||
|
if p.From > 0 && p.To > 0 && p.To < p.From {
|
||||||
|
glog.Warning("'To' should be after 'From'")
|
||||||
|
return nil, ErrTranscoderClipConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
fname := C.CString(input.Fname)
|
fname := C.CString(input.Fname)
|
||||||
defer C.free(unsafe.Pointer(fname))
|
defer C.free(unsafe.Pointer(fname))
|
||||||
if input.Transmuxing {
|
if input.Transmuxing {
|
||||||
@@ -438,6 +452,8 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions)
|
|||||||
name: C.CString(audioEncoder),
|
name: C.CString(audioEncoder),
|
||||||
opts: newAVOpts(p.AudioEncoder.Opts),
|
opts: newAVOpts(p.AudioEncoder.Opts),
|
||||||
}
|
}
|
||||||
|
fromMs := int(p.From.Milliseconds())
|
||||||
|
toMs := int(p.To.Milliseconds())
|
||||||
vfilt := C.CString(filters)
|
vfilt := C.CString(filters)
|
||||||
defer C.free(unsafe.Pointer(vidOpts.name))
|
defer C.free(unsafe.Pointer(vidOpts.name))
|
||||||
defer C.free(unsafe.Pointer(audioOpts.name))
|
defer C.free(unsafe.Pointer(audioOpts.name))
|
||||||
@@ -448,7 +464,7 @@ func (t *Transcoder) Transcode(input *TranscodeOptionsIn, ps []TranscodeOptions)
|
|||||||
}
|
}
|
||||||
params[i] = C.output_params{fname: oname, fps: fps,
|
params[i] = C.output_params{fname: oname, fps: fps,
|
||||||
w: C.int(w), h: C.int(h), bitrate: C.int(bitrate),
|
w: C.int(w), h: C.int(h), bitrate: C.int(bitrate),
|
||||||
gop_time: C.int(gopMs),
|
gop_time: C.int(gopMs), from: C.int(fromMs), to: C.int(toMs),
|
||||||
muxer: muxOpts, audio: audioOpts, video: vidOpts,
|
muxer: muxOpts, audio: audioOpts, video: vidOpts,
|
||||||
vfilters: vfilt, sfilters: nil, is_dnn: isDNN}
|
vfilters: vfilt, sfilters: nil, is_dnn: isDNN}
|
||||||
if p.CalcSign {
|
if p.CalcSign {
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ import (
|
|||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func setupTest(t *testing.T) (func(cmd string), string) {
|
func setupTest(t *testing.T) (func(cmd string), string) {
|
||||||
@@ -1602,3 +1606,83 @@ func TestTranscoder_ZeroFrameLongBadSegment(t *testing.T) {
|
|||||||
t.Errorf("Expecting false, got %v", res)
|
t.Errorf("Expecting false, got %v", res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTranscoder_Clip(t *testing.T) {
|
||||||
|
run, dir := setupTest(t)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
// If no format and no mux opts specified, should be based on file extension
|
||||||
|
in := &TranscodeOptionsIn{Fname: "../transcoder/test.ts"}
|
||||||
|
P144p30fps16x9 := P144p30fps16x9
|
||||||
|
P144p30fps16x9.Framerate = 0
|
||||||
|
out := []TranscodeOptions{{
|
||||||
|
Profile: P144p30fps16x9,
|
||||||
|
Oname: dir + "/test_0.mp4",
|
||||||
|
// Oname: "./test_0.mp4",
|
||||||
|
From: time.Second,
|
||||||
|
To: 3 * time.Second,
|
||||||
|
}}
|
||||||
|
res, err := Transcode3(in, out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, 480, res.Decoded.Frames)
|
||||||
|
assert.Equal(t, int64(442368000), res.Decoded.Pixels)
|
||||||
|
assert.Equal(t, 120, res.Encoded[0].Frames)
|
||||||
|
assert.Equal(t, int64(4423680), res.Encoded[0].Pixels)
|
||||||
|
|
||||||
|
cmd := `
|
||||||
|
# hardcode some checks for now. TODO make relative to source.
|
||||||
|
ffprobe -loglevel warning -select_streams v -show_streams -count_frames test_0.mp4 > test.out
|
||||||
|
grep start_pts=94410 test.out
|
||||||
|
grep start_time=1.049000 test.out
|
||||||
|
grep duration=2.000667 test.out
|
||||||
|
grep duration_ts=180060 test.out
|
||||||
|
grep nb_read_frames=120 test.out
|
||||||
|
`
|
||||||
|
run(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTranscoder_Clip2(t *testing.T) {
|
||||||
|
run, dir := setupTest(t)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
// If no format and no mux opts specified, should be based on file extension
|
||||||
|
in := &TranscodeOptionsIn{Fname: "../transcoder/test.ts"}
|
||||||
|
P144p30fps16x9 := P144p30fps16x9
|
||||||
|
P144p30fps16x9.GOP = 5 * time.Second
|
||||||
|
P144p30fps16x9.Framerate = 120
|
||||||
|
out := []TranscodeOptions{{
|
||||||
|
Profile: P144p30fps16x9,
|
||||||
|
Oname: dir + "/test_0.mp4",
|
||||||
|
// Oname: "./test_1.mp4",
|
||||||
|
From: time.Second,
|
||||||
|
To: 6 * time.Second,
|
||||||
|
}}
|
||||||
|
res, err := Transcode3(in, out)
|
||||||
|
require.NoError(t, err)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
assert.Equal(t, 480, res.Decoded.Frames)
|
||||||
|
assert.Equal(t, int64(442368000), res.Decoded.Pixels)
|
||||||
|
assert.Equal(t, 601, res.Encoded[0].Frames)
|
||||||
|
assert.Equal(t, int64(22155264), res.Encoded[0].Pixels)
|
||||||
|
|
||||||
|
cmd := `
|
||||||
|
# hardcode some checks for now. TODO make relative to source.
|
||||||
|
ffprobe -loglevel warning -select_streams v -show_streams -count_frames test_0.mp4 > test.out
|
||||||
|
grep start_pts=15867 test.out
|
||||||
|
grep start_time=1.033008 test.out
|
||||||
|
grep duration=5.008333 test.out
|
||||||
|
grep duration_ts=76928 test.out
|
||||||
|
grep nb_read_frames=601 test.out
|
||||||
|
|
||||||
|
# check that we have two keyframes
|
||||||
|
ffprobe -loglevel warning -hide_banner -show_frames test_0.mp4 | grep pict_type=I -c | grep 2
|
||||||
|
# check indexes of keyframes
|
||||||
|
ffprobe -loglevel warning -hide_banner -show_frames -show_entries frame=pict_type -of csv test_0.mp4 | grep -n "frame,I" | cut -d ':' -f 1 | awk 'BEGIN{ORS=":"} {print}' | grep '1:602:'
|
||||||
|
`
|
||||||
|
run(cmd)
|
||||||
|
}
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ struct output_ctx {
|
|||||||
|
|
||||||
int64_t gop_time, gop_pts_len, next_kf_pts; // for gop reset
|
int64_t gop_time, gop_pts_len, next_kf_pts; // for gop reset
|
||||||
|
|
||||||
|
int64_t clip_from, clip_to, clip_from_pts, clip_to_pts, clip_started, clip_start_pts, clip_start_pts_found; // for clipping
|
||||||
|
|
||||||
AVFilterGraph **dnn_filtergraph;
|
AVFilterGraph **dnn_filtergraph;
|
||||||
int is_dnn_profile; //if not dnn profile: 0
|
int is_dnn_profile; //if not dnn profile: 0
|
||||||
|
|
||||||
|
|||||||
@@ -170,6 +170,8 @@ int transcode(struct transcode_thread *h,
|
|||||||
if (params[i].bitrate) octx->bitrate = params[i].bitrate;
|
if (params[i].bitrate) octx->bitrate = params[i].bitrate;
|
||||||
if (params[i].fps.den) octx->fps = params[i].fps;
|
if (params[i].fps.den) octx->fps = params[i].fps;
|
||||||
if (params[i].gop_time) octx->gop_time = params[i].gop_time;
|
if (params[i].gop_time) octx->gop_time = params[i].gop_time;
|
||||||
|
if (params[i].from) octx->clip_from = params[i].from;
|
||||||
|
if (params[i].to) octx->clip_to = params[i].to;
|
||||||
octx->dv = ictx->vi < 0 || is_drop(octx->video->name);
|
octx->dv = ictx->vi < 0 || is_drop(octx->video->name);
|
||||||
octx->da = ictx->ai < 0 || is_drop(octx->audio->name);
|
octx->da = ictx->ai < 0 || is_drop(octx->audio->name);
|
||||||
octx->res = &results[i];
|
octx->res = &results[i];
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ typedef struct {
|
|||||||
char *fname;
|
char *fname;
|
||||||
char *vfilters;
|
char *vfilters;
|
||||||
char *sfilters;
|
char *sfilters;
|
||||||
int w, h, bitrate, gop_time;
|
int w, h, bitrate, gop_time, from, to;
|
||||||
AVRational fps;
|
AVRational fps;
|
||||||
int is_dnn;
|
int is_dnn;
|
||||||
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -7,6 +7,6 @@ require (
|
|||||||
github.com/golang/protobuf v1.5.2
|
github.com/golang/protobuf v1.5.2
|
||||||
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded
|
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded
|
||||||
github.com/livepeer/m3u8 v0.11.1
|
github.com/livepeer/m3u8 v0.11.1
|
||||||
|
github.com/stretchr/testify v1.3.0
|
||||||
google.golang.org/protobuf v1.26.0
|
google.golang.org/protobuf v1.26.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -5,20 +5,18 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA=
|
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded h1:ZQlvR5RB4nfT+cOQee+WqmaDOgGtP2oDMhcVvR4L0yA=
|
||||||
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw=
|
github.com/livepeer/joy4 v0.1.2-0.20191121080656-b2fea45cbded/go.mod h1:xkDdm+akniYxVT9KW1Y2Y7Hso6aW+rZObz3nrA9yTHw=
|
||||||
github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU=
|
github.com/livepeer/m3u8 v0.11.1 h1:VkUJzfNTyjy9mqsgp5JPvouwna8wGZMvd/gAfT5FinU=
|
||||||
github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04=
|
github.com/livepeer/m3u8 v0.11.1/go.mod h1:IUqAtwWPAG2CblfQa4SVzTQoDcEMPyfNOaBSxqHMS04=
|
||||||
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/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
|
||||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||||
|
|||||||
Reference in New Issue
Block a user