#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::RtpTimestamp;
using media::cast::proto::AggregatedFrameEvent;
using media::cast::proto::AggregatedPacketEvent;
+using media::cast::proto::BasePacketEvent;
using media::cast::proto::LogMetadata;
namespace {
-// Use 30MB of temp buffer to hold uncompressed data if |compress| is true.
-const int kMaxUncompressedBytes = 30 * 1000 * 1000;
+// 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(i);
+ if (from_base_event.packet_id() == to_base_event->packet_id()) {
+ to_base_event->MergeFrom(from_base_event);
+ merged = true;
+ break;
+ }
+ }
+ if (!merged) {
+ BasePacketEvent* to_base_event = to->add_base_packet_event();
+ to_base_event->CopyFrom(from_base_event);
+ }
+ }
+}
-bool DoDeserializeEvents(char* data,
- int data_bytes,
- LogMetadata* metadata,
- FrameEventMap* frame_events,
- PacketEventMap* packet_events) {
- base::BigEndianReader reader(data, data_bytes);
+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())
+ to->set_encoded_frame_size(from.encoded_frame_size());
+ if (!to->has_delay_millis())
+ to->set_delay_millis(from.delay_millis());
+ if (!to->has_key_frame())
+ to->set_key_frame(from.key_frame());
+}
- uint16 proto_size;
- if (!reader.ReadU16(&proto_size))
- return false;
- if (!metadata->ParseFromArray(reader.ptr(), proto_size))
- return false;
- if (!reader.Skip(proto_size))
- return false;
+bool PopulateDeserializedLog(base::BigEndianReader* reader,
+ media::cast::DeserializedLog* log) {
FrameEventMap frame_event_map;
PacketEventMap packet_event_map;
- int num_frame_events = metadata->num_frame_events();
+ 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))
+ if (!reader->ReadU16(&proto_size))
return false;
linked_ptr<AggregatedFrameEvent> frame_event(new AggregatedFrameEvent);
- if (!frame_event->ParseFromArray(reader.ptr(), proto_size))
+ if (!frame_event->ParseFromArray(reader->ptr(), proto_size))
return false;
- if (!reader.Skip(proto_size))
+ if (!reader->Skip(proto_size))
return false;
// During serialization the RTP timestamp in proto is relative to previous
frame_event->relative_rtp_timestamp() + relative_rtp_timestamp);
relative_rtp_timestamp = frame_event->relative_rtp_timestamp();
- std::pair<FrameEventMap::iterator, bool> result = frame_event_map.insert(
- std::make_pair(frame_event->relative_rtp_timestamp(), frame_event));
- if (!result.second) {
- VLOG(1) << "Duplicate frame event entry detected: "
- << frame_event->relative_rtp_timestamp();
- return false;
+ 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);
}
}
- frame_events->swap(frame_event_map);
+ log->frame_events.swap(frame_event_map);
- int num_packet_events = metadata->num_packet_events();
+ 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))
+ if (!reader->ReadU16(&proto_size))
return false;
linked_ptr<AggregatedPacketEvent> packet_event(new AggregatedPacketEvent);
- if (!packet_event->ParseFromArray(reader.ptr(), proto_size))
+ if (!packet_event->ParseFromArray(reader->ptr(), proto_size))
return false;
- if (!reader.Skip(proto_size))
+ 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();
- std::pair<PacketEventMap::iterator, bool> result = packet_event_map.insert(
- std::make_pair(packet_event->relative_rtp_timestamp(), packet_event));
- if (!result.second) {
- VLOG(1) << "Duplicate packet event entry detected: "
- << packet_event->relative_rtp_timestamp();
- return false;
+ 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);
}
}
- packet_events->swap(packet_event_map);
+ 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(char* data,
+bool Uncompress(const char* data,
int data_bytes,
int max_uncompressed_bytes,
char* uncompressed,
int* uncompressed_bytes) {
z_stream stream = {0};
- // 16 is added to read in gzip format.
- int result = inflateInit2(&stream, MAX_WBITS + 16);
- DCHECK_EQ(Z_OK, result);
- stream.next_in = reinterpret_cast<uint8*>(data);
+ 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;
- result = inflate(&stream, Z_FINISH);
- bool success = (result == Z_STREAM_END);
- if (!success)
- DVLOG(2) << "inflate() failed. Result: " << result;
+ 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);
+ result = inflateEnd(&stream);
+ DCHECK(result == Z_OK);
+ }
- if (success)
+ if (stream.avail_in == 0) {
+ success = true;
*uncompressed_bytes = max_uncompressed_bytes - stream.avail_out;
-
+ }
return success;
}
+
} // namespace
namespace media {
namespace cast {
-bool DeserializeEvents(char* data,
+bool DeserializeEvents(const char* data,
int data_bytes,
bool compressed,
- LogMetadata* log_metadata,
- FrameEventMap* frame_events,
- PacketEventMap* packet_events) {
- DCHECK(data);
+ DeserializedLog* audio_log,
+ DeserializedLog* video_log) {
DCHECK_GT(data_bytes, 0);
- DCHECK(log_metadata);
- DCHECK(frame_events);
- DCHECK(packet_events);
if (compressed) {
scoped_ptr<char[]> uncompressed(new char[kMaxUncompressedBytes]);
- int uncompressed_bytes;
+ int uncompressed_bytes = 0;
if (!Uncompress(data,
data_bytes,
kMaxUncompressedBytes,
&uncompressed_bytes))
return false;
- return DoDeserializeEvents(uncompressed.get(),
- uncompressed_bytes,
- log_metadata,
- frame_events,
- packet_events);
- } else {
return DoDeserializeEvents(
- data, data_bytes, log_metadata, frame_events, packet_events);
+ 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