Skip to content

Commit

Permalink
update 0.1.1
Browse files Browse the repository at this point in the history
renew almost all files
  • Loading branch information
aminyazdanpanah committed Apr 20, 2020
1 parent 5b857f1 commit 5d90aca
Show file tree
Hide file tree
Showing 13 changed files with 263 additions and 16 deletions.
28 changes: 13 additions & 15 deletions README.md
Expand Up @@ -53,7 +53,7 @@ import ffmpeg_streaming
There are several ways to open a resource.

#### 1. From a FFmpeg supported resource
You can pass a local path of video(or a supported resource) to the `open` method:
You can pass a local path of video(or a supported resource) to the `input` method:
```python
video = ffmpeg_streaming.input('/var/media/video.mp4')
```
Expand All @@ -66,7 +66,7 @@ video = ffmpeg_streaming.input('https://www.aminyazdanpanah.com/?"PATH TO A VIDE
```

#### 2. From Clouds
You can open a file from a cloud by passing an array of cloud configuration to the `openFromCloud` method.
You can open a file from a cloud by passing an instance of a cloud configuration to the `input` method.

```python
from ffmpeg_streaming import S3
Expand All @@ -77,10 +77,10 @@ video = ffmpeg_streaming.input(s3, bucket_name="bucket-name", key="video.mp4")
Visit **[this page](https://video.aminyazdanpanah.com/python/start/clouds?r=open)** to see some examples of opening a file from **[Amazon S3](https://aws.amazon.com/s3)**, **[Google Cloud Storage](https://console.cloud.google.com/storage)**, **[Microsoft Azure Storage](https://azure.microsoft.com/en-us/features/storage-explorer/)**, and a custom cloud.

#### 3. Capture Webcam or Screen (Live Streaming)
You can pass a name of the supported, connected capture device(i.e. name of webcam, camera, screen and etc) to the `capture` method to stream a live media over network.
You can pass a name of the supported, connected capture device(i.e. name of webcam, camera, screen and etc) to the `input` method to stream a live media over network.

```python
video = ffmpeg_streaming.input('CAMERA NAME OR SCREEN NAME', capture=True)
capture = ffmpeg_streaming.input('CAMERA NAME OR SCREEN NAME', capture=True)
```
To list the supported, connected capture devices, see **[FFmpeg Capture Webcam](https://trac.ffmpeg.org/wiki/Capture/Webcam)** and **[FFmpeg Capture Desktop](https://trac.ffmpeg.org/wiki/Capture/Desktop)**.

Expand Down Expand Up @@ -134,7 +134,7 @@ _360p = Representation(Size(640, 360), Bitrate(276 * 1024, 128 * 1024))
_480p = Representation(Size(854, 480), Bitrate(750 * 1024, 192 * 1024))
_720p = Representation(Size(1280, 720), Bitrate(2048 * 1024, 320 * 1024))

hls = video.hls(Formats.hevc())
hls = video.hls(Formats.h264())
hls.representations(_360p, _480p, _720p)
hls.output('/var/media/hls.m3u8')
```
Expand Down Expand Up @@ -174,7 +174,7 @@ See **[the example](https://video.aminyazdanpanah.com/python/start?r=enc-hls#hls
##### DRM
However FFmpeg supports AES encryption for HLS packaging, which you can encrypt your content, it is not a full **[DRM](https://en.wikipedia.org/wiki/Digital_rights_management)** solution. If you want to use a full DRM solution, I recommend trying **[FairPlay Streaming](https://developer.apple.com/streaming/fps/)** solution which then securely exchange keys, and protect playback on devices.

**[Apple鈥檚 FairPlay](https://developer.apple.com/streaming/fps/)** is a recommended DRM system, but you can use other DRM systems such as **[Microsoft's PlayReady](https://www.microsoft.com/playready/overview/)** and **[Google鈥檚 Widevine](https://www.widevine.com/)**.
**Besides [Apple鈥檚 FairPlay](https://developer.apple.com/streaming/fps/)** DRM system, you can also use other DRM systems such as **[Microsoft's PlayReady](https://www.microsoft.com/playready/overview/)** and **[Google鈥檚 Widevine](https://www.widevine.com/)**.

### Transcoding
You can get realtime information about the transcoding using the following code.
Expand Down Expand Up @@ -208,19 +208,19 @@ dash.auto_generate_representations()

dash.output('/var/media/dash.mpd')
```
It can also be null. The default path to save files is the input path.
``` python
It can also be None. The default path to save files is the input path.
```python
from ffmpeg_streaming import Formats

hls = video.hls(Formats.h264())
hls.auto_generate_representations()

hls.output()
```
**NOTE:** If you open a file from a cloud and do not pass a path to save the file to your local machine, you will have to pass a local path to the `save` method.
**NOTE:** If you open a file from a cloud and do not pass a path to save the file to your local machine, you will have to pass a local path to the `output` method.

#### 2. To Clouds
You can save your files to a cloud by passing an array of cloud configuration to the `save` method.
You can save your files to a cloud by passing an instance of a `CloudManager` to the `output` method.

```python
from ffmpeg_streaming import S3, CloudManager
Expand All @@ -244,9 +244,8 @@ Visit **[this page](https://video.aminyazdanpanah.com/python/start/clouds?r=save
<p align="center"><img src="https://github.com/aminyazdanpanah/aminyazdanpanah.github.io/blob/master/video-streaming/video-streaming.gif?raw=true" width="100%"></p>

#### 3. To a Server Instantly
You can pass a url(or a supported resource like `ftp`) to live method to upload all the segments files to the HTTP server(or other protocols) using the HTTP PUT method, and update the manifest files every refresh times.
You can pass a url(or a supported resource like `ftp`) to the `output` method to upload all the segments files to the HTTP server(or other protocols) using the HTTP PUT method, and update the manifest files every refresh times.

If you want to save stream files to your local machine, use the `save` method.

```python
# DASH
Expand All @@ -255,7 +254,7 @@ dash.output('http://YOUR-WEBSITE.COM/live-stream/out.mpd')
# HLS
hls.output('http://YOUR-WEBSITE.COM/live-stream/out.m3u8')
```
**NOTE:** In the HLS format, you must upload the master playlist to the server manually. (Upload the `/var/www/stream/live-master-manifest.m3u8` file to the `http://YOUR-WEBSITE.COM`)
**NOTE:** In the HLS format, you must upload the master playlist to the server manually.

See **[FFmpeg Protocols Documentation](https://ffmpeg.org/ffmpeg-protocols.html)** for more information.

Expand All @@ -270,7 +269,7 @@ ffprobe = FFProbe('/var/media/video.mp4')
See **[the example](https://video.aminyazdanpanah.com/python/start?r=metadata#metadata)** for more information.

### Conversion
You can convert your stream to a file or to another stream protocols. You should pass a manifest of the stream to the `open` method:
You can convert your stream to a file or to another stream protocols. You should pass a manifest of the stream to the `input` method:

#### 1. HLS To DASH
```python
Expand Down Expand Up @@ -300,7 +299,6 @@ video = ffmpeg_streaming.input('https://www.aminyazdanpanah.com/?PATH/TO/MANIFES

stream = video.stream2file(Formats.h264())
stream.output('/var/media/new-video.mp4')

```

## Several Open Source Players
Expand Down
15 changes: 15 additions & 0 deletions ffmpeg_streaming/_clouds.py
Expand Up @@ -19,6 +19,9 @@


class Clouds(abc.ABC):
"""
@TODO: add documentation
"""
@abc.abstractmethod
def upload_directory(self, directory: str, **options) -> None:
pass
Expand All @@ -30,6 +33,9 @@ def download(self, filename: str = None, **options) -> str:

class S3(Clouds):
def __init__(self, **options):
"""
@TODO: add documentation
"""
try:
import boto3
from botocore.exceptions import ClientError
Expand Down Expand Up @@ -81,6 +87,9 @@ def download(self, filename=None, **options):

class GCS(Clouds):
def __init__(self, **options):
"""
@TODO: add documentation
"""
try:
from google.cloud import storage
except ImportError as e:
Expand Down Expand Up @@ -124,6 +133,9 @@ def download(self, filename=None, **options):

class MAS(Clouds):
def __init__(self, **options):
"""
@TODO: add documentation
"""
try:
from azure.storage.blob import BlockBlobService
except ImportError as e:
Expand Down Expand Up @@ -170,6 +182,9 @@ def download(self, filename=None, **options):

class CloudManager:
def __init__(self):
"""
@TODO: add documentation
"""
self.clouds = []

def add(self, cloud: Clouds, **options):
Expand Down
11 changes: 10 additions & 1 deletion ffmpeg_streaming/_command_builder.py
Expand Up @@ -77,7 +77,7 @@ def _get_hls_stream(hls, rep, dirname, name):
'b:a': rep.bitrate.audio,
'maxrate': rep.bitrate.max_rate,
'hls_segment_filename': dirname + "/" + name + "_" + str(rep.size.height) + "p_%04d." + _hls_seg_ext(hls),
'hls_fmp4_init_filename': name + "_init.mp4",
'hls_fmp4_init_filename': name + "_" + str(rep.size.height) + "p_init.mp4",
'strict': '-2'
}
args.update(hls.options)
Expand All @@ -86,6 +86,9 @@ def _get_hls_stream(hls, rep, dirname, name):


def _hls(hls):
"""
@TODO: add documentation
"""
dirname, name = get_path_info(hls.output_)
streams = []
for rep in hls.reps:
Expand All @@ -95,10 +98,16 @@ def _hls(hls):


def stream_args(media):
"""
@TODO: add documentation
"""
return getattr(sys.modules[__name__], "_%s" % type(media).__name__.lower())(media)


def command_builder(ffmpeg_bin: str, media):
"""
@TODO: add documentation
"""
args = [ffmpeg_bin] + cnv_options_to_args(dict(media.media.input_opts)) + stream_args(media)
return " ".join(clean_args(args))

21 changes: 21 additions & 0 deletions ffmpeg_streaming/_format.py
Expand Up @@ -30,6 +30,9 @@ def _verify_codecs(codec, codecs):


class Format(abc.ABC):
"""
@TODO: add documentation
"""
def __init__(self, video: str, audio: str):
self.video = video
self.audio = audio
Expand All @@ -40,6 +43,9 @@ def multiply(self) -> int:


class H264(Format):
"""
@TODO: add documentation
"""
def __init__(self, video: str = "libx264", audio: str = 'copy'):
videos = ['libx264', 'h264', 'h264_afm']
audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac']
Expand All @@ -51,6 +57,9 @@ def multiply(self) -> int:


class HEVC(Format):
"""
@TODO: add documentation
"""
def __init__(self, video: str = "libx265", audio: str = 'copy'):
videos = ['libx265', 'h265']
audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac']
Expand All @@ -62,6 +71,9 @@ def multiply(self) -> int:


class VP9(Format):
"""
@TODO: add documentation
"""
def __init__(self, video: str = "libvpx-vp9", audio: str = 'copy'):
videos = ['libvpx', 'libvpx-vp9']
audios = ['copy', 'aac', 'libvo_aacenc', 'libfaac', 'libmp3lame', 'libfdk_aac']
Expand All @@ -75,14 +87,23 @@ def multiply(self) -> int:
class Formats:
@staticmethod
def h264(video: str = "libx264", audio: str = 'copy') -> Format:
"""
@TODO: add documentation
"""
return H264(video, audio)

@staticmethod
def hevc(video: str = "libx265", audio: str = 'copy') -> Format:
"""
@TODO: add documentation
"""
return HEVC(video, audio)

@staticmethod
def vp9(video: str = "libvpx-vp9", audio: str = 'copy') -> Format:
"""
@TODO: add documentation
"""
return VP9(video, audio)


Expand Down
9 changes: 9 additions & 0 deletions ffmpeg_streaming/_hls_helper.py
Expand Up @@ -19,6 +19,9 @@

class HLSKeyInfoFile:
def __init__(self, key_info_file_path: str, path: str, url: str, period: int = 0, needle: str = '', length: int = 16):
"""
@TODO: add documentation
"""
self.needle = needle
self.period = period
self.segments = []
Expand Down Expand Up @@ -71,6 +74,9 @@ def stream_info(rep) -> list:

class HLSMasterPlaylist:
def __init__(self, media):
"""
@TODO: add documentation
"""
self.media = media
self.path = media.output

Expand All @@ -81,6 +87,9 @@ def generate(cls, media):
playlist.write(cls(media)._content())

def _content(self) -> str:
"""
@TODO: add documentation
"""
content = ['#EXTM3U'] + self._get_version() + self.media.options.get('description', [])

for rep in self.media.reps:
Expand Down
9 changes: 9 additions & 0 deletions ffmpeg_streaming/_input.py
Expand Up @@ -17,6 +17,9 @@

class Capture(object):
def __init__(self, video, options):
"""
@TODO: add documentation
"""
self.options = options
self.video = video

Expand Down Expand Up @@ -58,6 +61,9 @@ def __iter__(self):


def get_from_cloud(cloud: Clouds, options: dict):
"""
@TODO: add documentation
"""
save_to = options.pop('save_to', None)
return {
'i': cloud.download(save_to, **options),
Expand All @@ -67,6 +73,9 @@ def get_from_cloud(cloud: Clouds, options: dict):

class InputOption(object):
def __init__(self, _input, **options):
"""
@TODO: add documentation
"""
self.input_ = _input
self.options = options

Expand Down

0 comments on commit 5d90aca

Please sign in to comment.