| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 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 #include <limits> |
| 6 |
| 7 #include "mojo/services/media/common/cpp/timeline.h" |
| 8 #include "mojo/services/media/common/cpp/video_renderer.h" |
| 9 |
| 10 namespace mojo { |
| 11 namespace media { |
| 12 |
| 13 VideoRenderer::VideoRenderer() |
| 14 : renderer_binding_(this), |
| 15 consumer_binding_(this), |
| 16 control_site_binding_(this), |
| 17 timeline_consumer_binding_(this) {} |
| 18 |
| 19 VideoRenderer::~VideoRenderer() {} |
| 20 |
| 21 void VideoRenderer::Bind(InterfaceRequest<MediaRenderer> renderer_request) { |
| 22 renderer_binding_.Bind(renderer_request.Pass()); |
| 23 } |
| 24 |
| 25 void VideoRenderer::GetRgbaFrame(uint8_t* rgba_buffer, |
| 26 size_t width, |
| 27 size_t height, |
| 28 int64_t reference_time) { |
| 29 MaybeApplyPendingTimelineChange(reference_time); |
| 30 MaybePublishEndOfStream(); |
| 31 |
| 32 int64_t presentation_time = current_timeline_function_(reference_time); |
| 33 |
| 34 // Discard empty and old packets. We keep one packet around even if it's old, |
| 35 // so we can show an old frame instead of no frame when we starve. |
| 36 while (!packet_queue_.empty() && |
| 37 (!packet_queue_.front().packet_->payload || |
| 38 packet_queue_.front().packet_->payload->length == 0 || |
| 39 (packet_queue_.size() > 1 && |
| 40 packet_queue_.front().packet_->pts < presentation_time))) { |
| 41 // TODO(dalesat): Add hysteresis. |
| 42 packet_queue_.pop(); |
| 43 } |
| 44 |
| 45 // TODO(dalesat): Detect starvation. |
| 46 |
| 47 if (packet_queue_.empty()) { |
| 48 memset(rgba_buffer, 0, width * height * 4); |
| 49 } else { |
| 50 const MediaPacketPtr& packet = packet_queue_.front().packet_; |
| 51 converter_.ConvertFrame( |
| 52 rgba_buffer, width, height, |
| 53 shared_buffer_.PtrFromOffset(packet->payload->offset), |
| 54 packet->payload->length); |
| 55 } |
| 56 } |
| 57 |
| 58 void VideoRenderer::GetSupportedMediaTypes( |
| 59 const GetSupportedMediaTypesCallback& callback) { |
| 60 VideoMediaTypeSetDetailsPtr video_details = VideoMediaTypeSetDetails::New(); |
| 61 video_details->min_width = 1; |
| 62 video_details->max_width = std::numeric_limits<uint32_t>::max(); |
| 63 video_details->min_height = 1; |
| 64 video_details->max_height = std::numeric_limits<uint32_t>::max(); |
| 65 MediaTypeSetPtr supported_type = MediaTypeSet::New(); |
| 66 supported_type->medium = MediaTypeMedium::VIDEO; |
| 67 supported_type->details = MediaTypeSetDetails::New(); |
| 68 supported_type->details->set_video(video_details.Pass()); |
| 69 supported_type->encodings = Array<String>::New(1); |
| 70 supported_type->encodings[0] = MediaType::kVideoEncodingUncompressed; |
| 71 Array<MediaTypeSetPtr> supported_types = Array<MediaTypeSetPtr>::New(1); |
| 72 supported_types[0] = supported_type.Pass(); |
| 73 callback.Run(supported_types.Pass()); |
| 74 } |
| 75 |
| 76 void VideoRenderer::SetMediaType(MediaTypePtr media_type) { |
| 77 MOJO_DCHECK(media_type); |
| 78 MOJO_DCHECK(media_type->details); |
| 79 const VideoMediaTypeDetailsPtr& details = media_type->details->get_video(); |
| 80 MOJO_DCHECK(details); |
| 81 |
| 82 converter_.SetMediaType(media_type); |
| 83 } |
| 84 |
| 85 void VideoRenderer::GetConsumer( |
| 86 InterfaceRequest<MediaConsumer> consumer_request) { |
| 87 consumer_binding_.Bind(consumer_request.Pass()); |
| 88 } |
| 89 |
| 90 void VideoRenderer::GetTimelineControlSite( |
| 91 InterfaceRequest<MediaTimelineControlSite> control_site_request) { |
| 92 control_site_binding_.Bind(control_site_request.Pass()); |
| 93 } |
| 94 |
| 95 void VideoRenderer::SetBuffer(ScopedSharedBufferHandle buffer, |
| 96 const SetBufferCallback& callback) { |
| 97 shared_buffer_.InitFromHandle(buffer.Pass()); |
| 98 callback.Run(); |
| 99 } |
| 100 |
| 101 void VideoRenderer::SendPacket(MediaPacketPtr packet, |
| 102 const SendPacketCallback& callback) { |
| 103 MOJO_DCHECK(packet); |
| 104 if (packet->end_of_stream) { |
| 105 MOJO_DLOG(INFO) << "END_OF_STREAM"; |
| 106 end_of_stream_pts_ = packet->pts; |
| 107 } |
| 108 |
| 109 packet_queue_.emplace(packet.Pass(), callback); |
| 110 } |
| 111 |
| 112 void VideoRenderer::Prime(const PrimeCallback& callback) { |
| 113 callback.Run(); |
| 114 } |
| 115 |
| 116 void VideoRenderer::Flush(const FlushCallback& callback) { |
| 117 while (!packet_queue_.empty()) { |
| 118 packet_queue_.pop(); |
| 119 } |
| 120 callback.Run(); |
| 121 } |
| 122 |
| 123 void VideoRenderer::GetStatus(uint64_t version_last_seen, |
| 124 const GetStatusCallback& callback) { |
| 125 if (version_last_seen < status_version_) { |
| 126 CompleteGetStatus(callback); |
| 127 } else { |
| 128 pending_status_callbacks_.push_back(callback); |
| 129 } |
| 130 } |
| 131 |
| 132 void VideoRenderer::GetTimelineConsumer( |
| 133 InterfaceRequest<TimelineConsumer> timeline_consumer_request) { |
| 134 timeline_consumer_binding_.Bind(timeline_consumer_request.Pass()); |
| 135 } |
| 136 |
| 137 void VideoRenderer::SetTimelineTransform( |
| 138 int64_t subject_time, |
| 139 uint32_t reference_delta, |
| 140 uint32_t subject_delta, |
| 141 int64_t effective_reference_time, |
| 142 int64_t effective_subject_time, |
| 143 const SetTimelineTransformCallback& callback) { |
| 144 // At most one of the effective times must be specified. |
| 145 MOJO_DCHECK(effective_reference_time == kUnspecifiedTime || |
| 146 effective_subject_time == kUnspecifiedTime); |
| 147 // effective_subject_time can only be used if we're progressing already. |
| 148 MOJO_DCHECK(effective_subject_time == kUnspecifiedTime || |
| 149 current_timeline_function_.subject_delta() != 0); |
| 150 MOJO_DCHECK(reference_delta != 0); |
| 151 |
| 152 if (subject_time != kUnspecifiedTime && |
| 153 end_of_stream_pts_ != kUnspecifiedTime) { |
| 154 end_of_stream_pts_ = kUnspecifiedTime; |
| 155 end_of_stream_published_ = false; |
| 156 } |
| 157 |
| 158 if (effective_subject_time != kUnspecifiedTime) { |
| 159 // Infer effective_reference_time from effective_subject_time. |
| 160 effective_reference_time = |
| 161 current_timeline_function_.ApplyInverse(effective_subject_time); |
| 162 |
| 163 if (subject_time == kUnspecifiedTime) { |
| 164 // Infer subject_time from effective_subject_time. |
| 165 subject_time = effective_subject_time; |
| 166 } |
| 167 } else { |
| 168 if (effective_reference_time == kUnspecifiedTime) { |
| 169 // Neither effective time was specified. Effective time is now. |
| 170 effective_reference_time = Timeline::local_now(); |
| 171 } |
| 172 |
| 173 if (subject_time == kUnspecifiedTime) { |
| 174 // Infer subject_time from effective_reference_time. |
| 175 subject_time = current_timeline_function_(effective_reference_time); |
| 176 } |
| 177 } |
| 178 |
| 179 // Eject any previous pending change. |
| 180 ClearPendingTimelineFunction(false); |
| 181 |
| 182 // Queue up the new pending change. |
| 183 pending_timeline_function_ = TimelineFunction( |
| 184 effective_reference_time, subject_time, reference_delta, subject_delta); |
| 185 |
| 186 set_timeline_transform_callback_ = callback; |
| 187 } |
| 188 |
| 189 void VideoRenderer::ClearPendingTimelineFunction(bool completed) { |
| 190 pending_timeline_function_ = |
| 191 TimelineFunction(kUnspecifiedTime, kUnspecifiedTime, 1, 0); |
| 192 if (!set_timeline_transform_callback_.is_null()) { |
| 193 set_timeline_transform_callback_.Run(completed); |
| 194 set_timeline_transform_callback_.reset(); |
| 195 } |
| 196 } |
| 197 |
| 198 void VideoRenderer::MaybeApplyPendingTimelineChange(int64_t reference_time) { |
| 199 if (pending_timeline_function_.reference_time() == kUnspecifiedTime || |
| 200 pending_timeline_function_.reference_time() > reference_time) { |
| 201 return; |
| 202 } |
| 203 |
| 204 current_timeline_function_ = pending_timeline_function_; |
| 205 pending_timeline_function_ = |
| 206 TimelineFunction(kUnspecifiedTime, kUnspecifiedTime, 1, 0); |
| 207 |
| 208 if (!set_timeline_transform_callback_.is_null()) { |
| 209 set_timeline_transform_callback_.Run(true); |
| 210 set_timeline_transform_callback_.reset(); |
| 211 } |
| 212 |
| 213 SendStatusUpdates(); |
| 214 } |
| 215 |
| 216 void VideoRenderer::MaybePublishEndOfStream() { |
| 217 if (!end_of_stream_published_ && end_of_stream_pts_ != kUnspecifiedTime && |
| 218 current_timeline_function_(Timeline::local_now()) >= end_of_stream_pts_) { |
| 219 end_of_stream_published_ = true; |
| 220 SendStatusUpdates(); |
| 221 } |
| 222 } |
| 223 |
| 224 void VideoRenderer::SendStatusUpdates() { |
| 225 ++status_version_; |
| 226 |
| 227 std::vector<GetStatusCallback> pending_status_callbacks; |
| 228 pending_status_callbacks_.swap(pending_status_callbacks); |
| 229 |
| 230 for (const GetStatusCallback& pending_status_callback : |
| 231 pending_status_callbacks) { |
| 232 CompleteGetStatus(pending_status_callback); |
| 233 } |
| 234 } |
| 235 |
| 236 void VideoRenderer::CompleteGetStatus(const GetStatusCallback& callback) { |
| 237 MediaTimelineControlSiteStatusPtr status = |
| 238 MediaTimelineControlSiteStatus::New(); |
| 239 status->timeline_transform = |
| 240 TimelineTransform::From(current_timeline_function_); |
| 241 status->end_of_stream = |
| 242 end_of_stream_pts_ != kUnspecifiedTime && |
| 243 current_timeline_function_(Timeline::local_now()) >= end_of_stream_pts_; |
| 244 callback.Run(status_version_, status.Pass()); |
| 245 } |
| 246 |
| 247 VideoRenderer::PacketAndCallback::PacketAndCallback( |
| 248 MediaPacketPtr packet, |
| 249 const SendPacketCallback& callback) |
| 250 : packet_(packet.Pass()), callback_(callback) { |
| 251 MOJO_DCHECK(packet_); |
| 252 MOJO_DCHECK(!callback.is_null()); |
| 253 } |
| 254 |
| 255 VideoRenderer::PacketAndCallback::~PacketAndCallback() { |
| 256 callback_.Run(MediaConsumer::SendResult::CONSUMED); |
| 257 } |
| 258 |
| 259 } // namespace media |
| 260 } // namespace mojo |
| OLD | NEW |