blob: b006df4951fdb23453ff2f98a019575262135a3d [file] [log] [blame]
// 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 <stdint.h>
#include <memory>
#include <utility>
#include "base/big_endian.h"
#include "third_party/zlib/zlib.h"
using media::cast::FrameEventMap;
using media::cast::PacketEventMap;
using media::cast::RtpTimeDelta;
using media::cast::RtpTimeTicks;
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,
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,
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();
RtpTimeTicks relative_rtp_timestamp;
uint16_t proto_size = 0;
for (int i = 0; i < num_frame_events; i++) {
if (!reader->ReadU16(&proto_size))
return false;
auto frame_event = std::make_unique<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.
relative_rtp_timestamp +=
RtpTimeDelta::FromTicks(frame_event->relative_rtp_timestamp());
frame_event->set_relative_rtp_timestamp(
relative_rtp_timestamp.lower_32_bits());
auto it = frame_event_map.find(relative_rtp_timestamp);
if (it == frame_event_map.end()) {
frame_event_map.insert(
std::make_pair(relative_rtp_timestamp, std::move(frame_event)));
} else {
// Events for the same frame might have been split into more than one
// proto. Merge them.
MergeFrameEvent(*frame_event, it->second.get());
}
}
log->frame_events.swap(frame_event_map);
int num_packet_events = log->metadata.num_packet_events();
relative_rtp_timestamp = RtpTimeTicks();
for (int i = 0; i < num_packet_events; i++) {
if (!reader->ReadU16(&proto_size))
return false;
auto packet_event = std::make_unique<AggregatedPacketEvent>();
if (!packet_event->ParseFromArray(reader->ptr(), proto_size))
return false;
if (!reader->Skip(proto_size))
return false;
relative_rtp_timestamp +=
RtpTimeDelta::FromTicks(packet_event->relative_rtp_timestamp());
packet_event->set_relative_rtp_timestamp(
relative_rtp_timestamp.lower_32_bits());
auto it = packet_event_map.find(relative_rtp_timestamp);
if (it == packet_event_map.end()) {
packet_event_map.insert(
std::make_pair(relative_rtp_timestamp, std::move(packet_event)));
} else {
// Events for the same frame might have been split into more than one
// proto. Merge them.
MergePacketEvent(*packet_event, it->second.get());
}
}
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_t 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_t*>(const_cast<char*>(data));
stream.avail_in = data_bytes;
stream.next_out = reinterpret_cast<uint8_t*>(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) {
std::unique_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() = default;
DeserializedLog::~DeserializedLog() = default;
} // namespace cast
} // namespace media