diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index 18a4c3a..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,69 +0,0 @@ ---- -name: 🐞 Bug Report -about: File a bug-report for DeFFcode -labels: 'bug, needs triage' -title: '[BUG] ' -assignees: 'abhiTronix' ---- - -<!-- - Please note that your issue will be fixed much faster if you spend about - half an hour preparing it, including the exact reproduction steps and a demo. - - If you're in a hurry or don't feel confident, it's fine to report bugs with - less details, but this makes it less likely they'll get fixed soon. - - If the important info is missing we'll add the 'need more info' label - or may choose to close the issue until there is enough information provided. ---> - -<!-- -Note: Please search to see if an issue already exists for the bug you encountered. ---> - - -## Brief Description -<!-- Provide a brief introduction to the issue, and why you consider it to be a bug --> -_Kindly briefly explain the issue here._ - - -### Acknowledgment -<!-- By posting an issue you acknowledge the following: (Put an `x` in all the boxes that apply(important)) --> - -- [ ] I have searched the [issues](https://github.com/abhiTronix/deffcode/issues) for my issue and found nothing related or helpful. -- [ ] I have read the DeFFcode [Documentation](https://abhitronix.github.io/deffcode/latest). -- [ ] I have read the DeFFcode [Issue Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/issue/). - - -### Current Environment -<!-- Include as many relevant details about the environment you experienced the bug in --> -* My DeFFcode version: <!-- Run command `python -c "import deffcode; print(deffcode.__version__)"` --> -* My Python version: <!---Run command `python -V` --> -* My Pip version: <!-- Run command `python -c "import pip; print(pip.__version__)"` --> -* My Operating System and its version: <!--- `Windows 10` | `Ubuntu 18.04` | `macOS big slur` etc. --> - -### Expected Behavior -<!-- Tell us what should happen --> - -### Actual Behavior -<!-- Tell us what happens instead --> -<!-- You can turn ```verbose=True``` in parameters of the respective DeFFcode API for getting debug output --> - -### Possible Fix -<!-- Not obligatory, but suggest a fix or reason for the bug or else remove this block--> - -### Steps to reproduce -<!-- - How would you describe your issue to someone who doesn’t know you or your project? - Try to write a sequence of steps that anybody can repeat to see the issue. ---> - -(Write your steps here:) - -1. -2. -3. - - -### Miscellaneous -<!-- Provide any screenshots or relevant information if available or else remove this block --> \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml new file mode 100644 index 0000000..02fa23f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -0,0 +1,85 @@ +name: Bug Report +description: File a bug-report for DeFFcode APIs 🐞 +title: "[Bug]: " +labels: ["Bug :lady_beetle:", "Needs Triage :monocle_face:"] +assignees: + - abhiTronix +body: + - type: textarea + attributes: + label: Description + description: Please provide a brief description of the bug in 1-2 sentences. + validations: + required: true + - type: checkboxes + attributes: + label: Issue Checklist + description: "By posting this issue you acknowledge the following:" + options: + - label: I have searched open or closed [issues](https://github.com/abhiTronix/deffcode/issues) for my problem and found nothing related or helpful. + required: true + - label: I have read the [Documentation](https://abhitronix.github.io/deffcode/latest) and found nothing related to my problem. + required: true + - label: I've read the [Issue Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/issue/#submitting-an-issue-guidelines) and wholeheartedly agree. + required: true + - type: textarea + attributes: + label: Expected behaviour + description: Please describe precisely what you'd expect to happen. + validations: + required: true + - type: textarea + attributes: + label: Actual behaviour + description: Please describe precisely what is actually happening, and why you consider it to be a bug? + validations: + required: true + - type: textarea + attributes: + label: Steps to reproduce + description: Please describe the steps to reproduce the bug. + placeholder: |- + 1. ... + 2. ... + 3. ... + validations: + required: true + - type: textarea + attributes: + label: Terminal log output + description: Please copy and paste any relevant terminal log output to help quickly debug bugs. You can enable verbose flag with `verbose=True` in any DeFFcode API for getting verbose terminal logs. + render: shell + - type: textarea + attributes: + label: Python Code(Optional) + description: Share python code you're using(only if available) here or else leave this block empty. You can directly paste your python code here, no need for any backticks or formatting. + render: python + - type: input + attributes: + label: DeFFcode Version + description: What version of DeFFcode are you running? Run command `python -c "import deffcode; print(deffcode.__version__)"` to find out. + placeholder: ex. 0.2.3 + validations: + required: true + - type: input + attributes: + label: Python version + description: What version of Python you're running DeFFcode on? Run command `python -V` to find out. + placeholder: ex. 3.7 + validations: + required: true + - type: input + attributes: + label: Operating System version + description: What Operating system you're using? + placeholder: ex. Linux Mint 20.3 Cinnamon + validations: + required: true + - type: textarea + attributes: + label: Any other Relevant Information? + description: Provide any other relevant information if available or else leave this block empty. If applicable, you can also drag-and-drop mockups or screenshots images here, or link to external assets. + placeholder: |- + ex. Related Issue: xyz/foo#4083 + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/idea.yaml b/.github/ISSUE_TEMPLATE/idea.yaml new file mode 100644 index 0000000..73707c7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/idea.yaml @@ -0,0 +1,43 @@ +name: Idea +description: Suggest an idea for improving DeFFcode 💡 +title: "[Idea]: " +labels: "Idea :bulb:" +body: + - type: checkboxes + attributes: + label: Issue guidelines + description: Please read the Issue guidelines before proceeding. + options: + - label: I've read the [Issue Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/issue/#submitting-an-issue-guidelines) and wholeheartedly agree. + required: true + - type: checkboxes + attributes: + label: Issue Checklist + description: "By posting this issue you acknowledge the following:" + options: + - label: I have searched open or closed [issues](https://github.com/abhiTronix/deffcode/issues) and found nothing related to my idea. + required: true + - label: I have read the [Documentation](https://abhitronix.github.io/deffcode/latest) and it doesn't mention anything about my idea. + required: true + - label: To my best knowledge, my idea wouldn't break something for other users. + required: true + - type: textarea + attributes: + label: Describe your Idea + description: Please describe your Idea thoroughly here. Will this change the existing DeFFcode APIs? How? + validations: + required: true + - type: textarea + attributes: + label: Use Cases + description: Please describe how would you use it? How can it benefit other users? + validations: + required: true + - type: textarea + attributes: + label: Any other Relevant Information? + description: Provide any other relevant information if available or else leave this block empty. If applicable, you can also drag-and-drop mockups or screenshots images here, or link to external assets. + placeholder: |- + ex. Related Issue: xyz/foo#4083 + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/proposal.md b/.github/ISSUE_TEMPLATE/proposal.md deleted file mode 100644 index 99e5ba7..0000000 --- a/.github/ISSUE_TEMPLATE/proposal.md +++ /dev/null @@ -1,31 +0,0 @@ ---- -name: 💡 Ideas -about: Suggest new ideas for improving DeFFcode -labels: 'idea' -title: '[Idea]: ' ---- - -<!--- Add a brief title for your issue above --> - - -## Brief Description - -<!--- Provide a brief description of the change or addition you are proposing --> -_Kindly explain the issue here._ - - -### Context -<!--- Why is this change important to you? How would you use it? --> -<!--- Will this change the existing DeFFcode APIs or docs? How? --> -<!--- How can it benefit other users? --> - - -### Your Current Environment -<!--- Include as many relevant details about the environment you worked in --> -* DeFFcode version: <!--- Run command `python -c "import deffcode; print(deffcode.__version__)"` --> -* Python version: <!---Run command `python -V` --> -* Operating System and version: - - -### Any Other Information -<!-- Provide any screenshots or relevant information if available or else remove this block --> \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md deleted file mode 100644 index ed5d946..0000000 --- a/.github/ISSUE_TEMPLATE/question.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: ❓ Question -about: Have any questions regarding DeFFcode? -labels: 'question' -title: '[Question]: ' -assignees: 'abhiTronix' ---- -<!--- Add a brief title for your issue above --> - - -## Question - -<!--- Provide your question description here --> -_Kindly describe the issue here._ - - -### Acknowledgment - -<!--- By posting an issue you acknowledge the following: (Put an `x` in all the boxes that apply(important)) --> -- [ ] I have searched the [issues](https://github.com/abhiTronix/deffcode/issues) for my issue and found nothing related or helpful. -- [ ] I have read the DeFFcode [Documentation](https://abhitronix.github.io/deffcode/latest). -- [ ] I have asked this question on [DeFFcode Community Channel](https://gitter.im/deffcode-python/community) but got no satisfactory answer. - - -### Context -<!--- How has this issue affected you? What are you trying to accomplish? --> -<!--- Providing context helps us come up with a solution that is most useful in the real world --> - - -### Your Environment -<!-- Include as many relevant details about the environment you experienced the bug in --> -* My DeFFcode version: <!-- Run command `python -c "import deffcode; print(deffcode.__version__)"` --> -* My Python version: <!---Run command `python -V` --> -* My Pip version: <!-- Run command `python -c "import pip; print(pip.__version__)"` --> -* My Operating System and its version: <!--- `Windows 10` | `Ubuntu 18.04` | `macOS big slur` etc. --> - - -### Any Other Information -<!-- Provide any screenshots or relevant information if available or else remove this block --> \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/question.yaml b/.github/ISSUE_TEMPLATE/question.yaml new file mode 100644 index 0000000..cd53fc7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yaml @@ -0,0 +1,66 @@ +name: Question +description: Have any questions regarding DeFFcode APIs ❔ +title: "[Question]: " +labels: "Question :grey_question:" +body: + - type: checkboxes + attributes: + label: Issue guidelines + description: Please read the Issue guidelines before proceeding. + options: + - label: I've read the [Issue Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/issue/#submitting-an-issue-guidelines) and wholeheartedly agree. + required: true + - type: checkboxes + attributes: + label: Issue Checklist + description: "By posting this issue you acknowledge the following:" + options: + - label: I have searched open or closed [issues](https://github.com/abhiTronix/deffcode/issues) for my problem and found nothing related or helpful. + required: true + - label: I have read the [Documentation](https://abhitronix.github.io/deffcode/latest) and found nothing related to my problem. + required: true + - type: textarea + attributes: + label: Describe your Question + description: Please describe your question thoroughly here. What are you trying to accomplish? + validations: + required: true + - type: textarea + attributes: + label: Terminal log output(Optional) + description: Copy and paste any relevant terminal log output(only if applicable) here or else leave this block empty. You can enable verbose flag with `verbose=True` in any DeFFcode API for getting verbose terminal logs. + render: shell + - type: textarea + attributes: + label: Python Code(Optional) + description: Share python code you're using(only if available) here or else leave this block empty. You can directly paste your python code here, no need for any backticks or formatting. + render: python + - type: input + attributes: + label: DeFFcode Version + description: What version of DeFFcode are you running? Run command `python -c "import deffcode; print(deffcode.__version__)"` to find out. + placeholder: ex. 0.2.3 + validations: + required: true + - type: input + attributes: + label: Python version + description: What version of Python you're running DeFFcode on? Run command `python -V` to find out. + placeholder: ex. 3.7 + validations: + required: true + - type: input + attributes: + label: Operating System version + description: What Operating system you're using? + placeholder: ex. Linux Mint 20.3 Cinnamon + validations: + required: true + - type: textarea + attributes: + label: Any other Relevant Information? + description: Provide any other relevant information if available or else leave this block empty. If applicable, you can also drag-and-drop mockups or screenshots images here, or link to external assets. + placeholder: |- + ex. Related Issue: xyz/foo#4083 + validations: + required: false diff --git a/.gitignore b/.gitignore index b6e4761..3193c96 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,7 @@ dmypy.json # Pyre type checker .pyre/ + +# vim temporary files +*~ +.*.sw? diff --git a/README.md b/README.md index d15656d..bdd3992 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ Once you have DeFFcode installed, checkout our Well-Documented **[Recipes 🍱][ <details open> <summary><b>Basic Decoding Recipes</b></summary> -- [Capturing RGB frames from a video file][capturing-rgb-frames-from-a-video-file] +- [Accessing RGB frames from a video file][accessing-rgb-frames-from-a-video-file] - [Capturing and Previewing BGR frames from a video file][capturing-and-previewing-bgr-frames-from-a-video-file] _(OpenCV Support)_ - [Playing with any other FFmpeg pixel formats][capturing-and-previewing-bgr-frames-from-a-video-file] - [Capturing and Previewing frames from a Webcam][capturing-and-previewing-frames-from-a-webcam] @@ -293,7 +293,7 @@ We're offering support for DeFFcode on [**Gitter Community Channel**][gitter]. C # Donations <div align="center"> - <img src="docs/overrides/assets/images/help_us.png" alt="PiGear" width="50%" /> + <img src="docs/overrides/assets/images/help_us.png" alt="Donation" width="50%" /> <p><i>DeFFcode is free and open source and will always remain so. ❤️</i></p> </div> @@ -306,6 +306,31 @@ It is something I am doing with my own free time. But so much more needs to be d   + + +# Citation + +Here is a Bibtex entry you can use to cite this project in a publication: + +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6976948.svg)](https://doi.org/10.5281/zenodo.6976948) + +```BibTeX +@software{deffcode, + author = {Abhishek Thakur}, + title = {abhiTronix/deffcode: v0.2.2}, + month = aug, + year = 2022, + publisher = {Zenodo}, + version = {v0.2.2}, + doi = {10.5281/zenodo.6976948}, + url = {https://doi.org/10.5281/zenodo.6976948} +} +``` + +  + +  + # Copyright **Copyright © abhiTronix 2021** @@ -353,7 +378,7 @@ Basic Recipes [saving-key-frames-as-image]:https://abhitronix.github.io/deffcode/latest/recipes/basic/save-keyframe-image/#saving-key-frames-as-image [extracting-video-metadata]:https://abhitronix.github.io/deffcode/latest/recipes/basic/extract-video-metadata/#extracting-video-metadata -[capturing-rgb-frames-from-a-video-file]:https://abhitronix.github.io/deffcode/latest/recipes/basic/decode-video-files/#capturing-rgb-frames-from-a-video-file +[accessing-rgb-frames-from-a-video-file]:https://abhitronix.github.io/deffcode/latest/recipes/basic/decode-video-files/#accessing-rgb-frames-from-a-video-file [capturing-and-previewing-bgr-frames-from-a-video-file]:https://abhitronix.github.io/deffcode/latest/recipes/basic/decode-video-files/#capturing-and-previewing-bgr-frames-from-a-video-file [capturing-and-previewing-bgr-frames-from-a-video-file]:https://abhitronix.github.io/deffcode/latest/recipes/basic/decode-video-files/#capturing-and-previewing-bgr-frames-from-a-video-file [capturing-and-previewing-frames-from-a-webcam]:https://abhitronix.github.io/deffcode/latest/recipes/basic/decode-live-feed-devices/#capturing-and-previewing-frames-from-a-webcam diff --git a/deffcode/ffdecoder.py b/deffcode/ffdecoder.py index 132f2aa..bcd190d 100644 --- a/deffcode/ffdecoder.py +++ b/deffcode/ffdecoder.py @@ -268,6 +268,8 @@ def formulate(self): """ This method formulates all necessary FFmpeg pipeline arguments and executes it inside the FFmpeg `subprocess` pipe. + + **Returns:** A reference to the FFdecoder class object. """ # assign values to class variables on first run if self.__initializing: @@ -562,15 +564,17 @@ def generateFrame(self): def __enter__(self): """ - Handles entry + Handles entry with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/). + + **Returns:** Output of `formulate()` method. """ - return self + return self.formulate() def __exit__(self, exc_type, exc_val, exc_tb): """ - Handles exit + Handles exit with the `with` statement. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/). """ - self.close() + self.terminate() @property def metadata(self): @@ -680,9 +684,11 @@ def terminate(self): # signal we are closing self.__verbose_logs and logger.debug("Terminating FFdecoder Pipeline...") self.__terminate_stream = True - # Attempt to close pipeline. + # check if no process was initiated at first place if self.__process is None or not (self.__process.poll() is None): - return # no process was initiated at first place + logger.warning("Pipeline already terminated!") + return + # Attempt to close pipeline. # close `stdin` output self.__process.stdin and self.__process.stdin.close() # close `stdout` output @@ -692,3 +698,6 @@ def terminate(self): self.__process.terminate() self.__process.wait() self.__process = None + self.__verbose_logs and logger.debug( + "FFdecoder Pipeline terminated successfully" + ) diff --git a/deffcode/ffhelper.py b/deffcode/ffhelper.py index cf31a5d..c71998b 100644 --- a/deffcode/ffhelper.py +++ b/deffcode/ffhelper.py @@ -240,7 +240,7 @@ def validate_ffmpeg(path, verbose=False): """ ## validate_ffmpeg - Validate FFmeg Binaries. returns `True` if tests are passed. + Validate FFmpeg Binaries. Returns `True` if validity test passes successfully. Parameters: path (string): absolute path of FFmpeg binaries @@ -273,7 +273,7 @@ def get_supported_pixfmts(path): """ ## get_supported_pixfmts - Find and returns FFmpeg's supported pixel formats + Find and returns all FFmpeg's supported pixel formats. Parameters: path (string): absolute path of FFmpeg binaries @@ -305,7 +305,7 @@ def get_supported_vdecoders(path): """ ## get_supported_vdecoders - Find and returns FFmpeg's supported video decoders + Find and returns all FFmpeg's supported video decoders. Parameters: path (string): absolute path of FFmpeg binaries @@ -332,7 +332,7 @@ def get_supported_demuxers(path): """ ## get_supported_demuxers - Find and returns FFmpeg's supported demuxers + Find and returns all FFmpeg's supported demuxers. Parameters: path (string): absolute path of FFmpeg binaries @@ -425,7 +425,7 @@ def is_valid_url(path, url=None, verbose=False): ## is_valid_url Checks URL validity by testing its scheme against - FFmpeg's supported protocols + FFmpeg's supported protocols. Parameters: path (string): absolute path of FFmpeg binaries @@ -462,7 +462,7 @@ def check_sp_output(*args, **kwargs): """ ## check_sp_output - Returns FFmpeg `stdout` output from subprocess module + Returns FFmpeg `stdout` output from subprocess module. Parameters: args (based on input): Non Keyword Arguments diff --git a/deffcode/version.py b/deffcode/version.py index b5fdc75..d31c31e 100644 --- a/deffcode/version.py +++ b/deffcode/version.py @@ -1 +1 @@ -__version__ = "0.2.2" +__version__ = "0.2.3" diff --git a/docs/changelog.md b/docs/changelog.md index 6d21ff0..b24a927 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -20,7 +20,63 @@ limitations under the License. # Release Notes -## v0.2.2 (2022-09-08) :material-new-box: + +## v0.2.3 (2022-08-11) :material-new-box: + +??? new "New Features" + - [x] **Docs:** + * Added Zenodo Bibtex entry and badge in docs for easily citing a DeFFcode release. + * Added new `<div>` tag bounding-box style to the Static FFmpeg binary download links in FFmpeg Installation Doc for better accessibility. + - [x] **Maintenance:** + * Switched to new Issue GitHub's form schema using YAML: + * Added new `bug_report.yaml` Issue GitHub's form schema for Bug Reports. + * Added new `idea.yaml` Issue GitHub's form schema for new Ideas. + * Added new `question.yaml` Issue GitHub's form schema for Questions. + * Deleted old depreciated markdown(`.md`) files. + * Polished forms. + +??? success "Updates/Improvements" + - [x] Maintenance: + * Added new patterns to `.gitignore` to ignore vim files. + - [x] CI: + * Updated `test_FFdecoder_params` unittest to include `with` statement access method. + - [x] Setup: + * Added new patches for using README.md text as `long_description` metadata. + * Implemented new patch to remove GitHub README UI specific text. + * Simplified multiple `str.replace` to chained `str.replace` of better readability. + * Bumped version to `0.2.3`. + - [x] Docs: + * Updated recipes to include `with` statement access method. + * Updated existing recipes to include `with` statement access method in FFdecoder APIs. + * Included new example code of accessing RGB frames using `with` statement access method. + * Updated Recipe title to "Accessing RGB frames from a video file" across docs. + * Included warning admonition for advising users to always use `trim` with `reverse` filter. + * Updated docs text font to `Libre Franklin`. + * Updated method description texts and logging messages. + * Update icons and admonition messages. + * Updated code comments. + * Updated `changelog.md`. + +??? bug "Bug-fixes" + - [x] FFdecoder API: + * Fixed Context Manager methods. + * Fixed `__enter__` method returning class instance instead of formulating pipeline. + * Fixed `__exit__` method calling wrong non-existent method. + - [x] Setup: + * Fixed missing `comma(,)` in keywords metadata. + * Fixed bug in patch string. + - [x] Docs: + * Fixed typos in code comments. + * Fixed several typos in docs. + +??? question "Pull Requests" + * PR #26 + +  + +  + +## v0.2.2 (2022-08-09) ??? new "New Features" - [x] **Sourcer API:** @@ -242,9 +298,9 @@ limitations under the License. * Updated date/version tag to `12-07-2022`. * Removed depreciated binaries download links and code. - [x] Setup: - * Bumped version to 0.2.1. + * Bumped version to `0.2.1`. - [x] Docs: - * Updated Changelog.md + * Updated `changelog.md`. ??? danger "Breaking Updates/Changes" - [x] :skull_crossbones: **Implement support for live input devices/sources.** diff --git a/docs/index.md b/docs/index.md index 1a9ebb1..0f0d44e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -23,13 +23,13 @@ limitations under the License. ![DeFFcode](assets/images/deffcode.png#only-light){ loading=lazy } ![DeFFcode](assets/images/deffcode-dark.png#only-dark){ loading=lazy } -<center>DeFFcode - A cross-platform **High-performance Video Frames Decoder** that flexibly executes <br>FFmpeg pipeline inside a subprocess pipe for generating real-time, low-overhead, <br>lightning fast video frames with robust error-handling <br>in just a few lines of python code :zap:</center> +<center>A cross-platform **:fontawesome-solid-gauge-high: High-performance Video Frames Decoder** that flexibly executes FFmpeg pipeline inside a subprocess pipe for generating real-time, low-overhead, <br>lightning fast video frames with robust error-handling <br>in just a few lines of python code :fontawesome-solid-fire-flame-curved:</center> <div class="spacer"></div> **==Highly Adaptive== -** DeFFcode APIs implements a **standalone highly-extensible wrapper around [FFmpeg][ffmpeg]** multimedia framework. These APIs **supports a wide-ranging media streams as input** source such as live USB/Virtual/IP camera feeds, regular multimedia files, screen recordings, image sequences, network protocols _(such as HTTP(s), RTP/RSTP, etc.)_, so on and so forth. -**==Highly Flexible== -** DeFFcode APIs gains an edge over other Wrappers by providing **complete control over the underline pipeline** including **access to almost any FFmpeg specification thinkable** such as specifying framerate, resolution, hardware decoder(s), filtergraph(s), and <br>pixel-format(s) that are readily **supported by all well known Computer Vision libraries**. +**==Highly Flexible== -** DeFFcode APIs gains an edge over other Wrappers by providing **complete control over the underline pipeline** including **access to almost any FFmpeg specification thinkable** such as specifying framerate, resolution, hardware decoder(s), filtergraph(s), and pixel-format(s) that are readily **supported by all well known Computer Vision libraries**. **==Highly Convenient== -** FFmpeg has a steep learning curve especially for users unfamiliar with a command line interface. DeFFcode helps users by keeping the **same [OpenCV-Python](https://docs.opencv.org/4.x/d6/d00/tutorial_py_root.html) _(Python API for OpenCV)_ coding syntax for its APIs**, thereby making it even **easier to learn, create, and develop FFmpeg based apps** in Python. @@ -43,7 +43,7 @@ Here are some key features that stand out: - [x] Flexible API with access to almost any FFmpeg specification thinkable. - [x] Supports a wide-range of media streams/devices/protocols as input source. - [x] Curated list of well-documented recipes ranging from [**Basic**](recipes/basic/) to [**Advanced**](recipes/advanced/) skill levels. -- [x] Easy to code **Live [Simple](recipes/basic/transcode-live-frames-simplegraphs/#transcoding-live-simple-filtergraphs) & [Complex](recipes/advanced/transcode-live-frames-complexgraphs/#transcoding-live-complex-filtergraphs) Filtergraphs**. _(Yes, You read it correctly "Real-time"!)_ +- [x] Memory efficient **Live [Simple](recipes/basic/transcode-live-frames-simplegraphs/#transcoding-live-simple-filtergraphs) & [Complex](recipes/advanced/transcode-live-frames-complexgraphs/#transcoding-live-complex-filtergraphs) Filtergraphs**. _(Yes, You read it correctly "Live"!)_ - [x] Lightning fast dedicated **:fontawesome-solid-microchip: GPU-Accelerated Video [Decoding](recipes/advanced/decode-hw-acceleration/#hardware-accelerated-video-decoding) & [Transcoding](recipes/advanced/transcode-hw-acceleration/#hardware-accelerated-video-transcoding)**. - [x] Enables precise FFmpeg [**Frame Seeking**](recipes/basic/save-keyframe-image/#extracting-key-frames-as-png-image) with pinpoint accuracy. - [x] Effortless [**Metadata Extraction**](recipes/basic/extract-video-metadata/#extracting-video-metadata) from all streams available in the source. @@ -192,6 +192,28 @@ It is something I am doing with my own free time. But so much more needs to be d   +## Citation + +Here is a Bibtex entry you can use to cite this project in a publication: + +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.6976948.svg)](https://doi.org/10.5281/zenodo.6976948) + +```BibTeX +@software{deffcode, + author = {Abhishek Thakur}, + title = {abhiTronix/deffcode: v0.2.2}, + month = aug, + year = 2022, + publisher = {Zenodo}, + version = {v0.2.2}, + doi = {10.5281/zenodo.6976948}, + url = {https://doi.org/10.5281/zenodo.6976948} +} +``` + +  + + <!-- External URLs --> diff --git a/docs/installation/ffmpeg_install.md b/docs/installation/ffmpeg_install.md index f0d893a..2204c86 100644 --- a/docs/installation/ffmpeg_install.md +++ b/docs/installation/ffmpeg_install.md @@ -51,9 +51,15 @@ If DeFFcode APIs do not receive any input from the user on [**`custom_ffmpeg`**] ### B. Manual Configuration -* **Download:** You can also manually download the latest Linux Static Binaries(*based on your machine arch(x86/x64)*) from the link below: - +* **Download:** You can also manually download the latest Linux Static Binaries _(based on your machine architecture)_ from the link below: + + <div style=" + padding: 10px; + border: 2px solid lightgray; + border-radius: 2px 10px; + margin: 10px;"> **:material-file-download: Linux Static Binaries:** http://johnvansickle.com/ffmpeg/ + </div> * **Assignment:** Then, you can easily assign the custom path to the folder containing FFmpeg executables(`for e.g 'ffmpeg/bin'`) or path of `ffmpeg` executable itself to the [**`custom_ffmpeg`**](../../reference/ffdecoder/params/#custom_ffmpeg) parameter in the DeFFcode APIs. @@ -82,9 +88,9 @@ If DeFFcode APIs do not receive any input from the user on [**`custom_ffmpeg`**] * You can also provide a custom save path for auto-downloading **FFmpeg Static Binaries** through exclusive [`-ffmpeg_download_path`](../../reference/sourcer/params/#exclusive-parameters) attribute in Sourcer API. - ??? question "How to use `-ffmpeg_download_path` attribute in FFdecoder API? + ??? question "How to use `-ffmpeg_download_path` attribute in FFdecoder API?" - `-ffmpeg_download_path` is also available in FFdecoder API through the [`-custom_sourcer_params`](../../reference/ffdecoder/params/#b-exclusive-parameters) attribute of its `ffparams` dictionary parameter + `-ffmpeg_download_path` is also available in FFdecoder API through the [`-custom_sourcer_params`](../../reference/ffdecoder/params/#b-exclusive-parameters) attribute of its `ffparams` dictionary parameter. * If binaries were found at the specified path, DeFFcode APIs automatically skips the Auto-Installation step. @@ -93,9 +99,15 @@ If DeFFcode APIs do not receive any input from the user on [**`custom_ffmpeg`**] ### B. Manual Configuration -* **Download:** You can also manually download the latest Windows Static Binaries(*based on your machine arch(x86/x64)*) from the link below: - +* **Download:** You can also manually download the latest Windows Static Binaries _(based on your machine arch(x86/x64))_ from the link below: + + <div style=" + padding: 10px; + border: 2px solid lightgray; + border-radius: 2px 10px; + margin: 10px;"> **:material-file-download: Windows Static Binaries:** https://ffmpeg.org/download.html#build-windows + </div> * **Assignment:** Then, you can easily assign the custom path to the folder containing FFmpeg executables(`for e.g 'C:/foo/Downloads/ffmpeg/bin'`) or path of `ffmpeg.exe` executable itself to the [**`custom_ffmpeg`**](../../reference/ffdecoder/params/#custom_ffmpeg) parameter in the DeFFcode APIs. @@ -120,9 +132,15 @@ If DeFFcode APIs do not receive any input from the user on [**`custom_ffmpeg`**] ### B. Manual Configuration -* **Download:** You can also manually download the latest MacOS Static Binaries(*only x64 Binaries*) from the link below: - +* **Download:** You can also manually download the latest MacOS Static Binaries _(only x64 Binaries)_ from the link below: + + <div style=" + padding: 10px; + border: 2px solid lightgray; + border-radius: 2px 10px; + margin: 10px;"> **:material-file-download: MacOS Static Binaries:** https://ffmpeg.org/download.html#build-mac + </div> * **Assignment:** Then, you can easily assign the custom path to the folder containing FFmpeg executables(`for e.g 'ffmpeg/bin'`) or path of `ffmpeg` executable itself to the [**`custom_ffmpeg`**](../../reference/ffdecoder/params/#custom_ffmpeg) parameter in the DeFFcode APIs. diff --git a/docs/recipes/advanced/decode-hw-acceleration.md b/docs/recipes/advanced/decode-hw-acceleration.md index 18ba134..d2e0c56 100644 --- a/docs/recipes/advanced/decode-hw-acceleration.md +++ b/docs/recipes/advanced/decode-hw-acceleration.md @@ -22,7 +22,9 @@ limitations under the License. !!! abstract "FFmpeg offer access to dedicated GPU hardware with varying support on different platforms for performing a range of video-related tasks to be completed faster or using less of other resources (particularly CPU)." -> By default, DeFFcode's FFdecoder API uses the Input Source's video-decoder _(extracted using Sourcer API)_ itself for decoding its input. However, you could easily change the video-decoder to your desired specific [**supported Video-Decoder**](../../reference/ffdecoder/params/#supported-decoders) using the `-vcodec` FFmpeg option by way of its [`ffparams`](../../reference/ffdecoder/params/#ffparams) dictionary parameter. This means easy access to GPU Accelerated Hardware Decoder to get better playback and accelerated video decoding on GPUs that will generate equivalent output to software decoders, but may use less power and CPU to do so. +> By default, DeFFcode's FFdecoder API uses the Input Source's video-decoder _(extracted using Sourcer API)_ itself for decoding its input. However, you could easily change the video-decoder to your desired specific **supported Video-Decoder** using the `-vcodec` FFmpeg option by way of its [`ffparams`](../../reference/ffdecoder/params/#ffparams) dictionary parameter. This means easy access to GPU Accelerated Hardware Decoder to get better playback and accelerated video decoding on GPUs that will generate equivalent output to software decoders, but may use less power and CPU to do so. + +!!! tip "Use `#!sh ffmpeg -decoders` terminal command to lists all FFmpeg supported decoders." We'll discuss its Hardware-Accelerated Video Decoding capabilities briefly in the following recipes: diff --git a/docs/recipes/advanced/transcode-art-filtergraphs.md b/docs/recipes/advanced/transcode-art-filtergraphs.md index cedb1f2..c4b1420 100644 --- a/docs/recipes/advanced/transcode-art-filtergraphs.md +++ b/docs/recipes/advanced/transcode-art-filtergraphs.md @@ -126,7 +126,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): @@ -203,7 +203,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): @@ -271,7 +271,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): @@ -339,7 +339,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): diff --git a/docs/recipes/advanced/transcode-hw-acceleration.md b/docs/recipes/advanced/transcode-hw-acceleration.md index ea1d7d8..4eddb4a 100644 --- a/docs/recipes/advanced/transcode-hw-acceleration.md +++ b/docs/recipes/advanced/transcode-hw-acceleration.md @@ -170,7 +170,7 @@ output_params = { # Define writer with defined parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): diff --git a/docs/recipes/advanced/transcode-live-frames-complexgraphs.md b/docs/recipes/advanced/transcode-live-frames-complexgraphs.md index 353dce0..d6c989e 100644 --- a/docs/recipes/advanced/transcode-live-frames-complexgraphs.md +++ b/docs/recipes/advanced/transcode-live-frames-complexgraphs.md @@ -72,6 +72,8 @@ We'll discuss the transcoding of live complex filtergraphs in the following reci !!! note "Always use FFdecoder API's [`terminate()`](../../reference/ffdecoder/#deffcode.ffdecoder.FFdecoder.terminate) method at the end to avoid undesired behavior." +!!! danger "==WriteGear's Compression Mode support for FFdecoder API is currently in beta so you can expect much higher than usual CPU utilization!==" +   @@ -89,6 +91,8 @@ In this example we will apply a watermark image _(say `watermark.png` with trans !!! tip "To learn about exclusive `-ffprefixes` & `-clones` parameter. See [Exclusive Parameters ➶](../../reference/ffdecoder/params/#b-exclusive-parameters)" +!!! alert "Remember to replace `watermark.png` watermark image file-path with yours before using this recipe." + ```python # import the necessary packages from deffcode import FFdecoder @@ -123,7 +127,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): @@ -160,8 +164,6 @@ writer.close() In this example we will blend **`10` seconds of Mandelbrot test pattern** _(generated using `lavfi` input virtual device)_ that serves as the "top" layer with **`10` seconds of Image Sequence** that serves as the "bottom" layer, using `blend` filter _(with `heat` blend mode)_, and decode live **BGR24** video frames in FFdecoder API. We'll also be encoding those decoded frames in real-time into lossless video file using WriteGear API with controlled framerate. -!!! danger "==WriteGear's Compression Mode support for FFdecoder API is currently in beta so you can expect much higher than usual CPU utilization!==" - ??? tip "Extracting Image Sequences from a video" **You can use following FFmpeg command to extract sequences of images from a video file `foo.mp4` _(restricted to 12 seconds)_:** @@ -226,7 +228,7 @@ output_params = { # Define writer with default parameters and suitable # output filename for e.g. `output_foo.mp4` -writer = WriteGear(output_filename="output_foo.mp4", logging=True, **output_params) +writer = WriteGear(output_filename="output_foo.mp4", **output_params) # grab the BGR24 frame from the decoder for frame in decoder.generateFrame(): diff --git a/docs/recipes/basic/decode-video-files.md b/docs/recipes/basic/decode-video-files.md index 3938033..dacd0e6 100644 --- a/docs/recipes/basic/decode-video-files.md +++ b/docs/recipes/basic/decode-video-files.md @@ -61,19 +61,19 @@ We'll discuss its video files support and pixel format capabilities briefly in t   -## Capturing RGB frames from a video file +## Accessing RGB frames from a video file !!! abstract "The default function of FFdecoder API is to **decode 24-bit RGB video frames** from the given source." -> FFdecoder API's [`generateFrame()`](../../reference/ffdecoder/#deffcode.ffdecoder.FFdecoder.generateFrame) function can be used both as a **Generator** _(Recommended Approach)_ and **Iterator**. +> FFdecoder API's [`generateFrame()`](../../reference/ffdecoder/#deffcode.ffdecoder.FFdecoder.generateFrame) function can be used in multiple methods to access RGB frames from a given source, such as **as a Generator** _(Recommended Approach)_, **calling `with` Statement**, and **as a Iterator**. -In this example we will decode the default **RGB24** video frames from a given Video file _(say `foo.mp4`)_ using both approaches: +In this example we will decode the default **RGB24** video frames from a given Video file _(say `foo.mp4`)_ using above mentioned accessing methods: === "As a Generator (Recommended)" - !!! quote "This is a recommended approach for faster and safer decoding." + !!! quote "This is a recommended approach for faster and error-proof access of decoded frames. We'll use it throughout the recipes." ```python # import the necessary packages @@ -98,6 +98,31 @@ In this example we will decode the default **RGB24** video frames from a given V decoder.terminate() ``` +=== "Calling `with` Statement" + + !!! quote "Calling `with` Statement approach can be used to make the code easier, cleaner, and much more readable. This approach also automatically handles management of `formulate()` and `terminate()` methods in FFdecoder API, so don't need to explicitly call them. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/) for more information on this approach." + + ```python + # import the necessary packages + from deffcode import FFdecoder + import cv2 + + # initialize and formulate the decoder + with FFdecoder("foo.mp4") as decoder: + + # 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} + + # lets print its shape + print(frame.shape) # for e.g. (1080, 1920, 3) + ``` + === "As a Iterator" !!! quote "This Iterator Approach bears a close resemblance to **OpenCV-Python** _(Python API for OpenCV)_ coding syntax, thereby easier to learn and remember." @@ -135,39 +160,74 @@ In this example we will decode the default **RGB24** video frames from a given V In this example we will decode **OpenCV supported** live **BGR24** video frames from a given Video file _(say `foo.mp4`)_ in FFdecoder API, and preview them using OpenCV Library's `cv2.imshow()` method. -!!! alert "By default, OpenCV expects `BGR` format frames in its `cv2.imshow()` method." +!!! alert "By default, OpenCV expects `BGR` format frames in its `cv2.imshow()` method by using two accessing methods." -```python -# import the necessary packages -from deffcode import FFdecoder -import cv2 +=== "As a Generator (Recommended)" -# initialize and formulate the decoder for BGR24 pixel format output -decoder = FFdecoder("foo.mp4", frame_format="bgr24").formulate() + ```python + # import the necessary packages + from deffcode import FFdecoder + import cv2 -# grab the BGR24 frames from decoder -for frame in decoder.generateFrame(): + # initialize and formulate the decoder for BGR24 pixel format output + decoder = FFdecoder("foo.mp4", frame_format="bgr24").formulate() - # check if frame is None - if frame is None: - break + # grab the BGR24 frames from decoder + for frame in decoder.generateFrame(): - # {do something with the frame here} - - # Show output window - cv2.imshow("Output", frame) + # 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 + + # close output window + cv2.destroyAllWindows() - # check for 'q' key if pressed - key = cv2.waitKey(1) & 0xFF - if key == ord("q"): - break + # terminate the decoder + decoder.terminate() + ``` + +=== "Calling `with` Statement" -# close output window -cv2.destroyAllWindows() + !!! quote "Calling `with` Statement approach can be used to make the code easier, cleaner, and much more readable. This approach also automatically handles management of `formulate()` and `terminate()` methods in FFdecoder API, so don't need to explicitly call them. See [PEP343 -- The 'with' statement'](https://peps.python.org/pep-0343/) for more information on this approach." -# terminate the decoder -decoder.terminate() -``` + ```python + # import the necessary packages + from deffcode import FFdecoder + import cv2 + + # initialize and formulate the decoder for BGR24 pixel format output + with FFdecoder("foo.mp4", frame_format="bgr24") as decoder: + + # 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 + + # close output window + cv2.destroyAllWindows() + ```   diff --git a/docs/recipes/basic/index.md b/docs/recipes/basic/index.md index ef8fe6d..fa87895 100644 --- a/docs/recipes/basic/index.md +++ b/docs/recipes/basic/index.md @@ -52,7 +52,7 @@ The following recipes should be reasonably accessible to beginners of any skill ## Basic :material-movie-play: Decoding Recipes - [x] **[:material-file-eye: Decoding Video files](../basic/decode-video-files/#decoding-video-files)** - - [Capturing RGB frames from a video file](../basic/decode-video-files/#capturing-rgb-frames-from-a-video-file) + - [Accessing RGB frames from a video file](../basic/decode-video-files/#accessing-rgb-frames-from-a-video-file) - [Capturing and Previewing BGR frames from a video file](../basic/decode-video-files/#capturing-and-previewing-bgr-frames-from-a-video-file) _(OpenCV Support)_ - [Playing with any other FFmpeg pixel formats](../basic/decode-video-files/#capturing-and-previewing-bgr-frames-from-a-video-file) - [x] **[:material-webcam: Decoding Live Feed Devices](../basic/decode-live-feed-devices/#decoding-live-feed-devices)** diff --git a/docs/recipes/basic/transcode-live-frames-simplegraphs.md b/docs/recipes/basic/transcode-live-frames-simplegraphs.md index 665e336..993240d 100644 --- a/docs/recipes/basic/transcode-live-frames-simplegraphs.md +++ b/docs/recipes/basic/transcode-live-frames-simplegraphs.md @@ -81,6 +81,8 @@ We'll discuss the transcoding of live simple filtergraphs in the following recip In this example we will take the first 5 seconds of a video clip _(using [`trim`](https://ffmpeg.org/ffmpeg-filters.html#toc-trim) filter)_ and reverse it _(by applying [`reverse`](https://ffmpeg.org/ffmpeg-filters.html#toc-reverse) filter)_, and encode them using OpenCV Library's [`VideoWriter()`](https://docs.opencv.org/3.4/dd/d9e/classcv_1_1VideoWriter.html#ad59c61d8881ba2b2da22cff5487465b5) method in real-time. +!!! warning "The `reverse` filter requires memory to buffer the entire clip, so applying [`trim`](https://ffmpeg.org/ffmpeg-filters.html#toc-trim) filter first is strongly recommended. Otherwise you might probably run Out of Memory." + !!! info "OpenCV's `VideoWriter()` class requires a valid Output filename _(e.g. output_foo.avi)_, [FourCC](https://www.fourcc.org/fourcc.php) code, framerate, and resolution as input." !!! tip "You can use FFdecoder's [`metadata`](../../reference/ffdecoder/#deffcode.ffdecoder.FFdecoder.metadata) property object that dumps source Video's metadata information _(as JSON string)_ to retrieve source framerate and resolution." diff --git a/mkdocs.yml b/mkdocs.yml index 105dc7c..23fd4eb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -61,7 +61,7 @@ theme: icon: fontawesome/solid/lightbulb name: Switch to light mode font: # font - text: Heebo + text: Libre Franklin code: JetBrains Mono icon: # icon logo: logo diff --git a/setup.py b/setup.py index 07fb6e6..c2019aa 100644 --- a/setup.py +++ b/setup.py @@ -30,15 +30,26 @@ with open("README.md", "r", encoding="utf-8") as fh: long_description = fh.read() + # patch internal hyperlinks long_description = long_description.replace( "(#", "(https://github.com/abhiTronix/deffcode#" ) long_description = long_description.replace( - "docs/overrides/", "(https://abhitronix.github.io/deffcode/latest/" + "docs/overrides/", "https://abhitronix.github.io/deffcode/latest/" + ) + # patch to remove github README specific text + long_description = ( + long_description.replace( + "![DeFFcode](docs/overrides/assets/images/deffcode-dark.png#gh-dark-mode-only)", + "", + ) + .replace("#gh-light-mode-only", "") + .replace("<ins>", "") + .replace("</ins>", "") ) # patch for unicodes - long_description = long_description.replace("➶", ">>") - long_description = long_description.replace("©", "(c)") + long_description = long_description.replace("➶", ">>").replace("©", "(c)") + setup( name="deffcode", @@ -63,7 +74,7 @@ "Decoder", "Realtime", "Framework", - "Cross-platform" + "Cross-platform", "Video Processing", "Computer Vision", "Video Decoding", diff --git a/tests/test_ffdecoder.py b/tests/test_ffdecoder.py index d1b6ed3..66a104e 100644 --- a/tests/test_ffdecoder.py +++ b/tests/test_ffdecoder.py @@ -349,28 +349,28 @@ def test_FFdecoder_params(source, ffparams, result): f_name = os.path.join(*[tempfile.gettempdir(), "temp_write", "output_foo.avi"]) try: # initialize and formulate the decode with suitable source - decoder = FFdecoder(source, frame_format="bgr24", **ffparams).formulate() + with FFdecoder(source, frame_format="bgr24", **ffparams) as decoder: - # retrieve JSON Metadata and convert it to dict - metadata_dict = json.loads(decoder.metadata) + # retrieve JSON Metadata and convert it to dict + metadata_dict = json.loads(decoder.metadata) - # prepare OpenCV parameters - FOURCC = cv2.VideoWriter_fourcc("M", "J", "P", "G") - FRAMERATE = metadata_dict["source_video_framerate"] - FRAMESIZE = tuple(metadata_dict["source_video_resolution"]) + # prepare OpenCV parameters + FOURCC = cv2.VideoWriter_fourcc("M", "J", "P", "G") + FRAMERATE = metadata_dict["source_video_framerate"] + FRAMESIZE = tuple(metadata_dict["source_video_resolution"]) - # Define writer with parameters and suitable output filename for e.g. `output_foo.avi` - writer = cv2.VideoWriter(f_name, FOURCC, FRAMERATE, FRAMESIZE) + # Define writer with parameters and suitable output filename for e.g. `output_foo.avi` + writer = cv2.VideoWriter(f_name, FOURCC, FRAMERATE, FRAMESIZE) - # grab the BGR24 frame from the decoder - for frame in decoder.generateFrame(): + # grab the BGR24 frame from the decoder + for frame in decoder.generateFrame(): - # check if frame is None - if frame is None: - break + # check if frame is None + if frame is None: + break - # writing BGR24 frame to writer - writer.write(frame) + # writing BGR24 frame to writer + writer.write(frame) except Exception as e: if result: pytest.fail(str(e)) @@ -378,7 +378,6 @@ def test_FFdecoder_params(source, ffparams, result): pytest.xfail(str(e)) finally: # terminate the decoder - not (decoder is None) and decoder.terminate() not (writer is None) and writer.release() and remove_file_safe(f_name)