|
@@ -3432,52 +3432,65 @@ static int read_thread(void *arg)
|
|
|
ffp_toggle_buffering(ffp, 1);
|
|
ffp_toggle_buffering(ffp, 1);
|
|
|
ffp_notify_msg3(ffp, FFP_MSG_BUFFERING_UPDATE, 0, 0);
|
|
ffp_notify_msg3(ffp, FFP_MSG_BUFFERING_UPDATE, 0, 0);
|
|
|
|
|
|
|
|
- // Workaround for ASF/WMA seek crash (FFmpeg ff_seek_frame_binary uninitialized variable issue)
|
|
|
|
|
- // The FFmpeg libavformat has a bug in ff_seek_frame_binary with uninitialized pos_min/pos_max
|
|
|
|
|
- // For ASF format, we completely disable seek to prevent crash
|
|
|
|
|
int seek_flags = is->seek_flags;
|
|
int seek_flags = is->seek_flags;
|
|
|
|
|
|
|
|
- // Debug: print format name to diagnose ASF detection
|
|
|
|
|
const char *format_name = (ic->iformat && ic->iformat->name) ? ic->iformat->name : "unknown";
|
|
const char *format_name = (ic->iformat && ic->iformat->name) ? ic->iformat->name : "unknown";
|
|
|
av_log(ffp, AV_LOG_WARNING, "heanup seek: format_name=%s\n", format_name);
|
|
av_log(ffp, AV_LOG_WARNING, "heanup seek: format_name=%s\n", format_name);
|
|
|
|
|
|
|
|
- // Check for ASF format (WMA files use ASF container)
|
|
|
|
|
- // Also check for "asf_o" which is the output variant
|
|
|
|
|
int is_asf_format = (ic->iformat && ic->iformat->name &&
|
|
int is_asf_format = (ic->iformat && ic->iformat->name &&
|
|
|
(strcmp(ic->iformat->name, "asf") == 0 ||
|
|
(strcmp(ic->iformat->name, "asf") == 0 ||
|
|
|
strcmp(ic->iformat->name, "asf_o") == 0 ||
|
|
strcmp(ic->iformat->name, "asf_o") == 0 ||
|
|
|
strstr(ic->iformat->name, "asf") != NULL));
|
|
strstr(ic->iformat->name, "asf") != NULL));
|
|
|
|
|
+ int is_flac_format = (ic->iformat && ic->iformat->name &&
|
|
|
|
|
+ strstr(ic->iformat->name, "flac") != NULL);
|
|
|
|
|
+ int use_safe_byte_seek = is_asf_format;
|
|
|
|
|
+
|
|
|
|
|
+ if (use_safe_byte_seek) {
|
|
|
|
|
+ // Avoid avformat_seek_file for fragile formats on network streams.
|
|
|
|
|
+ const char *safe_seek_name = is_flac_format ? "FLAC" : "ASF";
|
|
|
|
|
+ if (!ic->pb || !(ic->pb->seekable & AVIO_SEEKABLE_NORMAL)) {
|
|
|
|
|
+ av_log(ffp, AV_LOG_WARNING, "heanup: %s stream is not byte-seekable, reject seek\n", safe_seek_name);
|
|
|
|
|
+ ret = -1;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ int64_t file_size = avio_size(ic->pb);
|
|
|
|
|
+ int64_t duration = ic->duration;
|
|
|
|
|
+ int64_t bounded_target = seek_target;
|
|
|
|
|
+
|
|
|
|
|
+ if (duration > 0) {
|
|
|
|
|
+ if (bounded_target < 0)
|
|
|
|
|
+ bounded_target = 0;
|
|
|
|
|
+ if (bounded_target > duration)
|
|
|
|
|
+ bounded_target = duration;
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- if (is_asf_format) {
|
|
|
|
|
- // ASF/WMA format: use alternative seek method to avoid ff_seek_frame_binary crash
|
|
|
|
|
- // The crash occurs because ff_seek_frame_binary has uninitialized pos_min/pos_max
|
|
|
|
|
- // Try using AVSEEK_FLAG_BYTE to force byte-based seeking instead of timestamp-based
|
|
|
|
|
- av_log(ffp, AV_LOG_WARNING, "heanup: ASF format, trying byte-based seek\n");
|
|
|
|
|
-
|
|
|
|
|
- // Calculate byte position based on duration and file size
|
|
|
|
|
- int64_t file_size = avio_size(ic->pb);
|
|
|
|
|
- int64_t duration = ic->duration;
|
|
|
|
|
-
|
|
|
|
|
- if (file_size > 0 && duration > 0) {
|
|
|
|
|
- // Convert seek_target (in AV_TIME_BASE) to byte position
|
|
|
|
|
- int64_t byte_pos = av_rescale(seek_target, file_size, duration);
|
|
|
|
|
- av_log(ffp, AV_LOG_WARNING, "heanup: ASF byte seek: target=%lld, file_size=%lld, duration=%lld, byte_pos=%lld\n",
|
|
|
|
|
- (long long)seek_target, (long long)file_size, (long long)duration, (long long)byte_pos);
|
|
|
|
|
-
|
|
|
|
|
- // Use av_seek_frame with AVSEEK_FLAG_BYTE for byte-based seeking
|
|
|
|
|
- ret = av_seek_frame(ic, -1, byte_pos, AVSEEK_FLAG_BYTE | AVSEEK_FLAG_BACKWARD);
|
|
|
|
|
-
|
|
|
|
|
- if (ret < 0) {
|
|
|
|
|
- av_log(ffp, AV_LOG_WARNING, "heanup: ASF byte seek failed, ret=%d\n", ret);
|
|
|
|
|
|
|
+ if (file_size > 1 && duration > 0 && bounded_target >= 0) {
|
|
|
|
|
+ int64_t byte_pos = av_rescale_rnd(bounded_target, file_size - 1, duration, AV_ROUND_DOWN);
|
|
|
|
|
+ if (byte_pos < 0)
|
|
|
|
|
+ byte_pos = 0;
|
|
|
|
|
+ if (byte_pos >= file_size)
|
|
|
|
|
+ byte_pos = file_size - 1;
|
|
|
|
|
+
|
|
|
|
|
+ av_log(ffp, AV_LOG_WARNING,
|
|
|
|
|
+ "heanup: %s safe byte seek: target=%lld, bounded_target=%lld, file_size=%lld, duration=%lld, byte_pos=%lld\n",
|
|
|
|
|
+ safe_seek_name, (long long)seek_target, (long long)bounded_target,
|
|
|
|
|
+ (long long)file_size, (long long)duration, (long long)byte_pos);
|
|
|
|
|
+ ret = av_seek_frame(ic, -1, byte_pos, AVSEEK_FLAG_BYTE | AVSEEK_FLAG_BACKWARD);
|
|
|
|
|
+ if (ret < 0) {
|
|
|
|
|
+ av_log(ffp, AV_LOG_WARNING, "heanup: %s safe byte seek failed, ret=%d\n", safe_seek_name, ret);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ av_log(ffp, AV_LOG_WARNING,
|
|
|
|
|
+ "heanup: %s cannot calculate safe byte position, file_size=%lld, duration=%lld, target=%lld\n",
|
|
|
|
|
+ safe_seek_name, (long long)file_size, (long long)duration, (long long)seek_target);
|
|
|
|
|
+ ret = -1;
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- av_log(ffp, AV_LOG_WARNING, "heanup: ASF cannot calculate byte position, file_size=%lld, duration=%lld\n",
|
|
|
|
|
- (long long)file_size, (long long)duration);
|
|
|
|
|
- ret = -1;
|
|
|
|
|
}
|
|
}
|
|
|
- // Don't continue here - let the code below handle flush if seek succeeded
|
|
|
|
|
} else {
|
|
} else {
|
|
|
- // Normal format: use avformat_seek_file
|
|
|
|
|
|
|
+ if (is_flac_format) {
|
|
|
|
|
+ // FLAC is VBR-like for byte-position mapping; forcing byte-seek can land mid-frame.
|
|
|
|
|
+ seek_flags &= ~AVSEEK_FLAG_BYTE;
|
|
|
|
|
+ seek_flags |= AVSEEK_FLAG_ANY;
|
|
|
|
|
+ }
|
|
|
ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, seek_flags);
|
|
ret = avformat_seek_file(is->ic, -1, seek_min, seek_target, seek_max, seek_flags);
|
|
|
}
|
|
}
|
|
|
|
|
|