Skip to content

Add codec ffmpeg#621

Open
3DRX wants to merge 5 commits into
mainfrom
add_codec_ffmpeg
Open

Add codec ffmpeg#621
3DRX wants to merge 5 commits into
mainfrom
add_codec_ffmpeg

Conversation

@3DRX

@3DRX 3DRX commented Apr 8, 2025

Copy link
Copy Markdown
Member

This is a PR for adding ffmpeg based video encoders.

  • Documentation, since ffmpeg library is introduced as dependency, building requires CGO so it's not a working-out-of-the-box experience.
  • Update CI pipeline, or it will failed for not founding ffmpeg library. We add go-astiav as a dependency in pkg/codec/ffmpeg/go.mod to avoid breaking CI and other user's build. When using github.com/pion/mediadevices/pkg/codec/ffmpeg, it's a requirement for users to provide their own ffmpeg library as described here.
  • Add test. For now, changing image size on the fly is not supported, so TestImageSizeChange is skipped. Also, since CI environment can't have access to hardware encoders such as NVENC, only software encoder is tested.
  • Update CI to run new test.
    • Runs tests for pkg/codec/ffmpeg in CI.
    • Cache FFmpeg build.
    • Configure codecov result and other things in CI.

@3DRX 3DRX force-pushed the add_codec_ffmpeg branch from 25768e5 to 94cbd6b Compare April 10, 2025 13:54
@codecov

codecov Bot commented Apr 10, 2025

Copy link
Copy Markdown

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 44.49%. Comparing base (c4fd28c) to head (d8741c0).

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #621   +/-   ##
=======================================
  Coverage   44.49%   44.49%           
=======================================
  Files          80       80           
  Lines        5558     5558           
=======================================
  Hits         2473     2473           
  Misses       2915     2915           
  Partials      170      170           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@3DRX 3DRX marked this pull request as ready for review April 10, 2025 13:56
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 4 times, most recently from 35848ce to d1c609a Compare April 10, 2025 16:23
@3DRX 3DRX requested review from JoTurk and Sean-Der April 10, 2025 16:34
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 2 times, most recently from f4a451c to 39bc14a Compare April 13, 2025 13:44
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 4 times, most recently from fc86229 to 71168b7 Compare April 16, 2025 03:59
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 6 times, most recently from cd37985 to ac5650d Compare April 16, 2025 05:46
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 2 times, most recently from f741aed to 1bccd6b Compare April 23, 2025 16:19
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch 2 times, most recently from be4ca15 to 16f049b Compare May 9, 2025 04:43
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch from 673f782 to baea418 Compare May 24, 2025 13:30
@3DRX 3DRX requested a review from at-wat May 24, 2025 13:34

@at-wat at-wat left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Seems very nice!

Comment thread .github/workflows/ci.yaml Outdated
working-directory: pkg/codec/ffmpeg
run: |
ls -la tmp/n7.0/
ls -la tmp/n7.0/lib/ || echo "lib directory not found"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

false || echo will always success.
It should be like

Suggested change
ls -la tmp/n7.0/lib/ || echo "lib directory not found"
ls -la tmp/n7.0/lib/ || (echo "lib directory not found"; exit 1)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think we can just delete this "Verify FFmpeg installation" task? I only added it for the sake of debugging CI.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just removing this part would be fine

Comment thread .github/workflows/ci.yaml Outdated
Comment thread pkg/codec/ffmpeg/ffmpeg.go Outdated
Comment thread pkg/codec/ffmpeg/ffmpeg.go Outdated
Comment on lines +251 to +243

for {
if err = e.codecCtx.ReceivePacket(e.packet); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
continue

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Will it be a busy loop or is there a sleep in ReceivePacket?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This is a busy loop, avcodec_receive_packet which is the underlying C function ReceivePacket calls returns ErrEagain immediatly if no data available.

I'm not very familiar with FFmpeg, but as far as I know this is common pattern when using libavcodec, and it shouldn't have too much negative impact on performance?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm also not familiar with ffmpeg library, but a tutorial for C (with many stars) seems breaking the loop if avcodec_receive_packet returned EAGAIN or EOF.

Comment thread pkg/codec/ffmpeg/ffmpeg.go Outdated
Comment on lines +216 to +219
// TODO: check if this is a key frame
// if !r.(*encoder).isKeyFrame {
// t.Fatal("Not a key frame")
// }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Doesn't it work for now?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

The commented out code is from tests of other encoders in this project, the problem here is that isKeyFrame is not a property in the newly implemented softwareEncoder and hardwareEncoder, we only have nextIsKeyFrame here so we can't just test it like this😅. This ffmpeg encoder do supports forcing key frame though. Perhaps we can parse the nalu header of the output data to verify if it's a key frame?

@JoTurk

JoTurk commented May 28, 2025

Copy link
Copy Markdown
Member

@3DRX Sorry for taking time, I don't want to just review the code, I want to play with it and test it, I'll test it this weekend!

Thank you so much for making this happen.

@3DRX

3DRX commented May 28, 2025

Copy link
Copy Markdown
Member Author

@at-wat Thanks for reviewing! I will address them in seperate commits, for the easiness of review.

@3DRX

3DRX commented May 28, 2025

Copy link
Copy Markdown
Member Author

@joeturki No worries! I really hope this PR can be thoroughly tested and reviewed so people can have a smoother experience using it.

@3DRX

3DRX commented May 28, 2025

Copy link
Copy Markdown
Member Author

@at-wat I've resolved the conversations that I think may be resolved by follow up changes for less buzz. Feel free to reopen them if needed😊

@3DRX 3DRX requested a review from at-wat June 13, 2025 06:41

@at-wat at-wat left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for the delay!

Left some comments and replies.
Also it might be cleaner to make

run: |
  make

to

run: make

Comment thread .github/workflows/pkg-codec-ffmpeg-ci.yaml Outdated
Comment thread .github/workflows/ci.yaml Outdated
working-directory: pkg/codec/ffmpeg
run: |
ls -la tmp/n7.0/
ls -la tmp/n7.0/lib/ || echo "lib directory not found"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Just removing this part would be fine

Comment thread .github/workflows/pkg-codec-ffmpeg-ci.yaml Outdated
Comment thread .github/workflows/pkg-codec-ffmpeg-ci.yaml Outdated
Comment thread pkg/codec/ffmpeg/ffmpeg.go Outdated
Comment on lines +251 to +243

for {
if err = e.codecCtx.ReceivePacket(e.packet); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
continue

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm also not familiar with ffmpeg library, but a tutorial for C (with many stars) seems breaking the loop if avcodec_receive_packet returned EAGAIN or EOF.

@3DRX 3DRX force-pushed the add_codec_ffmpeg branch from f23ca08 to e49fdd9 Compare June 18, 2025 16:36
@3DRX

3DRX commented Jun 18, 2025

Copy link
Copy Markdown
Member Author

@at-wat Thanks for your review! This is ffmpeg's documentation

https://ffmpeg.org/doxygen/3.3/group__lavc__encdec.html

I think we need to keep trying when received EAGAIN, but I'm not sure about EOF, I will test it later, hopefully this weekend.

@at-wat

at-wat commented Jun 20, 2025

Copy link
Copy Markdown
Member

https://ffmpeg.org/doxygen/3.3/group__lavc__encdec.html

For encoding, call avcodec_receive_packet(). On success, it will return an AVPacket with a compressed frame. Repeat this call until it returns AVERROR(EAGAIN) or an error. The AVERROR(EAGAIN) return value means that new input data is required to return new output. In this case, continue with sending input. For each input frame/packet, the codec will typically return 1 output frame/packet, but it can also be 0 or more than 1.

It seems saying that we should end the loop and send a new input data if EAGAIN is returned.
Also, EOF usually means there will be no data after that so the loop should be stopped in this case as well.

@3DRX 3DRX force-pushed the add_codec_ffmpeg branch from 02d9788 to 29b9eaf Compare July 3, 2025 13:06
@3DRX 3DRX force-pushed the add_codec_ffmpeg branch from 4d17ebc to d8741c0 Compare July 3, 2025 13:44
@3DRX

3DRX commented Jul 3, 2025

Copy link
Copy Markdown
Member Author

@at-wat Sorry for the delay, I've tested and tweaked the codec config options and found that as long as the encoder is configured with constrained VBR and no B frames (suitable for realtime video), every ReceivePacket call should success. Therefore we can get rid of the for loop at all, and stop on any error returned.

Thank you so much for the review :)

@at-wat at-wat self-requested a review September 26, 2025 01:10

@at-wat at-wat left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Sorry for the delay!
hardwareEncoder is fixed but softwareEncoder seems not.

Also, please click Re-request review to make the updated PR listed at https://github.com/pulls/review-requested

Comment on lines +390 to +398
for {
if err = e.codecCtx.ReceivePacket(e.packet); err != nil {
if errors.Is(err, astiav.ErrEof) || errors.Is(err, astiav.ErrEagain) {
continue
}
return nil, func() {}, err
}
break
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This error handling should be fixed as well as the hardwareEncoder

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.

3 participants