mirror of
https://github.com/livepeer/lpms
synced 2025-11-01 12:03:00 +08:00
ffmpeg: Return stats on decoded stream from transcoder.
This commit is contained in:
@@ -75,7 +75,8 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for i, r := range res {
|
||||
fmt.Printf("profile=input frames=%v pixels=%v\n", res.Decoded.Frames, res.Decoded.Pixels)
|
||||
for i, r := range res.Encoded {
|
||||
fmt.Printf("profile=%v frames=%v pixels=%v\n", profiles[i].Name, r.Frames, r.Pixels)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,11 @@ type MediaInfo struct {
|
||||
Pixels int64
|
||||
}
|
||||
|
||||
type TranscodeResults struct {
|
||||
Decoded MediaInfo
|
||||
Encoded []MediaInfo
|
||||
}
|
||||
|
||||
func RTMPToHLS(localRTMPUrl string, outM3U8 string, tmpl string, seglen_secs string, seg_start int) error {
|
||||
inp := C.CString(localRTMPUrl)
|
||||
outp := C.CString(outM3U8)
|
||||
@@ -128,7 +133,7 @@ func Transcode2(input *TranscodeOptionsIn, ps []TranscodeOptions) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func Transcode3(input *TranscodeOptionsIn, ps []TranscodeOptions) ([]MediaInfo, error) {
|
||||
func Transcode3(input *TranscodeOptionsIn, ps []TranscodeOptions) (*TranscodeResults, error) {
|
||||
if input == nil {
|
||||
return nil, ErrTranscoderInp
|
||||
}
|
||||
@@ -182,7 +187,8 @@ func Transcode3(input *TranscodeOptionsIn, ps []TranscodeOptions) ([]MediaInfo,
|
||||
}
|
||||
inp := &C.input_params{fname: fname, hw_type: hw_type, device: device}
|
||||
results := make([]C.output_results, len(ps))
|
||||
ret := int(C.lpms_transcode(inp, (*C.output_params)(¶ms[0]), (*C.output_results)(&results[0]), C.int(len(params))))
|
||||
decoded := &C.output_results{}
|
||||
ret := int(C.lpms_transcode(inp, (*C.output_params)(¶ms[0]), (*C.output_results)(&results[0]), C.int(len(params)), decoded))
|
||||
if 0 != ret {
|
||||
glog.Infof("Transcoder Return : %v\n", Strerror(ret))
|
||||
return nil, ErrorMap[ret]
|
||||
@@ -194,7 +200,11 @@ func Transcode3(input *TranscodeOptionsIn, ps []TranscodeOptions) ([]MediaInfo,
|
||||
Pixels: int64(r.pixels),
|
||||
}
|
||||
}
|
||||
return tr, nil
|
||||
dec := MediaInfo{
|
||||
Frames: int(decoded.frames),
|
||||
Pixels: int64(decoded.pixels),
|
||||
}
|
||||
return &TranscodeResults{Encoded: tr, Decoded: dec}, nil
|
||||
}
|
||||
|
||||
func InitFFmpeg() {
|
||||
|
||||
@@ -377,8 +377,97 @@ func TestTranscoder_Timestamp(t *testing.T) {
|
||||
run(cmd)
|
||||
}
|
||||
|
||||
func TestTranscoder_Statistics(t *testing.T) {
|
||||
// Checks the stats returned after transcoding
|
||||
func TestTranscoderStatistics_Decoded(t *testing.T) {
|
||||
// Checks the decoded stats returned after transcoding
|
||||
|
||||
var (
|
||||
totalPixels int64
|
||||
totalFrames int
|
||||
)
|
||||
|
||||
run, dir := setupTest(t)
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// segment using our muxer. This should produce 4 segments.
|
||||
err := RTMPToHLS("../transcoder/test.ts", dir+"/test.m3u8", dir+"/test_%d.ts", "1", 0)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Use various resolutions to test input
|
||||
// Quickcheck style tests would be nice here one day?
|
||||
profiles := []VideoProfile{P144p30fps16x9, P240p30fps16x9, P360p30fps16x9, P576p30fps16x9}
|
||||
|
||||
// Transcode some data, save encoded statistics, then attempt to re-transcode
|
||||
// Ensure decoded re-transcode stats match original transcoded statistics
|
||||
for i, p := range profiles {
|
||||
oname := fmt.Sprintf("%s/out_%d.ts", dir, i)
|
||||
out := []TranscodeOptions{TranscodeOptions{Profile: p, Oname: oname}}
|
||||
in := &TranscodeOptionsIn{Fname: fmt.Sprintf("%s/test_%d.ts", dir, i)}
|
||||
res, err := Transcode3(in, out)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
info := res.Encoded[0]
|
||||
|
||||
// Now attempt to re-encode the transcoded data
|
||||
// and check decoded results from *that*
|
||||
p.Framerate = 10
|
||||
in = &TranscodeOptionsIn{Fname: oname}
|
||||
out = []TranscodeOptions{TranscodeOptions{Profile: p, Oname: "out.ts"}}
|
||||
res, err = Transcode3(in, out)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
w, h, err := VideoProfileResolution(p)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// Check pixel counts
|
||||
if info.Pixels != res.Decoded.Pixels {
|
||||
t.Error("Mismatched pixel counts")
|
||||
}
|
||||
if info.Pixels != int64(w*h*res.Decoded.Frames) {
|
||||
t.Error("Mismatched pixel counts")
|
||||
}
|
||||
// Check frame counts
|
||||
if info.Frames != res.Decoded.Frames {
|
||||
t.Error("Mismatched frame counts")
|
||||
}
|
||||
if info.Frames != int(res.Decoded.Pixels/int64(w*h)) {
|
||||
t.Error("Mismatched frame counts")
|
||||
}
|
||||
totalPixels += info.Pixels
|
||||
totalFrames += info.Frames
|
||||
}
|
||||
|
||||
// Now for something fun. Concatenate our segments of various resolutions
|
||||
// Run them through the transcoder, and check the sum of pixels / frames match
|
||||
// Ensures we can properly accommodate mid-stream resolution changes.
|
||||
cmd := `
|
||||
set -eux
|
||||
cd "$0"
|
||||
cat out_0.ts out_1.ts out_2.ts out_3.ts > combined.ts
|
||||
`
|
||||
run(cmd)
|
||||
in := &TranscodeOptionsIn{Fname: dir + "/combined.ts"}
|
||||
out := []TranscodeOptions{TranscodeOptions{Profile: P240p30fps16x9, Oname: "out.ts"}}
|
||||
|
||||
res, err := Transcode3(in, out)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if totalPixels != res.Decoded.Pixels {
|
||||
t.Error("Mismatched total pixel counts")
|
||||
}
|
||||
if totalFrames != res.Decoded.Frames {
|
||||
t.Errorf("Mismatched total frame counts - %d vs %d", totalFrames, res.Decoded.Frames)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTranscoder_Statistics_Encoded(t *testing.T) {
|
||||
// Checks the encoded stats returned after transcoding
|
||||
|
||||
run, dir := setupTest(t)
|
||||
defer os.RemoveAll(dir)
|
||||
@@ -412,7 +501,7 @@ func TestTranscoder_Statistics(t *testing.T) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
for i, r := range res {
|
||||
for i, r := range res.Encoded {
|
||||
w, h, err := VideoProfileResolution(out[i].Profile)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -481,10 +570,10 @@ func TestTranscoder_StatisticsAspectRatio(t *testing.T) {
|
||||
pAdj := VideoProfile{Resolution: "124x456", Framerate: 15, Bitrate: "100k"}
|
||||
out := []TranscodeOptions{TranscodeOptions{Profile: pAdj, Oname: dir + "/adj.mp4"}}
|
||||
res, err := Transcode3(&TranscodeOptionsIn{Fname: dir + "/test.ts"}, out)
|
||||
if err != nil || len(res) <= 0 {
|
||||
if err != nil || len(res.Encoded) <= 0 {
|
||||
t.Error(err)
|
||||
}
|
||||
r := res[0]
|
||||
r := res.Encoded[0]
|
||||
if r.Frames != int(pAdj.Framerate) || r.Pixels != int64(r.Frames*124*70) {
|
||||
t.Error(fmt.Errorf("Results did not match: %v ", r))
|
||||
}
|
||||
|
||||
@@ -759,7 +759,7 @@ proc_cleanup:
|
||||
#define MAX_OUTPUT_SIZE 10
|
||||
|
||||
int lpms_transcode(input_params *inp, output_params *params,
|
||||
output_results *results, int nb_outputs)
|
||||
output_results *results, int nb_outputs, output_results *decoded_results)
|
||||
{
|
||||
#define main_err(msg) { \
|
||||
if (!ret) ret = AVERROR(EINVAL); \
|
||||
@@ -821,6 +821,11 @@ int lpms_transcode(input_params *inp, output_params *params,
|
||||
else if (ret < 0) main_err("transcoder: Could not decode; stopping\n");
|
||||
ist = ictx.ic->streams[ipkt.stream_index];
|
||||
|
||||
if (AVMEDIA_TYPE_VIDEO == ist->codecpar->codec_type) {
|
||||
decoded_results->frames++;
|
||||
decoded_results->pixels += dframe->width * dframe->height;
|
||||
}
|
||||
|
||||
for (i = 0; i < nb_outputs; i++) {
|
||||
struct output_ctx *octx = &outputs[i];
|
||||
struct filter_ctx *filter = NULL;
|
||||
|
||||
@@ -31,6 +31,6 @@ typedef struct {
|
||||
|
||||
void lpms_init();
|
||||
int lpms_rtmp2hls(char *listen, char *outf, char *ts_tmpl, char *seg_time, char *seg_start);
|
||||
int lpms_transcode(input_params *inp, output_params *params, output_results *results, int nb_outputs);
|
||||
int lpms_transcode(input_params *inp, output_params *params, output_results *results, int nb_outputs, output_results *decoded_results);
|
||||
|
||||
#endif // _LPMS_FFMPEG_H_
|
||||
|
||||
Reference in New Issue
Block a user