summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYves Fischer <yvesf-git@xapek.org>2015-12-31 00:07:40 +0100
committerYves Fischer <yvesf-git@xapek.org>2016-01-08 20:38:18 +0100
commitd0158aa0c5f13ce55f64fa7c3029171d6bfe304f (patch)
tree5bf2a988dba6e3f87b864d384d9a87213fe23cf1
parent48f917e478ed365e58f6880a90dd00be120fcc83 (diff)
downloadflask-mediabrowser-d0158aa0c5f13ce55f64fa7c3029171d6bfe304f.tar.gz
flask-mediabrowser-d0158aa0c5f13ce55f64fa7c3029171d6bfe304f.zip
encode only UNTIL the next keyframe not INCLUDING
fix handling of last segment (end of file) in m3u8 playlist remove assumedly unneeded -async 1
-rw-r--r--README.md3
-rw-r--r--mediabrowser/__init__.py17
-rw-r--r--mediabrowser/ffmpeg.py35
3 files changed, 36 insertions, 19 deletions
diff --git a/README.md b/README.md
index 5083878..c67a24b 100644
--- a/README.md
+++ b/README.md
@@ -9,9 +9,6 @@ This webapplication serves the following purpose:
The chunking is done using ffmpeg's `-ss` and `-t` option.
This doesn't work properly on some video files.
-Also with some files we get chrome errors about audio-splicing with can lead to the point where the browser suddenly
-stops playback.
-
# Compatibility
The video stream is encoded as h.264 + AAC stream. Tested with
diff --git a/mediabrowser/__init__.py b/mediabrowser/__init__.py
index 0bdc21d..855c3e0 100644
--- a/mediabrowser/__init__.py
+++ b/mediabrowser/__init__.py
@@ -93,10 +93,21 @@ def build(root_directory, cache):
def stream(ss, t, path):
path = os.path.normpath(path)
ospath = os.path.join(root_directory, path)
+ data = ffprobe(ospath)
+ duration = float(data['format']['duration'])
# cut at next key frame after given time 'ss'
- new_ss = ffmpeg.find_next_keyframe(ospath, ss, t / 2)
- # find next key frame after given time 't'
- new_t = ffmpeg.find_next_keyframe(ospath, ss + t, t / 2) - new_ss
+ _, new_ss = ffmpeg.find_next_keyframe(ospath, ss, t / 2)
+
+ if ss + t * 2 > duration:
+ # encode all remain frames at once
+ new_t = duration - new_ss
+ else:
+ # find next key frame after given time 't'
+ new_t_prev_duration, new_t = ffmpeg.find_next_keyframe(ospath, ss + t, t / 2)
+ new_t -= new_ss
+ # minus one frame
+ new_t -= new_t_prev_duration
+
process = ffmpeg.stream(ospath, new_ss, new_t)
return Response(process.stdout, mimetype='video/MP2T')
diff --git a/mediabrowser/ffmpeg.py b/mediabrowser/ffmpeg.py
index ba4ee28..841c203 100644
--- a/mediabrowser/ffmpeg.py
+++ b/mediabrowser/ffmpeg.py
@@ -29,15 +29,15 @@ def ffprobe_data(ospath):
def stream(ospath, ss, t):
logging.info('start ffmpeg stream h264 480p on path=%s ss=%s t=%s', ospath, ss, t)
t_2 = t + 2.0
+ output_ts_offset = ss
cutter = LoggedPopen(
- shlex.split("ffmpeg -ss {ss:.6f} -async 1 -i ".format(**locals())) +
+ shlex.split("ffmpeg -ss {ss:.6f} -i ".format(**locals())) +
[ospath] +
shlex.split("-c:a aac -strict experimental -ac 2 -b:a 44k"
- ""
" -c:v libx264 -pix_fmt yuv420p -profile:v high -level 4.0 -preset ultrafast -trellis 0"
" -crf 31 -vf scale=w=trunc(oh*a/2)*2:h=360"
- ""
- " -f mpegts -output_ts_offset {ss:.6f} -t {t:.6f} pipe:%d.ts".format(**locals())),
+ " -f mpegts"
+ " -output_ts_offset {output_ts_offset:.6f} -t {t:.6f} pipe:%d.ts".format(**locals())),
stdout=PIPE, stderr=DEVNULL)
return cutter
@@ -46,41 +46,50 @@ def find_next_keyframe(ospath, start, max_offset):
"""
:param: start: start search pts as float '123.123'
:param: max_offset: max offset after start as float
- :return: PTS of next iframe but not search longer than max_offset
+ :return: (prev_duration, pts):
+ prev_duration: duration of the frame previous to the found key-frame
+ pts: PTS of next iframe but not search longer than max_offset
+ :raise: Exception if no keyframe found
"""
logging.info("start ffprobe to find next i-frame from {}".format(start))
if start == 0.0:
- return 0.0
+ logging.info("return (0.0, 0.0) for start == 0.0")
+ return 0.0, 0.0
process = LoggedPopen(
shlex.split("ffprobe -read_intervals {start:.6f}%+{max_offset:.6f} -show_frames "
"-select_streams v -print_format flat".format(**locals())) + [ospath],
stdout=PIPE, stderr=DEVNULL)
data = {'frame': None}
+ prev_duration = None
try:
line = process.stdout.readline()
while line:
frame, name, value = re.match('frames\\.frame\\.(\d+)\\.([^=]*)=(.*)', line.decode('ascii')).groups()
if data['frame'] != frame:
data.clear()
+ prev_duration = None
data['frame'] = frame
data[name] = value
- if 'key_frame' in data and data['key_frame'] == '1':
+
+ if 'pkt_duration_time' in data:
+ prev_duration = float(data['pkt_duration_time'][1:-1])
+
+ if 'key_frame' in data and data['key_frame'] == '1' and prev_duration is not None:
if 'pkt_pts_time' in data and data['pkt_pts_time'][1:-1] != 'N/A' and float(
data['pkt_pts_time'][1:-1]) > start:
- logging.info("Found pkt_pts_time={}".format(data['pkt_pts_time']))
- return float(data['pkt_pts_time'][1:-1])
+ logging.info("Found pkt_pts_time={} prev__duration={}".format(data['pkt_pts_time'], prev_duration))
+ return prev_duration, float(data['pkt_pts_time'][1:-1])
elif 'pkt_dts_time' in data and data['pkt_dts_time'][1:-1] != 'N/A' and float(
data['pkt_dts_time'][1:-1]) > start:
- logging.info("Found pkt_dts_time={}".format(data['pkt_dts_time']))
- return float(data['pkt_dts_time'][1:-1])
+ logging.info("Found pkt_dts_time={} prev_duration={}".format(data['pkt_dts_time'], prev_duration))
+ return prev_duration, float(data['pkt_dts_time'][1:-1])
line = process.stdout.readline()
- raise Exception("Failed to find next i-frame in {} .. {} of {}".format(start, max_offset, ospath))
+ raise Exception("Failed to find next i-frame in {} .. {} of {}".format(start, start + max_offset, ospath))
finally:
process.stdout.close()
process.terminate()
- logging.info("finished ffprobe to find next i-frame from {}".format(start))
def calculate_splittimes(ospath, chunk_duration):