mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-11 17:47:37 +03:00
new vod endpoint for clips to set discontinuity
ensure tracking-detail playlists emit #EXT-X-DISCONTINUITY (avoids fMP4 timestamp rewrites and playback stalls) while leaving standard recordings behavior unchanged
This commit is contained in:
parent
d0febd0460
commit
bccd45c052
@ -837,7 +837,19 @@ async def recording_clip(
|
|||||||
dependencies=[Depends(require_camera_access)],
|
dependencies=[Depends(require_camera_access)],
|
||||||
description="Returns an HLS playlist for the specified timestamp-range on the specified camera. Append /master.m3u8 or /index.m3u8 for HLS playback.",
|
description="Returns an HLS playlist for the specified timestamp-range on the specified camera. Append /master.m3u8 or /index.m3u8 for HLS playback.",
|
||||||
)
|
)
|
||||||
async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
|
async def vod_ts(
|
||||||
|
camera_name: str,
|
||||||
|
start_ts: float,
|
||||||
|
end_ts: float,
|
||||||
|
force_discontinuity: bool = False,
|
||||||
|
):
|
||||||
|
logger.debug(
|
||||||
|
"VOD: Generating VOD for %s from %s to %s with force_discontinuity=%s",
|
||||||
|
camera_name,
|
||||||
|
start_ts,
|
||||||
|
end_ts,
|
||||||
|
force_discontinuity,
|
||||||
|
)
|
||||||
recordings = (
|
recordings = (
|
||||||
Recordings.select(
|
Recordings.select(
|
||||||
Recordings.path,
|
Recordings.path,
|
||||||
@ -862,6 +874,14 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
|
|||||||
|
|
||||||
recording: Recordings
|
recording: Recordings
|
||||||
for recording in recordings:
|
for recording in recordings:
|
||||||
|
logger.debug(
|
||||||
|
"VOD: processing recording: %s start=%s end=%s duration=%s",
|
||||||
|
recording.path,
|
||||||
|
recording.start_time,
|
||||||
|
recording.end_time,
|
||||||
|
recording.duration,
|
||||||
|
)
|
||||||
|
|
||||||
clip = {"type": "source", "path": recording.path}
|
clip = {"type": "source", "path": recording.path}
|
||||||
duration = int(recording.duration * 1000)
|
duration = int(recording.duration * 1000)
|
||||||
|
|
||||||
@ -870,6 +890,11 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
|
|||||||
inpoint = int((start_ts - recording.start_time) * 1000)
|
inpoint = int((start_ts - recording.start_time) * 1000)
|
||||||
clip["clipFrom"] = inpoint
|
clip["clipFrom"] = inpoint
|
||||||
duration -= inpoint
|
duration -= inpoint
|
||||||
|
logger.debug(
|
||||||
|
"VOD: applied clipFrom %sms to %s",
|
||||||
|
inpoint,
|
||||||
|
recording.path,
|
||||||
|
)
|
||||||
|
|
||||||
# adjust end if recording.end_time is after end_ts
|
# adjust end if recording.end_time is after end_ts
|
||||||
if recording.end_time > end_ts:
|
if recording.end_time > end_ts:
|
||||||
@ -877,12 +902,23 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
|
|||||||
|
|
||||||
if duration < min_duration_ms:
|
if duration < min_duration_ms:
|
||||||
# skip if the clip has no valid duration (too short to contain frames)
|
# skip if the clip has no valid duration (too short to contain frames)
|
||||||
|
logger.debug(
|
||||||
|
"VOD: skipping recording %s - resulting duration %sms too short",
|
||||||
|
recording.path,
|
||||||
|
duration,
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if min_duration_ms <= duration < max_duration_ms:
|
if min_duration_ms <= duration < max_duration_ms:
|
||||||
clip["keyFrameDurations"] = [duration]
|
clip["keyFrameDurations"] = [duration]
|
||||||
clips.append(clip)
|
clips.append(clip)
|
||||||
durations.append(duration)
|
durations.append(duration)
|
||||||
|
logger.debug(
|
||||||
|
"VOD: added clip %s duration_ms=%s clipFrom=%s",
|
||||||
|
recording.path,
|
||||||
|
duration,
|
||||||
|
clip.get("clipFrom"),
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Recording clip is missing or empty: {recording.path}")
|
logger.warning(f"Recording clip is missing or empty: {recording.path}")
|
||||||
|
|
||||||
@ -902,7 +938,7 @@ async def vod_ts(camera_name: str, start_ts: float, end_ts: float):
|
|||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={
|
content={
|
||||||
"cache": hour_ago.timestamp() > start_ts,
|
"cache": hour_ago.timestamp() > start_ts,
|
||||||
"discontinuity": False,
|
"discontinuity": force_discontinuity,
|
||||||
"consistentSequenceMediaInfo": True,
|
"consistentSequenceMediaInfo": True,
|
||||||
"durations": durations,
|
"durations": durations,
|
||||||
"segment_duration": max(durations),
|
"segment_duration": max(durations),
|
||||||
@ -986,6 +1022,19 @@ async def vod_event(
|
|||||||
return vod_response
|
return vod_response
|
||||||
|
|
||||||
|
|
||||||
|
@router.get(
|
||||||
|
"/vod/clip/{camera_name}/start/{start_ts}/end/{end_ts}",
|
||||||
|
dependencies=[Depends(require_camera_access)],
|
||||||
|
description="Returns an HLS playlist for a timestamp range with HLS discontinuity enabled. Append /master.m3u8 or /index.m3u8 for HLS playback.",
|
||||||
|
)
|
||||||
|
async def vod_clip(
|
||||||
|
camera_name: str,
|
||||||
|
start_ts: float,
|
||||||
|
end_ts: float,
|
||||||
|
):
|
||||||
|
return await vod_ts(camera_name, start_ts, end_ts, force_discontinuity=True)
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
"/events/{event_id}/snapshot.jpg",
|
"/events/{event_id}/snapshot.jpg",
|
||||||
description="Returns a snapshot image for the specified object id. NOTE: The query params only take affect while the event is in-progress. Once the event has ended the snapshot configuration is used.",
|
description="Returns a snapshot image for the specified object id. NOTE: The query params only take affect while the event is in-progress. Once the event has ended the snapshot configuration is used.",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user