Skip to content

Instantly share code, notes, and snippets.

@j0sh

j0sh/Makefile Secret

Last active October 16, 2020 14:40
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save j0sh/640cadfe39b759c38f953baca11e78b0 to your computer and use it in GitHub Desktop.
Save j0sh/640cadfe39b759c38f953baca11e78b0 to your computer and use it in GitHub Desktop.
LPMS Benchmarking
package main
import (
"bufio"
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
//"runtime/pprof"
"github.com/livepeer/lpms/ffmpeg"
"github.com/livepeer/m3u8"
)
type segData struct {
tc *ffmpeg.Transcoder
seg *m3u8.MediaSegment
segNum int
stream int
}
type segChan chan *segData
func validRenditions() []string {
valids := make([]string, len(ffmpeg.VideoProfileLookup))
for p := range ffmpeg.VideoProfileLookup {
valids = append(valids, p)
}
return valids
}
func str2profs(inp string) []ffmpeg.VideoProfile {
profs := []ffmpeg.VideoProfile{}
strs := strings.Split(inp, ",")
for _, k := range strs {
p, ok := ffmpeg.VideoProfileLookup[k]
if !ok {
panic(fmt.Sprintf("Invalid rendition %s. Valid renditions are:\n%s", k, validRenditions()))
}
profs = append(profs, p)
}
return profs
}
func main() {
const usage = "Expected: [input file] [output prefix] [# concurrents] [# segments] [profiles] [sw/nv] <nv-device>"
/*
cprof, err := os.Create("bench.prof")
if err != nil {
panic(err)
}
pprof.StartCPUProfile(cprof)
defer pprof.StopCPUProfile()
*/
if len(os.Args) <= 6 {
panic(usage)
}
fname := os.Args[1]
f, err := os.Open(fname)
if err != nil {
panic(err)
}
p, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true)
if err != nil {
panic(err)
}
pl, ok := p.(*m3u8.MediaPlaylist)
if !ok {
panic("Expecting media PL")
}
//pfx := os.Args[2]
conc, err := strconv.Atoi(os.Args[3])
if err != nil {
panic(err)
}
segs, err := strconv.Atoi(os.Args[4])
if err != nil {
panic(err)
}
profiles := str2profs(os.Args[5])
accelStr := os.Args[6]
accel := ffmpeg.Software
devices := []string{}
if "nv" == accelStr {
accel = ffmpeg.Nvidia
if len(os.Args) <= 7 {
panic(usage)
}
devices = strings.Split(os.Args[7], ",")
}
ffmpeg.InitFFmpeg()
var wg sync.WaitGroup
dir := path.Dir(fname)
start := time.Now()
fmt.Fprintf(os.Stderr, "Source %s segments %d concurrency %d\n", fname, segs, conc)
fmt.Println("time,stream,segment,length")
transcodeLoop := func(gpu string, ch segChan, quit chan struct{}) {
for {
select {
case <-quit:
return
case segData := <-ch:
u := path.Join(dir, segData.seg.URI)
in := &ffmpeg.TranscodeOptionsIn{
Fname: u,
Accel: accel,
}
if ffmpeg.Software != accel {
in.Device = gpu
}
profs2opts := func(profs []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions {
opts := []ffmpeg.TranscodeOptions{}
//for n, p := range profs {
for _, p := range profs {
o := ffmpeg.TranscodeOptions{
//Oname: fmt.Sprintf("%s%s_%s_%d_%s_%d_%d_%d.ts", pfx, accelStr, p.Name, n, gpu, segData.stream, segData.stream, segData.segNum),
Oname: "-",
Muxer: ffmpeg.ComponentOptions{Name: "null"},
Profile: p,
Accel: accel,
AudioEncoder: ffmpeg.ComponentOptions{Name: "drop"},
}
opts = append(opts, o)
}
return opts
}
out := profs2opts(profiles)
t := time.Now()
_, err := segData.tc.Transcode(in, out)
end := time.Now()
fmt.Printf("%s,%s,%d,%d,%0.2v\n", end.Format("2006-01-02 15:04:05.999999999"), gpu, segData.stream, segData.segNum, end.Sub(t).Seconds())
wg.Done()
if err != nil {
panic(err)
}
}
}
}
ch := []segChan{}
quit := []chan struct{}{}
for i, d := range devices {
ch = append(ch, make(segChan, 1))
quit = append(quit, make(chan struct{}))
go transcodeLoop(d, ch[i], quit[i])
}
tcoders := []*ffmpeg.Transcoder{}
for i := 0; i < conc; i++ {
go func(k int, wg *sync.WaitGroup) {
device := i % len(devices)
tc := ffmpeg.NewTranscoder()
tcoders = append(tcoders, tc)
for j, v := range pl.Segments {
if j >= segs {
break
}
if v == nil {
continue
}
wg.Add(1)
ch[device] <- &segData{seg: v, segNum: j, stream: k, tc: tc}
}
}(i, &wg)
time.Sleep(300 * time.Millisecond)
}
wg.Wait()
fmt.Fprintf(os.Stderr, "Took %v to transcode %v segments\n",
time.Now().Sub(start).Seconds(), segs)
for _, v := range quit {
v <- struct{}{}
}
for _, v := range tcoders {
v.StopTranscoder()
}
}
#!/usr/bin/env bash
# This program takes the number of concurrents as a cli arg
if [ -z "$1" ]
then
echo "Expecting concurrent count"
exit 1
fi
trap exit SIGINT
for i in $(seq 0 $(($1-1)) )
do
echo "Starting ffmpeg stream $i with device $(( $i % 8 ))"
./ffmpeg-m3u8 $(( $i % 8 )) &
done
wait
#!/usr/bin/env bash
# This program takes the nvidia device as a cli arg
if [ -z "$1" ]
then
echo "Expecting nvidia device id"
exit 1
fi
# Expects segments following <input-name><seq-no>
# Puts outputs in folder out/
trap exit SIGINT
inp=in/bbb.m3u8
ffmpeg -hwaccel_device $1 -hwaccel cuvid -c:v h264_cuvid -i $inp \
-vf fps=30,scale_cuda=w=1280:h=720 -b:v 6000k -c:v h264_nvenc -f null - \
-vf fps=30,scale_cuda=w=1024:h=576 -b:v 1500k -c:v h264_nvenc -f null - \
-vf fps=30,scale_cuda=w=640:h=360 -b:v 1200k -c:v h264_nvenc -f null - \
-vf fps=30,scale_cuda=w=426:h=240 -b:v 600k -c:v h264_nvenc -f null - \
-loglevel warning -hide_banner -y
# source video
http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4
# ffmpeg segmentation command
ffmpeg -i bbb_sunflower_1080p_30fps_normal.mp4 -c:a copy -c:v libx264 -keyint_min 30 -g 30 -f hls -hls_time 2 -hls_playlist_type vod hls/bbb.m3u8
# equivalent ffmpeg transcoding command
time ffmpeg -benchmark -i hls/bbb.m3u8 -c:a aac -ac 2 -sample_rate 44100 -c:v libx264 -s hd720 -f null - -c:a aac -ac 2 -sample_rate 44100 -c:v libx264 -s 640x360 -f null - -c:a aac -ac 2 -sample_rate 44100 -c:v libx264 -s 426x240 -f null -
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
"sync"
"time"
//"runtime/pprof"
"github.com/livepeer/lpms/ffmpeg"
"github.com/livepeer/m3u8"
)
func validRenditions() []string {
valids := make([]string, len(ffmpeg.VideoProfileLookup))
for p := range ffmpeg.VideoProfileLookup {
valids = append(valids, p)
}
return valids
}
func str2profs(inp string) []ffmpeg.VideoProfile {
profs := []ffmpeg.VideoProfile{}
strs := strings.Split(inp, ",")
for _, k := range strs {
p, ok := ffmpeg.VideoProfileLookup[k]
if !ok {
panic(fmt.Sprintf("Invalid rendition %s. Valid renditions are:\n%s", k, validRenditions()))
}
profs = append(profs, p)
}
return profs
}
func main() {
const usage = "Expected: [input file] [output prefix] [# concurrents] [profiles] [sw/nv] <nv-device>"
if len(os.Args) <= 5 {
panic(usage)
}
fname := os.Args[1]
f, err := os.Open(fname)
if err != nil {
panic(err)
}
p, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true)
if err != nil {
panic(err)
}
_, ok := p.(*m3u8.MediaPlaylist)
if !ok {
panic("Expecting media PL")
}
//pfx := os.Args[2]
conc, err := strconv.Atoi(os.Args[3])
if err != nil {
panic(err)
}
profiles := str2profs(os.Args[4])
accelStr := os.Args[5]
accel := ffmpeg.Software
devices := []string{}
if "nv" == accelStr {
accel = ffmpeg.Nvidia
if len(os.Args) <= 6 {
panic(usage)
}
devices = strings.Split(os.Args[6], ",")
}
ffmpeg.InitFFmpeg()
var wg sync.WaitGroup
start := time.Now()
fmt.Fprintf(os.Stderr, "Source %s concurrency %d\n", fname, conc)
fmt.Println("time,stream")
for i := 0; i < conc; i++ {
wg.Add(1)
go func(k int, wg *sync.WaitGroup) {
in := &ffmpeg.TranscodeOptionsIn{
Fname: fname,
Accel: accel,
}
if ffmpeg.Software != accel {
in.Device = devices[k%len(devices)]
}
profs2opts := func(profs []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions {
opts := []ffmpeg.TranscodeOptions{}
for _, p := range profs {
o := ffmpeg.TranscodeOptions{
//Oname: fmt.Sprintf("%s%s_%s_%d.ts", pfx, accelStr, p.Name, k),
Oname: fmt.Sprintf("-"),
Profile: p,
Accel: accel,
Muxer: ffmpeg.ComponentOptions{Name: "null"},
}
opts = append(opts, o)
}
return opts
}
out := profs2opts(profiles)
t := time.Now()
err := ffmpeg.Transcode2(in, out)
end := time.Now()
fmt.Printf("%s,%d,%0.2v\n", end.Format("2006-01-02 15:04:05.999999999"), k, end.Sub(t).Seconds())
if err != nil {
panic(err)
}
wg.Done()
}(i, &wg)
}
wg.Wait()
fmt.Fprintf(os.Stderr, "Took %v to transcode\n",
time.Now().Sub(start).Seconds())
}
if [ -z "$1" ]
then
echo "Expecting output prefix"
exit 1
fi
pfx="$1"
cat <<EOF > config-sql
.mode csv
.import stats/"$pfx"_nv_3.csv nv3
.import stats/"$pfx"_nv_2.csv nv2
.import stats/"$pfx"_nv_1.csv nv1
.import stats/"$pfx"_sw_3.csv sw3
.import stats/"$pfx"_sw_2.csv sw2
.import stats/"$pfx"_sw_1.csv sw1
.timer on
EOF
sqlite3 -header -column -init config-sql
package main
import (
"bufio"
"fmt"
"os"
"path"
"strconv"
"strings"
"sync"
"time"
//"runtime/pprof"
"github.com/livepeer/lpms/ffmpeg"
"github.com/livepeer/m3u8"
)
func validRenditions() []string {
valids := make([]string, len(ffmpeg.VideoProfileLookup))
for p := range ffmpeg.VideoProfileLookup {
valids = append(valids, p)
}
return valids
}
func str2profs(inp string) []ffmpeg.VideoProfile {
profs := []ffmpeg.VideoProfile{}
strs := strings.Split(inp, ",")
for _, k := range strs {
p, ok := ffmpeg.VideoProfileLookup[k]
if !ok {
panic(fmt.Sprintf("Invalid rendition %s. Valid renditions are:\n%s", k, validRenditions()))
}
profs = append(profs, p)
}
return profs
}
func main() {
const usage = "Expected: [input file] [output prefix] [# concurrents] [# segments] [profiles] [sw/nv] <nv-device>"
/*
cprof, err := os.Create("bench.prof")
if err != nil {
panic(err)
}
pprof.StartCPUProfile(cprof)
defer pprof.StopCPUProfile()
*/
if len(os.Args) <= 6 {
panic(usage)
}
fname := os.Args[1]
f, err := os.Open(fname)
if err != nil {
panic(err)
}
p, _, err := m3u8.DecodeFrom(bufio.NewReader(f), true)
if err != nil {
panic(err)
}
pl, ok := p.(*m3u8.MediaPlaylist)
if !ok {
panic("Expecting media PL")
}
//pfx := os.Args[2]
conc, err := strconv.Atoi(os.Args[3])
if err != nil {
panic(err)
}
segs, err := strconv.Atoi(os.Args[4])
if err != nil {
panic(err)
}
profiles := str2profs(os.Args[5])
accelStr := os.Args[6]
accel := ffmpeg.Software
devices := []string{}
if "nv" == accelStr {
accel = ffmpeg.Nvidia
if len(os.Args) <= 7 {
panic(usage)
}
devices = strings.Split(os.Args[7], ",")
}
ffmpeg.InitFFmpeg()
var wg sync.WaitGroup
dir := path.Dir(fname)
start := time.Now()
fmt.Fprintf(os.Stderr, "Program %s Source %s Segments %d Concurrency %d\n", os.Args[0], fname, segs, conc)
fmt.Println("time,stream,segment,length")
for i := 0; i < conc; i++ {
wg.Add(1)
go func(k int, wg *sync.WaitGroup) {
tc := ffmpeg.NewTranscoder()
for j, v := range pl.Segments {
if j >= segs {
break
}
if v == nil {
continue
}
u := path.Join(dir, v.URI)
//u := v.URI
in := &ffmpeg.TranscodeOptionsIn{
Fname: u,
Accel: accel,
}
if ffmpeg.Software != accel {
in.Device = devices[k%len(devices)]
}
profs2opts := func(profs []ffmpeg.VideoProfile) []ffmpeg.TranscodeOptions {
opts := []ffmpeg.TranscodeOptions{}
//for n, p := range profs {
for _, p := range profs {
o := ffmpeg.TranscodeOptions{
//Oname: fmt.Sprintf("%s%s_%s_%d_%d_%d.ts", pfx, accelStr, p.Name, n, k, j),
Oname: "-",
Profile: p,
Accel: accel,
AudioEncoder: ffmpeg.ComponentOptions{Name: "drop"},
Muxer: ffmpeg.ComponentOptions{Name: "null"},
}
opts = append(opts, o)
}
return opts
}
out := profs2opts(profiles)
t := time.Now()
_, err := tc.Transcode(in, out)
end := time.Now()
fmt.Printf("%s,%d,%d,%0.2v\n", end.Format("2006-01-02 15:04:05.999999999"), k, j, end.Sub(t).Seconds())
if err != nil {
panic(err)
}
}
tc.StopTranscoder()
wg.Done()
}(i, &wg)
time.Sleep(300 * time.Millisecond)
}
wg.Wait()
fmt.Fprintf(os.Stderr, "Took %v to transcode %v segments\n",
time.Now().Sub(start).Seconds(), segs)
}
GOSRC=/home/josh/code/lpms/ffmpeg
LIBS=libavcodec libavfilter libavformat libavutil
all: gpu
test: test.c
gcc -Wall `pkg-config --cflags $(LIBS) ` $(GOSRC)/lpms_ffmpeg.c -g test.c `pkg-config --libs $(LIBS)`
test_conc: test_conc.c
#$(CC) -fsanitize=memory `pkg-config --cflags $(LIBS) ` $(GOSRC)/lpms_ffmpeg.c -g test_conc.c `pkg-config --libs $(LIBS)` -lpthread
$(CC) `pkg-config --cflags $(LIBS) ` $(GOSRC)/lpms_ffmpeg.c -g test_conc.c `pkg-config --libs $(LIBS)` -lpthread
test_multi: test_multi.c
$(CC) -Wall -fsanitize=address `pkg-config --cflags $(LIBS)` $(GOSRC)/lpms_ffmpeg.c -g test_multi.c -fsanitize=address `pkg-config --libs $(LIBS)` -lpthread
$(CC) -Wall `pkg-config --cflags $(LIBS)` $(GOSRC)/lpms_ffmpeg.c -g test_multi.c `pkg-config --libs $(LIBS)` -lpthread
test_rand: test_rand.c
$(CC) -Wall `pkg-config --cflags $(LIBS)` $(GOSRC)/lpms_ffmpeg.c -g $^ `pkg-config --libs $(LIBS)` -lpthread
memory: memory.c
gcc `pkg-config --cflags $(LIBS) ` $(GOSRC)/lpms_ffmpeg.c -g memory.c `pkg-config --libs $(LIBS)`
.PHONY: gpu
gpu: gpu.go
CPATH=/usr/local/cuda/include LIBRARY_PATH=/usr/local/cuda/lib64 go build $^
bench:
go build lpms-bench.go
buffered:
go build buffered-bench.go
nonp-buffered:
go build nonp-buffered-bench.go
rand:
go build rand-bench.go
.PHONY: hls
hls:
go build hls.go
clean:
rm -f gpu lpms-bench
#include <stdio.h>
#include "/home/josh/compiled/include/libavformat/avformat.h"
#include "/home/josh/code/lpms/ffmpeg/lpms_ffmpeg.h"
int main(int argc, char **argv) {
lpms_init();
struct transcode_thread *t = lpms_transcode_new();
input_params inp = {
.handle = t,
.fname = "bbb.mp4",
};
output_params out[] = { {
.fname = "out/c_bbb.ts",
.video = {.name = "libx264"},
.audio = {.name = "copy"},
.vfilters = "fps=30/1,scale=w=640:h=480",
.fps = {30, 1}
} };
output_results res = {0}, decoded = {0};
lpms_transcode(&inp, out, &res, 1, &decoded);
lpms_transcode_stop(t);
return 0;
}
if [ -z "$1" ]
then
echo "Expecting output prefix"
exit 1
fi
inp=in/testharness.m3u8
profs=P720p30fps16x9,P576p30fps16x9,P360p30fps16x9,P240p30fps16x9
pfx="$1"
./lpms-bench $inp $pfx 3 298 $profs nv 0 | tee stats/"$pfx"_nv_3.csv
./lpms-bench $inp $pfx 2 298 $profs nv 0 | tee stats/"$pfx"_nv_2.csv
./lpms-bench $inp $pfx 1 298 $profs nv 0 | tee stats/"$pfx"_nv_1.csv
./lpms-bench $inp $pfx 3 298 $profs sw | tee stats/"$pfx"_sw_3.csv
./lpms-bench $inp $pfx 2 298 $profs sw | tee stats/"$pfx"_sw_2.csv
./lpms-bench $inp $pfx 1 298 $profs sw | tee stats/"$pfx"_sw_1.csv
#include <stdio.h>
#include "/home/josh/compiled/include/libavformat/avformat.h"
#include "/home/josh/code/lpms/ffmpeg/lpms_ffmpeg.h"
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <input-file>\n", argv[0]);
return 1;
}
lpms_init();
//av_log_set_level(AV_LOG_TRACE);
struct transcode_thread *t = lpms_transcode_new();
input_params inp = {
handle: t,
//fname: "bbb.mp4",
//fname: "in/short_testharness.m3u8",
fname: argv[1],
hw_type: AV_HWDEVICE_TYPE_CUDA
};
output_params out[] = { {
fname: "out/c_bbb.ts",
video: { name: "h264_nvenc" },
vfilters: "fps=30/1,scale_cuda=w=640:h=480",
//video: { name: "libx264" },
//vfilters: "fps=30/1,scale=w=640:h=480",
//audio: { name: "copy" },
//vfilters: "hwupload_cuda=0,scale_cuda=w=640:h=480",
//vencoder: "libx264",
//w: 1920,
//h: 1080,
fps: {30, 1}
} };
output_results res[4] = {{0},{0},{0},{0}};
output_results decoded_res = {0};
lpms_transcode(&inp, out, &res[0], 1, &decoded_res);
lpms_transcode_stop(t);
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // sleep
#include <time.h>
#include <sys/time.h>
#include "/home/josh/compiled/include/libavformat/avformat.h"
#include "/home/josh/code/lpms/ffmpeg/lpms_ffmpeg.h"
typedef struct {
char in[64];
char out1[64], out2[64], out3[64], out4[64];
char device[64];
int nb;
} data;
void print(int seg, int i, struct timeval *start) {
time_t rawtime;
struct tm timeinfo;
struct timeval end;
unsigned long long ms;
char buf[64];
time(&rawtime);
localtime_r(&rawtime, &timeinfo);
gettimeofday(&end, NULL);
asctime_r(&timeinfo, buf);
buf[strlen(buf)-1] = '\0'; // remove trailing newline
ms = 1000*(end.tv_sec - start->tv_sec)+(end.tv_usec - start->tv_usec) / 1000;
printf("%s,%d,%d,%llu\n", buf, i, seg, ms);
}
void run_segment(int seg, input_params *inp, output_params *out, int nb_out, int thr_idx) {
int i;
sprintf(inp->fname, "in/bbb%d.ts", seg);
for (i = 0; i < nb_out; i++) {
sprintf(out[i].fname, "out/c_conc_%s_%d_%d.ts", inp->device, seg, i);
}
int ret;
output_results res[4];
output_results decoded_res;
struct timeval start;
gettimeofday(&start, NULL);
ret = lpms_transcode(inp, out, (&res[0]), 4, &decoded_res);
if (ret != 0) {
fprintf(stderr, "Error transcoding ret=%d stream=%d segment=%d\n",
ret, thr_idx, seg);
return;
}
print(seg, thr_idx, &start);
}
void* run(void *opaque) {
int i;
data* d = (data*)opaque;
struct transcode_thread *t = lpms_transcode_new();
input_params inp = {
handle: t,
fname: d->in,
device: d->device,
hw_type: AV_HWDEVICE_TYPE_CUDA
};
output_params out[] = {{
fname: d->out1,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
muxer: { name: "null" },
vfilters: "fps=30/1,scale_cuda=w=1280:h=720",
fps: {30, 1}
},{
fname: d->out2,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
muxer: { name: "null" },
vfilters: "fps=30/1,scale_cuda=w=1024:h=576",
fps: {30, 1}
},{
fname: d->out3,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
muxer: { name: "null" },
vfilters: "fps=30/1,scale_cuda=w=640:h=360",
fps: {30, 1}
},{
fname: d->out4,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
muxer: { name: "null" },
vfilters: "fps=30/1,scale_cuda=w=426:h=240",
fps: {30, 1}
}};
for (i = 0; i < 317; i++) {
run_segment(i, &inp, out, sizeof(out) / sizeof(output_params), d->nb);
}
lpms_transcode_stop(t);
return NULL;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <concurrency>\n", argv[0]);
return 1;
}
lpms_init();
int i, j;
int conc = atoi(argv[1]);
pthread_t threads[128];
data datas[128];
if (conc <= 0 || conc > 128) {
fprintf(stderr, "Concurrency must be between 1 and 128\n");
return 1;
}
printf("time,stream,segment,length\n");
for (i = 0; i < conc; i++) {
data *d = &datas[i];
/*
snprintf(d->out1, sizeof d->out1, "out/conc_%d_720.ts", i);
snprintf(d->out2, sizeof d->out2, "out/conc_%d_576.ts", i);
snprintf(d->out3, sizeof d->out3, "out/conc_%d_360.ts", i);
snprintf(d->out4, sizeof d->out4, "out/conc_%d_240.ts", i);
*/
snprintf(d->out1, sizeof d->out1, "-");
snprintf(d->out2, sizeof d->out2, "-");
snprintf(d->out3, sizeof d->out3, "-");
snprintf(d->out4, sizeof d->out4, "-");
snprintf(d->device, sizeof d->device, "%d", i % 8);
d->nb = i;
//printf("Processing %d on device %s\n", i, d->device);
pthread_create(&threads[i], NULL, run, (void*)d);
sleep(1);
}
for (i = 0; i < conc; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
#include <stdio.h>
#include "/home/josh/compiled/include/libavformat/avformat.h"
#include "/home/josh/code/lpms/ffmpeg/lpms_ffmpeg.h"
int main(int argc, char **argv) {
lpms_init();
struct transcode_thread *t = lpms_transcode_new();
int i = 0;
for (i = 0; i < 4; i++) {
char fname[64];
char oname[64];
snprintf(fname, sizeof fname, "in/bbb%d.ts", i);
snprintf(oname, sizeof oname, "out/bbb%d.ts", i);
AVDictionary *dict = NULL;
av_dict_set(&dict, "forced-idr", "1", 0);
input_params inp = {
handle: t,
fname: fname,
hw_type: AV_HWDEVICE_TYPE_CUDA,
//hw_type: AV_HWDEVICE_TYPE_NONE,
device: "0"
};
output_params out[] = { {
fname: oname,
video: { name: "h264_nvenc", opts: dict },
//video: { name: "libx264" },
audio: { name: "aac" },
vfilters: "fps=30/1,scale_cuda=w=640:h=480",
//vfilters: "fps=30/1,scale_npp=w=640:h=480",
//vfilters: "fps=30/1,scale=w=640:h=480",
//vfilters: "hwdownload,format=nv12,fps=30/1,scale=w=640:h=480,hwupload_cuda=device=1",
fps: {30, 1}
} };
output_results res[4] = {{0}, {0}, {0}, {0}};
output_results decoded_res = {0};
fprintf(stderr, "Transcoding %s\n", fname);
int ret = lpms_transcode(&inp, out, &res[0], 1, &decoded_res);
av_dict_free(&out[0].video.opts);
if (ret < 0) {
fprintf(stderr, "Error transcoding\n");
break;
}
}
lpms_transcode_stop(t);
return 0;
}
#include <stdio.h>
#include <pthread.h>
#include <unistd.h> // sleep
#include <signal.h> // signal
#include <time.h>
#include <sys/time.h>
#include "/home/josh/compiled/include/libavformat/avformat.h"
#include "/home/josh/code/src/github.com/livepeer/lpms/ffmpeg/lpms_ffmpeg.h"
static int done = 0;
typedef struct {
char in[64];
char out1[64], out2[64], out3[64], out4[64];
char device[64];
int nb, conc;
} data;
void sig_handler(int signo) {
fprintf(stderr, "Received signal; stopping transcodes\n");
done = 1;
}
void print(int seg, int i, struct timeval *start) {
time_t rawtime;
struct tm timeinfo;
struct timeval end;
unsigned long long ms;
char buf[64];
time(&rawtime);
localtime_r(&rawtime, &timeinfo);
gettimeofday(&end, NULL);
asctime_r(&timeinfo, buf);
buf[strlen(buf)-1] = '\0'; // remove trailing newline
ms = 1000*(end.tv_sec - start->tv_sec)+(end.tv_usec - start->tv_usec) / 1000;
printf("%s,%d,%d,%llu\n", buf, i, seg, ms);
}
void run_segment(int seg, input_params *inp, output_params *out, int nb_out, int thr_idx) {
int i;
sprintf(inp->fname, "in/bbb%d.ts", seg);
for (i = 0; i < nb_out; i++) {
sprintf(out[i].fname, "out/c_conc_%s_%d_%d.ts", inp->device, seg, i);
}
int ret;
output_results res[4];
output_results decoded_res;
struct timeval start;
gettimeofday(&start, NULL);
ret = lpms_transcode(inp, out, (&res[0]), 4, &decoded_res);
if (ret != 0) {
fprintf(stderr, "Error transcoding ret=%d stream=%d segment=%d\n",
ret, thr_idx, seg);
return;
}
print(seg, thr_idx, &start);
}
void stream(data *d) {
int i;
struct transcode_thread *t = lpms_transcode_new();
input_params inp = {
handle: t,
fname: d->in,
device: d->device,
hw_type: AV_HWDEVICE_TYPE_CUDA
};
output_params out[] = {{
fname: d->out1,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
vfilters: "fps=30/1,scale_cuda=w=1280:h=720",
//vfilters: "fps=30/1,scale_npp=w=1280:h=720",
fps: {30, 1}
},{
fname: d->out2,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
vfilters: "fps=30/1,scale_cuda=w=1024:h=576",
//vfilters: "fps=30/1,scale_npp=w=1024:h=576",
fps: {30, 1}
},{
fname: d->out3,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
vfilters: "fps=30/1,scale_cuda=w=640:h=360",
//vfilters: "fps=30/1,scale_npp=w=640:h=360",
fps: {30, 1}
},{
fname: d->out4,
video: { name: "h264_nvenc" },
audio: { name: "copy" },
vfilters: "fps=30/1,scale_cuda=w=426:h=240",
//vfilters: "fps=30/1,scale_npp=w=426:h=240",
fps: {30, 1}
}};
int segs = rand() % 30;
for (i = 0; i < segs; i++) {
if (done) break;
run_segment(i, &inp, out, sizeof(out) / sizeof(output_params), d->nb);
}
lpms_transcode_stop(t);
}
void* run(void *opaque) {
data *d = (data*)opaque;
int nb = d->nb, i = 0;
while (!done) {
d->nb = (i * d->conc) + nb;
stream(d);
i += 1;
}
return NULL;
}
int main(int argc, char **argv) {
if (argc < 2) {
fprintf(stderr, "Usage: %s <concurrency>\n", argv[0]);
return 1;
}
signal(SIGINT, sig_handler);
lpms_init();
int i;
int conc = atoi(argv[1]);
pthread_t threads[128];
data datas[128];
if (conc <= 0 || conc > 128) {
fprintf(stderr, "Concurrency must be between 1 and 128\n");
return 1;
}
printf("time,stream,segment,length\n");
for (i = 0; i < conc; i++) {
data *d = &datas[i];
snprintf(d->out1, sizeof d->out1, "out/conc_%d_720.ts", i);
snprintf(d->out2, sizeof d->out2, "out/conc_%d_576.ts", i);
snprintf(d->out3, sizeof d->out3, "out/conc_%d_360.ts", i);
snprintf(d->out4, sizeof d->out4, "out/conc_%d_240.ts", i);
snprintf(d->device, sizeof d->device, "%d", i % 8);
d->nb = i;
d->conc = conc;
//printf("Processing %d on device %s\n", i, d->device);
pthread_create(&threads[i], NULL, run, (void*)d);
sleep(1);
}
for (i = 0; i < conc; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment