普通文本  |  253行  |  8.12 KB

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/logging/log_deserializer.h"

#include <map>
#include <utility>

#include "base/big_endian.h"
#include "base/memory/scoped_ptr.h"
#include "third_party/zlib/zlib.h"

using media::cast::FrameEventMap;
using media::cast::PacketEventMap;
using media::cast::RtpTimestamp;
using media::cast::proto::AggregatedFrameEvent;
using media::cast::proto::AggregatedPacketEvent;
using media::cast::proto::BasePacketEvent;
using media::cast::proto::LogMetadata;

namespace {

// Use 60MB of temp buffer to hold uncompressed data if |compress| is true.
// This is double the size of temp buffer used during compression (30MB)
// since the there are two streams in the blob.
// Keep in sync with media/cast/logging/log_serializer.cc.
const int kMaxUncompressedBytes = 60 * 1000 * 1000;

void MergePacketEvent(const AggregatedPacketEvent& from,
    linked_ptr<AggregatedPacketEvent> to) {
  for (int i = 0; i < from.base_packet_event_size(); i++) {
    const BasePacketEvent& from_base_event = from.base_packet_event(i);
    bool merged = false;
    for (int j = 0; j < to->base_packet_event_size(); j++) {
      BasePacketEvent* to_base_event = to->mutable_base_packet_event(j);
      if (from_base_event.packet_id() == to_base_event->packet_id()) {
        int packet_size = std::max(
            from_base_event.size(), to_base_event->size());
        // Need special merge logic here because we need to prevent a valid
        // packet size (> 0) from being overwritten with an invalid one (= 0).
        to_base_event->MergeFrom(from_base_event);
        to_base_event->set_size(packet_size);
        merged = true;
        break;
      }
    }
    if (!merged) {
      BasePacketEvent* to_base_event = to->add_base_packet_event();
      to_base_event->CopyFrom(from_base_event);
    }
  }
}

void MergeFrameEvent(const AggregatedFrameEvent& from,
    linked_ptr<AggregatedFrameEvent> to) {
  to->mutable_event_type()->MergeFrom(from.event_type());
  to->mutable_event_timestamp_ms()->MergeFrom(from.event_timestamp_ms());
  if (!to->has_encoded_frame_size() && from.has_encoded_frame_size())
    to->set_encoded_frame_size(from.encoded_frame_size());
  if (!to->has_delay_millis() && from.has_delay_millis())
    to->set_delay_millis(from.delay_millis());
  if (!to->has_key_frame() && from.has_key_frame())
    to->set_key_frame(from.key_frame());
  if (!to->has_target_bitrate() && from.has_target_bitrate())
    to->set_target_bitrate(from.target_bitrate());
}

bool PopulateDeserializedLog(base::BigEndianReader* reader,
                             media::cast::DeserializedLog* log) {
  FrameEventMap frame_event_map;
  PacketEventMap packet_event_map;

  int num_frame_events = log->metadata.num_frame_events();
  RtpTimestamp relative_rtp_timestamp = 0;
  uint16 proto_size = 0;
  for (int i = 0; i < num_frame_events; i++) {
    if (!reader->ReadU16(&proto_size))
      return false;

    linked_ptr<AggregatedFrameEvent> frame_event(new AggregatedFrameEvent);
    if (!frame_event->ParseFromArray(reader->ptr(), proto_size))
      return false;
    if (!reader->Skip(proto_size))
      return false;

    // During serialization the RTP timestamp in proto is relative to previous
    // frame.
    // Adjust RTP timestamp back to value relative to first RTP timestamp.
    frame_event->set_relative_rtp_timestamp(
        frame_event->relative_rtp_timestamp() + relative_rtp_timestamp);
    relative_rtp_timestamp = frame_event->relative_rtp_timestamp();

    FrameEventMap::iterator it = frame_event_map.find(
        frame_event->relative_rtp_timestamp());
    if (it == frame_event_map.end()) {
      frame_event_map.insert(
          std::make_pair(frame_event->relative_rtp_timestamp(), frame_event));
    } else {
      // Events for the same frame might have been split into more than one
      // proto. Merge them.
      MergeFrameEvent(*frame_event, it->second);
    }
  }

  log->frame_events.swap(frame_event_map);

  int num_packet_events = log->metadata.num_packet_events();
  relative_rtp_timestamp = 0;
  for (int i = 0; i < num_packet_events; i++) {
    if (!reader->ReadU16(&proto_size))
      return false;

    linked_ptr<AggregatedPacketEvent> packet_event(new AggregatedPacketEvent);
    if (!packet_event->ParseFromArray(reader->ptr(), proto_size))
      return false;
    if (!reader->Skip(proto_size))
      return false;

    packet_event->set_relative_rtp_timestamp(
        packet_event->relative_rtp_timestamp() + relative_rtp_timestamp);
    relative_rtp_timestamp = packet_event->relative_rtp_timestamp();

    PacketEventMap::iterator it = packet_event_map.find(
        packet_event->relative_rtp_timestamp());
    if (it == packet_event_map.end()) {
      packet_event_map.insert(
          std::make_pair(packet_event->relative_rtp_timestamp(), packet_event));
    } else {
      // Events for the same frame might have been split into more than one
      // proto. Merge them.
      MergePacketEvent(*packet_event, it->second);
    }
  }

  log->packet_events.swap(packet_event_map);

  return true;
}

bool DoDeserializeEvents(const char* data,
                         int data_bytes,
                         media::cast::DeserializedLog* audio_log,
                         media::cast::DeserializedLog* video_log) {
  bool got_audio = false;
  bool got_video = false;
  base::BigEndianReader reader(data, data_bytes);

  LogMetadata metadata;
  uint16 proto_size = 0;
  while (reader.remaining() > 0) {
    if (!reader.ReadU16(&proto_size))
      return false;
    if (!metadata.ParseFromArray(reader.ptr(), proto_size))
      return false;
    reader.Skip(proto_size);

    if (metadata.is_audio()) {
      if (got_audio) {
        VLOG(1) << "Got audio data twice.";
        return false;
      }

      got_audio = true;
      audio_log->metadata = metadata;
      if (!PopulateDeserializedLog(&reader, audio_log))
        return false;
    } else {
      if (got_video) {
        VLOG(1) << "Got duplicate video log.";
        return false;
      }

      got_video = true;
      video_log->metadata = metadata;
      if (!PopulateDeserializedLog(&reader, video_log))
        return false;
    }
  }
  return true;
}

bool Uncompress(const char* data,
                int data_bytes,
                int max_uncompressed_bytes,
                char* uncompressed,
                int* uncompressed_bytes) {
  z_stream stream = {0};

  stream.next_in = reinterpret_cast<uint8*>(const_cast<char*>(data));
  stream.avail_in = data_bytes;
  stream.next_out = reinterpret_cast<uint8*>(uncompressed);
  stream.avail_out = max_uncompressed_bytes;

  bool success = false;
  while (stream.avail_in > 0 && stream.avail_out > 0) {
    // 16 is added to read in gzip format.
    int result = inflateInit2(&stream, MAX_WBITS + 16);
    DCHECK_EQ(Z_OK, result);

    result = inflate(&stream, Z_FINISH);
    success = (result == Z_STREAM_END);
    if (!success) {
      DVLOG(2) << "inflate() failed. Result: " << result;
      break;
    }

    result = inflateEnd(&stream);
    DCHECK(result == Z_OK);
  }

  if (stream.avail_in == 0) {
    success = true;
    *uncompressed_bytes = max_uncompressed_bytes - stream.avail_out;
  }
  return success;
}

}  // namespace

namespace media {
namespace cast {

bool DeserializeEvents(const char* data,
                       int data_bytes,
                       bool compressed,
                       DeserializedLog* audio_log,
                       DeserializedLog* video_log) {
  DCHECK_GT(data_bytes, 0);

  if (compressed) {
    scoped_ptr<char[]> uncompressed(new char[kMaxUncompressedBytes]);
    int uncompressed_bytes = 0;
    if (!Uncompress(data,
                    data_bytes,
                    kMaxUncompressedBytes,
                    uncompressed.get(),
                    &uncompressed_bytes))
      return false;

    return DoDeserializeEvents(
        uncompressed.get(), uncompressed_bytes, audio_log, video_log);
  } else {
    return DoDeserializeEvents(data, data_bytes, audio_log, video_log);
  }
}

DeserializedLog::DeserializedLog() {}
DeserializedLog::~DeserializedLog() {}

}  // namespace cast
}  // namespace media