Home | History | Annotate | Download | only in logging
      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