Closed philipqueen closed 2 months ago
@philipqueen Thanks for reporting this bug. Could you provide terminal outputs by executing following two codes: (Make sure to use your portrait video as source)
# import the necessary packages
from deffcode import FFdecoder
# initialize and formulate the decoder using your portrait video
decoder = FFdecoder("portrait_video.mp4", verbose=True).formulate()
# print metadata as `json.dump`
print(decoder.metadata)
# terminate the decoder
decoder.terminate()
# import the necessary packages
from deffcode import FFdecoder
# disable resolution
ffparams = {"-custom_resolution":"null"}
# initialize and formulate the decoder using your portrait video
decoder = FFdecoder("portrait_video.mp4", verbose=True, **ffparams).formulate()
# print metadata as `json.dump`
print(decoder.metadata)
# terminate the decoder
decoder.terminate()
10:08:56 :: Utilities :: INFO :: Running DeFFcode Version: 0.2.5
10:08:56 :: FFhelper :: DEBUG :: Final FFmpeg Path: ffmpeg
10:08:56 :: FFhelper :: DEBUG :: FFmpeg validity Test Passed!
10:08:56 :: FFhelper :: DEBUG :: Found valid FFmpeg Version: `b'5.1.2'` installed on this system
10:08:56 :: Sourcer :: DEBUG :: Found valid FFmpeg executable: `ffmpeg`.
10:08:56 :: Sourcer :: DEBUG :: Extracting Metadata...
10:08:56 :: Sourcer :: DEBUG :: Metadata Extraction completed successfully!
10:08:57 :: FFdecoder :: INFO :: Using default `rgb24` pixel-format for this pipeline.
10:08:57 :: FFdecoder :: INFO :: Default source resolution will be used for this pipeline for defining output resolution.
10:08:57 :: FFdecoder :: INFO :: Default source framerate will be used for this pipeline for defining output framerate.
10:08:57 :: FFdecoder :: CRITICAL :: Activating Video-Only Mode of Operation.
10:08:57 :: FFdecoder :: DEBUG :: Executing FFmpeg command: `ffmpeg -vcodec hevc -i /Users/username/Downloads/portrait.MOV -pix_fmt rgb24 -s 1920x1080 -framerate 30.0 -f rawvideo -`
{
"ffmpeg_binary_path": "ffmpeg",
"source": "/Users/username/Downloads/portrait.MOV",
"source_extension": ".MOV",
"source_video_resolution": [
1920,
1080
],
"source_video_pixfmt": "yuv420p",
"source_video_framerate": 30.0,
"source_video_decoder": "hevc",
"source_duration_sec": 4.37,
"approx_video_nframes": 131,
"source_video_bitrate": "8140k",
"source_audio_bitrate": "160k",
"source_audio_samplerate": "44100 Hz",
"source_has_video": true,
"source_has_audio": true,
"source_has_image_sequence": false,
"source_demuxer": "",
"output_frames_resolution": [
1920,
1080
],
"output_frames_pixfmt": "yuv420p",
"output_framerate": 30.0,
"ffdecoder_operational_mode": "Video-Only"
}
10:08:57 :: FFdecoder :: DEBUG :: Terminating FFdecoder Pipeline...
10:08:57 :: FFdecoder :: INFO :: Pipeline terminated successfully.
10:11:11 :: Utilities :: INFO :: Running DeFFcode Version: 0.2.5
10:11:11 :: FFhelper :: DEBUG :: Final FFmpeg Path: ffmpeg
10:11:12 :: FFhelper :: DEBUG :: FFmpeg validity Test Passed!
10:11:12 :: FFhelper :: DEBUG :: Found valid FFmpeg Version: `b'5.1.2'` installed on this system
10:11:12 :: Sourcer :: DEBUG :: Found valid FFmpeg executable: `ffmpeg`.
10:11:12 :: Sourcer :: DEBUG :: Extracting Metadata...
10:11:12 :: Sourcer :: DEBUG :: Metadata Extraction completed successfully!
10:11:12 :: FFdecoder :: INFO :: Using default `rgb24` pixel-format for this pipeline.
10:11:12 :: FFdecoder :: CRITICAL :: Manually discarding `-size/-s` FFmpeg parameter from this pipeline.
10:11:12 :: FFdecoder :: INFO :: Default source resolution will be used for this pipeline for defining output resolution.
10:11:12 :: FFdecoder :: INFO :: Default source framerate will be used for this pipeline for defining output framerate.
10:11:12 :: FFdecoder :: CRITICAL :: Activating Video-Only Mode of Operation.
10:11:12 :: FFdecoder :: DEBUG :: Executing FFmpeg command: `ffmpeg -vcodec hevc -i /Users/username/Downloads/portrait.MOV -pix_fmt rgb24 -framerate 30.0 -f rawvideo -`
{
"ffmpeg_binary_path": "ffmpeg",
"source": "/Users/username/Downloads/portrait.MOV",
"source_extension": ".MOV",
"source_video_resolution": [
1920,
1080
],
"source_video_pixfmt": "yuv420p",
"source_video_framerate": 30.0,
"source_video_decoder": "hevc",
"source_duration_sec": 4.37,
"approx_video_nframes": 131,
"source_video_bitrate": "8140k",
"source_audio_bitrate": "160k",
"source_audio_samplerate": "44100 Hz",
"source_has_video": true,
"source_has_audio": true,
"source_has_image_sequence": false,
"source_demuxer": "",
"output_frames_resolution": [
1920,
1080
],
"output_frames_pixfmt": "yuv420p",
"output_framerate": 30.0,
"ffdecoder_operational_mode": "Video-Only"
}
10:11:12 :: FFdecoder :: DEBUG :: Terminating FFdecoder Pipeline...
10:11:12 :: FFdecoder :: INFO :: Pipeline terminated successfully.
@philipqueen Thanks. Could you see the output of the following code, if it's correct or having the same landscape problem:
# import the necessary packages
from deffcode import FFdecoder
import cv2
# disable resolution
ffparams = {"-custom_resolution":"null"}
# initialize and formulate the decoder using your portrait video
decoder = FFdecoder("portrait_video.mp4", frame_format="bgr24", verbose=True, **ffparams).formulate()
# grab the BGR24 frames from decoder
for frame in decoder.generateFrame():
# check if frame is None
if frame is None:
break
# {do something with the frame here}
# Show output window
cv2.imshow("Output", frame)
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# terminate the decoder
decoder.terminate()
Here is a screenshot of the cv2 imshow output from the code from your last message:
I'm currently looking through the FFMPEG bug alerts to see if this is a known FFMPEG bug, I'll let you know if I find anything.
@philipqueen This bug is because of your video reporting 1920x1080
resolution in FFmpeg. See:Stream #0:0[0x1](und): Video: hevc (Main) (hvc1 / 0x31637668), yuv420p(tv, bt709), 1920x1080, 7680 kb/s, 29.97 fps, 29.97 tbr, 600 tbn (default)
That's a good point. I notice below that it says Side data: displaymatrix: rotation of -90.00 degrees
- I'm wondering if there is some way that metadata can be used to ensure the video is rotated properly down the line.
When I add ffparams = {"-vf": "transpose=1"}
to the decoder on an iPhone video, the video is displayed sideways but not stretched/distorted, which is not ideal but is much better for my needs than being stretched/distorted.
There is an older bug related to rotation and iPhone videos. It is supposed to be fixed in ffmpeg 2.7, but this stack overflow post has some discussion about how the rotation is supposed to be handled: stack overflow discussion of iphone rotation bug in FFMPEG
That's a good point. I notice below that it says Side data: displaymatrix: rotation of -90.00 degrees - I'm wondering if there is some way that metadata can be used to ensure the video is rotated properly down the line.
@philipqueen Excellent, This can be used to correctly handle this bug. Could you share sample video (with this bug) for testing? That would be helpful.
When I add ffparams = {"-vf": "transpose=1"} to the decoder on an iPhone video, the video is displayed sideways but not stretched/distorted, which is not ideal but is much better for my needs than being stretched/distorted. There is an older bug related to rotation and iPhone videos. It is supposed to be fixed in ffmpeg 2.7, but this stack overflow post has some discussion about how the rotation is supposed to be handled: stack overflow discussion of iphone rotation bug in FFMPEG
@philipqueen Thanks for bring this to my attention. I'll look into it.
Here is a short video that has the bug:
I really appreciate the quick support! Continue to let me know how I can help.
@philipqueen Thanks, I'll run some tests on my system and Let you know.
@philipqueen The FFMPEG changed the default behavior to auto rotate video sources with rotation metadata in 2015 since v2.7. So to resolve it you need to turn off auto rotation with -noautorotate
flag and use (rotate
or transpose
) filter to rotate manually as follows:
# import the necessary packages
from deffcode import FFdecoder
import cv2
# disable autorotate and transpose clockwise(1)
# 0 = 90° counter-clockwise and vertical flip (default)
# 1 = 90° clockwise.
# 2 = 90° counter-clockwise.
# 3 = 90° clockwise and vertical flip.
ffparams = {"-ffprefixes": ["-noautorotate"], "-vf": "transpose=1"}
# initialize and formulate the decoder using your portrait video
decoder = FFdecoder(
"test.mov", frame_format="bgr24", verbose=True, **ffparams
).formulate()
# grab the BGR24 frames from decoder
for frame in decoder.generateFrame():
# check if frame is None
if frame is None:
break
# {do something with the frame here}
# Show output window
cv2.imshow("Output", frame)
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# print metadata as `json.dump`
print(decoder.metadata)
# terminate the decoder
decoder.terminate()
I'm adding metadata video-orientation
to aware user of video orientation so he/she can make changes accordingly.
Thanks @abhiTronix, that rotate flag works well on iphone videos, although on one video it ran it upside down. That's easy to fix manually, but the ideal would be to do it programatically, and have it work regardless of the video passed in.
Would your proposed video-orientation
metadata just check if it's vertical, or would it look for the displaymatrix rotation parameter?
That's easy to fix manually, but the ideal would be to do it programatically, and have it work regardless of the video passed in.
@philipqueen Actually I looked into it and I found out not everyone wants there video to be rotated automatically. Like you're manually flipping the video, you don't want to auto-rotate to original. Also, filters used with -noautorotate
fail to work correctly if we're adding these flags automatically, and it will break things for devs already using deffcode in there applications. Therefore it is better to be handled by the user. A doc mentioning this issue and solution is enough for a end-user developer.
Would your proposed video-orientation metadata just check if it's vertical, or would it look for the displaymatrix rotation parameter?
Checks for displaymatrix
rotation metadata
I agree, as long as there is an easy automatic/programatic fix to the issue. Hopefully adding the displaymatrix rotation to the metadata will be enough for that to work properly, with the workflow check the metadata for displaymatrix rotation -> conditionally set the ffparams depending on the metadata -> feed those conditionally set params into the FFdecoder
.
I found a good temporary solution for my needs. Because deffcode reports the metadata for the problematic videos reversed, but opencv does not, comparing the width reported from deffcode vs the width reported from opencv identifies the problematic videos. So if the two library's metadata reporting agrees, don't transpose the videos, and if they do agree, transpose the videos.
Getting the displaymatrix
rotation metadata will provide the next step of indicating which direction to transpose the videos
I found a good temporary solution for my needs. Because deffcode reports the metadata for the problematic videos reversed, but opencv does not, comparing the width reported from deffcode vs the width reported from opencv identifies the problematic videos. So if the two library's metadata reporting agrees, don't transpose the videos, and if they do agree, transpose the videos.
Sounds reasonable. Thanks for insight.
Getting the displaymatrix rotation metadata will provide the next step of indicating which direction to transpose the videos
Yes. I've added source_video_orientation
metadata to get display source video orientation that checks for displaymatrix
rotation metadata
@abhiTronix thanks, I got it working very smoothly with the dev version! Here's the code that is correctly rotating my videos for anyone that is curious:
sourcer = Sourcer(input_video_pathstring).probe_stream()
metadata_dictionary = sourcer.retrieve_metadata()
tranposition_dictionary = {
90.0: "transpose=cclock",
-270.0: "transpose=cclock",
-90.0: "transpose=clock",
270.0: "transpose=clock",
180.0: "transpose=cclock,transpose=cclock",
}
if metadata_dictionary["source_video_orientation"] != 0:
ffparams = {
"-ffprefixes": ["-noautorotate"],
"-vf": tranposition_dictionary[
metadata_dictionary["source_video_orientation"]
],
}
else:
ffparams = {}
Successfully resolved in commit https://github.com/abhiTronix/deffcode/commit/b6ee94b2c0b9db95dbd600fbeb311701d8358b05
Description
Trying to decode a portrait iPhone video results in the video being squashed into landscape. The shape is reversed (width and height values switched), and the video content is stretched horizontally.
This behavior occurs with the native MOV output and persists with conversion to mp4.
If it helps, the behavior matches this bug from MoviePy: https://github.com/Zulko/moviepy/issues/1911 I found this library searching for a MoviePy alternative to circumvent this exact issue, so I would appreciate help in either squashing the bug or figuring out a good workaround.
Issue Checklist
Expected behaviour
The video is loaded in portrait mode, matching the shape in the metadata. Playing in quicktime, my example looks like this:
Actual behaviour
The video is squashed into landscape mode, and the shape is reversed from the metadata. Visually it looks like this:
And in the terminal output, the difference in shape is apparent here:
Steps to reproduce
Terminal log output
Python Code(Optional)
DeFFcode Version
0.2.5
Python version
Python 3.9.16
Operating System version
MacOS Monterey 12.6.1
Any other Relevant Information?
No response