Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 64 additions & 27 deletions patch/ffmpeg/ffmpeg-hls-kazumi-combined.patch
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
diff --git a/libavformat/hls.c b/libavformat/hls.c
index 900e96c..f6d1a80 100644
index 900e96c..5bf01f7 100644
--- a/libavformat/hls.c
+++ b/libavformat/hls.c
@@ -84,6 +84,8 @@ struct segment {
Expand Down Expand Up @@ -76,39 +76,65 @@ index 900e96c..f6d1a80 100644

if ((ret = ffio_copy_url_options(s->pb, &c->avio_opts)) < 0)
return ret;
@@ -2186,7 +2211,30 @@ static int hls_read_header(AVFormatContext *s)
@@ -2186,7 +2211,56 @@ static int hls_read_header(AVFormatContext *s)
pls->ctx->max_analyze_duration = s->max_analyze_duration > 0 ? s->max_analyze_duration : 4 * AV_TIME_BASE;
pls->ctx->interrupt_callback = s->interrupt_callback;
url = av_strdup(pls->segments[0]->url);
- ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, 0, 0);
+ unsigned skip = 0;
+ if (!c->seg_allow_img) {
+ uint8_t b[10] = { 0 }; // probe at most 10
+ avio_read(&pls->pb.pub, b, sizeof(b));
+ avio_seek(&pls->pb.pub, 0, SEEK_SET);
+ const AVProbeData pd = {
+ .buf = b, // png, gif read_probe only use this field
+ .buf_size = sizeof(b),
+ };
+// optional: ffifmt(av_find_input_format("gif" or "gif_pipe" or "png_pipe"))->read_probe
+ int max_score = AVPROBE_SCORE_MAX - 2; // png_pipe, gif, gif_pipe score >= AVPROBE_SCORE_MAX - 1
+ const AVInputFormat* img_fmt = av_probe_input_format2(&pd, 1, &max_score);
+ if (img_fmt) {
+ if (av_strstart(img_fmt->name, "png", NULL)) { // "png_pipe"
+ skip = 3; // skip until ts sync byte 'G'(0x47)
+ av_log(s, AV_LOG_INFO, "segments pretend to be png\n");
+ } else if (av_strstart(img_fmt->name, "gif", NULL)) { // "gif", "gif_pipe"
+ skip = 10;
+ av_log(s, AV_LOG_INFO, "segments pretend to be gif\n");
+ /* Peek at the beginning of the first segment to detect
+ * png/gif-magic-pretending segments, then always restore the
+ * read position. When a fake image magic is found we do not
+ * assert the format ourselves: the generic probe is re-run with
+ * (a) an offset that breaks the image signature and (b) a NULL
+ * filename so that the .webp-style URL extension can no longer
+ * boost image demuxer scores. The probe then decides from the
+ * actual payload (e.g. mpegts via 0x47 + 188 alignment). This
+ * keeps the HLS layer free of format assumptions: malformed
+ * png/gif-looking garbage simply fails the probe at header time
+ * instead of being forced into mpegts. */
+ {
+ uint8_t b[10] = { 0 };
+ unsigned probe_offset = 0;
+ const char *probe_url = url;
+ int n = avio_read(&pls->pb.pub, b, sizeof(b));
+ if (n < 0 && n != AVERROR_EOF) {
+ /* propagate real I/O errors like av_probe_input_buffer2()
+ * does, instead of degrading them into a second probe */
+ ret = n;
+ av_log(s, AV_LOG_ERROR, "Failed to read segment header for probing: %s\n",
+ av_err2str(ret));
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
+ av_free(url);
+ return ret;
+ }
+ if (n == AVERROR_EOF)
+ n = 0;
+ if ((ret = avio_seek(&pls->pb.pub, 0, SEEK_SET)) < 0) {
+ av_log(s, AV_LOG_ERROR, "Failed to restore segment read position after signature check: %s\n",
+ av_err2str(ret));
+ avformat_free_context(pls->ctx);
+ pls->ctx = NULL;
+ av_free(url);
+ return ret;
+ }
+ if (!c->seg_allow_img) {
+ if (n >= 8 && !memcmp(b, "\x89PNG\r\n\x1a\n", 8)) {
+ probe_offset = 3; /* break the png signature for the probe */
+ probe_url = NULL; /* drop misleading extension scores */
+ av_log(s, AV_LOG_INFO, "Segment starts with png magic, probing without filename at offset %u\n", probe_offset);
+ } else if (n >= 6 && (!memcmp(b, "GIF87a", 6) || !memcmp(b, "GIF89a", 6))) {
+ probe_offset = 10; /* break the gif signature for the probe */
+ probe_url = NULL;
+ av_log(s, AV_LOG_INFO, "Segment starts with gif magic, probing without filename at offset %u\n", probe_offset);
+ }
+ }
+ ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, probe_url, NULL, probe_offset, 0);
+ }
+
+ ret = av_probe_input_buffer(&pls->pb.pub, &in_fmt, url, NULL, skip, 0);

for (int n = 0; n < pls->n_segments; n++)
if (ret >= 0)
@@ -2406,6 +2454,67 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
@@ -2406,6 +2480,67 @@ static int hls_read_packet(AVFormatContext *s, AVPacket *pkt)
}

seg = current_segment(pls);
Expand Down Expand Up @@ -176,7 +202,7 @@ index 900e96c..f6d1a80 100644
if (seg && seg->key_type == KEY_SAMPLE_AES && !strstr(pls->ctx->iformat->name, "mov")) {
enum AVCodecID codec_id = pls->ctx->streams[pls->pkt->stream_index]->codecpar->codec_id;
memcpy(c->crypto_ctx.iv, seg->iv, sizeof(seg->iv));
@@ -2519,6 +2628,9 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
@@ -2519,6 +2654,9 @@ static int hls_read_seek(AVFormatContext *s, int stream_index,
int64_t timestamp, int flags)
{
HLSContext *c = s->priv_data;
Expand All @@ -186,11 +212,22 @@ index 900e96c..f6d1a80 100644
struct playlist *seek_pls = NULL;
int i, j;
int stream_subdemuxer_index;
@@ -2683,6 +2795,10 @@ static const AVOption hls_options[] = {
@@ -2632,8 +2770,8 @@ static int hls_probe(const AVProbeData *p)
!mime_x &&
!av_match_ext (p->filename, "m3u8,m3u") &&
ff_match_url_ext(p->filename, "m3u8,m3u") <= 0) {
- av_log(NULL, AV_LOG_ERROR, "Not detecting m3u8/hls with non standard extension and non standard mime type\n");
- return 0;
+ av_log(NULL, AV_LOG_WARNING, "HLS playlist with non-standard extension/MIME, lowering probe score\n");
+ return AVPROBE_SCORE_MAX / 2;
}
if (mime_x)
av_log(NULL, AV_LOG_WARNING, "mime type is not rfc8216 compliant\n");
@@ -2683,6 +2821,10 @@ static const AVOption hls_options[] = {
OFFSET(seg_format_opts), AV_OPT_TYPE_DICT, {.str = NULL}, 0, 0, FLAGS},
{"seg_max_retry", "Maximum number of times to reload a segment on error.",
OFFSET(seg_max_retry), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, FLAGS},
+ {"seg_allow_img", "Allow segments detected as gif and png images, 0 = disable, 1 = enable",
+ {"seg_allow_img", "Allow PNG/GIF-signature HLS segments to participate in normal image probing; 0 = probe past image disguise, 1 = normal probing",
+ OFFSET(seg_allow_img), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS},
+ {"hls_ad_filter", "Filter ad segments based on PTS discontinuity (Kazumi)",
+ OFFSET(hls_ad_filter), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, FLAGS},
Expand Down
Loading