mirror of
https://github.com/scottlamb/moonfire-nvr.git
synced 2025-02-27 21:39:15 -05:00
fixes to mp4 generation
* typo: the subtitle should use its own mdhd, not alias the video one * use 64-bit ints for the edit lists; the 32-bit values overflow at 13.25 hours * use etags that reflect the edit list
This commit is contained in:
parent
0aadf227c1
commit
6b6137f8e7
@ -344,6 +344,16 @@ TEST_F(IntegrationTest, RoundTrip) {
|
|||||||
recording, 0, std::numeric_limits<int32_t>::max(), false);
|
recording, 0, std::numeric_limits<int32_t>::max(), false);
|
||||||
WriteMp4(f.get());
|
WriteMp4(f.get());
|
||||||
CompareMp4s(0);
|
CompareMp4s(0);
|
||||||
|
|
||||||
|
// This test is brittle, which is the point. Any time the digest comparison
|
||||||
|
// here fails, it can be updated, but the etag must change as well!
|
||||||
|
// Otherwise clients may combine ranges from the new format with ranges
|
||||||
|
// from the old format!
|
||||||
|
EXPECT_EQ("1e5331e8371bd97ac3158b3a86494abc87cdc70e", Digest(f.get()));
|
||||||
|
EXPECT_EQ("\"268db2cd6e4814676d38832f1f9340c7555e4e71\"", f->etag());
|
||||||
|
|
||||||
|
// 10 seconds later than the segment's start time.
|
||||||
|
EXPECT_EQ(1430006410, f->last_modified());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntegrationTest, RoundTripWithSubtitle) {
|
TEST_F(IntegrationTest, RoundTripWithSubtitle) {
|
||||||
@ -355,6 +365,13 @@ TEST_F(IntegrationTest, RoundTripWithSubtitle) {
|
|||||||
recording, 0, std::numeric_limits<int32_t>::max(), true);
|
recording, 0, std::numeric_limits<int32_t>::max(), true);
|
||||||
WriteMp4(f.get());
|
WriteMp4(f.get());
|
||||||
CompareMp4s(0);
|
CompareMp4s(0);
|
||||||
|
|
||||||
|
// This test is brittle, which is the point. Any time the digest comparison
|
||||||
|
// here fails, it can be updated, but the etag must change as well!
|
||||||
|
// Otherwise clients may combine ranges from the new format with ranges
|
||||||
|
// from the old format!
|
||||||
|
EXPECT_EQ("0081a442ba73092027fc580eeac2ebf25cb1ef50", Digest(f.get()));
|
||||||
|
EXPECT_EQ("\"8a29042355e1e28c10fbba328d1ddc9d54e450cd\"", f->etag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(IntegrationTest, RoundTripWithEditList) {
|
TEST_F(IntegrationTest, RoundTripWithEditList) {
|
||||||
@ -366,25 +383,13 @@ TEST_F(IntegrationTest, RoundTripWithEditList) {
|
|||||||
recording, 1, std::numeric_limits<int32_t>::max(), false);
|
recording, 1, std::numeric_limits<int32_t>::max(), false);
|
||||||
WriteMp4(f.get());
|
WriteMp4(f.get());
|
||||||
CompareMp4s(-1);
|
CompareMp4s(-1);
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(IntegrationTest, Metadata) {
|
|
||||||
Recording recording = CopyMp4ToSingleRecording();
|
|
||||||
if (HasFailure()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto f = CreateMp4FromSingleRecording(
|
|
||||||
recording, 0, std::numeric_limits<int32_t>::max(), false);
|
|
||||||
|
|
||||||
// This test is brittle, which is the point. Any time the digest comparison
|
// This test is brittle, which is the point. Any time the digest comparison
|
||||||
// here fails, it can be updated, but the etag must change as well!
|
// here fails, it can be updated, but the etag must change as well!
|
||||||
// Otherwise clients may combine ranges from the new format with ranges
|
// Otherwise clients may combine ranges from the new format with ranges
|
||||||
// from the old format!
|
// from the old format!
|
||||||
EXPECT_EQ("1e5331e8371bd97ac3158b3a86494abc87cdc70e", Digest(f.get()));
|
EXPECT_EQ("685e026af44204bc9cc52115c5e17058e9fb7c70", Digest(f.get()));
|
||||||
EXPECT_EQ("\"a9ce99a83a177516e7f862fed405e2b47b7d4ae3\"", f->etag());
|
EXPECT_EQ("\"1373289ddc7c05580deeeb1f1624e2d6cac7ddd3\"", f->etag());
|
||||||
|
|
||||||
// 10 seconds later than the segment's start time.
|
|
||||||
EXPECT_EQ(1430006410, f->last_modified());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
25
src/mp4.cc
25
src/mp4.cc
@ -298,10 +298,10 @@ struct EditBox { // ISO/IEC 14496-12 section 8.6.5, edts.
|
|||||||
const char type[4] = {'e', 'd', 't', 's'};
|
const char type[4] = {'e', 'd', 't', 's'};
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
struct EditListBoxVersion0 { // ISO/IEC 14496-12 section 8.6.6, elst.
|
struct EditListBoxVersion1 { // ISO/IEC 14496-12 section 8.6.6, elst.
|
||||||
uint32_t size = NET_UINT32_C(0);
|
uint32_t size = NET_UINT32_C(0);
|
||||||
const char type[4] = {'e', 'l', 's', 't'};
|
const char type[4] = {'e', 'l', 's', 't'};
|
||||||
const uint32_t version_and_flags = NET_UINT32_C(0);
|
const uint32_t version_and_flags = NET_UINT32_C(1 << 24);
|
||||||
uint32_t entry_count = NET_UINT32_C(0);
|
uint32_t entry_count = NET_UINT32_C(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -542,8 +542,9 @@ class Mp4File : public VirtualFile {
|
|||||||
std::string segment_times;
|
std::string segment_times;
|
||||||
for (const auto &segment : segments_) {
|
for (const auto &segment : segments_) {
|
||||||
segment_times.clear();
|
segment_times.clear();
|
||||||
Append64(segment->pieces.sample_pos().begin, &segment_times);
|
Append64(segment->recording.start_time_90k, &segment_times);
|
||||||
Append64(segment->pieces.sample_pos().end, &segment_times);
|
Append32(segment->rel_start_90k, &segment_times);
|
||||||
|
Append32(segment->pieces.end_90k(), &segment_times);
|
||||||
etag_digest->Update(segment_times);
|
etag_digest->Update(segment_times);
|
||||||
etag_digest->Update(segment->recording.sample_file_sha1);
|
etag_digest->Update(segment->recording.sample_file_sha1);
|
||||||
}
|
}
|
||||||
@ -609,11 +610,11 @@ class Mp4File : public VirtualFile {
|
|||||||
|
|
||||||
void MaybeAppendVideoEdts() {
|
void MaybeAppendVideoEdts() {
|
||||||
struct Entry {
|
struct Entry {
|
||||||
Entry(int32_t segment_duration, int32_t media_time)
|
Entry(int64_t segment_duration, int64_t media_time)
|
||||||
: segment_duration(segment_duration), media_time(media_time) {}
|
: segment_duration(segment_duration), media_time(media_time) {}
|
||||||
int32_t segment_duration = 0;
|
int64_t segment_duration = 0;
|
||||||
int32_t media_time = 0;
|
int64_t media_time = 0;
|
||||||
int32_t end() const { return segment_duration + media_time; }
|
int64_t end() const { return segment_duration + media_time; }
|
||||||
};
|
};
|
||||||
std::vector<Entry> entries;
|
std::vector<Entry> entries;
|
||||||
int64_t cur_media_time = 0;
|
int64_t cur_media_time = 0;
|
||||||
@ -640,8 +641,8 @@ class Mp4File : public VirtualFile {
|
|||||||
for (const auto &entry : entries) {
|
for (const auto &entry : entries) {
|
||||||
VLOG(2) << "...duration=" << entry.segment_duration
|
VLOG(2) << "...duration=" << entry.segment_duration
|
||||||
<< ", time=" << entry.media_time;
|
<< ", time=" << entry.media_time;
|
||||||
AppendU32(entry.segment_duration, s);
|
AppendU64(entry.segment_duration, s);
|
||||||
AppendU32(entry.media_time, s);
|
AppendU64(entry.media_time, s);
|
||||||
AppendU16(1, s); // media_rate_integer
|
AppendU16(1, s); // media_rate_integer
|
||||||
AppendU16(1, s); // media_rate_fraction
|
AppendU16(1, s); // media_rate_fraction
|
||||||
}
|
}
|
||||||
@ -761,7 +762,7 @@ class Mp4File : public VirtualFile {
|
|||||||
{
|
{
|
||||||
CONSTRUCT_BOX(moov_subtitle_trak_mdia_);
|
CONSTRUCT_BOX(moov_subtitle_trak_mdia_);
|
||||||
{
|
{
|
||||||
CONSTRUCT_BOX(moov_video_trak_mdia_mdhd_);
|
CONSTRUCT_BOX(moov_subtitle_trak_mdia_mdhd_);
|
||||||
moov_subtitle_trak_mdia_mdhd_.header().creation_time = net_creation_ts;
|
moov_subtitle_trak_mdia_mdhd_.header().creation_time = net_creation_ts;
|
||||||
moov_subtitle_trak_mdia_mdhd_.header().modification_time =
|
moov_subtitle_trak_mdia_mdhd_.header().modification_time =
|
||||||
net_creation_ts;
|
net_creation_ts;
|
||||||
@ -903,7 +904,7 @@ class Mp4File : public VirtualFile {
|
|||||||
Mp4Box<TrackBox> moov_video_trak_;
|
Mp4Box<TrackBox> moov_video_trak_;
|
||||||
Mp4Box<TrackHeaderBoxVersion0> moov_video_trak_tkhd_;
|
Mp4Box<TrackHeaderBoxVersion0> moov_video_trak_tkhd_;
|
||||||
Mp4Box<EditBox> moov_video_trak_edts_;
|
Mp4Box<EditBox> moov_video_trak_edts_;
|
||||||
Mp4Box<EditListBoxVersion0> moov_video_trak_edts_elst_;
|
Mp4Box<EditListBoxVersion1> moov_video_trak_edts_elst_;
|
||||||
StringPieceSlice moov_video_trak_edts_elst_entries_;
|
StringPieceSlice moov_video_trak_edts_elst_entries_;
|
||||||
std::string moov_video_trak_edts_elst_entries_str_;
|
std::string moov_video_trak_edts_elst_entries_str_;
|
||||||
Mp4Box<MediaBox> moov_video_trak_mdia_;
|
Mp4Box<MediaBox> moov_video_trak_mdia_;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user