1 // Copyright (c) 2012 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/webm/cluster_builder.h"
7 #include "base/logging.h"
8 #include "media/base/data_buffer.h"
12 static const uint8 kClusterHeader
[] = {
13 0x1F, 0x43, 0xB6, 0x75, // CLUSTER ID
14 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cluster(size = 0)
16 0x88, // timecode(size=8)
17 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value
20 static const uint8 kSimpleBlockHeader
[] = {
21 0xA3, // SimpleBlock ID
22 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
25 static const uint8 kBlockGroupHeader
[] = {
26 0xA0, // BlockGroup ID
27 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
28 0x9B, // BlockDuration ID
29 0x88, // BlockDuration(size = 8)
30 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // duration
32 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
36 kClusterSizeOffset
= 4,
37 kClusterTimecodeOffset
= 14,
39 kSimpleBlockSizeOffset
= 1,
41 kBlockGroupSizeOffset
= 1,
42 kBlockGroupDurationOffset
= 11,
43 kBlockGroupBlockSizeOffset
= 20,
45 kInitialBufferSize
= 32768,
48 Cluster::Cluster(scoped_array
<uint8
> data
, int size
)
49 : data_(data
.Pass()), size_(size
) {}
50 Cluster::~Cluster() {}
52 ClusterBuilder::ClusterBuilder() { Reset(); }
53 ClusterBuilder::~ClusterBuilder() {}
55 void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode
) {
56 DCHECK_EQ(cluster_timecode_
, -1);
58 cluster_timecode_
= cluster_timecode
;
60 // Write the timecode into the header.
61 uint8
* buf
= buffer_
.get() + kClusterTimecodeOffset
;
62 for (int i
= 7; i
>= 0; --i
) {
63 buf
[i
] = cluster_timecode
& 0xff;
64 cluster_timecode
>>= 8;
68 void ClusterBuilder::AddSimpleBlock(int track_num
, int64 timecode
, int flags
,
69 const uint8
* data
, int size
) {
70 int block_size
= size
+ 4;
71 int bytes_needed
= sizeof(kSimpleBlockHeader
) + block_size
;
72 if (bytes_needed
> (buffer_size_
- bytes_used_
))
73 ExtendBuffer(bytes_needed
);
75 uint8
* buf
= buffer_
.get() + bytes_used_
;
76 int block_offset
= bytes_used_
;
77 memcpy(buf
, kSimpleBlockHeader
, sizeof(kSimpleBlockHeader
));
78 UpdateUInt64(block_offset
+ kSimpleBlockSizeOffset
, block_size
);
79 buf
+= sizeof(kSimpleBlockHeader
);
81 WriteBlock(buf
, track_num
, timecode
, flags
, data
, size
);
83 bytes_used_
+= bytes_needed
;
86 void ClusterBuilder::AddBlockGroup(int track_num
, int64 timecode
, int duration
,
87 int flags
, const uint8
* data
, int size
) {
88 int block_size
= size
+ 4;
89 int bytes_needed
= sizeof(kBlockGroupHeader
) + block_size
;
90 int block_group_size
= bytes_needed
- 9;
92 if (bytes_needed
> (buffer_size_
- bytes_used_
))
93 ExtendBuffer(bytes_needed
);
95 uint8
* buf
= buffer_
.get() + bytes_used_
;
96 int block_group_offset
= bytes_used_
;
97 memcpy(buf
, kBlockGroupHeader
, sizeof(kBlockGroupHeader
));
98 UpdateUInt64(block_group_offset
+ kBlockGroupSizeOffset
, block_group_size
);
99 UpdateUInt64(block_group_offset
+ kBlockGroupDurationOffset
, duration
);
100 UpdateUInt64(block_group_offset
+ kBlockGroupBlockSizeOffset
, block_size
);
101 buf
+= sizeof(kBlockGroupHeader
);
103 // Make sure the 4 most-significant bits are 0.
104 // http://www.matroska.org/technical/specs/index.html#block_structure
107 WriteBlock(buf
, track_num
, timecode
, flags
, data
, size
);
109 bytes_used_
+= bytes_needed
;
112 void ClusterBuilder::WriteBlock(uint8
* buf
, int track_num
, int64 timecode
,
113 int flags
, const uint8
* data
, int size
) {
114 DCHECK_GE(track_num
, 0);
115 DCHECK_LE(track_num
, 126);
117 DCHECK_LE(flags
, 0xff);
120 DCHECK_NE(cluster_timecode_
, -1);
122 int64 timecode_delta
= timecode
- cluster_timecode_
;
123 DCHECK_GE(timecode_delta
, -32768);
124 DCHECK_LE(timecode_delta
, 32767);
126 buf
[0] = 0x80 | (track_num
& 0x7F);
127 buf
[1] = (timecode_delta
>> 8) & 0xff;
128 buf
[2] = timecode_delta
& 0xff;
129 buf
[3] = flags
& 0xff;
130 memcpy(buf
+ 4, data
, size
);
133 scoped_ptr
<Cluster
> ClusterBuilder::Finish() {
134 DCHECK_NE(cluster_timecode_
, -1);
136 UpdateUInt64(kClusterSizeOffset
, bytes_used_
- (kClusterSizeOffset
+ 8));
138 scoped_ptr
<Cluster
> ret(new Cluster(buffer_
.Pass(), bytes_used_
));
143 void ClusterBuilder::Reset() {
144 buffer_size_
= kInitialBufferSize
;
145 buffer_
.reset(new uint8
[buffer_size_
]);
146 memcpy(buffer_
.get(), kClusterHeader
, sizeof(kClusterHeader
));
147 bytes_used_
= sizeof(kClusterHeader
);
148 cluster_timecode_
= -1;
151 void ClusterBuilder::ExtendBuffer(int bytes_needed
) {
152 int new_buffer_size
= 2 * buffer_size_
;
154 while ((new_buffer_size
- bytes_used_
) < bytes_needed
)
155 new_buffer_size
*= 2;
157 scoped_array
<uint8
> new_buffer(new uint8
[new_buffer_size
]);
159 memcpy(new_buffer
.get(), buffer_
.get(), bytes_used_
);
160 buffer_
.reset(new_buffer
.release());
161 buffer_size_
= new_buffer_size
;
164 void ClusterBuilder::UpdateUInt64(int offset
, int64 value
) {
165 DCHECK_LE(offset
+ 7, buffer_size_
);
166 uint8
* buf
= buffer_
.get() + offset
;
168 // Fill the last 7 bytes of size field in big-endian order.
169 for (int i
= 7; i
> 0; i
--) {
170 buf
[i
] = value
& 0xff;