1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "media/formats/webm/cluster_builder.h"
7 #include "base/logging.h"
8 #include "media/base/data_buffer.h"
9 #include "media/formats/webm/webm_constants.h"
13 static const uint8 kClusterHeader
[] = {
14 0x1F, 0x43, 0xB6, 0x75, // CLUSTER ID
15 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cluster(size = 0)
17 0x88, // timecode(size=8)
18 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value
21 static const uint8 kSimpleBlockHeader
[] = {
22 0xA3, // SimpleBlock ID
23 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
26 static const uint8 kBlockGroupHeader
[] = {
27 0xA0, // BlockGroup ID
28 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
29 0x9B, // BlockDuration ID
30 0x88, // BlockDuration(size = 8)
31 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // duration
33 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
36 static const uint8 kBlockGroupHeaderWithoutBlockDuration
[] = {
37 0xA0, // BlockGroup ID
38 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
40 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
44 kClusterSizeOffset
= 4,
45 kClusterTimecodeOffset
= 14,
47 kSimpleBlockSizeOffset
= 1,
49 kBlockGroupSizeOffset
= 1,
50 kBlockGroupWithoutBlockDurationBlockSizeOffset
= 10,
51 kBlockGroupDurationOffset
= 11,
52 kBlockGroupBlockSizeOffset
= 20,
54 kInitialBufferSize
= 32768,
57 Cluster::Cluster(scoped_ptr
<uint8
[]> data
, int size
)
58 : data_(data
.Pass()), size_(size
) {}
59 Cluster::~Cluster() {}
61 ClusterBuilder::ClusterBuilder() { Reset(); }
62 ClusterBuilder::~ClusterBuilder() {}
64 void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode
) {
65 DCHECK_EQ(cluster_timecode_
, -1);
67 cluster_timecode_
= cluster_timecode
;
69 // Write the timecode into the header.
70 uint8
* buf
= buffer_
.get() + kClusterTimecodeOffset
;
71 for (int i
= 7; i
>= 0; --i
) {
72 buf
[i
] = cluster_timecode
& 0xff;
73 cluster_timecode
>>= 8;
77 void ClusterBuilder::AddSimpleBlock(int track_num
, int64 timecode
, int flags
,
78 const uint8
* data
, int size
) {
79 int block_size
= size
+ 4;
80 int bytes_needed
= sizeof(kSimpleBlockHeader
) + block_size
;
81 if (bytes_needed
> (buffer_size_
- bytes_used_
))
82 ExtendBuffer(bytes_needed
);
84 uint8
* buf
= buffer_
.get() + bytes_used_
;
85 int block_offset
= bytes_used_
;
86 memcpy(buf
, kSimpleBlockHeader
, sizeof(kSimpleBlockHeader
));
87 UpdateUInt64(block_offset
+ kSimpleBlockSizeOffset
, block_size
);
88 buf
+= sizeof(kSimpleBlockHeader
);
90 WriteBlock(buf
, track_num
, timecode
, flags
, data
, size
);
92 bytes_used_
+= bytes_needed
;
95 void ClusterBuilder::AddBlockGroup(int track_num
, int64 timecode
, int duration
,
96 int flags
, const uint8
* data
, int size
) {
97 AddBlockGroupInternal(track_num
, timecode
, true, duration
, flags
, data
, size
);
100 void ClusterBuilder::AddBlockGroupWithoutBlockDuration(int track_num
,
105 AddBlockGroupInternal(track_num
, timecode
, false, 0, flags
, data
, size
);
109 void ClusterBuilder::AddBlockGroupInternal(int track_num
, int64 timecode
,
110 bool include_block_duration
,
111 int duration
, int flags
,
112 const uint8
* data
, int size
) {
113 int block_size
= size
+ 4;
114 int bytes_needed
= block_size
;
115 if (include_block_duration
) {
116 bytes_needed
+= sizeof(kBlockGroupHeader
);
118 bytes_needed
+= sizeof(kBlockGroupHeaderWithoutBlockDuration
);
121 int block_group_size
= bytes_needed
- 9;
123 if (bytes_needed
> (buffer_size_
- bytes_used_
))
124 ExtendBuffer(bytes_needed
);
126 uint8
* buf
= buffer_
.get() + bytes_used_
;
127 int block_group_offset
= bytes_used_
;
128 if (include_block_duration
) {
129 memcpy(buf
, kBlockGroupHeader
, sizeof(kBlockGroupHeader
));
130 UpdateUInt64(block_group_offset
+ kBlockGroupDurationOffset
, duration
);
131 UpdateUInt64(block_group_offset
+ kBlockGroupBlockSizeOffset
, block_size
);
132 buf
+= sizeof(kBlockGroupHeader
);
134 memcpy(buf
, kBlockGroupHeaderWithoutBlockDuration
,
135 sizeof(kBlockGroupHeaderWithoutBlockDuration
));
137 block_group_offset
+ kBlockGroupWithoutBlockDurationBlockSizeOffset
,
139 buf
+= sizeof(kBlockGroupHeaderWithoutBlockDuration
);
142 UpdateUInt64(block_group_offset
+ kBlockGroupSizeOffset
, block_group_size
);
144 // Make sure the 4 most-significant bits are 0.
145 // http://www.matroska.org/technical/specs/index.html#block_structure
148 WriteBlock(buf
, track_num
, timecode
, flags
, data
, size
);
150 bytes_used_
+= bytes_needed
;
153 void ClusterBuilder::WriteBlock(uint8
* buf
, int track_num
, int64 timecode
,
154 int flags
, const uint8
* data
, int size
) {
155 DCHECK_GE(track_num
, 0);
156 DCHECK_LE(track_num
, 126);
158 DCHECK_LE(flags
, 0xff);
161 DCHECK_NE(cluster_timecode_
, -1);
163 int64 timecode_delta
= timecode
- cluster_timecode_
;
164 DCHECK_GE(timecode_delta
, -32768);
165 DCHECK_LE(timecode_delta
, 32767);
167 buf
[0] = 0x80 | (track_num
& 0x7F);
168 buf
[1] = (timecode_delta
>> 8) & 0xff;
169 buf
[2] = timecode_delta
& 0xff;
170 buf
[3] = flags
& 0xff;
171 memcpy(buf
+ 4, data
, size
);
174 scoped_ptr
<Cluster
> ClusterBuilder::Finish() {
175 DCHECK_NE(cluster_timecode_
, -1);
177 UpdateUInt64(kClusterSizeOffset
, bytes_used_
- (kClusterSizeOffset
+ 8));
179 scoped_ptr
<Cluster
> ret(new Cluster(buffer_
.Pass(), bytes_used_
));
184 scoped_ptr
<Cluster
> ClusterBuilder::FinishWithUnknownSize() {
185 DCHECK_NE(cluster_timecode_
, -1);
187 UpdateUInt64(kClusterSizeOffset
, kWebMUnknownSize
);
189 scoped_ptr
<Cluster
> ret(new Cluster(buffer_
.Pass(), bytes_used_
));
194 void ClusterBuilder::Reset() {
195 buffer_size_
= kInitialBufferSize
;
196 buffer_
.reset(new uint8
[buffer_size_
]);
197 memcpy(buffer_
.get(), kClusterHeader
, sizeof(kClusterHeader
));
198 bytes_used_
= sizeof(kClusterHeader
);
199 cluster_timecode_
= -1;
202 void ClusterBuilder::ExtendBuffer(int bytes_needed
) {
203 int new_buffer_size
= 2 * buffer_size_
;
205 while ((new_buffer_size
- bytes_used_
) < bytes_needed
)
206 new_buffer_size
*= 2;
208 scoped_ptr
<uint8
[]> new_buffer(new uint8
[new_buffer_size
]);
210 memcpy(new_buffer
.get(), buffer_
.get(), bytes_used_
);
211 buffer_
.reset(new_buffer
.release());
212 buffer_size_
= new_buffer_size
;
215 void ClusterBuilder::UpdateUInt64(int offset
, int64 value
) {
216 DCHECK_LE(offset
+ 7, buffer_size_
);
217 uint8
* buf
= buffer_
.get() + offset
;
219 // Fill the last 7 bytes of size field in big-endian order.
220 for (int i
= 7; i
> 0; i
--) {
221 buf
[i
] = value
& 0xff;