ffmpeg: Return stats on decoded stream from transcoder.

This commit is contained in:
Josh Allmann
2019-08-16 03:33:52 -07:00
parent a31a7c3b79
commit f32e37b8cd
5 changed files with 116 additions and 11 deletions

View File

@@ -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)
}
}

View File

@@ -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)(&params[0]), (*C.output_results)(&results[0]), C.int(len(params))))
decoded := &C.output_results{}
ret := int(C.lpms_transcode(inp, (*C.output_params)(&params[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() {

View File

@@ -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))
}

View File

@@ -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;

View File

@@ -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_