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

videoio: Add raw encoded video stream encapsulation to cv::VideoWriter with CAP_FFMPEG #24363

Merged

Conversation

cudawarped
Copy link
Contributor

@cudawarped cudawarped commented Oct 5, 2023

Allow raw encoded video streams (e.g. h264[5]) to be encapsulated by cv::VideoWriter to video containers (e.g. mp4/mkv).

Operates in a similar way to #15290 where encapsulation is enabled by setting the VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO flag when constructing cv::VideoWriter e.g.

VideoWriter container(fileNameOut, api, fourcc, fps, { width, height }, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1 });

and each raw encoded frame is passed as single row of a CV_8U cv::Mat.

The main reason for this PR is to allow cudacodec::VideoWriter to output its encoded streams to a suitable container, see opencv/opencv_contrib#3569.

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • The PR is proposed to the proper branch
  • There is a reference to the original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • The feature is well documented and sample code can be built with the project CMake

@cudawarped
Copy link
Contributor Author

@asmorkalov Thanks for running the CI, unfortunately I left in one of my own tests

[ RUN ] videoio_encapsulate.write/6, where GetParam() = ("video/big_buck_bunny.vob", "mp4", 125, 125)
unknown file: Failure
C++ exception with description "OpenCV(4.8.0-dev) /home/ci/opencv/modules/ts/src/ts.cpp:1071: error: (-2:Unspecified error) OpenCV tests: Can't find required data file: video/big_buck_bunny.vob in function 'findData'
" thrown in the test body.
[ FAILED ] videoio_encapsulate.write/6, where GetParam() = ("video/big_buck_bunny.vob", "mp4", 125, 125) (0 ms)

I've removed the test, so everything should pass now.

@cudawarped cudawarped changed the title videoio: Add raw encoded video stream encapsulation to cv::VideoWriter with CAP_FFMEG videoio: Add raw encoded video stream encapsulation to cv::VideoWriter with CAP_FFMPEG Oct 6, 2023
@asmorkalov
Copy link
Contributor

n file included from /build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg.cpp:50:0:
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp: In function 'int icv_av_encapsulate_video_FFMPEG(AVFormatContext*, AVStream*, AVCodecContext*, uint8_t*, int, int, bool)':
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:2337:37: error: 'av_packet_alloc' was not declared in this scope
     AVPacket* pkt = av_packet_alloc();
                                     ^
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:2345:24: error: 'av_packet_free' was not declared in this scope
     av_packet_free(&pkt);
                        ^
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp: In member function 'bool CvVideoWriter_FFMPEG::open(const char*, int, double, int, int, const cv::VideoWriterParameters&)':
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:3086:9: error: 'AVHWDeviceType' was not declared in this scope
         AVHWDeviceType hw_type = AV_HWDEVICE_TYPE_NONE;
         ^
/build/precommit_linux64/4.x/opencv/modules/videoio/src/cap_ffmpeg_impl.hpp:3085:23: warning: unused variable 'hw_format' [-Wunused-variable]
         AVPixelFormat hw_format = AV_PIX_FMT_NONE;
                       ^
modules/videoio/CMakeFiles/opencv_videoio.dir/build.make:302: recipe for target 'modules/videoio/CMakeFiles/opencv_videoio.dir/src/cap_ffmpeg.cpp.o' failed
make[2]: *** [modules/videoio/CMakeFiles/opencv_videoio.dir/src/cap_ffmpeg.cpp.o] Error 1
make[2]: *** Waiting for unfinished jobs....

@asmorkalov asmorkalov self-requested a review October 18, 2023 13:01
@asmorkalov asmorkalov self-assigned this Oct 18, 2023
@asmorkalov asmorkalov added this to the 4.9.0 milestone Oct 18, 2023
@asmorkalov
Copy link
Contributor

Looks like av_packet_alloc and other mentioned functions are not implemented in FFmpeg n2.8.17 provided with Ubuntu 16.04. I understand, that it's old, but we need to able to build it even with limited support.

@cudawarped cudawarped force-pushed the videoio_ffmpeg_add_stream_encapsulation branch from 16c58e5 to b0f4231 Compare October 19, 2023 12:13
@cudawarped
Copy link
Contributor Author

[ RUN ] videoio_encapsulate.write/17, where GetParam() = ("video/sample_322x242_15frames.yuv420p.libx265.mp4", "mp4", 15, 15)
OpenCV: FFMPEG: tag 0x63766568/'hevc' is not supported with codec id 173 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31766568/'hev1'
Checking encapsulated video container: /tmp/__opencv_temp.DBKT5f.test_encapsulated_stream.mp4
Error: The action has timed out.

This is the same test which timed out last time, I don't have access to macOS-ARM64 to debug this, should I just disable that test to see if it passes?

@asmorkalov
Copy link
Contributor

asmorkalov commented Oct 20, 2023

I get strange test failures locally:

[  FAILED  ] 6 tests, listed below:
[  FAILED  ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125)
[  FAILED  ] videoio_encapsulate.write/1, where GetParam() = ("video/big_buck_bunny.h265", "mp4", 125, 125)
[  FAILED  ] videoio_encapsulate.write/16, where GetParam() = ("video/sample_322x242_15frames.yuv420p.libx264.mp4", "mp4", 15, 15)
[  FAILED  ] videoio_encapsulate.write/17, where GetParam() = ("video/sample_322x242_15frames.yuv420p.libx265.mp4", "mp4", 15, 15)
[  FAILED  ] videoio_encapsulate.write/18, where GetParam() = ("video/sample_322x242_15frames.yuv420p.libx264.mp4", ".mp4", 15, 15)
[  FAILED  ] videoio_encapsulate.write/22, where GetParam() = ("../cv/video/768x576.avi", "mp4", 15, 16)

Example:

[ RUN      ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125)
[ INFO:0@0.135] global cap_ffmpeg_impl.hpp:1047 open VIDEOIO/FFMPEG: enabled demuxer only mode: '/home/alexander/opencv_extra/testdata/highgui/video/big_buck_bunny.h264'
[DEBUG:0@0.142] global cap_ffmpeg_impl.hpp:2870 open Selected pixel format: bgr24
OpenCV: FFMPEG: tag 0x34363268/'h264' is not supported with codec id 28 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x31637661/'avc1'
Checking encapsulated video container: /tmp/__opencv_temp.G7BrNb.test_encapsulated_stream.mp4
[DEBUG:0@0.149] global cap_ffmpeg_impl.hpp:1203 open FFMPEG: stream[0] is video stream with codecID=28 width=672 height=384
[DEBUG:0@0.155] global cap_ffmpeg_impl.hpp:1203 open FFMPEG: stream[0] is video stream with codecID=28 width=672 height=384
[ INFO:0@0.155] global cap_ffmpeg_impl.hpp:1047 open VIDEOIO/FFMPEG: enabled demuxer only mode: '/tmp/__opencv_temp.G7BrNb.test_encapsulated_stream.mp4'
[DEBUG:0@0.168] global cap_ffmpeg_impl.hpp:1638 retrieveFrame Input picture format: yuv420p
[DEBUG:0@0.176] global cap_ffmpeg_impl.hpp:1638 retrieveFrame Input picture format: yuv420p
/home/alexander/Projects/OpenCV/opencv-master/modules/videoio/test/test_ffmpeg.cpp:314: Failure
Expected equality of these values:
  keyFrameReference
    Which is: true
  keyFrameActual
    Which is: false
[  FAILED  ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125) (48 ms)
[----------] 1 test from videoio_encapsulate (48 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (48 ms total)
[  PASSED  ] 0 tests.
[  FAILED  ] 1 test, listed below:
[  FAILED  ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125)

OS: Ubuntu 18.04 with default FFmpeg instance.

@cudawarped
Copy link
Contributor Author

I knew this PR was a bad idea, so it works on FFMpeg 2.8.17 and 4.2.4 but not 3.4.8. I'll take a look.

@cudawarped cudawarped force-pushed the videoio_ffmpeg_add_stream_encapsulation branch from b0f4231 to c03ae53 Compare October 22, 2023 04:29
@cudawarped
Copy link
Contributor Author

@asmorkalov It looks like the problem is not that the keyframe is not marked when the stream is encapsulated but that FFMpeg discards that information when the stream is read back and filtered.

In FFMpeg >= 3.1 av_bitstream_filter_filter takes keyframe as its last argument but doesn't use it. I haven't looked through all of the internals to check what is going on and to see if the packet.flags argument is used somewhere but incorrectly however the end result is that if packet.flags & AV_PKT_FLAG_KEY == 1, packet_filtered.flags & AV_PKT_FLAG_KEY != 1.

I've introduced a workaround which fixes the issue on Ubuntu 20.04 with OpencV built against the lattest version of FFMpeg 3.4 (3.4.13) and would appreciate it if you could test it again on your Ubuntu 18.04 instance.

@cudawarped cudawarped force-pushed the videoio_ffmpeg_add_stream_encapsulation branch from c03ae53 to 6ebc81e Compare October 22, 2023 05:37
@asmorkalov
Copy link
Contributor

@cudawarped Thanks a lot for the effort. I tried Ubuntu 16.04, 18.04, 22.04. The last 2 works great! Ubuntu 16.04 triggers some errors with h264 and h265 codecs in the same way as on Buildbot. I'll try to debug it tomorrow. Error message example:

Note: Google Test filter = videoio_encapsulate.write/0
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from videoio_encapsulate
[ RUN      ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125)
OpenCV: FFMPEG: tag 0x34363268/'h264' is not supported with codec id 28 and format 'mp4 / MP4 (MPEG-4 Part 14)'
OpenCV: FFMPEG: fallback to use tag 0x00000021/'!???'
Checking encapsulated video container: /tmp/__opencv_temp.ri43re.test_encapsulated_stream.mp4
[NULL @ 0x311770] Packet header is not contained in global extradata, corrupted stream or invalid MP4/AVCC bitstream
/home/ubuntu/Projects/opencv/modules/videoio/test/test_ffmpeg.cpp:311: Failure
Value of: capActualRaw.grab()
  Actual: false
Expected: true
[  FAILED  ] videoio_encapsulate.write/0, where GetParam() = ("video/big_buck_bunny.h264", "mp4", 125, 125) (212 ms)

@asmorkalov
Copy link
Contributor

@cudawarped I tuned test parameters a bit to satisfy old FFmpeg versions. Looks like they support not all combinations of codec-container. Please take a look.

@cudawarped
Copy link
Contributor Author

cudawarped commented Oct 24, 2023

@asmorkalov Nice fix, working on my Ubuntu 20.04 with FFMpeg 2.8.17 and its passing on IOS!

I was also thinking to reduce the number of frames in the following to 10 as there is only one key frame so it doesn't make any sense to read all 125.

i.e. From

videoio_encapsulate_params_t("video/big_buck_bunny.h264", "avi", 125, 125),
videoio_encapsulate_params_t("video/big_buck_bunny.h265", "mp4", 125, 125),
videoio_encapsulate_params_t("video/big_buck_bunny.avi", "mp4", 125, 125),

to

videoio_encapsulate_params_t("video/big_buck_bunny.h264", "avi", 10, 10),
videoio_encapsulate_params_t("video/big_buck_bunny.h265", "mp4", 10, 10),
videoio_encapsulate_params_t("video/big_buck_bunny.avi", "mp4", 10, 10),

Do you want to do that or should I?

Copy link
Contributor

@asmorkalov asmorkalov left a comment

Choose a reason for hiding this comment

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

👍

@asmorkalov asmorkalov merged commit 38bc519 into opencv:4.x Oct 25, 2023
26 checks passed
@asmorkalov asmorkalov mentioned this pull request Nov 3, 2023
IskXCr pushed a commit to Haosonn/opencv that referenced this pull request Dec 20, 2023
…ream_encapsulation

videoio: Add raw encoded video stream muxing to cv::VideoWriter with CAP_FFMPEG opencv#24363

Allow raw encoded video streams (e.g. h264[5]) to be encapsulated by `cv::VideoWriter` to video containers (e.g. mp4/mkv).

Operates in a similar way to opencv#15290 where encapsulation is enabled by setting the `VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO` flag when constructing `cv::VideoWriter` e.g.
```
VideoWriter container(fileNameOut, api, fourcc, fps, { width, height }, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1 });
```
and each raw encoded frame is passed as single row of a `CV_8U` `cv::Mat`.

The main reason for this PR is to allow `cudacodec::VideoWriter` to output its encoded streams to a suitable container, see opencv/opencv_contrib#3569.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
thewoz pushed a commit to thewoz/opencv that referenced this pull request Jan 4, 2024
…ream_encapsulation

videoio: Add raw encoded video stream muxing to cv::VideoWriter with CAP_FFMPEG opencv#24363

Allow raw encoded video streams (e.g. h264[5]) to be encapsulated by `cv::VideoWriter` to video containers (e.g. mp4/mkv).

Operates in a similar way to opencv#15290 where encapsulation is enabled by setting the `VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO` flag when constructing `cv::VideoWriter` e.g.
```
VideoWriter container(fileNameOut, api, fourcc, fps, { width, height }, { VideoWriterProperties::VIDEOWRITER_PROP_RAW_VIDEO, 1 });
```
and each raw encoded frame is passed as single row of a `CV_8U` `cv::Mat`.

The main reason for this PR is to allow `cudacodec::VideoWriter` to output its encoded streams to a suitable container, see opencv/opencv_contrib#3569.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [x] I agree to contribute to the project under Apache 2 License.
- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
- [x] The PR is proposed to the proper branch
- [x] There is a reference to the original bug report and related work
- [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
      Patch to opencv_extra has the same branch name.
- [x] The feature is well documented and sample code can be built with the project CMake
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants