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 // The serialization format is as follows:
6 // 16-bit integer describing the following LogMetadata proto size in bytes.
7 // The LogMetadata proto.
8 // 32-bit integer describing number of frame events.
9 // (The following repeated for number of frame events):
10 // 16-bit integer describing the following AggregatedFrameEvent proto size
12 // The AggregatedFrameEvent proto.
13 // 32-bit integer describing number of packet events.
14 // (The following repeated for number of packet events):
15 // 16-bit integer describing the following AggregatedPacketEvent proto
17 // The AggregatedPacketEvent proto.
19 #include "media/cast/logging/log_serializer.h"
21 #include "base/big_endian.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_ptr.h"
24 #include "third_party/zlib/zlib.h"
31 using media::cast::proto::AggregatedFrameEvent
;
32 using media::cast::proto::AggregatedPacketEvent
;
33 using media::cast::proto::LogMetadata
;
35 // Use 30MB of temp buffer to hold uncompressed data if |compress| is true.
36 const int kMaxUncompressedBytes
= 30 * 1000 * 1000;
38 // The maximum allowed size per serialized proto.
39 const int kMaxSerializedProtoBytes
= (1 << 16) - 1;
40 bool DoSerializeEvents(const LogMetadata
& metadata
,
41 const FrameEventList
& frame_events
,
42 const PacketEventList
& packet_events
,
43 const int max_output_bytes
,
46 base::BigEndianWriter
writer(output
, max_output_bytes
);
48 int proto_size
= metadata
.ByteSize();
49 DCHECK(proto_size
<= kMaxSerializedProtoBytes
);
50 if (!writer
.WriteU16(proto_size
))
52 if (!metadata
.SerializeToArray(writer
.ptr(), writer
.remaining()))
54 if (!writer
.Skip(proto_size
))
57 RtpTimestamp prev_rtp_timestamp
= 0;
58 for (media::cast::FrameEventList::const_iterator it
= frame_events
.begin();
59 it
!= frame_events
.end();
61 media::cast::proto::AggregatedFrameEvent
frame_event(**it
);
63 // Adjust relative RTP timestamp so that it is relative to previous frame,
64 // rather than relative to first RTP timestamp.
65 // This is done to improve encoding size.
66 RtpTimestamp old_relative_rtp_timestamp
=
67 frame_event
.relative_rtp_timestamp();
68 frame_event
.set_relative_rtp_timestamp(
69 old_relative_rtp_timestamp
- prev_rtp_timestamp
);
70 prev_rtp_timestamp
= old_relative_rtp_timestamp
;
72 proto_size
= frame_event
.ByteSize();
73 DCHECK(proto_size
<= kMaxSerializedProtoBytes
);
75 // Write size of the proto, then write the proto.
76 if (!writer
.WriteU16(proto_size
))
78 if (!frame_event
.SerializeToArray(writer
.ptr(), writer
.remaining()))
80 if (!writer
.Skip(proto_size
))
84 // Write packet events.
85 prev_rtp_timestamp
= 0;
86 for (media::cast::PacketEventList::const_iterator it
= packet_events
.begin();
87 it
!= packet_events
.end();
89 media::cast::proto::AggregatedPacketEvent
packet_event(**it
);
90 RtpTimestamp old_relative_rtp_timestamp
=
91 packet_event
.relative_rtp_timestamp();
92 packet_event
.set_relative_rtp_timestamp(
93 old_relative_rtp_timestamp
- prev_rtp_timestamp
);
94 prev_rtp_timestamp
= old_relative_rtp_timestamp
;
96 proto_size
= packet_event
.ByteSize();
97 DCHECK(proto_size
<= kMaxSerializedProtoBytes
);
99 // Write size of the proto, then write the proto.
100 if (!writer
.WriteU16(proto_size
))
102 if (!packet_event
.SerializeToArray(writer
.ptr(), writer
.remaining()))
104 if (!writer
.Skip(proto_size
))
108 *output_bytes
= max_output_bytes
- writer
.remaining();
112 bool Compress(char* uncompressed_buffer
,
113 int uncompressed_bytes
,
114 int max_output_bytes
,
117 z_stream stream
= {0};
118 int result
= deflateInit2(&stream
,
119 Z_DEFAULT_COMPRESSION
,
121 // 16 is added to produce a gzip header + trailer.
123 8, // memLevel = 8 is default.
125 DCHECK_EQ(Z_OK
, result
);
127 stream
.next_in
= reinterpret_cast<uint8
*>(uncompressed_buffer
);
128 stream
.avail_in
= uncompressed_bytes
;
129 stream
.next_out
= reinterpret_cast<uint8
*>(output
);
130 stream
.avail_out
= max_output_bytes
;
132 // Do a one-shot compression. This will return Z_STREAM_END only if |output|
133 // is large enough to hold all compressed data.
134 result
= deflate(&stream
, Z_FINISH
);
135 bool success
= (result
== Z_STREAM_END
);
138 DVLOG(2) << "deflate() failed. Result: " << result
;
140 result
= deflateEnd(&stream
);
141 DCHECK(result
== Z_OK
|| result
== Z_DATA_ERROR
);
144 *output_bytes
= max_output_bytes
- stream
.avail_out
;
151 bool SerializeEvents(const LogMetadata
& log_metadata
,
152 const FrameEventList
& frame_events
,
153 const PacketEventList
& packet_events
,
155 int max_output_bytes
,
158 DCHECK_GT(max_output_bytes
, 0);
160 DCHECK(output_bytes
);
163 // Allocate a reasonably large temp buffer to hold uncompressed data.
164 scoped_ptr
<char[]> uncompressed_buffer(new char[kMaxUncompressedBytes
]);
165 int uncompressed_bytes
;
166 bool success
= DoSerializeEvents(log_metadata
,
169 kMaxUncompressedBytes
,
170 uncompressed_buffer
.get(),
171 &uncompressed_bytes
);
174 return Compress(uncompressed_buffer
.get(),
180 return DoSerializeEvents(log_metadata
,