8000 Merge remote-tracking branch 'origin/master' into feature-80 · suryatmodulus/ffmpeg-python@0cc0bfa · GitHub
[go: up one dir, main page]

Skip to content

Commit 0cc0bfa

Browse files
committed
Merge remote-tracking branch 'origin/master' into feature-80
Conflicts: ffmpeg/_run.py ffmpeg/tests/test_ffmpeg.py
2 parents 0ed77a3 + 1681e8c commit 0cc0bfa

12 files changed

+428
-17
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ $ python
9595
>>> import ffmpeg
9696
```
9797

98+
## [Examples](https://github.com/kkroening/ffmpeg-python/tree/master/examples)
99+
100+
When in doubt, take a look at the [examples](https://github.com/kkroening/ffmpeg-python/tree/master/examples) to see if there's something that's close to whatever you're trying to do.
101+
98102
## [API Reference](https://kkroening.github.io/ffmpeg-python/)
99103

100104
API documentation is automatically generated from python docstrings and hosted on github pages: https://kkroening.github.io/ffmpeg-python/
@@ -140,6 +144,13 @@ Pull requests are welcome as well.
140144

141145
<br />
142146

147+
### Special thanks
148+
149+
- [Arne de Laat](https://github.com/153957)
150+
- [Davide Depau](https://github.com/depau)
151+
- [Dim](https://github.com/lloti)
152+
- [Noah Stier](https://github.com/noahstier)
153+
143154
## Additional Resources
144155

145156
- [API Reference](https://kkroening.github.io/ffmpeg-python/)

doc/jupyter-screenshot.png

461 KB
Loading

examples/README.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Examples
2+
3+
## [Get video info](https://github.com/kkroening/ffmpeg-python/blob/master/examples/video_info.py#L15)
4+
5+
```python
6+
probe = ffmpeg.probe(args.in_filename)
7+
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
8+
width = int(video_stream['width'])
9+
height = int(video_stream['height'])
10+
```
11+
12+
## [Convert video to numpy array](https://github.com/kkroening/ffmpeg-python/blob/master/examples/ffmpeg-numpy.ipynb)
13+
14+
```python
15+
out, _ = (
16+
ffmpeg
17+
.input('in.mp4')
18+
.output('pipe:', format='rawvideo', pix_fmt='rgb24')
19+
.run(capture_stdout=True)
20+
)
21+
video = (
22+
np
23+
.frombuffer(out, np.uint8)
24+
.reshape([-1, height, width, 3])
25+
)
26+
```
27+
28+
## [Generate thumbnail for video](https://github.com/kkroening/ffmpeg-python/blob/master/examples/get_video_thumbnail.py#L21)
29+
```python
30+
(
31+
ffmpeg
32+
.input(in_filename, ss=time)
33+
.filter_('scale', width, -1)
34+
.output(out_filename, vframes=1)
35+
.run()
36+
)
37+
```
38+
39+
## [Read single video frame as jpeg through pipe](https://github.com/kkroening/ffmpeg-python/blob/master/examples/read_frame_as_jpeg.py#L16)
40+
```python
41+
out, _ = (
42+
ffmpeg
43+
.input(in_filename)
44+
.filter_('select', 'gte(n,{})'.format(frame_num))
45+
.output('pipe:', vframes=1, format='image2', vcodec='mjpeg')
46+
.run(capture_output=True)
47+
)
48+
```
49+
50+
## [Convert sound to raw PCM audio](https://github.com/kkroening/ffmpeg-python/blob/master/examples/transcribe.py#L23)
51+
```python
52+
out, _ = (ffmpeg
53+
.input(in_filename, **input_kwargs)
54+
.output('-', format='s16le', acodec='pcm_s16le', ac=1, ar='16k')
55+
.overwrite_output()
56+
.run(capture_stdout=True)
57+
)
58+
```
59+
60+
## [JupyterLab/Notebook widgets](https://github.com/kkroening/ffmpeg-python/blob/master/examples/ffmpeg-numpy.ipynb)
61+
62+
<img src="https://raw.githubusercontent.com/kkroening/ffmpeg-python/master/doc/jupyter-screenshot.png" alt="jupyter screenshot" width="75%" />
63+

examples/ffmpeg-numpy.ipynb

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 116,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"from ipywidgets import interact\n",
10+
"from matplotlib import pyplot as plt\n",
11+
"import ffmpeg\n",
12+
"import ipywidgets as widgets\n",
13+
"import numpy as np"
14+
]
15+
},
16+
{
17+
"cell_type": "code",
18+
"execution_count": 117,
19+
"metadata": {},
20+
"outputs": [],
21+
"source": [
22+
"probe = ffmpeg.probe('in.mp4')\n",
23+
"video_info = next(stream for stream in probe['streams'] if stream['codec_type'] == 'video')\n",
24+
"width = int(video_info['width'])\n",
25+
"height = int(video_info['height'])\n",
26+
"num_frames = int(video_info['nb_frames'])"
27+
]
28+
},
29+
{
30+
"cell_type": "code",
31+
"execution_count": 118,
32+
"metadata": {},
33+
"outputs": [],
34+
"source": [
35+
"out, err = (\n",
36+
" ffmpeg\n",
37+
" .input('in.mp4')\n",
38+
" .output('pipe:', format='rawvideo', pix_fmt='rgb24')\n",
39+
" .run(capture_stdout=True)\n",
40+
")\n",
41+
"video = (\n",
42+
" np\n",
43+
" .frombuffer(out, np.uint8)\n",
44+
" .reshape([-1, height, width, 3])\n",
45+
")"
46+
]
47+
},
48+
{
49+
"cell_type": "code",
50+
"execution_count": 115,
51+
"metadata": {},
52+
"outputs": [
53+
{
54+
"data": {
55+
"application/vnd.jupyter.widget-view+json": {
56+
"model_id": "17d13d7551114fb39a1fad933cf0398a",
57+
"version_major": 2,
58+
"version_minor": 0
59+
},
60+
"text/plain": [
61+
"interactive(children=(IntSlider(value=0, description='frame', max=209), Output()), _dom_classes=('widget-inter…"
62+
]
63+
},
64+
"metadata": {},
65+
"output_type": "display_data"
66+
}
67+
],
68+
"source": [
69+
"@interact(frame=(0, num_frames))\n",
70+
"def show_frame(frame=0):\n",
71+
" plt.imshow(video[frame,:,:,:])"
72+
]
73+
},
74+
{
75+
"cell_type": "code",
76+
"execution_count": null,
77+
"metadata": {},
78+
"outputs": [],
79+
"source": []
80+
}
81+
],
82+
"metadata": {
83+
"kernelspec": {
84+
"display_name": "Python 3",
85+
"language": "python",
86+
"name": "python3"
87+
},
88+
"language_info": {
89+
"codemirror_mode": {
90+
"name": "ipython",
91+
"version": 3
92+
},
93+
"file_extension": ".py",
94+
"mimetype": "text/x-python",
95+
"name": "python",
96+
"nbconvert_exporter": "python",
97+
"pygments_lexer": "ipython3",
98+
"version": "3.6.4"
99+
}
100+
},
101+
"nbformat": 4,
102+
"nbformat_minor": 2
103+
}

examples/get_video_thumbnail.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/usr/bin/env python
2+
from __future__ import unicode_literals, print_function
3+
import argparse
4+
import ffmpeg
5+
import sys
6+
7+
8+
parser = argparse.ArgumentParser(description='Generate video thumbnail')
9+
parser.add_argument('in_filename', help='Input filename')
10+
parser.add_argument('out_filename', help='Output filename')
11+
parser.add_argument(
12+
'--time', type=int, default=0.1, help='Time offset')
13+
parser.add_argument(
14+
'--width', type=int, default=120,
15+
help='Width of output thumbnail (height automatically determined by aspect ratio)')
16+
17+
18+
def generate_thumbnail(in_filename, out_filename, time, width):
19+
try:
20+
(
21+
ffmpeg
22+
.input(in_filename, ss=time)
23+
.filter_('scale', width, -1)
24+
.output(out_filename, vframes=1)
25+
.overwrite_output()
26+
.run(capture_stdout=True, capture_stderr=True)
27+
)
28+
except ffmpeg.Error as e:
29+
print(e.stderr.decode(), file=sys.stderr)
30+
sys.exit(1)
31+
32+
33+
if __name__ == '__main__':
34+
args = parser.parse_args()
35+
generate_thumbnail(args.in_filename, args.out_filename, args.time, args.width)

examples/read_frame_as_jpeg.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python
2+
from __future__ import unicode_literals
3+
import argparse
4+
import ffmpeg
5+
import sys
6+
7+
8+
parser = argparse.ArgumentParser(
9+
description='Read individual video frame into memory as jpeg and write to stdout')
10+
parser.add_argument('in_filename', help='Input filename')
11+
parser.add_argument('frame_num', help='Frame number')
12+
13+
14+
def read_frame_as_jpeg(in_filename, frame_num):
15+
out, err = (
16+
ffmpeg
17+
.input(in_filename)
18+
.filter_('select', 'gte(n,{})'.format(frame_num))
19+
.output('pipe:', vframes=1, format='image2', vcodec='mjpeg')
20+
.run(capture_stdout=True)
21+
)
22+
return out
23+
24+
25+
if __name__ == '__main__':
26+
args = parser.parse_args()
27+
out = read_frame_as_jpeg(args.in_filename, args.frame_num)
28+
sys.stdout.buffer.write(out)

examples/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
11
ffmpeg-python
2+
gevent
23
google-cloud-speech
4+
tqdm

examples/show_progress.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python
2+
from __future__ import unicode_literals, print_function
3+
from tqdm import tqdm
4+
import argparse
5+
import contextlib
6+
import ffmpeg
7+
import gevent
8+
import gevent.monkey; gevent.monkey.patch_all(thread=False)
9+
import os
10+
import shutil
11+
import socket
12+
import sys
13+
import tempfile
14+
import textwrap
15+
16+
17+
parser = argparse.ArgumentParser(description=textwrap.dedent('''\
18+
Process video and report and show progress bar.
19+
20+
This is an example of using the ffmpeg `-progress` option with a
21+
unix-domain socket to report progress in the form of a progress
22+
bar.
23+
24+
The video processing simply consists of converting the video to
25+
sepia colors, but the same pattern can be applied to other use
26+
cases.
27+
'''))
28+
29+
parser.add_argument('in_filename', help='Input filename')
30+
parser.add_argument('out_filename', help='Output filename')
31+
32+
33+
@contextlib.contextmanager
34+
def _tmpdir_scope():
35+
tmpdir = tempfile.mkdtemp()
36+
try:
37+
yield tmpdir
38+
finally:
39+
shutil.rmtree(tmpdir)
40+
41+
42+
def _do_watch_progress(filename, sock, handler):
43+
"""Function to run in a separate gevent greenlet to read progress
44+
events from a unix-domain socket."""
45+
connection, client_address = sock.accept()
46+
data = b''
47+
try:
48+
while True:
49+
more_data = connection.recv(16)
50+
if not more_data:
51+
break
52+
data += more_data
53+
lines = data.split(b'\n')
54+
for line in lines[:-1]:
55+
line = line.decode()
56+
parts = line.split('=')
57+
key = parts[0] if len(parts) > 0 else None
58+
value = parts[1] if len(parts) > 1 else None
59+
handler(key, value)
60+
data = lines[-1]
61+
finally:
62+
connection.close()
63+
64+
65+
@contextlib.contextmanager
66+
def _watch_progress(handler):
67+
"""Context manager for creating a unix-domain socket and listen for
68+
ffmpeg progress events.
69+
70+
The socket filename is yielded from the context manager and the
71+
socket is closed when the context manager is exited.
72+
73+
Args:
74+
handler: a function to be called when progress events are
75+
received; receives a ``key`` argument and ``value``
76+
argument. (The example ``show_progress`` below uses tqdm)
77+
78+
Yields:
79+
socket_filename: the name of the socket file.
80+
"""
81+
with _tmpdir_scope() as tmpdir:
82+
socket_filename = os.path.join(tmpdir, 'sock')
83+
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
84+
with contextlib.closing(sock):
85+
sock.bind(socket_filename)
86+
sock.listen(1)
87+
child = gevent.spawn(_do_watch_progress, socket_filename, sock, handler)
88+
try:
89+
yield socket_filename
90+
except:
91+
gevent.kill(child)
92+
raise
93+
94+
95+
96+
@contextlib.contextmanager
97+
def show_progress(total_duration):
98+
"""Create a unix-domain socket to watch progress and render tqdm
99+
progress bar."""
100+
with tqdm(total=round(total_duration, 2)) as bar:
101+
def handler(key, value):
102+
if key == 'out_time_ms':
103+
time = round(float(value) / 1000000., 2)
104+
bar.update(time - bar.n)
105+
elif key == 'progress' and value == 'end':
106+
bar.update(bar.total - bar.n)
107+
with _watch_progress(handler) as socket_filename:
108+
yield socket_filename
109+
110+
111+
if __name__ == '__main__':
112+
args = parser.parse_args()
113+
total_duration = float(ffmpeg.probe(args.in_filename)['format']['duration'])
114+
115+
with show_progress(total_duration) as socket_filename:
116+
# See https://ffmpeg.org/ffmpeg-filters.html#Examples-44
117+
sepia_values = [.393, .769, .189, 0, .349, .686, .168, 0, .272, .534, .131]
118+
try:
119+
(ffmpeg
120+
.input(args.in_filename)
121+
.colorchannelmixer(*sepia_values)
122+
.output(args.out_filename)
123+
.global_args('-progress', 'unix://{}'.format(socket_filename))
124+
.overwrite_output()
125+
.run(capture_stdout=True, capture_stderr=True)
126+
)
127+
except ffmpeg.Error as e:
128+
print(e.stderr, file=sys.stderr)
129+
sys.exit(1)
130+

0 commit comments

Comments
 (0)
0