-
Notifications
You must be signed in to change notification settings - Fork 74
Add video encoding tutorial doc #1063
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
Changes from 1 commit
ba3cbbf
d5be152
fd59e4c
3eaee28
9bbeb1f
49a6614
1bcb9ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,262 @@ | ||||||||||||||||||||||||
| # Copyright (c) Meta Platforms, Inc. and affiliates. | ||||||||||||||||||||||||
| # All rights reserved. | ||||||||||||||||||||||||
| # | ||||||||||||||||||||||||
| # This source code is licensed under the BSD-style license found in the | ||||||||||||||||||||||||
| # LICENSE file in the root directory of this source tree. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
| ======================================= | ||||||||||||||||||||||||
| Encoding video frames with VideoEncoder | ||||||||||||||||||||||||
| ======================================= | ||||||||||||||||||||||||
| In this example, we'll learn how to encode video frames to a file or to raw | ||||||||||||||||||||||||
| bytes using the :class:`~torchcodec.encoders.VideoEncoder` class. | ||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # %% | ||||||||||||||||||||||||
| # First, we'll download a video and decode some frames to tensors. | ||||||||||||||||||||||||
| # These will be the input to the VideoEncoder. For more details on decoding, | ||||||||||||||||||||||||
| # see :ref:`sphx_glr_generated_examples_decoding_basic_example.py`. | ||||||||||||||||||||||||
| # Otherwise, skip ahead to :ref:`creating_encoder`. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| import requests | ||||||||||||||||||||||||
| from torchcodec.decoders import VideoDecoder | ||||||||||||||||||||||||
| from IPython.display import Video | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| def play_video(encoded_bytes): | ||||||||||||||||||||||||
| return Video( | ||||||||||||||||||||||||
| data=encoded_bytes.numpy().tobytes(), | ||||||||||||||||||||||||
| embed=True, | ||||||||||||||||||||||||
| width=640, | ||||||||||||||||||||||||
| height=360, | ||||||||||||||||||||||||
| mimetype="video/mp4", | ||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| # Video source: https://www.pexels.com/video/adorable-cats-on-the-lawn-4977395/ | ||||||||||||||||||||||||
| # License: CC0. Author: Altaf Shah. | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
| # License: CC0. Author: Altaf Shah. | |
| # Author: Altaf Shah. |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use get_frames_in_range instead, it's more efficient, and we want users to use the most efficient decoding methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Slicing actually calls get_frames_in_range():
torchcodec/src/torchcodec/decoders/_video_decoder.py
Lines 193 to 203 in 1ea235a
| def _getitem_slice(self, key: slice) -> Tensor: | |
| assert isinstance(key, slice) | |
| start, stop, step = key.indices(len(self)) | |
| frame_data, *_ = core.get_frames_in_range( | |
| self._decoder, | |
| start=start, | |
| stop=stop, | |
| step=step, | |
| ) | |
| return frame_data |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| # file-like objects via the :meth:`~torchcodec.encoders.VideoEncoder.to_filelike` | |
| # file-like objects via the :meth:`~torchcodec.encoders.VideoEncoder.to_file_like` |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is an excellent place to explain that the format parameter selects the default codec - we can also briefly explain the difference between, say, an mp4 video file and the actual codec used to decode and encode the video streams in that file. If this is well explained in any externall FFmpeg docs, we can link to those as well.
That then sets us up for the next section, as the natural next question a reader may have is, what if I don't want the default codec?
At the end of the "Codec Selection" section, we should give some guidance on when to just use format and when to specify codec as well. Nothing elaborate, just a sentence or two. I think that will go a long way to informing our about the relationship between these two options.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the suggestions, I added some brief guidance on codec vs format at the end.
I drafted text to explain the difference between container-format and codec, but I am worried it dilutes the "Codec Selection" section with text that is not specific to the API. I would be happy to add a link, but I was not able to find useful FFmpeg docs on this subject.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we could start this section by indicating that by default, the codec is selected automatically based on the container format, for example "mp4" tends to default to h264 (I think? Please check me on this)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NicolasHug, made a similar comment to what I did above. :) Doing it here or in the previous section are both great to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added an intro here explaining the default behavior, this way all codec related text is under the same header. I added the mp4 -> h264 example, as it is often the case in my experience.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this works well. My one follow-up suggestion is to connect the sentence about codec selection with the default. Something like, "If you want a codec other than the default, use the codec parameter." Followed by the explanation of what it is.
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great section above. The only issue is that it pollutes the codespace with libx264_encoded.mp4 and hevc_encoded.mp4. Let's use temporary files instead, with e.g. https://docs.python.org/3/library/tempfile.html
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| # - Values 17 or 18 are conisdered visually lossless, and the default is 23. | |
| # - Values 17 or 18 are considered visually lossless, and the default is 23. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
well done, it's really cool to visually see the effect it has on quality!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, I came here to say the same thing. :)
NicolasHug marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can use pathlib instead https://docs.python.org/3/library/pathlib.html#pathlib.Path.stat
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the second "For example" is from before adding the first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make sure to use
:class:`~torchcodec.encoders.VideoEncoder`everywhere.It should link to the docstring page. Right now it doesn't, because you need to add it to https://github.com/meta-pytorch/torchcodec/blob/main/docs/source/api_ref_encoders.rst?plain=1