fix fps (alter tfhd and trun size, add logs

This commit is contained in:
2025-12-30 15:01:41 -05:00
parent a307f9b7e8
commit c0b352dc1d

View File

@@ -885,9 +885,11 @@ class HlsMuxer(
): ByteArray {
// Calculate sizes to determine data offset
val mfhdBox = buildMfhdBox(sequenceNumber)
val tfhdSize = 8 + 8 // box header + content (version/flags + track_id)
// tfhd: 8 header + 4 version/flags + 4 track_id + 4 duration + 4 size + 4 flags = 28 bytes
val tfhdSize = 8 + 20
val tfdtSize = 8 + 12 // box header + version 1 content
val trunSize = 8 + 12 + (samples.size * 12) // header + fixed + per-sample (no composition offset)
// trun: 8 header + 12 fixed + per-sample (size + flags only, no duration)
val trunSize = 8 + 12 + (samples.size * 8)
val trafSize = 8 + tfhdSize + tfdtSize + trunSize
val moofSize = 8 + mfhdBox.size + trafSize
@@ -922,9 +924,21 @@ class HlsMuxer(
val output = ByteArrayOutputStream()
val dos = DataOutputStream(output)
// Flags: default-base-is-moof (0x020000)
dos.writeInt(0x00020000)
// Calculate default sample duration for this fragment
// This helps ffprobe calculate correct frame rate when reading via HLS
val defaultSampleDuration = timescale / fps // e.g., 30000/30 = 1000
// Match iOS AVFoundation's tfhd structure (28 bytes total)
// Flags: default-base-is-moof (0x020000) + default-sample-duration (0x000008)
// + default-sample-size (0x000010) + default-sample-flags (0x000020)
val flags = 0x00020000 or 0x000008 or 0x000010 or 0x000020
dos.writeInt(flags)
dos.writeInt(1) // track ID
dos.writeInt(defaultSampleDuration) // default sample duration in timescale units
dos.writeInt(0) // default sample size (0 = variable, specified in trun)
dos.writeInt(0x01010000) // default sample flags (non-keyframe, depends on others)
Log.d(TAG, "tfhd: default_sample_duration=$defaultSampleDuration (timescale=$timescale, fps=$fps)")
return wrapBox("tfhd", output.toByteArray())
}
@@ -947,16 +961,17 @@ class HlsMuxer(
val output = ByteArrayOutputStream()
val dos = DataOutputStream(output)
// Flags: data-offset + sample-duration + sample-size + sample-flags
val flags = 0x000001 or 0x000100 or 0x000200 or 0x000400
// Flags: data-offset + sample-size + sample-flags
// NOTE: We intentionally OMIT sample-duration (0x000100) so ffprobe uses
// the default_sample_duration from tfhd instead of per-sample durations.
// This ensures consistent frame rate calculation via HLS.
val flags = 0x000001 or 0x000200 or 0x000400
dos.writeInt(flags)
dos.writeInt(samples.size)
dos.writeInt(dataOffset)
for (sample in samples) {
// Convert duration to timescale units
val durationInTimescale = ((sample.durationUs * timescale) / 1_000_000).toInt()
dos.writeInt(durationInTimescale)
// No duration - using default from tfhd
dos.writeInt(sample.data.size)
dos.writeInt(buildSampleFlags(sample.isKeyFrame))
}