New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

deffcode

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

deffcode - pypi Package Compare versions

Comparing version
0.1.0
to
0.2.0
+159
-120
deffcode.egg-info/PKG-INFO
Metadata-Version: 2.1
Name: deffcode
Version: 0.1.0
Summary: Performant Pythonic FFmpeg Decoder with easy to adapt flexible API.
Version: 0.2.0
Summary: High-performance Real-time Video frames Generator for generating blazingly fast video frames in python.
Home-page: https://abhitronix.github.io/deffcode

@@ -37,3 +37,3 @@ Author: Abhishek Thakur

===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -56,106 +56,110 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

<h1 align="center">
<i>de</i><b>FF</b><i>code</i>
</h1>
<p align="center">Performant ⚡️ Pythonic FFmpeg Decoder with easy to adapt flexible API 🐍.</p>
<h2 align="center">
</h2>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode.png" alt="DeFFcode" title="Logo designed by Abhishek Thakur(@abhiTronix), under CC-BY-NC-SA 4.0 License" width="80%"/>
</div>
<div align="center">
&nbsp;
[![Build Status][github-cli]][github-flow] [![Codecov branch][codecov]][code] [![Azure DevOps builds (branch)][azure-badge]][azure-pipeline]
**deffcode** is a Performant and Robust FFmpeg Pythonic Wrapper that aimed at decoding any stream that you throw at it. Requiring minimal efforts, deffcode provides an easy-to-adapt flexible API to read a wide range of streams, and can ingest video using any decoder(even hardware ones) into any pixel format ffmpeg supports. It also provides pin-point accurate seeking for extracting only a specific part of your input as required.
[![Glitter chat][gitter-bagde]][gitter] [![Build Status][appveyor]][app] [![PyPi version][pypi-badge]][pypi]
It is cross-platform, runs on Python 3.7+, and is easy to install.
[![Code Style][black-badge]][black]
&nbsp;
</div>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode-tagline.png" alt="DeFFcode tagline" width="40%"/>
## Examples
### Basic Example
----
```python
# import the necessary packages
from deffcode import FFdecoder
[Releases][release]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Documentation][docs]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Installation][install]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[License](https://github.com/abhiTronix/deffcode#copyright)
# initialize and formulate the decoder
decoder = FFdecoder("foo.mp4").formulate()
----
# grab the RGB24(default) frame from
# the decoder(generator)
for frame in decoder.generateFrame():
print(frame.shape)
</div>
# terminate the decoder
decoder.terminate()
```
&thinsp;
The output:
## Overview
> DeFFcode is a powerful High-performance Real-time **Video frames Generator** that wraps FFmpeg pipeline inside a subprocess module for generating blazingly fast video frames in python 🔥
The primary purpose of DeFFcode is to provide a cross-platform solution for fast and low-overhead decoding of a wide range of video streams into 3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray) frames while providing **complete control over the underlying FFmpeg pipeline** without the need to go deeper into hefty documentation and in just a few lines of python code.
DeFFcode can **extract frames in real-time with any custom specification imaginable** such as Framerate, Resolution, Hardware decoding, Complex Filters into any pixel format while giving users the complete freedom to play with any desired FFmpeg supported parameter. On top of that, DeFFcode enables **effortless and precise FFmpeg Frame Seeking** natively.
Finally, DeFFcode APIs are designed with **simplicity, flexibility, and modularity** in mind for the best developer experience.
&thinsp;
## Key Features
DeFFcode APIs are build on [**FFmpeg**][ffmpeg] - a leading multimedia framework, that gives you the following:
- Extremely exceptional real-time performance ⚡ with low-memory footprints.
- Flexible API with access to almost every parameter available within FFmpeg.
- Fast dedicated [Hardware-Accelerated Decoding](https://abhitronix.github.io/deffcode/latest/examples/advanced/#gpu-enabled-hardware-accelerated-decoding).
- Precise FFmpeg [Frame Seeking](https://abhitronix.github.io/deffcode/latest/examples/basic/#saving-keyframes-as-image) with pinpoint accuracy.
- Extensive support for real-time [Complex FFmpeg Filters](https://abhitronix.github.io/deffcode/latest/examples/advanced/#generating-video-with-complex-filter-applied).
- Out-of-the-box support for Computer Vision libraries like OpenCV, Pytorch, etc.
- Support a wide range of media files, devices, image-sequence and network streams.
- Easier to ingest streams into any pixel format that FFmpeg supports.
- Lossless Transcoding support with [WriteGear](https://abhitronix.github.io/deffcode/latest/gears/writegear/introduction/).
- Fewer hard dependencies, and easy to install.
- Designed modular for best developer experience.
- Cross-platform and runs on Python 3.7+
&nbsp;
## Requirements
- Python 3.7+
- FFmpeg _(See [this doc](https://abhitronix.github.io/deffcode/latest/installation/ffmpeg_install/) for its installation)_
&nbsp;
## Installation
Installation is as simple as:
```sh
(720, 1280, 3)
(720, 1280, 3)
...
...
...
(720, 1280, 3)
$ (sudo) pip install deffcode
```
### Basic OpenCV Example
💡 For more details, see [installation notes][install].
```python
# import the necessary packages
from deffcode import FFdecoder
import cv2
&nbsp;
# initialize and formulate the decoder for BGR24 output
decoder = FFdecoder("foo.mp4", frame_format="bgr24").formulate()
# loop over frames
while True:
# grab the BGR24 frame from the decoder
frame = next(decoder.generateFrame(), None)
## Getting Started
# check if frame is None
if frame is None:
break
# Show output window
cv2.imshow("Output", frame)
---
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
**📚 Documentation: https://abhitronix.github.io/deffcode**
# close output window
cv2.destroyAllWindows()
# terminate the decoder
decoder.terminate()
```
---
### Basic PIL Example
The default function of DeFFcode's [FFdecoder API](https://abhitronix.github.io/deffcode/latest/reference/ffdecoder/#ffdecoder-api) is to generate 24-bit RGB (3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray)) frames from the given source:
```python
```py title="grab_frames.py"
# import the necessary packages
from deffcode import FFdecoder
from PIL import Image
# define the FFmpeg parameter to seek
# to 00:00:01 in time and get one single frame
extraparams = {"-ss": "00:00:01", "-frames:v":1}
# formulate the decoder with suitable source(for e.g. foo.mp4)
decoder = FFdecoder("foo.mp4").formulate()
# initialize and formulate the decode
decoder = FFdecoder("foo.mp4", **extraparams).formulate()
# grab RGB24(default) 3D frames from decoder
for frame in decoder.generateFrame():
# lets print its shape
print(frame.shape)
# grab the RGB24(default) frame from the decoder
frame = next(decoder.generateFrame(), None)
# print metadata as `json.dump`
print(decoder.metadata)
# check if frame is None
if not(frame is None)
# Convert and Show output window
im = Image.fromarray(frame)
im.show()
# terminate the decoder

@@ -165,40 +169,34 @@ decoder.terminate()

### Basic Matplotlib Example
For more examples and in-depth usage guide, kindly refer our **[Basic Recipes 🥧](https://abhitronix.github.io/deffcode/latest/examples/basic)** and **[Advanced Recipes 🔬](https://abhitronix.github.io/deffcode/latest/examples/advanced)**
```python
# import the necessary packages
from deffcode import FFdecoder
import matplotlib.pyplot as plt
# define the FFmpeg parameter to seek
# to 00:00:02.01 in time and get one single frame
extraparams = {"-ss": "00:00:02.01", "-frames:v":1}
💡 In case you're run into any problems, consult our [Help](https://abhitronix.github.io/deffcode/latest/help/) section.
# initialize and formulate the decode for Grayscale output
decoder = FFdecoder("foo.mp4", frame_format="gray", **extraparams).formulate()
# grab single Grayscale frame from the decoder
frame = next(decoder.generateFrame(), None)
&nbsp;
# Show output window
plt.imshow(frame, cmap='gray', vmin=0, vmax=255)
plt.show()
## Roadmap
# terminate the decoder
decoder.terminate()
```
- [x] Add clean and elegant documentation.
- [x] Add project Issue and PR templates.
- [x] Add related unit tests with `pytests`.
- [x] Automate stuff with Continuous Integration.
- [ ] Add Multiple Source Inputs support.
- [ ] Add Devices and Screen Capture support.
- [ ] Resolve High CPU usage issue with WriteGear API.
- [ ] Add more parameters to Sourcer API's metadata.
- [ ] Implement Buffer and Audio pass-through modes.
- [ ] Add recipe for Source with Multiple Embedded Streams.
- [ ] Add example to dynamically change writable FFdecoder API's metadata parameters.
- [ ] Add more Advanced Recipes and use cases.
- [ ] Add preliminary benchmarks.
- [ ] Make Frame Seeking dynamic.
&nbsp;
## Dependencies
## Contributions
Minimal requirements:
- Python 3.7+
- FFmpeg (See [this](https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) for its installation)
- NumPy >=1.20.0
- requests
- colorlog
- tqdm
> Contributions are welcome. We'd love to have your contributions to fix bugs or to implement new features!
:bulb: These requirements are installed automatically(except FFmpeg).
Please see our **[Contribution Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/)** for more details.

@@ -208,29 +206,70 @@

## Installation
## Community Support
Join our Gitter community channel for quick discussions:
```sh
# Install latest stable release
pip install -U deffcode
```
[![Gitter](https://badges.gitter.im/deffcode/community.svg)](https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
**And if you prefer to install deffcode directly from the repository:**
```sh
# Install latest stable release
pip install git+git://github.com/abhiTronix/deffcode@master#egg=deffcode
```
&nbsp;
**Or you can also download its wheel (`.whl`) package from our repository's [releases](https://github.com/abhiTronix/deffcode/releases) section, and thereby can be installed as follows:**
# Donations
```sh
# Install latest stable release
pip install deffcode-0.1.0-py3-none-any.whl
```
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/help_us.png" alt="PiGear" width="50%" />
> DeFFcode is free and open source and will always remain so. ❤️
It is something I am doing with my own free time. If you would like to say thanks, please feel free to make a donation:
[![ko-fi][kofi-badge]][kofi]
&nbsp;
----
<p align="center"><i><b>deffcode</b> is <a href="https://github.com/abhiTronix/deffcode/blob/master/LICENSE.md">Apache 2.0 Licensed</a> code.<br/>Designed & crafted with care.</i></br>⭐️</p>
# Copyright
**Copyright (c) abhiTronix 2021**
This library is released under the **[Apache 2.0 License][license]**.
<!--
Badges
-->
[appveyor]:https://img.shields.io/appveyor/ci/abhitronix/deffcode.svg?style=for-the-badge&logo=appveyor
[codecov]:https://img.shields.io/codecov/c/gh/abhiTronix/deffcode?logo=codecov&style=for-the-badge&token=zrES4mwVKe
[github-cli]:https://img.shields.io/github/workflow/status/abhiTronix/deffcode/GitHub%20Action%20workflow%20for%20Linux?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAgMWE5IDkgMCAwMTkgOSA5IDkgMCAwMS05IDkgOSA5IDAgMDEtOS05IDkgOSAwIDAxOS05ek0yMyAxOWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyek0yMyAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MSAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1zZWNvbmRhcnksICM3OUI4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNS4wMzcgMjMuNjA3bC0zLjA3IDMuMDY1LTEuNDktMS40ODUiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDEgMTlhNiA2IDAgMTEwIDEyIDYgNiAwIDAxMC0xMnoiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTQzLjAzNiAyMy42MDdsLTMuMDY5IDMuMDY1LTEuNDktMS40ODVNNyA2LjgxMmExIDEgMCAwMTEuNTMzLS44NDZsNS4xMTMgMy4yMmExIDEgMCAwMS0uMDA2IDEuNjk3bC01LjExMyAzLjE3QTEgMSAwIDAxNyAxMy4yMDNWNi44MTN6TTkgMTl2MTVjMCAzLjg2NiAzLjE3NyA3IDcgN2gxIiBzdHJva2U9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xNi45NDkgMjZhMSAxIDAgMTAwLTJ2MnpNOCAxOS4wMzVBNi45NjUgNi45NjUgMCAwMDE0Ljk2NSAyNnYtMkE0Ljk2NSA0Ljk2NSAwIDAxMTAgMTkuMDM1SDh6TTE0Ljk2NSAyNmgxLjk4NHYtMmgtMS45ODR2MnoiIGZpbGw9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIi8+PHBhdGggZD0iTTI5LjA1NSAyNWg1Ljk0NCIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIxIDQwYTEgMSAwIDExLS4wMDEgMi4wMDFBMSAxIDAgMDEyMSA0MHpNMjUgNDBhMSAxIDAgMTEtLjAwMSAyLjAwMUExIDEgMCAwMTI1IDQweiIgZmlsbD0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIvPjxwYXRoIGQ9Ik0zNC4wMDUgNDEuMDA3bC0xLjAxMy4wMzMiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=
[prs-badge]:https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABC0lEQVRYhdWVPQoCMRCFX6HY2ghaiZUXsLW0EDyBrbWtN/EUHsHTWFnYyCL4gxibVZZlZzKTnWz0QZpk5r0vIdkF/kBPAMOKeddE+CQPKoc5Yt5cTjBMdQSwDQToWgBJAn3jmhqgltapAV6E6b5U17MGGAUaUj07TficMfIBZDV6vxowBm1BP9WbSQE4o5h9IjPJmy73TEPDDxVmoZdQrQ5jRhly9Q8tgMUXkIIWn0oG4GYQfAXQzz1PGoCiQndM7b4RgJay/h7zBLT3hASgoKjamQJMreKf0gfuAGyYtXEIAKcL/Dss15iq6ohXghozLYiAMxPuACwtIT4yeQUxAaLrZwAoqGRKGk7qDSYTfYQ8LuYnAAAAAElFTkSuQmCC
[azure-badge]:https://img.shields.io/azure-devops/build/abhiuna12/942b3b13-d745-49e9-8d7d-b3918ff43ac2/3/master?logo=azure-pipelines&style=for-the-badge
[pypi-badge]:https://img.shields.io/pypi/v/deffcode.svg?style=for-the-badge&logo=pypi
[gitter-bagde]:https://img.shields.io/badge/Chat-Gitter-blueviolet.svg?style=for-the-badge&logo=gitter
[Coffee-badge]:https://abhitronix.github.io/img/deffcode/orange_img.png
[kofi-badge]:https://www.ko-fi.com/img/githubbutton_sm.svg
[black-badge]:https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge&logo=github
<!--
Internal URLs
-->
[docs]:https://abhitronix.github.io/deffcode/latest/
[install]:https://abhitronix.github.io/deffcode/latest/installation/
[release]:https://github.com/abhiTronix/deffcode/releases/latest
[license]:https://github.com/abhiTronix/deffcode/blob/master/LICENSE
[github-flow]:https://github.com/abhiTronix/deffcode/actions/workflows/CIlinux.yml
[azure-pipeline]:https://dev.azure.com/abhiuna12/public/_build?definitionId=3
[app]:https://ci.appveyor.com/project/abhiTronix/deffcode
[code]:https://codecov.io/gh/abhiTronix/deffcode
[black]: https://github.com/psf/black
<!--
External URLs
-->
[ffmpeg]:https://www.ffmpeg.org/
[pypi]:https://pypi.org/project/deffcode/
[gitter]:https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[coffee]:https://www.buymeacoffee.com/2twOXFvlA
[kofi]: https://ko-fi.com/W7W8WTYO

@@ -1,2 +0,2 @@

"""deffcode - Performant Pythonic FFmpeg Decoder with easy to adapt flexible API."""
"""DeFFcode - High-performance Real-time Video frames Generator for generating blazingly fast video frames in python."""

@@ -6,2 +6,3 @@ __author__ = "Abhishek Thakur (@abhiTronix) <abhi.una12@gmail.com>"

from .ffdecoder import FFdecoder
from .sourcer import Sourcer
from .sourcer import Sourcer
from .version import __version__
"""
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -22,3 +22,4 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

# import the necessary packages
import logging, os
import logging
import json
import numpy as np

@@ -28,7 +29,6 @@ import subprocess as sp

# import helper packages
# import utils packages
from .utils import dict2Args, logger_handler
from .sourcer import Sourcer
from .ffhelper import (
get_valid_ffmpeg_path,
get_supported_pixfmts,

@@ -55,3 +55,3 @@ get_supported_vdecoders,

Parameters:
source (str): defines the source for the input stream.
source (str): defines the default input source.
frame_format (str): sets pixel format(-pix_fmt) of the decoded frames.

@@ -62,4 +62,2 @@ custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executable.

"""
# checks if machine in-use is running windows os or not
self.__os_windows = True if os.name == "nt" else False

@@ -115,36 +113,39 @@ # enable verbose if specified

# handle where to save the downloaded FFmpeg Static assets on Windows(if specified)
__ffmpeg_download_path = self.__extra_params.pop("-ffmpeg_download_path", "")
if not isinstance(__ffmpeg_download_path, str):
# reset improper values
__ffmpeg_download_path = ""
# handle custom Sourcer API params
sourcer_params = self.__extra_params.pop("-custom_sourcer_params", {})
# reset improper values
sourcer_params = {} if not isinstance(sourcer_params, dict) else sourcer_params
# validate the FFmpeg assets and return location (also downloads static assets on windows)
self.__ffmpeg = get_valid_ffmpeg_path(
str(custom_ffmpeg),
self.__os_windows,
ffmpeg_download_path=__ffmpeg_download_path,
verbose=self.__verbose_logs,
# pass parameter(if specified) to Sourcer API, specifying where to save the downloaded FFmpeg Static
# assets on Windows(if specified)
sourcer_params["-ffmpeg_download_path"] = self.__extra_params.pop(
"-ffmpeg_download_path", ""
)
# check if valid FFmpeg path returned
if self.__ffmpeg:
self.__verbose_logs and logger.debug(
"Found valid FFmpeg executables: `{}`.".format(self.__ffmpeg)
# handle video and audio stream indexes in case of multiple ones.
default_stream_indexes = self.__extra_params.pop(
"-default_stream_indexes", (0, 0)
)
# reset improper values
default_stream_indexes = (
(0, 0)
if not isinstance(default_stream_indexes, (list, tuple))
else default_stream_indexes
)
# extract and assign source metadata as dict
self.__source_metadata = (
Sourcer(
source=source,
verbose=verbose,
ffmpeg_path=self.__ffmpeg,
**sourcer_params
)
else:
# else raise error
raise RuntimeError(
"[StreamGear:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!"
)
.probe_stream(default_stream_indexes=default_stream_indexes)
.retrieve_metadata()
)
# handle where to save the downloaded FFmpeg Static assets on Windows(if specified)
sourcer_params = self.__extra_params.pop("-custom_source_params", {})
# reset improper values
sourcer_params = {} if not isinstance(sourcer_params, dict) else sourcer_params
# get valid ffmpeg path
self.__ffmpeg = self.__source_metadata["ffmpeg_binary_path"]
# handle input source metadata(extracts all required data)
self.__source_metadata = Sourcer(
source=source, verbose=False, ffmpeg_path=self.__ffmpeg, **sourcer_params
).decode_stream()
# handle pass-through audio mode works in conjunction with WriteGear [WIP]

@@ -155,8 +156,10 @@ self.__passthrough_mode = self.__extra_params.pop("-passthrough_audio", False)

# handle mode of operation
if self.__source_metadata.contains_images:
if self.__source_metadata["source_has_image_sequence"]:
# image-sequence mode
self.__opmode = "imgseq"
elif (
self.__source_metadata.contains_video # audio is only for pass-through, not really for audio decoding yet.
and self.__source_metadata.contains_audio
self.__source_metadata[
"source_has_video"
] # audio is only for pass-through, not really for audio decoding yet.
and self.__source_metadata["source_has_audio"]
and self.__passthrough_mode # [WIP]

@@ -167,3 +170,3 @@ ):

# self.__opmode = "ao"
elif self.__source_metadata.contains_video:
elif self.__source_metadata["source_has_video"]:
# video-only mode

@@ -176,3 +179,7 @@ self.__opmode = "vo"

)
# log it
# store as metadata
self.__source_metadata["ffdecoder_operational_mode"] = self.__supported_opmodes[
self.__opmode
]
# and log it
self.__verbose_logs and logger.critical(

@@ -185,10 +192,18 @@ "Activating {} Mode of Operation.".format(

# handle user-defined framerate
self.__constantframerate = self.__extra_params.pop("-constant_framerate", 0.0)
if isinstance(self.__constantframerate, (float, int)):
self.__inputframerate = self.__extra_params.pop("-framerate", 0.0)
if (
isinstance(self.__inputframerate, (float, int))
and self.__inputframerate > 0.0
):
# must be float
self.__constantframerate = float(self.__constantframerate)
self.__inputframerate = float(self.__inputframerate)
else:
# reset improper values
self.__constantframerate = 0.0
self.__inputframerate = 0.0
# FFmpeg parameter `-s` is unsupported
if not (self.__extra_params.pop("-s", None) is None):
logger.warning(
"Discarding user-defined `-s` FFmpeg parameter as it can only be assigned with `-custom_resolution`!"
)
# handle user defined decoded frame resolution(must be a tuple or list)

@@ -229,4 +244,4 @@ self.__custom_resolution = self.__extra_params.pop("-custom_resolution", None)

default_vdecodec = (
self.__source_metadata.default_video_decoder
if self.__source_metadata.default_video_decoder in supported_vdecodecs
self.__source_metadata["source_video_decoder"]
if self.__source_metadata["source_video_decoder"] in supported_vdecodecs
else "unknown"

@@ -243,14 +258,8 @@ )

self.__extra_params.pop("-vcodec", None)
# handle exclusive input sequence framerate
sequence_framerate = self.__extra_params.pop("-r", 1.0)
if (
isinstance(sequence_framerate, (float, int))
and sequence_framerate > 1.0
):
# must be float
sequence_framerate = float(sequence_framerate)
else:
# reset improper values
sequence_framerate = 1.0
input_params["-r"] = sequence_framerate
elif (
"-vcodec" in self.__extra_params
and self.__extra_params["-vcodec"] is None
):
# special case when -vcodec is not needed intentionally
self.__extra_params.pop("-vcodec", None)
else:

@@ -298,9 +307,7 @@ # assign video decoder selected here.

if "rgb24" in supported_pixfmts
else self.__source_metadata.default_video_pixfmt
else self.__source_metadata["source_video_pixfmt"]
)
if "-vf" in self.__extra_params:
self.__extra_params["-pix_fmt"] = self.__extra_params.pop("-vf", None)
if "-pix_fmt" in self.__extra_params:
logger.warning(
"Discarding user-defined `-pix_fmt/-vf` value as it can only be assigned with `frame_format` parameter!"
"Discarding user-defined `-pix_fmt` value as it can only be assigned with `frame_format` parameter!"
)

@@ -314,3 +321,3 @@ self.__extra_params.pop("-pix_fmt", None)

not (self.__frame_format is None) and logger.critical(
"Provided FFmpeg does not support `{}` frame format(pix_fmt). Switching to default `{}`!".format(
"Provided FFmpeg does not support `{}` pixel format(pix_fmt). Switching to default `{}`!".format(
self.__frame_format, "rgb24"

@@ -355,3 +362,3 @@ )

if not (self.__custom_resolution is None)
else self.__source_metadata.default_video_resolution
else self.__source_metadata["source_video_resolution"]
)

@@ -365,5 +372,5 @@ dimensions = "{}x{}".format(

framerate = (
self.__constantframerate
if self.__constantframerate > 0.0
else self.__source_metadata.default_video_framerate
self.__inputframerate
if self.__inputframerate > 0.0
else self.__source_metadata["source_video_framerate"]
)

@@ -378,4 +385,4 @@ output_params["-framerate"] = str(framerate)

self.__raw_frame_num = input_params["-frames:v"]
elif self.__source_metadata.approx_video_nframes:
self.__raw_frame_num = self.__source_metadata.approx_video_nframes
elif self.__source_metadata["approx_video_nframes"]:
self.__raw_frame_num = self.__source_metadata["approx_video_nframes"]
else:

@@ -404,3 +411,3 @@ self.__raw_frame_num = None

self.__process is None
), "Pipeline is not running! Did you called `create()`?"
), "Pipeline is not running! Check if you called `create()` method."

@@ -449,5 +456,3 @@ # formulated raw frame size

)[:, :, 0]
elif self.__raw_frame_pixfmt.startswith(
"yuv"
) and self.__raw_frame_pixfmt.startswith("444p"):
elif self.__raw_frame_pixfmt == "yuv444p":
# reconstruct exclusive frames

@@ -505,2 +510,25 @@ frame = frame.reshape(

@property
def metadata(self):
"""
A property object that dumps Source metadata dict as JSON for pretty printing. As well as can be used to update source metadata with user-defined dictionary.
**Returns:** A [`json.dumps`](https://docs.python.org/3/library/json.html#json.dumps) output.
"""
return json.dumps(self.__source_metadata, indent=2)
@metadata.setter
def metadata(self, value):
"""
A property object that updates source metadata with user-defined dictionary.
Parameters:
value (dict): User-defined dictionary.
"""
self.__verbose_logs and logger.info("Updating Metadata...")
if value and isinstance(value, dict):
self.__source_metadata.update(value)
else:
raise ValueError("Invalid value specified.")
def __launch_FFdecoderline(self, input_params, output_params):

@@ -528,3 +556,3 @@

+ ["-i"]
+ [self.__source_metadata.source]
+ [self.__source_metadata["source"]]
+ output_parameters

@@ -561,3 +589,5 @@ + ["-f", "rawvideo", "-"]

# wait if still process is still processing some information
if self.__process.poll() is None:
self.__process.terminate()
self.__process.wait()
self.__process = None
"""
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -33,4 +33,4 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

# import helper packages
from .utils import logger_handler
# import utils packages
from .utils import logger_handler, delete_file_safe

@@ -354,3 +354,3 @@ # define logger

def validate_imgseqdir(source, extension="jpg"):
def validate_imgseqdir(source, extension="jpg", verbose=False):
"""

@@ -371,3 +371,3 @@ ## validate_imgseqdir

if not (dirpath.exists() and dirpath.is_dir()):
logger.warning(
verbose and logger.warning(
"Specified path `{}` doesn't exists or valid.".format(dirpath)

@@ -468,3 +468,9 @@ )

Returns stdin output from subprocess module
Returns FFmpeg `stdout` output from subprocess module
Parameters:
args (based on input): Non Keyword Arguments
kwargs (based on input): Keyword Arguments
**Returns:** A string value.
"""

@@ -471,0 +477,0 @@ # workaround for python bug: https://bugs.python.org/issue37380

"""
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -25,5 +25,10 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

# import helper packages
# import utils packages
from .utils import logger_handler
from .ffhelper import check_sp_output, is_valid_url, is_valid_image_seq
from .ffhelper import (
check_sp_output,
is_valid_url,
is_valid_image_seq,
get_valid_ffmpeg_path,
)

@@ -40,3 +45,3 @@ # define logger

def __init__(self, source, verbose=False, ffmpeg_path=None, **sourcer_params):
def __init__(self, source, custom_ffmpeg="", verbose=False, **sourcer_params):
"""

@@ -46,14 +51,16 @@ This constructor method initializes the object state and attributes of the Sourcer.

Parameters:
source (str): defines the source for the input stream.
source (str): defines the default input source.
verbose (bool): enables/disables verbose.
ffmpeg_path (str): assigns the location of custom path/directory for custom FFmpeg executables.
custom_ffmpeg (str): assigns the location of custom path/directory for custom FFmpeg executable.
sourcer_params (dict): provides the flexibility to control supported internal Sourcer parameters.
"""
# checks if machine in-use is running windows os or not
self.__os_windows = True if os.name == "nt" else False
# define internal parameters
self.__verbose = ( # enable verbose if specified
self.__verbose_logs = ( # enable verbose if specified
verbose if (verbose and isinstance(verbose, bool)) else False
)
self.__metadata = None # handles metadata recieved
self.__ffmpeg = ffmpeg_path # handles FFmpeg executable path
self.__sourcer_params = { # No use yet (Reserved for future) [TODO]
self.__ffsp_output = None # handles metadata received
self.__sourcer_params = {
str(k).strip(): str(v).strip()

@@ -65,52 +72,114 @@ if not isinstance(v, (dict, list, int, float))

# handle whether to force validate source
self.__forcevalidatesource = self.__sourcer_params.pop(
"-force_validate_source", False
)
if not isinstance(self.__forcevalidatesource, bool):
# reset improper values
self.__forcevalidatesource = False
# handle where to save the downloaded FFmpeg Static assets on Windows(if specified)
__ffmpeg_download_path = self.__sourcer_params.pop("-ffmpeg_download_path", "")
if not isinstance(__ffmpeg_download_path, str):
# reset improper values
__ffmpeg_download_path = ""
# validate the FFmpeg assets and return location (also downloads static assets on windows)
self.__ffmpeg = get_valid_ffmpeg_path(
str(custom_ffmpeg),
self.__os_windows,
ffmpeg_download_path=__ffmpeg_download_path,
verbose=self.__verbose_logs,
)
# check if valid FFmpeg path returned
if self.__ffmpeg:
self.__verbose_logs and logger.debug(
"Found valid FFmpeg executable: `{}`.".format(self.__ffmpeg)
)
else:
# else raise error
raise RuntimeError(
"[DeFFcode:ERROR] :: Failed to find FFmpeg assets on this system. Kindly compile/install FFmpeg or provide a valid custom FFmpeg binary path!"
)
# define externally accessible parameters
self.source = source # handles source stream
self.source_extension = os.path.splitext(source)[
self.__source = source # handles source stream
self.__source_extension = os.path.splitext(source)[
-1
] # handles source stream extension
self.default_video_resolution = None # handle stream resolution
self.default_video_framerate = 0 # handle stream framerate
self.default_video_bitrate = 0 # handle stream's video bitrate
self.default_video_pixfmt = None # handle stream's video pixfmt
self.default_video_decoder = None # handle stream's video decoder
self.default_source_duration = 0 # handle stream's video duration
self.approx_video_nframes = 0 # handle approx stream frame number
self.default_audio_bitrate = None # handle stream's audio bitrate
self.__default_video_resolution = "" # handle stream resolution
self.__default_video_framerate = "" # handle stream framerate
self.__default_video_bitrate = "" # handle stream's video bitrate
self.__default_video_pixfmt = "" # handle stream's video pixfmt
self.__default_video_decoder = "" # handle stream's video decoder
self.__default_source_duration = "" # handle stream's video duration
self.__approx_video_nframes = "" # handle approx stream frame number
self.__default_audio_bitrate = "" # handle stream's audio bitrate
self.__default_audio_samplerate = "" # handle stream's audio samplerate
# handle flags
self.contains_video = False # contain video
self.contains_audio = False # contain audio
self.contains_images = False # contain image-sequence
self.__contains_video = False # contain video
self.__contains_audio = False # contain audio
self.__contains_images = False # contain image-sequence
def decode_stream(self):
# check whether metadata probed or not
self.__metadata_probed = False
def probe_stream(self, default_stream_indexes=(0, 0)):
"""
Parses Source's FFmpeg Metadata and stores them in externally accessible parameters
Parses Source's FFmpeg Output and populates metadata in private class variables
Parameters:
default_stream_indexes (list, tuple): selects specific video and audio stream index in case of multiple ones. Value can be of format: (int,int). For example (0,1) is ("0th video stream", "1st audio stream").
**Returns:** Reference to the instance object.
"""
assert (
isinstance(default_stream_indexes, (list, tuple))
and len(default_stream_indexes) == 2
and all(isinstance(x, int) for x in default_stream_indexes)
), "Invalid default_stream_indexes value!"
# validate source and extract metadata
self.__metadata = self.__validate_source(self.source)
self.__ffsp_output = self.__validate_source(self.__source)
# parse resolution and framerate
video_rfparams = self.extract_resolution_framerate()
video_rfparams = self.__extract_resolution_framerate(
default_stream=default_stream_indexes[0]
)
if video_rfparams:
self.default_video_resolution = video_rfparams["resolution"]
self.default_video_framerate = video_rfparams["framerate"]
self.__default_video_resolution = video_rfparams["resolution"]
self.__default_video_framerate = video_rfparams["framerate"]
# parse pixel format
self.default_video_pixfmt = self.extract_video_pixfmt()
self.__default_video_pixfmt = self.__extract_video_pixfmt(
default_stream=default_stream_indexes[0]
)
# parse video decoder
self.default_video_decoder = self.extract_video_decoder()
self.__default_video_decoder = self.__extract_video_decoder(
default_stream=default_stream_indexes[0]
)
# parse rest of metadata
if not self.contains_images:
if not self.__contains_images:
# parse video bitrate
self.default_video_bitrate = self.extract_video_bitrate()
# parse audio bitrate
self.default_audio_bitrate = self.extract_audio_bitrate()
self.__default_video_bitrate = self.__extract_video_bitrate(
default_stream=default_stream_indexes[0]
)
# parse audio bitrate and samplerate
audio_params = self.__extract_audio_bitrate_nd_samplerate(
default_stream=default_stream_indexes[1]
)
if audio_params:
self.__default_audio_bitrate = audio_params["bitrate"]
self.__default_audio_samplerate = audio_params["samplerate"]
# parse video duration
self.default_source_duration = self.extract_duration()
self.__default_source_duration = self.__extract_duration()
# calculate all flags
if self.default_video_bitrate and self.default_audio_bitrate:
self.contains_video = True
self.contains_audio = True
elif self.default_video_bitrate:
self.contains_video = True
elif self.default_audio_bitrate:
self.contains_audio = True
if (self.__default_video_bitrate or self.__default_video_framerate) and (
self.__default_audio_bitrate or self.__default_audio_samplerate
):
self.__contains_video = True
self.__contains_audio = True
elif self.__default_video_bitrate or self.__default_video_framerate:
self.__contains_video = True
elif self.__default_audio_bitrate or self.__default_audio_samplerate:
self.__contains_audio = True
else:

@@ -121,8 +190,46 @@ raise IOError(

# calculate approximate number of video frame
if self.default_video_framerate and self.default_source_duration:
self.approx_video_nframes = np.rint(
self.default_video_framerate * self.default_source_duration
if self.__default_video_framerate and self.__default_source_duration:
self.__approx_video_nframes = np.rint(
self.__default_video_framerate * self.__default_source_duration
).astype(int, casting="unsafe")
# signal metadata has been probed
self.__metadata_probed = True
# return reference to the instance object.
return self
def retrieve_metadata(self):
"""
Returns Source metadata formatted as python dictionary.
**Returns:** A dictionary value containing metadata.
"""
# check if metadata has been probed or not
assert (
self.__metadata_probed
), "Source Metadata not been probed yet! Check if you called `probe_stream()` method."
self.__verbose_logs and logger.debug("Retrieving Metadata...")
metadata = {
"ffmpeg_binary_path": self.__ffmpeg,
"source": self.__source,
"source_extension": self.__source_extension,
"source_video_resolution": self.__default_video_resolution,
"source_video_framerate": self.__default_video_framerate,
"source_video_pixfmt": self.__default_video_pixfmt,
"source_video_decoder": self.__default_video_decoder,
"source_duration_sec": self.__default_source_duration,
"approx_video_nframes": int(self.__approx_video_nframes)
if self.__approx_video_nframes
else None,
"source_video_bitrate": self.__default_video_bitrate,
"source_audio_bitrate": self.__default_audio_bitrate,
"source_audio_samplerate": self.__default_audio_samplerate,
"source_has_video": self.__contains_video,
"source_has_audio": self.__contains_audio,
"source_has_image_sequence": self.__contains_images,
}
return metadata
def __validate_source(self, source):

@@ -137,9 +244,14 @@ """

self.__video_source = os.path.abspath(source)
elif is_valid_image_seq(self.__ffmpeg, source=source, verbose=self.__verbose):
elif is_valid_image_seq(
self.__ffmpeg, source=source, verbose=self.__verbose_logs
):
self.__video_source = source
self.contains_images = True
elif is_valid_url(self.__ffmpeg, url=source, verbose=self.__verbose):
self.__contains_images = True
elif is_valid_url(self.__ffmpeg, url=source, verbose=self.__verbose_logs):
self.__video_source = source
elif self.__forcevalidatesource:
logger.critical("Forcefully passing validation test for given source!")
self.__video_source = source
else:
logger.error("`source` value is unusuable or unsupported!")
logger.error("`source` value is unusable or unsupported!")
# discard the value otherwise

@@ -151,5 +263,6 @@ raise ValueError("Input source is invalid. Aborting!")

)
# filter and return
return metadata.decode("utf-8").strip()
def extract_video_bitrate(self, default_stream=0):
def __extract_video_bitrate(self, default_stream=0):
"""

@@ -159,11 +272,10 @@ Parses default video-stream bitrate from metadata.

Parameters:
default_stream (int): selects particular video-stream in case of multiple ones.
default_stream (int): selects specific video-stream in case of multiple ones.
**Returns:** A string value.
"""
assert isinstance(default_stream, int), "Invalid input!"
identifiers = ["Video:", "Stream #"]
video_bitrate_text = [
line.strip()
for line in self.__metadata.split("\n")
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)

@@ -178,14 +290,14 @@ ]

filtered_bitrate = re.findall(
r",\s[0-9]+\s\w\w[/]s", selected_stream.strip()
r",\s[0-9]+\s\w\w[\/]s", selected_stream.strip()
)
default_video_bitrate = filtered_bitrate[0].split(" ")[1:3]
final_bitrate = "{}{}".format(
int(default_video_bitrate[0].strip()),
"k" if (default_video_bitrate[1].strip().startswith("k")) else "M",
)
return final_bitrate
else:
return ""
if len(filtered_bitrate):
default_video_bitrate = filtered_bitrate[0].split(" ")[1:3]
final_bitrate = "{}{}".format(
int(default_video_bitrate[0].strip()),
"k" if (default_video_bitrate[1].strip().startswith("k")) else "M",
)
return final_bitrate
return ""
def extract_video_decoder(self, default_stream=0):
def __extract_video_decoder(self, default_stream=0):
"""

@@ -195,3 +307,3 @@ Parses default video-stream decoder from metadata.

Parameters:
default_stream (int): selects particular video-stream in case of multiple ones.
default_stream (int): selects specific video-stream in case of multiple ones.

@@ -204,3 +316,3 @@ **Returns:** A string value.

line.strip()
for line in self.__metadata.split("\n")
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)

@@ -217,7 +329,7 @@ ]

)
return filtered_pixfmt[0].split(" ")[-1]
else:
return None
if filtered_pixfmt:
return filtered_pixfmt[0].split(" ")[-1]
return ""
def extract_video_pixfmt(self, default_stream=0):
def __extract_video_pixfmt(self, default_stream=0):
"""

@@ -227,11 +339,10 @@ Parses default video-stream pixel format from metadata.

Parameters:
default_stream (int): selects particular video-stream in case of multiple ones.
default_stream (int): selects specific video-stream in case of multiple ones.
**Returns:** A string value.
"""
assert isinstance(default_stream, int), "Invalid input!"
identifiers = ["Video:", "Stream #"]
meta_text = [
line.strip()
for line in self.__metadata.split("\n")
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)

@@ -248,55 +359,51 @@ ]

)
return filtered_pixfmt[0].split(" ")[-1]
else:
return None
if filtered_pixfmt:
return filtered_pixfmt[0].split(" ")[-1]
return ""
def extract_audio_bitrate(self, default_stream=0):
def __extract_audio_bitrate_nd_samplerate(self, default_stream=0):
"""
Parses default audio-stream bitrate from metadata.
Parses default audio-stream bitrate and samplerate from metadata.
Parameters:
default_stream (int): selects particular audio-stream in case of multiple ones.
default_stream (int): selects specific audio-stream in case of multiple ones.
**Returns:** A string value.
"""
assert isinstance(default_stream, int), "Invalid input!"
default_audio_bitrate = re.findall(r"fltp,\s[0-9]+\s\w\w[/]s", self.__metadata)
sample_rate_identifiers = ["Audio", "Hz"] + (
["fltp"] if isinstance(self.source, str) else []
)
audio_sample_rate = [
identifiers = ["Audio:", "Stream #"]
meta_text = [
line.strip()
for line in self.__metadata.split("\n")
if all(x in line for x in sample_rate_identifiers)
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)
]
if default_audio_bitrate:
selected_stream = (
result = {}
if meta_text:
selected_stream = meta_text[
default_stream
if default_stream > 0 and default_stream < len(default_audio_bitrate)
if default_stream > 0 and default_stream < len(meta_text)
else 0
]
# filter data
filtered_audio_bitrate = re.findall(
r"fltp,\s[0-9]+\s\w\w[\/]s", selected_stream.strip()
)
filtered = default_audio_bitrate[selected_stream].split(" ")[1:3]
final_bitrate = "{}{}".format(
int(filtered[0].strip()),
"k" if (filtered[1].strip().startswith("k")) else "M",
filtered_audio_samplerate = re.findall(
r",\s[0-9]+\sHz", selected_stream.strip()
)
return final_bitrate
elif audio_sample_rate:
selected_stream = (
default_stream
if default_stream > 0 and default_stream < len(audio_sample_rate)
else 0
)
sample_rate = re.findall(r"[0-9]+\sHz", audio_sample_rate[selected_stream])[
0
]
sample_rate_value = int(sample_rate.split(" ")[0])
samplerate_2_bitrate = int(
(sample_rate_value - 44100) * (320 - 96) / (48000 - 44100) + 96
)
return str(samplerate_2_bitrate) + "k"
else:
return None
# get audio bitrate and samplerate metadata
if filtered_audio_bitrate:
filtered = filtered_audio_bitrate[0].split(" ")[1:3]
result["bitrate"] = "{}{}".format(
int(filtered[0].strip()),
"k" if (filtered[1].strip().startswith("k")) else "M",
)
else:
result["bitrate"] = ""
if filtered_audio_samplerate:
result["samplerate"] = filtered_audio_samplerate[0].split(", ")[1]
else:
result["samplerate"] = ""
return result if result and (len(result) == 2) else {}
def extract_resolution_framerate(self):
def __extract_resolution_framerate(self, default_stream=0):
"""

@@ -307,18 +414,32 @@ Parses default video-stream resolution and framerate from metadata.

"""
self.__verbose and logger.debug(stripped_data)
identifiers = ["Video:", "Stream #"]
meta_text = [
line.strip()
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)
]
result = {}
stripped_data = [x.strip() for x in self.__metadata.split("\n")]
for data in stripped_data:
output_a = re.findall(r"([1-9]\d+)x([1-9]\d+)", data)
output_b = re.findall(r"\d+(?:\.\d+)?\sfps", data)
if len(result) == 2:
break
if output_b and not "framerate" in result:
result["framerate"] = float(re.findall(r"[\d\.\d]+", output_b[0])[0])
if output_a and not "resolution" in result:
result["resolution"] = [int(x) for x in output_a[-1]]
# return values
return result if (len(result) == 2) else None
if meta_text:
selected_stream = meta_text[
default_stream
if default_stream > 0 and default_stream < len(meta_text)
else 0
]
# filter data
filtered_resolution = re.findall(
r"([1-9]\d+)x([1-9]\d+)", selected_stream.strip()
)
filtered_framerate = re.findall(
r"\d+(?:\.\d+)?\sfps", selected_stream.strip()
)
# get framerate and resolution metadata
if filtered_framerate:
result["framerate"] = float(
re.findall(r"[\d\.\d]+", filtered_framerate[0])[0]
)
if filtered_resolution:
result["resolution"] = [int(x) for x in filtered_resolution[0]]
return result if result and (len(result) == 2) else {}
def extract_duration(self, inseconds=True):
def __extract_duration(self, inseconds=True):
"""

@@ -332,3 +453,3 @@ Parses stream duration from metadata.

line.strip()
for line in self.__metadata.split("\n")
for line in self.__ffsp_output.split("\n")
if all(x in line for x in identifiers)

@@ -344,3 +465,3 @@ ]

sum(
float(x) * 60 ** i
float(x) * 60**i
for i, x in enumerate(reversed(t_duration[0].split(":")))

@@ -347,0 +468,0 @@ )

"""
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -24,7 +24,8 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

# import the necessary packages
import os
import os, sys
import logging
from pathlib import Path
from colorlog import ColoredFormatter
# import helper packages
# import internal packages
from .version import __version__

@@ -56,3 +57,3 @@

# check if FFdecoder_LOGFILE defined
file_mode = os.environ.get("FFRAVEL_LOGFILE", False)
file_mode = os.environ.get("DEFFCODE_LOGFILE", False)
# define handler

@@ -66,3 +67,3 @@ handler = logging.StreamHandler()

file_path = (
os.path.join(file_path, "FFdecoder.log")
os.path.join(file_path, "deffcode.log")
if os.path.isdir(file_path)

@@ -88,3 +89,3 @@ else file_path

# log current version for debugging
logger.info("Running FFdecoder Version: {}".format(str(__version__)))
logger.info("Running DeFFcode Version: {}".format(str(__version__)))

@@ -119,1 +120,20 @@

return args
def delete_file_safe(file_path):
"""
## delete_ext_safe
Safely deletes files at given path.
Parameters:
file_path (string): path to the file
"""
try:
dfile = Path(file_path)
if sys.version_info >= (3, 8, 0):
dfile.unlink(missing_ok=True)
else:
dfile.exists() and dfile.unlink()
except Exception as e:
logger.exception(str(e))

@@ -1,1 +0,1 @@

__version__ = "0.1.0"
__version__ = "0.2.0"

@@ -203,3 +203,3 @@ Apache License

======================================
============================================================================

@@ -221,1 +221,3 @@ This library also borrows code from vidgear(https://github.com/abhiTronix/vidgear)

limitations under the License.
============================================================================
+159
-120
Metadata-Version: 2.1
Name: deffcode
Version: 0.1.0
Summary: Performant Pythonic FFmpeg Decoder with easy to adapt flexible API.
Version: 0.2.0
Summary: High-performance Real-time Video frames Generator for generating blazingly fast video frames in python.
Home-page: https://abhitronix.github.io/deffcode

@@ -37,3 +37,3 @@ Author: Abhishek Thakur

===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -56,106 +56,110 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

<h1 align="center">
<i>de</i><b>FF</b><i>code</i>
</h1>
<p align="center">Performant ⚡️ Pythonic FFmpeg Decoder with easy to adapt flexible API 🐍.</p>
<h2 align="center">
</h2>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode.png" alt="DeFFcode" title="Logo designed by Abhishek Thakur(@abhiTronix), under CC-BY-NC-SA 4.0 License" width="80%"/>
</div>
<div align="center">
&nbsp;
[![Build Status][github-cli]][github-flow] [![Codecov branch][codecov]][code] [![Azure DevOps builds (branch)][azure-badge]][azure-pipeline]
**deffcode** is a Performant and Robust FFmpeg Pythonic Wrapper that aimed at decoding any stream that you throw at it. Requiring minimal efforts, deffcode provides an easy-to-adapt flexible API to read a wide range of streams, and can ingest video using any decoder(even hardware ones) into any pixel format ffmpeg supports. It also provides pin-point accurate seeking for extracting only a specific part of your input as required.
[![Glitter chat][gitter-bagde]][gitter] [![Build Status][appveyor]][app] [![PyPi version][pypi-badge]][pypi]
It is cross-platform, runs on Python 3.7+, and is easy to install.
[![Code Style][black-badge]][black]
&nbsp;
</div>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode-tagline.png" alt="DeFFcode tagline" width="40%"/>
## Examples
### Basic Example
----
```python
# import the necessary packages
from deffcode import FFdecoder
[Releases][release]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Documentation][docs]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Installation][install]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[License](https://github.com/abhiTronix/deffcode#copyright)
# initialize and formulate the decoder
decoder = FFdecoder("foo.mp4").formulate()
----
# grab the RGB24(default) frame from
# the decoder(generator)
for frame in decoder.generateFrame():
print(frame.shape)
</div>
# terminate the decoder
decoder.terminate()
```
&thinsp;
The output:
## Overview
> DeFFcode is a powerful High-performance Real-time **Video frames Generator** that wraps FFmpeg pipeline inside a subprocess module for generating blazingly fast video frames in python 🔥
The primary purpose of DeFFcode is to provide a cross-platform solution for fast and low-overhead decoding of a wide range of video streams into 3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray) frames while providing **complete control over the underlying FFmpeg pipeline** without the need to go deeper into hefty documentation and in just a few lines of python code.
DeFFcode can **extract frames in real-time with any custom specification imaginable** such as Framerate, Resolution, Hardware decoding, Complex Filters into any pixel format while giving users the complete freedom to play with any desired FFmpeg supported parameter. On top of that, DeFFcode enables **effortless and precise FFmpeg Frame Seeking** natively.
Finally, DeFFcode APIs are designed with **simplicity, flexibility, and modularity** in mind for the best developer experience.
&thinsp;
## Key Features
DeFFcode APIs are build on [**FFmpeg**][ffmpeg] - a leading multimedia framework, that gives you the following:
- Extremely exceptional real-time performance ⚡ with low-memory footprints.
- Flexible API with access to almost every parameter available within FFmpeg.
- Fast dedicated [Hardware-Accelerated Decoding](https://abhitronix.github.io/deffcode/latest/examples/advanced/#gpu-enabled-hardware-accelerated-decoding).
- Precise FFmpeg [Frame Seeking](https://abhitronix.github.io/deffcode/latest/examples/basic/#saving-keyframes-as-image) with pinpoint accuracy.
- Extensive support for real-time [Complex FFmpeg Filters](https://abhitronix.github.io/deffcode/latest/examples/advanced/#generating-video-with-complex-filter-applied).
- Out-of-the-box support for Computer Vision libraries like OpenCV, Pytorch, etc.
- Support a wide range of media files, devices, image-sequence and network streams.
- Easier to ingest streams into any pixel format that FFmpeg supports.
- Lossless Transcoding support with [WriteGear](https://abhitronix.github.io/deffcode/latest/gears/writegear/introduction/).
- Fewer hard dependencies, and easy to install.
- Designed modular for best developer experience.
- Cross-platform and runs on Python 3.7+
&nbsp;
## Requirements
- Python 3.7+
- FFmpeg _(See [this doc](https://abhitronix.github.io/deffcode/latest/installation/ffmpeg_install/) for its installation)_
&nbsp;
## Installation
Installation is as simple as:
```sh
(720, 1280, 3)
(720, 1280, 3)
...
...
...
(720, 1280, 3)
$ (sudo) pip install deffcode
```
### Basic OpenCV Example
💡 For more details, see [installation notes][install].
```python
# import the necessary packages
from deffcode import FFdecoder
import cv2
&nbsp;
# initialize and formulate the decoder for BGR24 output
decoder = FFdecoder("foo.mp4", frame_format="bgr24").formulate()
# loop over frames
while True:
# grab the BGR24 frame from the decoder
frame = next(decoder.generateFrame(), None)
## Getting Started
# check if frame is None
if frame is None:
break
# Show output window
cv2.imshow("Output", frame)
---
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
**📚 Documentation: https://abhitronix.github.io/deffcode**
# close output window
cv2.destroyAllWindows()
# terminate the decoder
decoder.terminate()
```
---
### Basic PIL Example
The default function of DeFFcode's [FFdecoder API](https://abhitronix.github.io/deffcode/latest/reference/ffdecoder/#ffdecoder-api) is to generate 24-bit RGB (3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray)) frames from the given source:
```python
```py title="grab_frames.py"
# import the necessary packages
from deffcode import FFdecoder
from PIL import Image
# define the FFmpeg parameter to seek
# to 00:00:01 in time and get one single frame
extraparams = {"-ss": "00:00:01", "-frames:v":1}
# formulate the decoder with suitable source(for e.g. foo.mp4)
decoder = FFdecoder("foo.mp4").formulate()
# initialize and formulate the decode
decoder = FFdecoder("foo.mp4", **extraparams).formulate()
# grab RGB24(default) 3D frames from decoder
for frame in decoder.generateFrame():
# lets print its shape
print(frame.shape)
# grab the RGB24(default) frame from the decoder
frame = next(decoder.generateFrame(), None)
# print metadata as `json.dump`
print(decoder.metadata)
# check if frame is None
if not(frame is None)
# Convert and Show output window
im = Image.fromarray(frame)
im.show()
# terminate the decoder

@@ -165,40 +169,34 @@ decoder.terminate()

### Basic Matplotlib Example
For more examples and in-depth usage guide, kindly refer our **[Basic Recipes 🥧](https://abhitronix.github.io/deffcode/latest/examples/basic)** and **[Advanced Recipes 🔬](https://abhitronix.github.io/deffcode/latest/examples/advanced)**
```python
# import the necessary packages
from deffcode import FFdecoder
import matplotlib.pyplot as plt
# define the FFmpeg parameter to seek
# to 00:00:02.01 in time and get one single frame
extraparams = {"-ss": "00:00:02.01", "-frames:v":1}
💡 In case you're run into any problems, consult our [Help](https://abhitronix.github.io/deffcode/latest/help/) section.
# initialize and formulate the decode for Grayscale output
decoder = FFdecoder("foo.mp4", frame_format="gray", **extraparams).formulate()
# grab single Grayscale frame from the decoder
frame = next(decoder.generateFrame(), None)
&nbsp;
# Show output window
plt.imshow(frame, cmap='gray', vmin=0, vmax=255)
plt.show()
## Roadmap
# terminate the decoder
decoder.terminate()
```
- [x] Add clean and elegant documentation.
- [x] Add project Issue and PR templates.
- [x] Add related unit tests with `pytests`.
- [x] Automate stuff with Continuous Integration.
- [ ] Add Multiple Source Inputs support.
- [ ] Add Devices and Screen Capture support.
- [ ] Resolve High CPU usage issue with WriteGear API.
- [ ] Add more parameters to Sourcer API's metadata.
- [ ] Implement Buffer and Audio pass-through modes.
- [ ] Add recipe for Source with Multiple Embedded Streams.
- [ ] Add example to dynamically change writable FFdecoder API's metadata parameters.
- [ ] Add more Advanced Recipes and use cases.
- [ ] Add preliminary benchmarks.
- [ ] Make Frame Seeking dynamic.
&nbsp;
## Dependencies
## Contributions
Minimal requirements:
- Python 3.7+
- FFmpeg (See [this](https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) for its installation)
- NumPy >=1.20.0
- requests
- colorlog
- tqdm
> Contributions are welcome. We'd love to have your contributions to fix bugs or to implement new features!
:bulb: These requirements are installed automatically(except FFmpeg).
Please see our **[Contribution Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/)** for more details.

@@ -208,29 +206,70 @@

## Installation
## Community Support
Join our Gitter community channel for quick discussions:
```sh
# Install latest stable release
pip install -U deffcode
```
[![Gitter](https://badges.gitter.im/deffcode/community.svg)](https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
**And if you prefer to install deffcode directly from the repository:**
```sh
# Install latest stable release
pip install git+git://github.com/abhiTronix/deffcode@master#egg=deffcode
```
&nbsp;
**Or you can also download its wheel (`.whl`) package from our repository's [releases](https://github.com/abhiTronix/deffcode/releases) section, and thereby can be installed as follows:**
# Donations
```sh
# Install latest stable release
pip install deffcode-0.1.0-py3-none-any.whl
```
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/help_us.png" alt="PiGear" width="50%" />
> DeFFcode is free and open source and will always remain so. ❤️
It is something I am doing with my own free time. If you would like to say thanks, please feel free to make a donation:
[![ko-fi][kofi-badge]][kofi]
&nbsp;
----
<p align="center"><i><b>deffcode</b> is <a href="https://github.com/abhiTronix/deffcode/blob/master/LICENSE.md">Apache 2.0 Licensed</a> code.<br/>Designed & crafted with care.</i></br>⭐️</p>
# Copyright
**Copyright (c) abhiTronix 2021**
This library is released under the **[Apache 2.0 License][license]**.
<!--
Badges
-->
[appveyor]:https://img.shields.io/appveyor/ci/abhitronix/deffcode.svg?style=for-the-badge&logo=appveyor
[codecov]:https://img.shields.io/codecov/c/gh/abhiTronix/deffcode?logo=codecov&style=for-the-badge&token=zrES4mwVKe
[github-cli]:https://img.shields.io/github/workflow/status/abhiTronix/deffcode/GitHub%20Action%20workflow%20for%20Linux?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAgMWE5IDkgMCAwMTkgOSA5IDkgMCAwMS05IDkgOSA5IDAgMDEtOS05IDkgOSAwIDAxOS05ek0yMyAxOWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyek0yMyAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MSAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1zZWNvbmRhcnksICM3OUI4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNS4wMzcgMjMuNjA3bC0zLjA3IDMuMDY1LTEuNDktMS40ODUiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDEgMTlhNiA2IDAgMTEwIDEyIDYgNiAwIDAxMC0xMnoiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTQzLjAzNiAyMy42MDdsLTMuMDY5IDMuMDY1LTEuNDktMS40ODVNNyA2LjgxMmExIDEgMCAwMTEuNTMzLS44NDZsNS4xMTMgMy4yMmExIDEgMCAwMS0uMDA2IDEuNjk3bC01LjExMyAzLjE3QTEgMSAwIDAxNyAxMy4yMDNWNi44MTN6TTkgMTl2MTVjMCAzLjg2NiAzLjE3NyA3IDcgN2gxIiBzdHJva2U9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xNi45NDkgMjZhMSAxIDAgMTAwLTJ2MnpNOCAxOS4wMzVBNi45NjUgNi45NjUgMCAwMDE0Ljk2NSAyNnYtMkE0Ljk2NSA0Ljk2NSAwIDAxMTAgMTkuMDM1SDh6TTE0Ljk2NSAyNmgxLjk4NHYtMmgtMS45ODR2MnoiIGZpbGw9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIi8+PHBhdGggZD0iTTI5LjA1NSAyNWg1Ljk0NCIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIxIDQwYTEgMSAwIDExLS4wMDEgMi4wMDFBMSAxIDAgMDEyMSA0MHpNMjUgNDBhMSAxIDAgMTEtLjAwMSAyLjAwMUExIDEgMCAwMTI1IDQweiIgZmlsbD0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIvPjxwYXRoIGQ9Ik0zNC4wMDUgNDEuMDA3bC0xLjAxMy4wMzMiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=
[prs-badge]:https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABC0lEQVRYhdWVPQoCMRCFX6HY2ghaiZUXsLW0EDyBrbWtN/EUHsHTWFnYyCL4gxibVZZlZzKTnWz0QZpk5r0vIdkF/kBPAMOKeddE+CQPKoc5Yt5cTjBMdQSwDQToWgBJAn3jmhqgltapAV6E6b5U17MGGAUaUj07TficMfIBZDV6vxowBm1BP9WbSQE4o5h9IjPJmy73TEPDDxVmoZdQrQ5jRhly9Q8tgMUXkIIWn0oG4GYQfAXQzz1PGoCiQndM7b4RgJay/h7zBLT3hASgoKjamQJMreKf0gfuAGyYtXEIAKcL/Dss15iq6ohXghozLYiAMxPuACwtIT4yeQUxAaLrZwAoqGRKGk7qDSYTfYQ8LuYnAAAAAElFTkSuQmCC
[azure-badge]:https://img.shields.io/azure-devops/build/abhiuna12/942b3b13-d745-49e9-8d7d-b3918ff43ac2/3/master?logo=azure-pipelines&style=for-the-badge
[pypi-badge]:https://img.shields.io/pypi/v/deffcode.svg?style=for-the-badge&logo=pypi
[gitter-bagde]:https://img.shields.io/badge/Chat-Gitter-blueviolet.svg?style=for-the-badge&logo=gitter
[Coffee-badge]:https://abhitronix.github.io/img/deffcode/orange_img.png
[kofi-badge]:https://www.ko-fi.com/img/githubbutton_sm.svg
[black-badge]:https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge&logo=github
<!--
Internal URLs
-->
[docs]:https://abhitronix.github.io/deffcode/latest/
[install]:https://abhitronix.github.io/deffcode/latest/installation/
[release]:https://github.com/abhiTronix/deffcode/releases/latest
[license]:https://github.com/abhiTronix/deffcode/blob/master/LICENSE
[github-flow]:https://github.com/abhiTronix/deffcode/actions/workflows/CIlinux.yml
[azure-pipeline]:https://dev.azure.com/abhiuna12/public/_build?definitionId=3
[app]:https://ci.appveyor.com/project/abhiTronix/deffcode
[code]:https://codecov.io/gh/abhiTronix/deffcode
[black]: https://github.com/psf/black
<!--
External URLs
-->
[ffmpeg]:https://www.ffmpeg.org/
[pypi]:https://pypi.org/project/deffcode/
[gitter]:https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[coffee]:https://www.buymeacoffee.com/2twOXFvlA
[kofi]: https://ko-fi.com/W7W8WTYO
+156
-118
<!--
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -21,106 +21,110 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

<h1 align="center">
<i>de</i><b>FF</b><i>code</i>
</h1>
<p align="center">Performant ⚡️ Pythonic FFmpeg Decoder with easy to adapt flexible API 🐍.</p>
<h2 align="center">
</h2>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode.png" alt="DeFFcode" title="Logo designed by Abhishek Thakur(@abhiTronix), under CC-BY-NC-SA 4.0 License" width="80%"/>
</div>
<div align="center">
&nbsp;
[![Build Status][github-cli]][github-flow] [![Codecov branch][codecov]][code] [![Azure DevOps builds (branch)][azure-badge]][azure-pipeline]
**deffcode** is a Performant and Robust FFmpeg Pythonic Wrapper that aimed at decoding any stream that you throw at it. Requiring minimal efforts, deffcode provides an easy-to-adapt flexible API to read a wide range of streams, and can ingest video using any decoder(even hardware ones) into any pixel format ffmpeg supports. It also provides pin-point accurate seeking for extracting only a specific part of your input as required.
[![Glitter chat][gitter-bagde]][gitter] [![Build Status][appveyor]][app] [![PyPi version][pypi-badge]][pypi]
It is cross-platform, runs on Python 3.7+, and is easy to install.
[![Code Style][black-badge]][black]
&nbsp;
</div>
<div align="center">
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/deffcode-tagline.png" alt="DeFFcode tagline" width="40%"/>
## Examples
### Basic Example
----
```python
# import the necessary packages
from deffcode import FFdecoder
[Releases][release]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Documentation][docs]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[Installation][install]&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;[License](#copyright)
# initialize and formulate the decoder
decoder = FFdecoder("foo.mp4").formulate()
----
# grab the RGB24(default) frame from
# the decoder(generator)
for frame in decoder.generateFrame():
print(frame.shape)
</div>
# terminate the decoder
decoder.terminate()
```
&thinsp;
The output:
## Overview
> DeFFcode is a powerful High-performance Real-time **Video frames Generator** that wraps FFmpeg pipeline inside a subprocess module for generating blazingly fast video frames in python 🔥
The primary purpose of DeFFcode is to provide a cross-platform solution for fast and low-overhead decoding of a wide range of video streams into 3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray) frames while providing **complete control over the underlying FFmpeg pipeline** without the need to go deeper into hefty documentation and in just a few lines of python code.
DeFFcode can **extract frames in real-time with any custom specification imaginable** such as Framerate, Resolution, Hardware decoding, Complex Filters into any pixel format while giving users the complete freedom to play with any desired FFmpeg supported parameter. On top of that, DeFFcode enables **effortless and precise FFmpeg Frame Seeking** natively.
Finally, DeFFcode APIs are designed with **simplicity, flexibility, and modularity** in mind for the best developer experience.
&thinsp;
## Key Features
DeFFcode APIs are build on [**FFmpeg**][ffmpeg] - a leading multimedia framework, that gives you the following:
- Extremely exceptional real-time performance ⚡ with low-memory footprints.
- Flexible API with access to almost every parameter available within FFmpeg.
- Fast dedicated [Hardware-Accelerated Decoding](https://abhitronix.github.io/deffcode/latest/examples/advanced/#gpu-enabled-hardware-accelerated-decoding).
- Precise FFmpeg [Frame Seeking](https://abhitronix.github.io/deffcode/latest/examples/basic/#saving-keyframes-as-image) with pinpoint accuracy.
- Extensive support for real-time [Complex FFmpeg Filters](https://abhitronix.github.io/deffcode/latest/examples/advanced/#generating-video-with-complex-filter-applied).
- Out-of-the-box support for Computer Vision libraries like OpenCV, Pytorch, etc.
- Support a wide range of media files, devices, image-sequence and network streams.
- Easier to ingest streams into any pixel format that FFmpeg supports.
- Lossless Transcoding support with [WriteGear](https://abhitronix.github.io/deffcode/latest/gears/writegear/introduction/).
- Fewer hard dependencies, and easy to install.
- Designed modular for best developer experience.
- Cross-platform and runs on Python 3.7+
&nbsp;
## Requirements
- Python 3.7+
- FFmpeg _(See [this doc](https://abhitronix.github.io/deffcode/latest/installation/ffmpeg_install/) for its installation)_
&nbsp;
## Installation
Installation is as simple as:
```sh
(720, 1280, 3)
(720, 1280, 3)
...
...
...
(720, 1280, 3)
$ (sudo) pip install deffcode
```
### Basic OpenCV Example
💡 For more details, see [installation notes][install].
```python
# import the necessary packages
from deffcode import FFdecoder
import cv2
&nbsp;
# initialize and formulate the decoder for BGR24 output
decoder = FFdecoder("foo.mp4", frame_format="bgr24").formulate()
# loop over frames
while True:
# grab the BGR24 frame from the decoder
frame = next(decoder.generateFrame(), None)
## Getting Started
# check if frame is None
if frame is None:
break
# Show output window
cv2.imshow("Output", frame)
---
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
**📚 Documentation: https://abhitronix.github.io/deffcode**
# close output window
cv2.destroyAllWindows()
# terminate the decoder
decoder.terminate()
```
---
### Basic PIL Example
The default function of DeFFcode's [FFdecoder API](https://abhitronix.github.io/deffcode/latest/reference/ffdecoder/#ffdecoder-api) is to generate 24-bit RGB (3D [`ndarray`](https://numpy.org/doc/stable/reference/arrays.ndarray.html#the-n-dimensional-array-ndarray)) frames from the given source:
```python
```py title="grab_frames.py"
# import the necessary packages
from deffcode import FFdecoder
from PIL import Image
# define the FFmpeg parameter to seek
# to 00:00:01 in time and get one single frame
extraparams = {"-ss": "00:00:01", "-frames:v":1}
# formulate the decoder with suitable source(for e.g. foo.mp4)
decoder = FFdecoder("foo.mp4").formulate()
# initialize and formulate the decode
decoder = FFdecoder("foo.mp4", **extraparams).formulate()
# grab RGB24(default) 3D frames from decoder
for frame in decoder.generateFrame():
# lets print its shape
print(frame.shape)
# grab the RGB24(default) frame from the decoder
frame = next(decoder.generateFrame(), None)
# print metadata as `json.dump`
print(decoder.metadata)
# check if frame is None
if not(frame is None)
# Convert and Show output window
im = Image.fromarray(frame)
im.show()
# terminate the decoder

@@ -130,40 +134,34 @@ decoder.terminate()

### Basic Matplotlib Example
For more examples and in-depth usage guide, kindly refer our **[Basic Recipes 🥧](https://abhitronix.github.io/deffcode/latest/examples/basic)** and **[Advanced Recipes 🔬](https://abhitronix.github.io/deffcode/latest/examples/advanced)**
```python
# import the necessary packages
from deffcode import FFdecoder
import matplotlib.pyplot as plt
# define the FFmpeg parameter to seek
# to 00:00:02.01 in time and get one single frame
extraparams = {"-ss": "00:00:02.01", "-frames:v":1}
💡 In case you're run into any problems, consult our [Help](https://abhitronix.github.io/deffcode/latest/help/) section.
# initialize and formulate the decode for Grayscale output
decoder = FFdecoder("foo.mp4", frame_format="gray", **extraparams).formulate()
# grab single Grayscale frame from the decoder
frame = next(decoder.generateFrame(), None)
&nbsp;
# Show output window
plt.imshow(frame, cmap='gray', vmin=0, vmax=255)
plt.show()
## Roadmap
# terminate the decoder
decoder.terminate()
```
- [x] Add clean and elegant documentation.
- [x] Add project Issue and PR templates.
- [x] Add related unit tests with `pytests`.
- [x] Automate stuff with Continuous Integration.
- [ ] Add Multiple Source Inputs support.
- [ ] Add Devices and Screen Capture support.
- [ ] Resolve High CPU usage issue with WriteGear API.
- [ ] Add more parameters to Sourcer API's metadata.
- [ ] Implement Buffer and Audio pass-through modes.
- [ ] Add recipe for Source with Multiple Embedded Streams.
- [ ] Add example to dynamically change writable FFdecoder API's metadata parameters.
- [ ] Add more Advanced Recipes and use cases.
- [ ] Add preliminary benchmarks.
- [ ] Make Frame Seeking dynamic.
&nbsp;
## Dependencies
## Contributions
Minimal requirements:
- Python 3.7+
- FFmpeg (See [this](https://abhitronix.github.io/vidgear/latest/gears/writegear/compression/advanced/ffmpeg_install/#ffmpeg-installation-instructions) for its installation)
- NumPy >=1.20.0
- requests
- colorlog
- tqdm
> Contributions are welcome. We'd love to have your contributions to fix bugs or to implement new features!
:bulb: These requirements are installed automatically(except FFmpeg).
Please see our **[Contribution Guidelines](https://abhitronix.github.io/deffcode/latest/contribution/)** for more details.

@@ -173,28 +171,68 @@

## Installation
## Community Support
Join our Gitter community channel for quick discussions:
```sh
# Install latest stable release
pip install -U deffcode
```
[![Gitter](https://badges.gitter.im/deffcode/community.svg)](https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
**And if you prefer to install deffcode directly from the repository:**
```sh
# Install latest stable release
pip install git+git://github.com/abhiTronix/deffcode@master#egg=deffcode
```
&nbsp;
**Or you can also download its wheel (`.whl`) package from our repository's [releases](https://github.com/abhiTronix/deffcode/releases) section, and thereby can be installed as follows:**
# Donations
```sh
# Install latest stable release
pip install deffcode-0.1.0-py3-none-any.whl
```
<img src="https://abhitronix.github.io/deffcode/latest/assets/images/help_us.png" alt="PiGear" width="50%" />
> DeFFcode is free and open source and will always remain so. ❤️
It is something I am doing with my own free time. If you would like to say thanks, please feel free to make a donation:
[![ko-fi][kofi-badge]][kofi]
&nbsp;
----
<p align="center"><i><b>deffcode</b> is <a href="https://github.com/abhiTronix/deffcode/blob/master/LICENSE.md">Apache 2.0 Licensed</a> code.<br/>Designed & crafted with care.</i></br>⭐️</p>
# Copyright
**Copyright © abhiTronix 2021**
This library is released under the **[Apache 2.0 License][license]**.
<!--
Badges
-->
[appveyor]:https://img.shields.io/appveyor/ci/abhitronix/deffcode.svg?style=for-the-badge&logo=appveyor
[codecov]:https://img.shields.io/codecov/c/gh/abhiTronix/deffcode?logo=codecov&style=for-the-badge&token=zrES4mwVKe
[github-cli]:https://img.shields.io/github/workflow/status/abhiTronix/deffcode/GitHub%20Action%20workflow%20for%20Linux?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB3aWR0aD0iNDgiIGhlaWdodD0iNDgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNMTAgMWE5IDkgMCAwMTkgOSA5IDkgMCAwMS05IDkgOSA5IDAgMDEtOS05IDkgOSAwIDAxOS05ek0yMyAxOWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyek0yMyAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00MSAzNWE2IDYgMCAxMTAgMTIgNiA2IDAgMDEwLTEyeiIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1zZWNvbmRhcnksICM3OUI4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0yNS4wMzcgMjMuNjA3bC0zLjA3IDMuMDY1LTEuNDktMS40ODUiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNDEgMTlhNiA2IDAgMTEwIDEyIDYgNiAwIDAxMC0xMnoiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tcHJpbWFyeSwgIzIwODhGRikiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+PHBhdGggZD0iTTQzLjAzNiAyMy42MDdsLTMuMDY5IDMuMDY1LTEuNDktMS40ODVNNyA2LjgxMmExIDEgMCAwMTEuNTMzLS44NDZsNS4xMTMgMy4yMmExIDEgMCAwMS0uMDA2IDEuNjk3bC01LjExMyAzLjE3QTEgMSAwIDAxNyAxMy4yMDNWNi44MTN6TTkgMTl2MTVjMCAzLjg2NiAzLjE3NyA3IDcgN2gxIiBzdHJva2U9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIiBzdHJva2Utd2lkdGg9IjIiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIvPjxwYXRoIGQ9Ik0xNi45NDkgMjZhMSAxIDAgMTAwLTJ2MnpNOCAxOS4wMzVBNi45NjUgNi45NjUgMCAwMDE0Ljk2NSAyNnYtMkE0Ljk2NSA0Ljk2NSAwIDAxMTAgMTkuMDM1SDh6TTE0Ljk2NSAyNmgxLjk4NHYtMmgtMS45ODR2MnoiIGZpbGw9InZhcigtLWNvbG9yLW1hcmtldGluZy1pY29uLXByaW1hcnksICMyMDg4RkYpIi8+PHBhdGggZD0iTTI5LjA1NSAyNWg1Ljk0NCIgc3Ryb2tlPSJ2YXIoLS1jb2xvci1tYXJrZXRpbmctaWNvbi1wcmltYXJ5LCAjMjA4OEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz48cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTIxIDQwYTEgMSAwIDExLS4wMDEgMi4wMDFBMSAxIDAgMDEyMSA0MHpNMjUgNDBhMSAxIDAgMTEtLjAwMSAyLjAwMUExIDEgMCAwMTI1IDQweiIgZmlsbD0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIvPjxwYXRoIGQ9Ik0zNC4wMDUgNDEuMDA3bC0xLjAxMy4wMzMiIHN0cm9rZT0idmFyKC0tY29sb3ItbWFya2V0aW5nLWljb24tc2Vjb25kYXJ5LCAjNzlCOEZGKSIgc3Ryb2tlLXdpZHRoPSIyIiBzdHJva2UtbGluZWNhcD0icm91bmQiLz48L3N2Zz4=
[prs-badge]:https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABC0lEQVRYhdWVPQoCMRCFX6HY2ghaiZUXsLW0EDyBrbWtN/EUHsHTWFnYyCL4gxibVZZlZzKTnWz0QZpk5r0vIdkF/kBPAMOKeddE+CQPKoc5Yt5cTjBMdQSwDQToWgBJAn3jmhqgltapAV6E6b5U17MGGAUaUj07TficMfIBZDV6vxowBm1BP9WbSQE4o5h9IjPJmy73TEPDDxVmoZdQrQ5jRhly9Q8tgMUXkIIWn0oG4GYQfAXQzz1PGoCiQndM7b4RgJay/h7zBLT3hASgoKjamQJMreKf0gfuAGyYtXEIAKcL/Dss15iq6ohXghozLYiAMxPuACwtIT4yeQUxAaLrZwAoqGRKGk7qDSYTfYQ8LuYnAAAAAElFTkSuQmCC
[azure-badge]:https://img.shields.io/azure-devops/build/abhiuna12/942b3b13-d745-49e9-8d7d-b3918ff43ac2/3/master?logo=azure-pipelines&style=for-the-badge
[pypi-badge]:https://img.shields.io/pypi/v/deffcode.svg?style=for-the-badge&logo=pypi
[gitter-bagde]:https://img.shields.io/badge/Chat-Gitter-blueviolet.svg?style=for-the-badge&logo=gitter
[Coffee-badge]:https://abhitronix.github.io/img/deffcode/orange_img.png
[kofi-badge]:https://www.ko-fi.com/img/githubbutton_sm.svg
[black-badge]:https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge&logo=github
<!--
Internal URLs
-->
[docs]:https://abhitronix.github.io/deffcode/latest/
[install]:https://abhitronix.github.io/deffcode/latest/installation/
[release]:https://github.com/abhiTronix/deffcode/releases/latest
[license]:https://github.com/abhiTronix/deffcode/blob/master/LICENSE
[github-flow]:https://github.com/abhiTronix/deffcode/actions/workflows/CIlinux.yml
[azure-pipeline]:https://dev.azure.com/abhiuna12/public/_build?definitionId=3
[app]:https://ci.appveyor.com/project/abhiTronix/deffcode
[code]:https://codecov.io/gh/abhiTronix/deffcode
[black]: https://github.com/psf/black
<!--
External URLs
-->
[ffmpeg]:https://www.ffmpeg.org/
[pypi]:https://pypi.org/project/deffcode/
[gitter]:https://gitter.im/deffcode-python/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge
[coffee]:https://www.buymeacoffee.com/2twOXFvlA
[kofi]: https://ko-fi.com/W7W8WTYO
"""
===============================================
deffcode library source-code is deployed under the Apache 2.0 License:
DeFFcode library source-code is deployed under the Apache 2.0 License:

@@ -44,3 +44,3 @@ Copyright (c) 2021 Abhishek Thakur(@abhiTronix) <abhi.una12@gmail.com>

version=pkg_version["__version__"],
description="Performant Pythonic FFmpeg Decoder with easy to adapt flexible API.",
description="High-performance Real-time Video frames Generator for generating blazingly fast video frames in python.",
license="Apache License 2.0",

@@ -47,0 +47,0 @@ author="Abhishek Thakur",