Implemented Id3Tag class to handle ID3 tag
Also switched the original code in mp4 to use the new Id3Tag class. Change-Id: I1db2c6c6142ed98b72a432980a6a54815f1a8cc4
This commit is contained in:
parent
8f3a45c497
commit
8333908df1
|
@ -0,0 +1,101 @@
|
||||||
|
// Copyright 2018 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "packager/media/base/id3_tag.h"
|
||||||
|
|
||||||
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
#include "packager/media/base/fourccs.h"
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// ID3v2 header: http://id3.org/id3v2.4.0-structure.
|
||||||
|
const char kID3v2Identifier[] = "ID3";
|
||||||
|
const uint16_t kID3v2Version = 0x0400; // id3v2.4.0
|
||||||
|
|
||||||
|
const uint32_t kMaxSynchsafeSize = 0x0FFFFFFF; // 28 effective bits.
|
||||||
|
|
||||||
|
// Convert the specified size into synchsafe integer, where the most significant
|
||||||
|
// bit (bit 7) is set to zero in every byte.
|
||||||
|
uint32_t EncodeSynchsafe(uint32_t size) {
|
||||||
|
return (size & 0x7F) | (((size >> 7) & 0x7F) << 8) |
|
||||||
|
(((size >> 14) & 0x7F) << 16) | (((size >> 21) & 0x7F) << 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteId3v2Header(uint32_t frames_size, BufferWriter* buffer_writer) {
|
||||||
|
buffer_writer->AppendString(kID3v2Identifier);
|
||||||
|
buffer_writer->AppendInt(kID3v2Version);
|
||||||
|
const uint8_t flags = 0;
|
||||||
|
buffer_writer->AppendInt(flags);
|
||||||
|
|
||||||
|
if (frames_size > kMaxSynchsafeSize) {
|
||||||
|
LOG(ERROR) << "Input size (" << frames_size
|
||||||
|
<< ") is out of range (> max synchsafe integer "
|
||||||
|
<< kMaxSynchsafeSize << ").";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer_writer->AppendInt(EncodeSynchsafe(frames_size));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void Id3Tag::AddPrivateFrame(const std::string& owner,
|
||||||
|
const std::string& data) {
|
||||||
|
private_frames_.push_back({owner, data});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Id3Tag::WriteToBuffer(BufferWriter* buffer_writer) {
|
||||||
|
BufferWriter frames_buffer;
|
||||||
|
for (const PrivateFrame& private_frame : private_frames_) {
|
||||||
|
if (!WritePrivateFrame(private_frame, &frames_buffer))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!WriteId3v2Header(frames_buffer.Size(), buffer_writer))
|
||||||
|
return false;
|
||||||
|
buffer_writer->AppendBuffer(frames_buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Id3Tag::WriteToVector(std::vector<uint8_t>* output) {
|
||||||
|
BufferWriter buffer_writer;
|
||||||
|
if (!WriteToBuffer(&buffer_writer))
|
||||||
|
return false;
|
||||||
|
buffer_writer.SwapBuffer(output);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implemented per http://id3.org/id3v2.4.0-frames 4.27.
|
||||||
|
bool Id3Tag::WritePrivateFrame(const PrivateFrame& private_frame,
|
||||||
|
BufferWriter* buffer_writer) {
|
||||||
|
buffer_writer->AppendInt(static_cast<uint32_t>(FOURCC_PRIV));
|
||||||
|
|
||||||
|
const uint32_t frame_size = static_cast<uint32_t>(
|
||||||
|
private_frame.owner.size() + 1 + private_frame.data.size());
|
||||||
|
if (frame_size > kMaxSynchsafeSize) {
|
||||||
|
LOG(ERROR) << "Input size (" << frame_size
|
||||||
|
<< ") is out of range (> max synchsafe integer "
|
||||||
|
<< kMaxSynchsafeSize << ").";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer_writer->AppendInt(EncodeSynchsafe(frame_size));
|
||||||
|
|
||||||
|
const uint16_t flags = 0;
|
||||||
|
buffer_writer->AppendInt(flags);
|
||||||
|
|
||||||
|
buffer_writer->AppendString(private_frame.owner);
|
||||||
|
uint8_t byte = 0; // NULL terminating byte between owner and value.
|
||||||
|
buffer_writer->AppendInt(byte);
|
||||||
|
buffer_writer->AppendString(private_frame.data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2018 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#ifndef PACKAGER_MEDIA_BASE_ID3_TAG_H_
|
||||||
|
#define PACKAGER_MEDIA_BASE_ID3_TAG_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
class BufferWriter;
|
||||||
|
|
||||||
|
/// Implements ID3 tag defined in: http://id3.org/.
|
||||||
|
/// Only PrivateFrame is supported right now.
|
||||||
|
class Id3Tag {
|
||||||
|
public:
|
||||||
|
Id3Tag() = default;
|
||||||
|
~Id3Tag() = default;
|
||||||
|
|
||||||
|
/// Add a "Private Frame".
|
||||||
|
/// See http://id3.org/id3v2.4.0-frames 4.27.
|
||||||
|
/// @owner contains the owner identifier.
|
||||||
|
/// @data contains the data for this private frame.
|
||||||
|
void AddPrivateFrame(const std::string& owner, const std::string& data);
|
||||||
|
|
||||||
|
/// Write the ID3 tag to a buffer.
|
||||||
|
/// @param buffer_writer points to the @a BufferWriter to write to.
|
||||||
|
/// @return true on success.
|
||||||
|
bool WriteToBuffer(BufferWriter* buffer_writer);
|
||||||
|
|
||||||
|
/// Write the ID3 tag to vector.
|
||||||
|
/// @param output points to the vector to write to.
|
||||||
|
/// @return true on success.
|
||||||
|
bool WriteToVector(std::vector<uint8_t>* output);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Id3Tag(const Id3Tag&) = delete;
|
||||||
|
Id3Tag& operator=(const Id3Tag&) = delete;
|
||||||
|
|
||||||
|
struct PrivateFrame {
|
||||||
|
std::string owner;
|
||||||
|
std::string data;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool WritePrivateFrame(const PrivateFrame& private_frame,
|
||||||
|
BufferWriter* buffer_writer);
|
||||||
|
|
||||||
|
std::vector<PrivateFrame> private_frames_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
} // namespace shaka
|
||||||
|
|
||||||
|
#endif // PACKAGER_MEDIA_BASE_ID3_TAG_H_
|
|
@ -0,0 +1,105 @@
|
||||||
|
// Copyright 2018 Google LLC. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file or at
|
||||||
|
// https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
#include "packager/media/base/id3_tag.h"
|
||||||
|
|
||||||
|
#include <gmock/gmock.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
|
||||||
|
using ::testing::ElementsAreArray;
|
||||||
|
|
||||||
|
namespace shaka {
|
||||||
|
namespace media {
|
||||||
|
|
||||||
|
TEST(Id3TagTest, WriteToVector) {
|
||||||
|
Id3Tag id3_tag;
|
||||||
|
|
||||||
|
std::vector<uint8_t> output;
|
||||||
|
id3_tag.WriteToVector(&output);
|
||||||
|
|
||||||
|
const uint8_t kExpectedOutput[] = {
|
||||||
|
'I', 'D', '3', 4, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
EXPECT_THAT(output, ElementsAreArray(kExpectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Id3TagTest, WriteToBuffer) {
|
||||||
|
Id3Tag id3_tag;
|
||||||
|
|
||||||
|
BufferWriter buffer_writer;
|
||||||
|
id3_tag.WriteToBuffer(&buffer_writer);
|
||||||
|
|
||||||
|
const uint8_t kExpectedOutput[] = {
|
||||||
|
'I', 'D', '3', 4, 0, 0, 0, 0, 0, 0,
|
||||||
|
};
|
||||||
|
EXPECT_EQ(
|
||||||
|
std::vector<uint8_t>(std::begin(kExpectedOutput),
|
||||||
|
std::end(kExpectedOutput)),
|
||||||
|
std::vector<uint8_t>(buffer_writer.Buffer(),
|
||||||
|
buffer_writer.Buffer() + buffer_writer.Size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Id3TagTest, AddPrivateFrameOnce) {
|
||||||
|
Id3Tag id3_tag;
|
||||||
|
id3_tag.AddPrivateFrame("testing.owner", "data");
|
||||||
|
|
||||||
|
std::vector<uint8_t> output;
|
||||||
|
id3_tag.WriteToVector(&output);
|
||||||
|
|
||||||
|
const uint8_t kExpectedOutput[] = {
|
||||||
|
'I', 'D', '3', 4, 0, 0, 0, 0, 0, 28, // Header
|
||||||
|
'P', 'R', 'I', 'V', 0, 0, 0, 18, 0, 0, 't', 'e', 's', 't',
|
||||||
|
'i', 'n', 'g', '.', 'o', 'w', 'n', 'e', 'r', 0, 'd', 'a', 't', 'a',
|
||||||
|
};
|
||||||
|
EXPECT_THAT(output, ElementsAreArray(kExpectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Id3TagTest, AddPrivateFrameTwice) {
|
||||||
|
Id3Tag id3_tag;
|
||||||
|
id3_tag.AddPrivateFrame("testing.owner1", "data1");
|
||||||
|
id3_tag.AddPrivateFrame("testing.owner2", "data2");
|
||||||
|
|
||||||
|
std::vector<uint8_t> output;
|
||||||
|
id3_tag.WriteToVector(&output);
|
||||||
|
|
||||||
|
const uint8_t kExpectedOutput[] = {
|
||||||
|
'I', 'D', '3', 4, 0, 0, 0, 0, 0, 60, // Header
|
||||||
|
'P', 'R', 'I', 'V', 0, 0, 0, 20, 0, 0, 't', 'e', 's', 't', 'i',
|
||||||
|
'n', 'g', '.', 'o', 'w', 'n', 'e', 'r', '1', 0, 'd', 'a', 't', 'a', '1',
|
||||||
|
'P', 'R', 'I', 'V', 0, 0, 0, 20, 0, 0, 't', 'e', 's', 't', 'i',
|
||||||
|
'n', 'g', '.', 'o', 'w', 'n', 'e', 'r', '2', 0, 'd', 'a', 't', 'a', '2',
|
||||||
|
};
|
||||||
|
EXPECT_THAT(output, ElementsAreArray(kExpectedOutput));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Id3TagTest, AddBigPrivateFrameOnce) {
|
||||||
|
const size_t kTestOwnerSize = 200;
|
||||||
|
const size_t kTestDataSize = 200;
|
||||||
|
std::string test_owner(kTestOwnerSize, 0);
|
||||||
|
std::string test_data(kTestDataSize, 0);
|
||||||
|
|
||||||
|
Id3Tag id3_tag;
|
||||||
|
id3_tag.AddPrivateFrame(test_owner, test_data);
|
||||||
|
|
||||||
|
std::vector<uint8_t> output;
|
||||||
|
id3_tag.WriteToVector(&output);
|
||||||
|
|
||||||
|
const uint8_t kExpectedOutputHead[] = {
|
||||||
|
'I', 'D', '3', 4, 0, 0, 0, 0, 3, 27, // Header
|
||||||
|
'P', 'R', 'I', 'V', 0, 0, 3, 17, 0, 0,
|
||||||
|
};
|
||||||
|
std::vector<uint8_t> expected_output(std::begin(kExpectedOutputHead),
|
||||||
|
std::end(kExpectedOutputHead));
|
||||||
|
expected_output.resize(expected_output.size() + kTestOwnerSize + 1 +
|
||||||
|
kTestDataSize);
|
||||||
|
EXPECT_EQ(expected_output, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace media
|
||||||
|
|
||||||
|
} // namespace shaka
|
|
@ -47,6 +47,8 @@
|
||||||
'fourccs.h',
|
'fourccs.h',
|
||||||
'http_key_fetcher.cc',
|
'http_key_fetcher.cc',
|
||||||
'http_key_fetcher.h',
|
'http_key_fetcher.h',
|
||||||
|
'id3_tag.cc',
|
||||||
|
'id3_tag.h',
|
||||||
'key_fetcher.cc',
|
'key_fetcher.cc',
|
||||||
'key_fetcher.h',
|
'key_fetcher.h',
|
||||||
'key_source.cc',
|
'key_source.cc',
|
||||||
|
@ -171,6 +173,7 @@
|
||||||
'container_names_unittest.cc',
|
'container_names_unittest.cc',
|
||||||
'decryptor_source_unittest.cc',
|
'decryptor_source_unittest.cc',
|
||||||
'http_key_fetcher_unittest.cc',
|
'http_key_fetcher_unittest.cc',
|
||||||
|
'id3_tag_unittest.cc',
|
||||||
'muxer_util_unittest.cc',
|
'muxer_util_unittest.cc',
|
||||||
'offset_byte_queue_unittest.cc',
|
'offset_byte_queue_unittest.cc',
|
||||||
'producer_consumer_queue_unittest.cc',
|
'producer_consumer_queue_unittest.cc',
|
||||||
|
|
|
@ -67,11 +67,6 @@ bool IsIvSizeValid(uint8_t per_sample_iv_size) {
|
||||||
// bit(5) Reserved // 0
|
// bit(5) Reserved // 0
|
||||||
const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0};
|
const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0};
|
||||||
|
|
||||||
// ID3v2 header: http://id3.org/id3v2.4.0-structure
|
|
||||||
const uint32_t kID3v2HeaderSize = 10;
|
|
||||||
const char kID3v2Identifier[] = "ID3";
|
|
||||||
const uint16_t kID3v2Version = 0x0400; // id3v2.4.0
|
|
||||||
|
|
||||||
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
// Utility functions to check if the 64bit integers can fit in 32bit integer.
|
||||||
bool IsFitIn32Bits(uint64_t a) {
|
bool IsFitIn32Bits(uint64_t a) {
|
||||||
return a <= std::numeric_limits<uint32_t>::max();
|
return a <= std::numeric_limits<uint32_t>::max();
|
||||||
|
@ -1328,84 +1323,24 @@ uint32_t Language::ComputeSize() const {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PrivFrame::ReadWrite(BoxBuffer* buffer) {
|
|
||||||
FourCC fourcc = FOURCC_PRIV;
|
|
||||||
RCHECK(buffer->ReadWriteFourCC(&fourcc));
|
|
||||||
if (fourcc != FOURCC_PRIV) {
|
|
||||||
VLOG(1) << "Skip unrecognized id3 frame during read: "
|
|
||||||
<< FourCCToString(fourcc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t frame_size = static_cast<uint32_t>(owner.size() + 1 + value.size());
|
|
||||||
// size should be encoded as synchsafe integer, which is not support here.
|
|
||||||
// We don't expect frame_size to be larger than 0x7F. Synchsafe integers less
|
|
||||||
// than 0x7F is encoded in the same way as normal integer.
|
|
||||||
DCHECK_LT(frame_size, 0x7Fu);
|
|
||||||
uint16_t flags = 0;
|
|
||||||
RCHECK(buffer->ReadWriteUInt32(&frame_size) &&
|
|
||||||
buffer->ReadWriteUInt16(&flags));
|
|
||||||
|
|
||||||
if (buffer->Reading()) {
|
|
||||||
std::string str;
|
|
||||||
RCHECK(buffer->ReadWriteString(&str, frame_size));
|
|
||||||
// |owner| is null terminated.
|
|
||||||
size_t pos = str.find('\0');
|
|
||||||
RCHECK(pos < str.size());
|
|
||||||
owner = str.substr(0, pos);
|
|
||||||
value = str.substr(pos + 1);
|
|
||||||
} else {
|
|
||||||
uint8_t byte = 0; // Null terminating byte between owner and value.
|
|
||||||
RCHECK(buffer->ReadWriteString(&owner, owner.size()) &&
|
|
||||||
buffer->ReadWriteUInt8(&byte) &&
|
|
||||||
buffer->ReadWriteString(&value, value.size()));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t PrivFrame::ComputeSize() const {
|
|
||||||
if (owner.empty() && value.empty())
|
|
||||||
return 0;
|
|
||||||
const uint32_t kFourCCSize = 4;
|
|
||||||
return kFourCCSize +
|
|
||||||
static_cast<uint32_t>(sizeof(uint32_t) + sizeof(uint16_t) +
|
|
||||||
owner.size() + 1 + value.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
ID3v2::ID3v2() {}
|
ID3v2::ID3v2() {}
|
||||||
ID3v2::~ID3v2() {}
|
ID3v2::~ID3v2() {}
|
||||||
|
|
||||||
FourCC ID3v2::BoxType() const { return FOURCC_ID32; }
|
FourCC ID3v2::BoxType() const { return FOURCC_ID32; }
|
||||||
|
|
||||||
bool ID3v2::ReadWriteInternal(BoxBuffer* buffer) {
|
bool ID3v2::ReadWriteInternal(BoxBuffer* buffer) {
|
||||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
RCHECK(ReadWriteHeaderInternal(buffer) && language.ReadWrite(buffer) &&
|
||||||
language.ReadWrite(buffer));
|
buffer->ReadWriteVector(&id3v2_data, buffer->Reading()
|
||||||
|
? buffer->BytesLeft()
|
||||||
// Read/Write ID3v2 header
|
: id3v2_data.size()));
|
||||||
std::string id3v2_identifier = kID3v2Identifier;
|
|
||||||
uint16_t version = kID3v2Version;
|
|
||||||
// We only support PrivateFrame in ID3.
|
|
||||||
uint32_t data_size = private_frame.ComputeSize();
|
|
||||||
// size should be encoded as synchsafe integer, which is not support here.
|
|
||||||
// We don't expect data_size to be larger than 0x7F. Synchsafe integers less
|
|
||||||
// than 0x7F is encoded in the same way as normal integer.
|
|
||||||
DCHECK_LT(data_size, 0x7Fu);
|
|
||||||
uint8_t flags = 0;
|
|
||||||
RCHECK(buffer->ReadWriteString(&id3v2_identifier, id3v2_identifier.size()) &&
|
|
||||||
buffer->ReadWriteUInt16(&version) &&
|
|
||||||
buffer->ReadWriteUInt8(&flags) &&
|
|
||||||
buffer->ReadWriteUInt32(&data_size));
|
|
||||||
|
|
||||||
RCHECK(private_frame.ReadWrite(buffer));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ID3v2::ComputeSizeInternal() {
|
size_t ID3v2::ComputeSizeInternal() {
|
||||||
uint32_t private_frame_size = private_frame.ComputeSize();
|
// Skip ID3v2 box generation if there is no id3 data.
|
||||||
// Skip ID3v2 box generation if there is no private frame.
|
return id3v2_data.size() == 0
|
||||||
return private_frame_size == 0 ? 0 : HeaderSize() + language.ComputeSize() +
|
? 0
|
||||||
kID3v2HeaderSize +
|
: HeaderSize() + language.ComputeSize() + id3v2_data.size();
|
||||||
private_frame_size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Metadata::Metadata() {}
|
Metadata::Metadata() {}
|
||||||
|
|
|
@ -237,24 +237,12 @@ struct Language {
|
||||||
std::string code;
|
std::string code;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Implemented per http://id3.org/id3v2.4.0-frames.
|
/// Implemented per http://mp4ra.org/#/references.
|
||||||
struct PrivFrame {
|
|
||||||
bool ReadWrite(BoxBuffer* buffer);
|
|
||||||
uint32_t ComputeSize() const;
|
|
||||||
|
|
||||||
std::string owner;
|
|
||||||
std::string value;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Implemented per http://mp4ra.org/specs.html#id3v2 and
|
|
||||||
/// http://id3.org/id3v2.4.0-structure.
|
|
||||||
struct ID3v2 : FullBox {
|
struct ID3v2 : FullBox {
|
||||||
DECLARE_BOX_METHODS(ID3v2);
|
DECLARE_BOX_METHODS(ID3v2);
|
||||||
|
|
||||||
Language language;
|
Language language;
|
||||||
|
std::vector<uint8_t> id3v2_data;
|
||||||
/// We only support PrivateFrame in ID3. Other frames are ignored.
|
|
||||||
PrivFrame private_frame;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Metadata : FullBox {
|
struct Metadata : FullBox {
|
||||||
|
|
|
@ -229,12 +229,8 @@ inline bool operator==(const Language& lhs,
|
||||||
return lhs.code == rhs.code;
|
return lhs.code == rhs.code;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const PrivFrame& lhs, const PrivFrame& rhs) {
|
|
||||||
return lhs.owner == rhs.owner && lhs.value == rhs.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator==(const ID3v2& lhs, const ID3v2& rhs) {
|
inline bool operator==(const ID3v2& lhs, const ID3v2& rhs) {
|
||||||
return lhs.language == rhs.language && lhs.private_frame == rhs.private_frame;
|
return lhs.language == rhs.language && lhs.id3v2_data == rhs.id3v2_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator==(const Metadata& lhs, const Metadata& rhs) {
|
inline bool operator==(const Metadata& lhs, const Metadata& rhs) {
|
||||||
|
|
|
@ -309,13 +309,12 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
||||||
|
|
||||||
void Fill(ID3v2* id3v2) {
|
void Fill(ID3v2* id3v2) {
|
||||||
id3v2->language.code = "eng";
|
id3v2->language.code = "eng";
|
||||||
id3v2->private_frame.owner = "shaka-packager";
|
id3v2->id3v2_data.assign(std::begin(kData16Bytes), std::end(kData16Bytes));
|
||||||
id3v2->private_frame.value = "version 1.2.0-debug";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Modify(ID3v2* id3v2) {
|
void Modify(ID3v2* id3v2) {
|
||||||
id3v2->language.code = "fre";
|
id3v2->language.code = "fre";
|
||||||
id3v2->private_frame.value = "version 1.3.1-release";
|
id3v2->id3v2_data.assign(std::begin(kData8Bytes), std::end(kData8Bytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(Metadata* metadata) {
|
void Fill(Metadata* metadata) {
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
|
#include "packager/media/base/id3_tag.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
|
@ -92,8 +93,10 @@ Status Segmenter::Initialize(
|
||||||
if (!version.empty()) {
|
if (!version.empty()) {
|
||||||
moov_->metadata.handler.handler_type = FOURCC_ID32;
|
moov_->metadata.handler.handler_type = FOURCC_ID32;
|
||||||
moov_->metadata.id3v2.language.code = "eng";
|
moov_->metadata.id3v2.language.code = "eng";
|
||||||
moov_->metadata.id3v2.private_frame.owner = GetPackagerProjectUrl();
|
|
||||||
moov_->metadata.id3v2.private_frame.value = version;
|
Id3Tag id3_tag;
|
||||||
|
id3_tag.AddPrivateFrame(GetPackagerProjectUrl(), version);
|
||||||
|
CHECK(id3_tag.WriteToVector(&moov_->metadata.id3v2.id3v2_data));
|
||||||
}
|
}
|
||||||
return DoInitialize();
|
return DoInitialize();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue