Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support H.265/HEVC, VP8, VP9 #279

Merged
merged 15 commits into from
Jan 11, 2022
Merged

Support H.265/HEVC, VP8, VP9 #279

merged 15 commits into from
Jan 11, 2022

Conversation

cyberj0g
Copy link
Contributor

@cyberj0g cyberj0g commented Dec 9, 2021

Issue link: livepeer/go-livepeer#2123

Details:

  • Added support for H.265 codecs: libx265 (CPU, encoding and decoding), NVENC (GPU, encoding and decoding)
  • Added support for VP8/VP9 codecs: libvpx (CPU, encoding and decoding), NVENC (GPU, decoding only, where supported)
  • Added transcoding test for all combinations of codecs and acceleration

Testing:

  • install dependencies using install_ffmpeg.sh (WIP, please report errors)
  • run tests:
    go test -v --tags=nvidia -timeout 30m -run TestNvidia_Transcoding ./ffmpeg/
    go test -v --tags=nvidia -timeout 30m -run TestSW_Transcoding ./ffmpeg/
    

They will output lines like below, and fail, if anything is not supported (on CI, only CPU test will run for now). SW test should work anywhere, Nvidia test should pass on GPUs starting from desktop GTX 1070 - it can't decode VP9 on my GTX 1070m, despite support matrix stating it should.

    ffmpeg_test.go:754: Transcode VP8 (Accel: 0) -> H.265/HEVC (Accel: 0) Prepare: true Transcode: true Quality: true
    ffmpeg_test.go:754: Transcode VP8 (Accel: 0) -> VP8 (Accel: 0) Prepare: true Transcode: true Quality: true
    ffmpeg_test.go:754: Transcode VP8 (Accel: 0) -> VP9 (Accel: 0) Prepare: true Transcode: true Quality: true

@cyberj0g
Copy link
Contributor Author

cyberj0g commented Dec 10, 2021

Finished testing new 'unified' install_ffmpeg.sh from go-livepeer and added it to circle-ci job. We shouldn't need a separate one for lpms now. For dev/test install, add debug-video to BUILD_TAGS e.g.:

BUILD_TAGS="experimental debug-video" ./install_ffmpeg.sh /projects/livepeer/src/

I checked new codecs tests with this install_ffmpeg.sh, but not sure if all other tests are fine. @JamesWanglf is welcome to add his changes and create PR in go-livepeer.

@darkdarkdragon
Copy link
Contributor

darkdarkdragon commented Dec 10, 2021

Finished testing new 'unified' install_ffmpeg.sh from go-livepeer and added it to circle-ci job. We shouldn't need a separate one for lpms now. For dev/test install, add debug-video to BUILD_TAGS e.g.:

BUILD_TAGS="experimental debug-video" ./install_ffmpeg.sh /projects/livepeer/src/

I checked new codecs tests with this install_ffmpeg.sh, but not sure if all other tests are fine. @JamesWanglf is welcome to add his changes and create PR in go-livepeer.

@leszko What is the reason for including matroska,opus,webm,webm_chunk,webm_dash_manifest into production build?

@cyberj0g
Copy link
Contributor Author

@darkdarkdragon I added these. It makes sense to support MKV and WebM containers for VP8/9, and AAC is not supported for WebM - that's why Opus is added. DASH is a great alternative to HLS (especially for WebM), we might want to support it in the near future too.

@darkdarkdragon
Copy link
Contributor

@darkdarkdragon I added these. It makes sense to support MKV and WebM containers for VP8/9, and AAC is not supported for WebM - that's why Opus is added. DASH is a great alternative to HLS (especially for WebM), we might want to support it in the near future too.

Any addition to the production build increases attack surface - so we shouldn't add anything that don't officially support.
So I think we should remove webm_dash_manifest and matroska, and create new issue in go-livepeer repo for adding support for .webm segments.

Also, shouldn't --enable-libopus be added for opus support?

@cyberj0g
Copy link
Contributor Author

Any addition to the production build increases attack surface - so we shouldn't add anything that don't officially support.

That's a good practice, but aren't we adding VP8/9 support at least for decoding with this PR? If so, common containers used with these are MKV and WEBM, and lpms now supports them. Or, since VP8/9 is an exotic choice for HLS+MPEG-TS, we should not enable these codecs at all until other streaming protocols are not implemented on go-livepeer side?

@livepeer livepeer deleted a comment from leszko Dec 10, 2021
@livepeer livepeer deleted a comment from darkdarkdragon Dec 10, 2021
@livepeer livepeer deleted a comment from leszko Dec 10, 2021
@livepeer livepeer deleted a comment from darkdarkdragon Dec 10, 2021
@livepeer livepeer deleted a comment from leszko Dec 10, 2021
@darkdarkdragon
Copy link
Contributor

I think webm would be enough? But it's better to create issue in go-livepeer to discuss what containers should we support.

Or, since VP8/9 is an exotic choice for HLS+MPEG-TS, we should not enable these codecs at all until other streaming protocols are not implemented on go-livepeer side?

We're really not using HLS output of go-livepeer node nowdays - it is being handled by Mist (I think in the near future, when we will integrate Mist and go-livepeer better we'll remove RTMP and HLS support from go-livepeer node)
So it is enough for go-livepeer node to being able to take .webm file and respond back with .mp4 or .ts (or .webm).

@cyberj0g
Copy link
Contributor Author

cyberj0g commented Dec 11, 2021

Created a new issue for additional container formats, and moved these flags to debug build for now.

Copy link
Contributor

@oscar-davids oscar-davids left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall LGTM!

As a separate matter, one thing I'm thinking of is to statically link x265 and libvpx like x264. Because if we link it dynamically, we need to set LD_LIBRARY_PATH or add related .so files to the distribution. WDYT?

and I wonder how this part will work on the VP8/VP9/H265.

@cyberj0g
Copy link
Contributor Author

one thing I'm thinking of is to statically link x265 and libvpx like x264

Spent quite a bit of time and got Ffmpeg to build with new codecs as static libraries. However, libx265 is a C++ lib, and I couldn't get CGO to build lpms properly when it statically linked, and don't know if it's a good idea to switch to g++ in lpms. At this point, I believe it's not worth the effort to get it to build statically with libx265. If someone is willing to continue looking into that, my efforts stopped there (set proper PKG_CONFIG_PATH and use linked install_ffmpeg.sh):

CGO_CFLAGS="-Wno-error" CGO_CPPFLAGS="-Wno-error" CC=g++ go build -a cmd/transcoding/transcoding.go
# runtime/cgo
In file included from gcc_context.c:8:
libcgo.h:97:15: error: unnecessary parentheses in declaration of ‘_cgo_get_context_function’ [-Werror=parentheses]
   97 | extern void (*(_cgo_get_context_function(void)))(struct context_arg*);
      |               ^
cc1plus: all warnings being treated as errors

Without CC=g++, I'm getting:

/usr/bin/ld: /projects/livepeer/src/compiled/lib/libx265.a(encoder.cpp.o): undefined reference to symbol '_ZTVN10__cxxabiv121__vmi_class_type_infoE@@CXXABI_1.3'
/usr/bin/ld: /usr/lib/x86_64-linux-gnu//libstdc++.so.6: error adding symbols: DSO missing from command line

@cyberj0g
Copy link
Contributor Author

cyberj0g commented Dec 16, 2021

and I wonder how this part will work on the VP8/VP9/H265.

It doesn't, none of these parameters work for HEVC or VP8/9 to my knowledge, so they are ignored.

@iameli
Copy link
Member

iameli commented Dec 28, 2021

@cyberj0g Does the node boot without libx265, only loading it dynamically when needed? I don't think we probably want to add an external libx265 dependency if not.

@cyberj0g
Copy link
Contributor Author

@cyberj0g Does the node boot without libx265, only loading it dynamically when needed? I don't think we probably want to add an external libx265 dependency if not.

I think we agreed to not include software versions of new codecs into production build for now, so it won't be an issue. In debug mode, --enable-shared is added, so all dependencies are linked dynamically. As an experiment, I quickly tried building without --enable-shared with all dependencies static, except libx265.so - it didn't work, probably something is missing from Ffmpeg's configure.sh to support mixing static and shared dependencies.

@@ -232,7 +232,7 @@ int open_output(struct output_ctx *octx, struct input_ctx *ictx)
if (octx->fps.den) vc->time_base = av_buffersink_get_time_base(octx->vf.sink_ctx);
else if (ictx->vc->time_base.num && ictx->vc->time_base.den) vc->time_base = ictx->vc->time_base;
else vc->time_base = ictx->ic->streams[ictx->vi]->time_base;
if (octx->bitrate) vc->rc_min_rate = vc->rc_max_rate = vc->rc_buffer_size = octx->bitrate;
if (octx->bitrate) vc->rc_min_rate = vc->bit_rate = vc->rc_max_rate = vc->rc_buffer_size = octx->bitrate;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Were there problems with the bitrate prior to this change?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, IIRC hevc_nvenc didn't work with rc_max_rate set without setting bit_rate. I might be wrong, but for H.264 it was incorrect to not set bit_rate, if requested, either, because otherwise it won't switch to average bitrate mode. And the idea behind setting bit_rate equal to rc_max_rate is to force constant bitrate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Seems reasonable, but I'm curious if this has resulted in any issues for anyone.

cc @iameli - have we seen issues with bitrate before?

ffmpeg/ffmpeg_test.go Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
Copy link
Contributor Author

@cyberj0g cyberj0g left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for review @yondonfu, I addressed all comments.

ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
ffmpeg/decoder.c Show resolved Hide resolved
@@ -232,7 +232,7 @@ int open_output(struct output_ctx *octx, struct input_ctx *ictx)
if (octx->fps.den) vc->time_base = av_buffersink_get_time_base(octx->vf.sink_ctx);
else if (ictx->vc->time_base.num && ictx->vc->time_base.den) vc->time_base = ictx->vc->time_base;
else vc->time_base = ictx->ic->streams[ictx->vi]->time_base;
if (octx->bitrate) vc->rc_min_rate = vc->rc_max_rate = vc->rc_buffer_size = octx->bitrate;
if (octx->bitrate) vc->rc_min_rate = vc->bit_rate = vc->rc_max_rate = vc->rc_buffer_size = octx->bitrate;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, IIRC hevc_nvenc didn't work with rc_max_rate set without setting bit_rate. I might be wrong, but for H.264 it was incorrect to not set bit_rate, if requested, either, because otherwise it won't switch to average bitrate mode. And the idea behind setting bit_rate equal to rc_max_rate is to force constant bitrate.

ffmpeg/ffmpeg_test.go Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
ffmpeg/ffmpeg_test.go Outdated Show resolved Hide resolved
Copy link
Member

@yondonfu yondonfu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! I'd like to get an approval from another set of eyes that has familiarity with this codebase as well. cc @oscar-davids since you already left an initial review.

I left a minor comment about updating a comment - feel free to address prior to merging. Also, I recommend rebasing off master to pick up a fix that landed recently that should address the failing test in CI and to squash the commits to clean up the commit history before merging.

ffmpeg/ffmpeg_test.go Show resolved Hide resolved
Copy link
Contributor

@darkdarkdragon darkdarkdragon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@oscar-davids oscar-davids left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!
please rebase on master to fix CI errors before merging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants