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. 4 // 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 11 // in bytes. 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 16 // size in bytes. 17 // The AggregatedPacketEvent proto. 18 19 #include "media/cast/logging/log_serializer.h" 20 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" 25 26 namespace media { 27 namespace cast { 28 29 namespace { 30 31 using media::cast::proto::AggregatedFrameEvent; 32 using media::cast::proto::AggregatedPacketEvent; 33 using media::cast::proto::LogMetadata; 34 35 // Use 30MB of temp buffer to hold uncompressed data if |compress| is true. 36 const int kMaxUncompressedBytes = 30 * 1000 * 1000; 37 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, 44 char* output, 45 int* output_bytes) { 46 base::BigEndianWriter writer(output, max_output_bytes); 47 48 int proto_size = metadata.ByteSize(); 49 DCHECK(proto_size <= kMaxSerializedProtoBytes); 50 if (!writer.WriteU16(proto_size)) 51 return false; 52 if (!metadata.SerializeToArray(writer.ptr(), writer.remaining())) 53 return false; 54 if (!writer.Skip(proto_size)) 55 return false; 56 57 RtpTimestamp prev_rtp_timestamp = 0; 58 for (media::cast::FrameEventList::const_iterator it = frame_events.begin(); 59 it != frame_events.end(); 60 ++it) { 61 media::cast::proto::AggregatedFrameEvent frame_event(**it); 62 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; 71 72 proto_size = frame_event.ByteSize(); 73 DCHECK(proto_size <= kMaxSerializedProtoBytes); 74 75 // Write size of the proto, then write the proto. 76 if (!writer.WriteU16(proto_size)) 77 return false; 78 if (!frame_event.SerializeToArray(writer.ptr(), writer.remaining())) 79 return false; 80 if (!writer.Skip(proto_size)) 81 return false; 82 } 83 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(); 88 ++it) { 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; 95 96 proto_size = packet_event.ByteSize(); 97 DCHECK(proto_size <= kMaxSerializedProtoBytes); 98 99 // Write size of the proto, then write the proto. 100 if (!writer.WriteU16(proto_size)) 101 return false; 102 if (!packet_event.SerializeToArray(writer.ptr(), writer.remaining())) 103 return false; 104 if (!writer.Skip(proto_size)) 105 return false; 106 } 107 108 *output_bytes = max_output_bytes - writer.remaining(); 109 return true; 110 } 111 112 bool Compress(char* uncompressed_buffer, 113 int uncompressed_bytes, 114 int max_output_bytes, 115 char* output, 116 int* output_bytes) { 117 z_stream stream = {0}; 118 int result = deflateInit2(&stream, 119 Z_DEFAULT_COMPRESSION, 120 Z_DEFLATED, 121 // 16 is added to produce a gzip header + trailer. 122 MAX_WBITS + 16, 123 8, // memLevel = 8 is default. 124 Z_DEFAULT_STRATEGY); 125 DCHECK_EQ(Z_OK, result); 126 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; 131 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); 136 137 if (!success) 138 DVLOG(2) << "deflate() failed. Result: " << result; 139 140 result = deflateEnd(&stream); 141 DCHECK(result == Z_OK || result == Z_DATA_ERROR); 142 143 if (success) 144 *output_bytes = max_output_bytes - stream.avail_out; 145 146 return success; 147 } 148 149 } // namespace 150 151 bool SerializeEvents(const LogMetadata& log_metadata, 152 const FrameEventList& frame_events, 153 const PacketEventList& packet_events, 154 bool compress, 155 int max_output_bytes, 156 char* output, 157 int* output_bytes) { 158 DCHECK_GT(max_output_bytes, 0); 159 DCHECK(output); 160 DCHECK(output_bytes); 161 162 if (compress) { 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, 167 frame_events, 168 packet_events, 169 kMaxUncompressedBytes, 170 uncompressed_buffer.get(), 171 &uncompressed_bytes); 172 if (!success) 173 return false; 174 return Compress(uncompressed_buffer.get(), 175 uncompressed_bytes, 176 max_output_bytes, 177 output, 178 output_bytes); 179 } else { 180 return DoSerializeEvents(log_metadata, 181 frame_events, 182 packet_events, 183 max_output_bytes, 184 output, 185 output_bytes); 186 } 187 } 188 189 } // namespace cast 190 } // namespace media 191