Skip to content

Commit 23049ef

Browse files
Add time left to monitor callback method
Bug fixes and minor improvement
1 parent db420a2 commit 23049ef

12 files changed

Lines changed: 222 additions & 98 deletions

File tree

README.md

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -181,16 +181,50 @@ You can get realtime information about the transcoding using the following code.
181181
from ffmpeg_streaming import Formats
182182
import sys
183183

184-
def monitor(ffmpeg, duration, time_, process):
185-
# You can update a field in your database or log it to a file
186-
# You can also create a socket connection and show a progress bar to users
187-
# logging.info(ffmpeg) or print(ffmpeg)
188-
189-
# if "something happened":
190-
# process.terminate()
191-
184+
def monitor(ffmpeg, duration, time_, time_left, process):
185+
"""
186+
You can monitor ffmpeg command line and handle the process. For example, you can update a field in your database
187+
or log it to a file.
188+
189+
Examples:
190+
1. Logging or printing ffmpeg command
191+
logging.info(ffmpeg) or print(ffmpeg)
192+
193+
2. Handling Process object
194+
if "something happened":
195+
process.terminate()
196+
197+
3. Email someone to notice the time of finishing process
198+
if time_left > 3600 and not already_send: # if it takes more than one hour and you did not email them already
199+
Email.send(
200+
201+
subject='Your video will be ready at %s seconds' % time_left,
202+
message='Your video takes more than an hour ...'
203+
)
204+
already_send = True
205+
206+
4. Create a socket connection and show a progress bar(and other parameters) to users
207+
Socket.broadcast(
208+
address=127.0.0.1
209+
port=5050
210+
data={
211+
percentage = per,
212+
time_left = datetime.timedelta(seconds=int(time_left))
213+
}
214+
)
215+
216+
:param ffmpeg: ffmpeg command line
217+
:param duration: duration of the video
218+
:param time_: current time of transcoded video
219+
:param time_left: seconds left to finish the video process
220+
:param process: subprocess object
221+
:return: None
222+
"""
192223
per = round(time_ / duration * 100)
193-
sys.stdout.write("\rTranscoding...(%s%%) [%s%s]" % (per, '#' * per, '-' * (100 - per)))
224+
sys.stdout.write(
225+
"\rTranscoding...(%s%%) %s left [%s%s]" %
226+
(per, datetime.timedelta(seconds=int(time_left)), '#' * per, '-' * (100 - per))
227+
)
194228
sys.stdout.flush()
195229

196230
hls = video.hls(Formats.h264())
@@ -274,7 +308,7 @@ ffprobe = FFProbe('/var/media/video.mp4')
274308
See **[the example](https://video.aminyazdanpanah.com/python/start?r=metadata#metadata)** for more information.
275309

276310
### Conversion
277-
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:
311+
You can convert your stream to a file or to another stream protocol. You should pass a manifest of the stream to the `input` method:
278312

279313
#### 1. HLS To DASH
280314
```python
@@ -340,7 +374,7 @@ You can use these libraries to play your streams.
340374

341375
**NOTE-1:** You must pass a **link of the master playlist(manifest)**(i.e. `https://www.aminyazdanpanah.com/?"PATH TO STREAM DIRECTORY"/dash-stream.mpd` or `/PATH_TO_STREAM_DIRECTORY/hls-stream.m3u8` ) to these players.
342376

343-
**NOTE-2:** If you save your stream content to a cloud(i.e. **[Amazon S3](https://aws.amazon.com/s3)**), the link of your playlist and other content **MUST BE PUBLIC**.
377+
**NOTE-2:** If you save your stream content to a cloud(i.e. **[Amazon S3](https://aws.amazon.com/s3)**), the link of your master playlist and other contents **MUST BE PUBLIC**.
344378

345379
**NOTE-3:** As you may know, **[IOS](https://www.apple.com/ios)** does not have native support for DASH. Although there are some libraries such as **[Viblast](https://github.com/Viblast/ios-player-sdk)** and **[MPEGDASH-iOS-Player](https://github.com/MPEGDASHPlayer/MPEGDASH-iOS-Player)** to support this technique, I have never tested them. So maybe some of them will not work properly.
346380

examples/dash.py

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,58 @@
1414
import argparse
1515
import datetime
1616
import sys
17-
import time
1817
import logging
1918

2019
import ffmpeg_streaming
2120
from ffmpeg_streaming import Formats
2221

2322
logging.basicConfig(filename='streaming.log', level=logging.NOTSET, format='[%(asctime)s] %(levelname)s: %(message)s')
24-
start_time = time.time()
2523

2624

27-
def time_left(time_, total):
28-
if time_ != 0:
29-
diff_time = time.time() - start_time
30-
seconds_left = total * diff_time / time_ - diff_time
31-
time_left = str(datetime.timedelta(seconds=int(seconds_left))) + ' left'
32-
else:
33-
time_left = 'calculating...'
34-
35-
return time_left
36-
37-
38-
def monitor(ffmpeg, duration, time_, process):
39-
# You can update a field in your database or log it to a file
40-
# You can also create a socket connection and show a progress bar to users
41-
# logging.info(ffmpeg) or print(ffmpeg)
42-
43-
# if "something happened":
44-
# process.terminate()
45-
25+
def monitor(ffmpeg, duration, time_, time_left, process):
26+
"""
27+
You can monitor ffmpeg command line and handle the process. For example, you can update a field in your database
28+
or log it to a file.
29+
30+
Examples:
31+
1. Logging or printing ffmpeg command
32+
logging.info(ffmpeg) or print(ffmpeg)
33+
34+
2. Handling Process object
35+
if "something happened":
36+
process.terminate()
37+
38+
3. Email someone to notice the time of finishing process
39+
if time_left > 3600 and not already_send: # if it takes more than one hour and you did not email them already
40+
Email.send(
41+
42+
subject='Your video will be ready at %s seconds' % time_left,
43+
message='Your video takes more than an hour ...'
44+
)
45+
already_send = True
46+
47+
4. Create a socket connection and show a progress bar(and other parameters) to users
48+
Socket.broadcast(
49+
address=127.0.0.1
50+
port=5050
51+
data={
52+
percentage = per,
53+
time_left = datetime.timedelta(seconds=int(time_left))
54+
}
55+
)
56+
57+
:param ffmpeg: ffmpeg command line
58+
:param duration: duration of the video
59+
:param time_: current time of transcoded video
60+
:param time_left: seconds left to finish the video process
61+
:param process: subprocess object
62+
:return: None
63+
"""
4664
per = round(time_ / duration * 100)
4765
sys.stdout.write(
48-
"\rTranscoding...(%s%%) %s [%s%s]" % (per, time_left(time_, duration), '#' * per, '-' * (100 - per)))
66+
"\rTranscoding...(%s%%) %s left [%s%s]" %
67+
(per, datetime.timedelta(seconds=int(time_left)), '#' * per, '-' * (100 - per))
68+
)
4969
sys.stdout.flush()
5070

5171

examples/hls.py

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,58 @@
1414
import argparse
1515
import datetime
1616
import sys
17-
import time
1817
import logging
1918

2019
import ffmpeg_streaming
2120
from ffmpeg_streaming import Formats
2221

2322
logging.basicConfig(filename='streaming.log', level=logging.NOTSET, format='[%(asctime)s] %(levelname)s: %(message)s')
24-
start_time = time.time()
2523

2624

27-
def time_left(time_, total):
28-
if time_ != 0:
29-
diff_time = time.time() - start_time
30-
seconds_left = total * diff_time / time_ - diff_time
31-
time_left = str(datetime.timedelta(seconds=int(seconds_left))) + ' left'
32-
else:
33-
time_left = 'calculating...'
34-
35-
return time_left
36-
37-
38-
def monitor(ffmpeg, duration, time_, process):
39-
# You can update a field in your database or log it to a file
40-
# You can also create a socket connection and show a progress bar to users
41-
# logging.info(ffmpeg) or print(ffmpeg)
42-
43-
# if "something happened":
44-
# process.terminate()
45-
25+
def monitor(ffmpeg, duration, time_, time_left, process):
26+
"""
27+
You can monitor ffmpeg command line and handle the process. For example, you can update a field in your database
28+
or log it to a file.
29+
30+
Examples:
31+
1. Logging or printing ffmpeg command
32+
logging.info(ffmpeg) or print(ffmpeg)
33+
34+
2. Handling Process object
35+
if "something happened":
36+
process.terminate()
37+
38+
3. Email someone to notice the time of finishing process
39+
if time_left > 3600 and not already_send: # if it takes more than one hour and you did not email them already
40+
Email.send(
41+
42+
subject='Your video will be ready at %s seconds' % time_left,
43+
message='Your video takes more than an hour ...'
44+
)
45+
already_send = True
46+
47+
4. Create a socket connection and show a progress bar(and other parameters) to users
48+
Socket.broadcast(
49+
address=127.0.0.1
50+
port=5050
51+
data={
52+
percentage = per,
53+
time_left = datetime.timedelta(seconds=int(time_left))
54+
}
55+
)
56+
57+
:param ffmpeg: ffmpeg command line
58+
:param duration: duration of the video
59+
:param time_: current time of transcoded video
60+
:param time_left: seconds left to finish the video process
61+
:param process: subprocess object
62+
:return: None
63+
"""
4664
per = round(time_ / duration * 100)
47-
sys.stdout.write("\rTranscoding...(%s%%) %s [%s%s]" % (per, time_left(time_, duration), '#' * per, '-' * (100 - per)))
65+
sys.stdout.write(
66+
"\rTranscoding...(%s%%) %s left [%s%s]" %
67+
(per, datetime.timedelta(seconds=int(time_left)), '#' * per, '-' * (100 - per))
68+
)
4869
sys.stdout.flush()
4970

5071

ffmpeg_streaming/_clouds.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def upload_directory(self, directory, **options):
6161
logging.error(e)
6262
raise RuntimeError(e)
6363

64-
logging.info("The " + directory + "directory was uploaded to Amazon S3 successfully")
64+
logging.info("The " + directory + " directory was uploaded to Amazon S3 successfully")
6565

6666
def download(self, filename=None, **options):
6767
bucket_name = options.pop('bucket_name', None)

ffmpeg_streaming/_command_builder.py

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414

1515
from ._utiles import cnv_options_to_args, get_path_info, clean_args
1616

17+
# DASH default values
18+
USE_TIMELINE = 1
19+
USE_TEMPLATE = 1
20+
21+
# HLS default values
22+
HLS_LIST_SIZE = 0
23+
HLS_TIME = 10
24+
HLS_ALLOW_CACHE = 1
25+
1726

1827
def _stream2file(stream2file):
1928
"""
@@ -42,9 +51,9 @@ def _get_dash_stream(key, rep):
4251
@TODO: add documentation
4352
"""
4453
args = {
45-
'map': 0,
46-
's:v:' + str(key): rep.size.normalize,
47-
'b:v:' + str(key): rep.bitrate.calc_video()
54+
'map': 0,
55+
's:v:' + str(key): rep.size,
56+
'b:v:' + str(key): rep.bitrate.calc_video()
4857
}
4958
args.update(_get_audio_bitrate(rep, key))
5059
args.update(rep.options)
@@ -58,11 +67,11 @@ def _dash(dash):
5867
dirname, name = get_path_info(dash.output_)
5968
_args = dash.format.all
6069
_args.update({
61-
'use_timeline': 1,
62-
'use_template': 1,
63-
'init_seg_name': '{}_init_$RepresentationID$.$ext$'.format(name),
64-
"media_seg_name": '{}_chunk_$RepresentationID$_$Number%05d$.$ext$'.format(name),
65-
'f': 'dash'
70+
'use_timeline': USE_TIMELINE,
71+
'use_template': USE_TEMPLATE,
72+
'init_seg_name': '{}_init_$RepresentationID$.$ext$'.format(name),
73+
"media_seg_name": '{}_chunk_$RepresentationID$_$Number%05d$.$ext$'.format(name),
74+
'f': 'dash'
6675
})
6776
_args.update(dash.options)
6877
args = cnv_options_to_args(_args)
@@ -86,13 +95,13 @@ def _get_hls_stream(hls, rep, dirname, name):
8695
"""
8796
args = hls.format.all
8897
args.update({
89-
'hls_list_size': 0,
90-
'hls_time': 10,
91-
'hls_allow_cache': 1,
92-
'hls_segment_filename': "{}/{}_{}p_%04d.{}".format(dirname, name, rep.size.height, _hls_seg_ext(hls)),
93-
'hls_fmp4_init_filename': "{}_{}p_init.mp4".format(name, rep.size.height),
94-
's:v': rep.size.normalize,
95-
'b:v': rep.bitrate.calc_video()
98+
'hls_list_size': HLS_LIST_SIZE,
99+
'hls_time': HLS_TIME,
100+
'hls_allow_cache': HLS_ALLOW_CACHE,
101+
'hls_segment_filename': "{}/{}_{}p_%04d.{}".format(dirname, name, rep.size.height, _hls_seg_ext(hls)),
102+
'hls_fmp4_init_filename': "{}_{}p_init.mp4".format(name, rep.size.height),
103+
's:v': rep.size,
104+
'b:v': rep.bitrate.calc_video()
96105
})
97106
args.update(_get_audio_bitrate(rep))
98107
args.update(rep.options)
@@ -140,4 +149,3 @@ def command_builder(ffmpeg_bin: str, media):
140149
@TODO: add documentation
141150
"""
142151
return " ".join(clean_args([ffmpeg_bin] + input_args(media) + stream_args(media)))
143-

ffmpeg_streaming/_hls_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def stream_info(rep) -> list:
8585
tag = '#EXT-X-STREAM-INF:'
8686
info = [
8787
f'BANDWIDTH={rep.bitrate.calc_overall}',
88-
f'RESOLUTION={rep.size.normalize}',
88+
f'RESOLUTION={rep.size}',
8989
f'NAME="{rep.size.height}"'
9090
]
9191
custom = rep.options.pop('stream_info', [])

ffmpeg_streaming/_input.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
ffmpeg_streaming.media
2+
ffmpeg_streaming.input
33
~~~~~~~~~~~~
44
55
Input options

0 commit comments

Comments
 (0)