Compare commits

...

1 Commits

Author SHA1 Message Date
Jacob Su
42ad02cf31 refine fmp4 trun box's enties
set the trun box entry[i].sample_flags by the the keyFrame.
set sample_flags, sample_depends_on and sample_is_non_sync_sample bits
according to ISO 14496-12 section 8.6.4.1 and 8.8.3.1
2025-12-07 16:12:13 +08:00
2 changed files with 207 additions and 6 deletions

View File

@ -5791,20 +5791,49 @@ srs_error_t SrsMp4SampleManager::write(SrsMp4TrackFragmentBox *traf, uint64_t dt
srs_error_t err = srs_success;
SrsMp4TrackFragmentRunBox *trun = traf->trun();
if (trun == NULL) {
trun = new SrsMp4TrackFragmentRunBox();
traf->set_trun(trun);
}
trun->flags_ = SrsMp4TrunFlagsDataOffset | SrsMp4TrunFlagsSampleDuration | SrsMp4TrunFlagsSampleSize | SrsMp4TrunFlagsSampleFlag | SrsMp4TrunFlagsSampleCtsOffset;
SrsMp4Sample *previous = NULL;
// ISO_IEC_14496-12-base-format-2012.pdf, 8.8.8.1 page 57
// Because trun->flags_ has not include SrsMp4TrunFlagsFirstSample(0x000004),
// so trun->first_sample_flags_ is not present, and each SrsMp4TrunEntry has sample_flags_.
// ISO_IEC_14496-12-base-format-2012.pdf, 8.8.3.1 page 53 define the sample_flags_'s layout.
// int(32) sample_flags = {
// bit(4) reserved = 0;
// int(2) is_leading;
// int(2) sample_depends_on;
// int(2) sample_is_depends_on;
// int(2) sample_has_redundancy;
// bit(3) sample_padding_value;
// bit(1) sample_is_non_sync_sample;
// int(16) sample_degradation_priority;
// }
// ISO_IEC_14496-12-base-format-2012.pdf, 8.6.4.1, 8.6.4.3 page 41 define the sample_flags_'s values.
// sample_depends_on == 1: this sample does depend on others;
// sample_depends_on == 2: this sample does not depend on others;
vector<SrsMp4Sample *>::iterator it;
for (it = samples_.begin(); it != samples_.end(); ++it) {
SrsMp4Sample *sample = *it;
SrsMp4TrunEntry *entry = new SrsMp4TrunEntry(trun);
if (!previous) {
previous = sample;
entry->sample_flags_ = 0x02000000;
if (sample->type_ == SrsFrameTypeVideo) {
if (sample->frame_type_ == SrsVideoAvcFrameTypeKeyFrame) {
// For video keyframe: no dependencies (sample_depends_on = 2), IS a sync sample (sample_is_non_sync_sample = 0)
// ISO 14496-12 section 8.8.3.1: sample_is_non_sync_sample = 0 means this IS a sync sample
// This provides equivalent information to being in a sync sample table (stss box)
entry->sample_flags_ = 0x02000000 | 0x00000000;
} else {
// For video non-keyframe: has dependencies (sample_depends_on = 1), NOT a sync sample (sample_is_non_sync_sample = 1)
// ISO 14496-12 section 8.8.3.1: sample_is_non_sync_sample = 1 means this is NOT a sync sample
entry->sample_flags_ = 0x01000000 | 0x00010000;
}
} else {
entry->sample_flags_ = 0x01000000;
// For audio, all samples are independent: no dependencies (sample_depends_on = 2), IS a sync sample (sample_is_non_sync_sample = 0)
// ISO 14496-12 section 8.8.3.1: sample_is_non_sync_sample = 0 means this IS a sync sample
entry->sample_flags_ = 0x02000000 | 0x00000000;
}
vector<SrsMp4Sample *>::iterator iter = (it + 1);

View File

@ -2595,3 +2595,175 @@ VOID TEST(KernelMp4Test, SrsMp4DvrJitter)
EXPECT_EQ(0, jitter.get_first_sample_delta(SrsFrameTypeAudio));
}
}
VOID TEST(KernelMp4Test, SrsMp4SampleManagerWrite)
{
srs_error_t err;
// Test with video keyframe samples
if (true) {
SrsMp4SampleManager manager;
SrsMp4TrackFragmentBox traf;
// Create a video keyframe sample
SrsMp4Sample *video_sample = new SrsMp4Sample();
video_sample->type_ = SrsFrameTypeVideo;
video_sample->dts_ = 1000;
video_sample->pts_ = 1000;
video_sample->tbn_ = 90000;
video_sample->frame_type_ = SrsVideoAvcFrameTypeKeyFrame;
video_sample->nb_data_ = 100;
// Add sample to manager
manager.append(video_sample);
// Write samples to traf with appropriate next_dts
err = manager.write(&traf, video_sample->dts_ + 1000);
HELPER_EXPECT_SUCCESS(err);
// Verify TRUN box was created with correct entries
SrsMp4TrackFragmentRunBox *trun = traf.trun();
ASSERT_TRUE(trun != NULL);
ASSERT_EQ(1, (int)trun->entries_.size());
// Verify TRUN entry for video keyframe
SrsMp4TrunEntry *entry = trun->entries_.at(0);
EXPECT_EQ(1000, entry->sample_duration_);
EXPECT_EQ(100, entry->sample_size_);
EXPECT_EQ(0, entry->sample_composition_time_offset_);
// Keyframe should have sample_depends_on_no and sample_is_non_sync_sample=0 (sync sample)
EXPECT_EQ(0, entry->sample_flags_ & 0x00010000);
}
// Test with video non-keyframe samples
if (true) {
SrsMp4SampleManager manager;
SrsMp4TrackFragmentBox traf;
// Create a video non-keyframe sample
SrsMp4Sample *video_sample = new SrsMp4Sample();
video_sample->type_ = SrsFrameTypeVideo;
video_sample->dts_ = 2000;
video_sample->pts_ = 2000;
video_sample->tbn_ = 90000;
video_sample->frame_type_ = SrsVideoAvcFrameTypeInterFrame;
video_sample->nb_data_ = 50;
// Add sample to manager
manager.append(video_sample);
// Write samples to traf with appropriate next_dts
err = manager.write(&traf, video_sample->dts_ + 1000);
HELPER_EXPECT_SUCCESS(err);
// Verify TRUN box was created with correct entries
SrsMp4TrackFragmentRunBox *trun = traf.trun();
ASSERT_TRUE(trun != NULL);
ASSERT_EQ(1, (int)trun->entries_.size());
// Verify TRUN entry for video non-keyframe
SrsMp4TrunEntry *entry = trun->entries_.at(0);
EXPECT_EQ(1000, entry->sample_duration_);
EXPECT_EQ(50, entry->sample_size_);
EXPECT_EQ(0, entry->sample_composition_time_offset_);
// Non-keyframe should have sample_depends_on_yes and sample_is_non_sync_sample=1 (non-sync sample)
EXPECT_EQ((uint32_t)(0x00010000 | 0x00000000), entry->sample_flags_ & 0x00010000);
}
// Test with audio samples
if (true) {
SrsMp4SampleManager manager;
SrsMp4TrackFragmentBox traf;
// Create an audio sample
SrsMp4Sample *audio_sample = new SrsMp4Sample();
audio_sample->type_ = SrsFrameTypeAudio;
audio_sample->dts_ = 3000;
audio_sample->pts_ = 3000;
audio_sample->tbn_ = 44100;
audio_sample->nb_data_ = 200;
// Add sample to manager
manager.append(audio_sample);
// Write samples to traf with appropriate next_dts
err = manager.write(&traf, audio_sample->dts_ + 1000);
HELPER_EXPECT_SUCCESS(err);
// Verify TRUN box was created with correct entries
SrsMp4TrackFragmentRunBox *trun = traf.trun();
ASSERT_TRUE(trun != NULL);
ASSERT_EQ(1, (int)trun->entries_.size());
// Verify TRUN entry for audio sample
SrsMp4TrunEntry *entry = trun->entries_.at(0);
EXPECT_EQ(1000, entry->sample_duration_);
EXPECT_EQ(200, entry->sample_size_);
EXPECT_EQ(0, entry->sample_composition_time_offset_);
// Audio should have sample_depends_on_no and sample_is_non_sync_sample=0 (sync sample)
EXPECT_EQ(0, entry->sample_flags_ & 0x00010000);
}
// Test with multiple samples (video keyframe, video non-keyframe, audio)
if (true) {
SrsMp4SampleManager manager;
SrsMp4TrackFragmentBox traf;
// Create video keyframe sample
SrsMp4Sample *keyframe = new SrsMp4Sample();
keyframe->type_ = SrsFrameTypeVideo;
keyframe->dts_ = 1000;
keyframe->pts_ = 1000;
keyframe->tbn_ = 90000;
keyframe->frame_type_ = SrsVideoAvcFrameTypeKeyFrame;
keyframe->nb_data_ = 100;
// Create video non-keyframe sample
SrsMp4Sample *deltaframe = new SrsMp4Sample();
deltaframe->type_ = SrsFrameTypeVideo;
deltaframe->dts_ = 2000;
deltaframe->pts_ = 2000;
deltaframe->tbn_ = 90000;
deltaframe->frame_type_ = SrsVideoAvcFrameTypeInterFrame;
deltaframe->nb_data_ = 50;
// Create audio sample
SrsMp4Sample *audio = new SrsMp4Sample();
audio->type_ = SrsFrameTypeAudio;
audio->dts_ = 3000;
audio->pts_ = 3000;
audio->tbn_ = 44100;
audio->nb_data_ = 200;
// Add samples to manager
manager.append(keyframe);
manager.append(deltaframe);
manager.append(audio);
// Write samples to traf with appropriate next_dts
uint64_t next_dts = audio->dts_ + 1000;
err = manager.write(&traf, next_dts);
HELPER_EXPECT_SUCCESS(err);
// Verify TRUN box was created with correct entries
SrsMp4TrackFragmentRunBox *trun = traf.trun();
ASSERT_TRUE(trun != NULL);
ASSERT_EQ(3, (int)trun->entries_.size());
// Verify each entry
SrsMp4TrunEntry *entry1 = trun->entries_.at(0);
EXPECT_EQ(1000, entry1->sample_duration_);
EXPECT_EQ(100, entry1->sample_size_);
EXPECT_EQ(0, entry1->sample_flags_ & 0x00010000); // Keyframe (sample_is_non_sync_sample=0)
SrsMp4TrunEntry *entry2 = trun->entries_.at(1);
EXPECT_EQ(1000, entry2->sample_duration_);
EXPECT_EQ(50, entry2->sample_size_);
EXPECT_EQ((uint32_t)(0x00010000 | 0x00000000), entry2->sample_flags_ & 0x00010000); // Non-keyframe (sample_is_non_sync_sample=1)
SrsMp4TrunEntry *entry3 = trun->entries_.at(2);
EXPECT_EQ(1000, entry3->sample_duration_);
EXPECT_EQ(200, entry3->sample_size_);
EXPECT_EQ(0, entry3->sample_flags_ & 0x00010000); // Audio (sample_is_non_sync_sample=0)
}
}