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

FIx video-to-image examples not working with H264 videos #90

Merged
merged 4 commits into from
Feb 4, 2019

Conversation

fand
Copy link
Contributor

@fand fand commented Feb 4, 2019

I tried video-to-xxx.go examples with H264/MP4 videos then I encountered several errors.
This PR includes fixes to work with H264 videos.

I used my own VJ loops and other H264 videos downloaded from beeple.

Retry decoding if err is EAGAIN 034ff4f

When I run video-to-image.go with H264 file, it threw an error: Resource temporarily unavailable on packet.Frames().
The error message came from ffmpeg's EAGAIN, it means we should retry avcodec_receive_frame until it succeeds.

I added retry code to video-to-*.go files.
It uses goto for simplicity, but if you don't like it I'll rewrite them with for loop.

Set encoder's timebase in video-to-jpeg-p.go aebce39

go run examples/video-to-jpeg-p.go foo.mp4 throws an error:
[jpeg2000 @ 0x880dc00] The encoder timebase is not set.

I set the codec context's timebase to srcVideoStream.TimeBase.AVR().

Scale frames in video-to-mjpeg.go a557b34

go run examples/video-to-mjpeg.go foo.mp4 throws an error:
[mjpeg @ 0xc001200] Invalid pts (0) <= last (0)

I noticed video-to-mjpeg.go was not converting frames with sws, so I added swsCtx.Scale(frame, dstFrame) before encoding.
It works, but I'm not sure this is the right decision... 😅

Use CLI args instead of flags, like other examples 62f2394

video-to-jpeg-p.go takes input video from CLI flag while other examples takes input from CLI args.
It's confusing, so I fixed it.


BTW gmf is the best repo for video editing in go, thanks @3d0c ! 💪

Request frame for the sake its temporarily unavailable

Remove unnecessary code

tmp

Revert "Remove unnecessary code"

This reverts commit b70dd5feba6bcfa48c68d9f6367044bdbd861cd0.
@3d0c 3d0c merged commit 9ee6405 into 3d0c:master Feb 4, 2019
@3d0c
Copy link
Owner

3d0c commented Feb 4, 2019

@fand Hi.
Thanks for your contribution. These examples are pretty old and should be completely rewritten sometimes.

About EAGAIN — there is a mess in encoding/decoding api, packet.Frames() is a very old method and it does it in a wrong way, but still, there is no completely right way.
For now it's better to use CodecCtx.Encode()/Decode() methods, which return a slice of frames/packets and support drain. But error handling is still wrong.
For manual management, you can use CodecCtx.Decode2() which returns retval as is.

Will fix and unify it soon.

@fand
Copy link
Contributor Author

fand commented Feb 5, 2019

@3d0c
Thx! I didn't know there is CodecCtx.Decode2() 😅
so we should write like this?

	decode:
		var errorCode int
		frame, errorCode := codecCtx.Decode2(packet)
		if errorCode != 0 {
			// Retry if EAGAIN
			if errorCode == -35 {
				goto decode
			}
			log.Fatal(errorCode)
		}

If so, I'll make another PR to replace it

@3d0c
Copy link
Owner

3d0c commented Feb 5, 2019

@fand
In fact, there is two function in Decode2 method, which could return EAGAIN - avcodec_send_packet and avcodec_receive_frame, but we don't expect it from first one. And for receive_frame we should feed more input, not the same packet, so goto decode shouldn't work, so we have to continue loop, read next packet and try again.

@fand
Copy link
Contributor Author

fand commented Feb 5, 2019

hmm... but it seems we should loop avcodec_receive_frame for some codecs.

And for receive_frame we should feed more input, not the same packet, so goto decode shouldn't work,

I didn't mean that we call avcodec_send_packet on EAGAIN.

btw, I tried using continue like this: 0680bef

but when I run it with H264 files it always drops a few frames on my PC.
libavcodec returns EAGAIN in avcodec_receive_frame() at the beginning of decoding.

I inspected the implementation of avcodec_receive_frame and I found a comment:

Certain decoders might consume partial packets without
returning any output, so this function needs to be called in a loop until it
returns EAGAIN.

maybe it's typo of until it doesn't return EAGAIN ... ?

It seems we should wait til libx264 gets ready, by calling avcodec_receive_frame() repeatedly.

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.

2 participants