| OLD | NEW |
| 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "base/compiler_specific.h" |
| 6 #include "media/base/filter_host_impl.h" |
| 7 #include "media/base/media_format.h" |
| 5 #include "media/base/pipeline_impl.h" | 8 #include "media/base/pipeline_impl.h" |
| 6 | 9 |
| 7 namespace media { | 10 namespace media { |
| 8 | 11 |
| 9 PipelineImpl::PipelineImpl() { | 12 PipelineImpl::PipelineImpl() { |
| 10 // TODO(ralphl): implement PipelineImpl constructor. | 13 ResetState(); |
| 11 NOTIMPLEMENTED(); | |
| 12 } | 14 } |
| 13 | 15 |
| 14 PipelineImpl::~PipelineImpl() { | 16 PipelineImpl::~PipelineImpl() { |
| 15 // TODO(ralphl): implement PipelineImpl destructor. | 17 Stop(); |
| 16 NOTIMPLEMENTED(); | |
| 17 } | 18 } |
| 18 | 19 |
| 19 bool PipelineImpl::IsInitialized() const { | 20 bool PipelineImpl::IsInitialized() const { |
| 20 // TODO(ralphl): implement IsInitialized. | 21 return initialized_; |
| 21 NOTIMPLEMENTED(); | 22 } |
| 23 |
| 24 base::TimeDelta PipelineImpl::GetDuration() const { |
| 25 return duration_; |
| 26 } |
| 27 |
| 28 base::TimeDelta PipelineImpl::GetBufferedTime() const { |
| 29 return buffered_time_; |
| 30 } |
| 31 |
| 32 int64 PipelineImpl::GetTotalBytes() const { |
| 33 return total_bytes_; |
| 34 } |
| 35 |
| 36 int64 PipelineImpl::GetBufferedBytes() const { |
| 37 return buffered_bytes_; |
| 38 } |
| 39 |
| 40 void PipelineImpl::GetVideoSize(size_t* width_out, size_t* height_out) const { |
| 41 DCHECK(width_out); |
| 42 DCHECK(height_out); |
| 43 AutoLock auto_lock(const_cast<Lock&>(video_size_access_lock_)); |
| 44 *width_out = video_width_; |
| 45 *height_out = video_height_; |
| 46 } |
| 47 |
| 48 float PipelineImpl::GetVolume() const { |
| 49 return volume_; |
| 50 } |
| 51 |
| 52 float PipelineImpl::GetPlaybackRate() const { |
| 53 return playback_rate_; |
| 54 } |
| 55 |
| 56 base::TimeDelta PipelineImpl::GetTime() const { |
| 57 return time_; |
| 58 } |
| 59 |
| 60 PipelineError PipelineImpl::GetError() const { |
| 61 return error_; |
| 62 } |
| 63 |
| 64 // Creates the PipelineThread and calls it's start method. |
| 65 bool PipelineImpl::Start(FilterFactory* factory, |
| 66 const std::string& url, |
| 67 Callback1<bool>::Type* init_complete_callback) { |
| 68 DCHECK(!pipeline_thread_); |
| 69 DCHECK(factory); |
| 70 DCHECK(!initialized_); |
| 71 if (!pipeline_thread_ && factory) { |
| 72 pipeline_thread_ = new PipelineThread(this); |
| 73 if (pipeline_thread_) { |
| 74 // TODO(ralphl): Does the callback get copied by these fancy templates? |
| 75 // if so, then do I want to always delete it here??? |
| 76 if (pipeline_thread_->Start(factory, url, init_complete_callback)) { |
| 77 return true; |
| 78 } |
| 79 pipeline_thread_ = NULL; // Releases reference to destroy thread |
| 80 } |
| 81 } |
| 82 delete init_complete_callback; |
| 22 return false; | 83 return false; |
| 23 } | 84 } |
| 24 | 85 |
| 25 int64 PipelineImpl::GetDuration() const { | 86 // Stop the PipelineThread and return to a state identical to that of a newly |
| 26 // TODO(ralphl): implement GetDuration. | 87 // created PipelineImpl object. |
| 27 NOTIMPLEMENTED(); | 88 void PipelineImpl::Stop() { |
| 28 return 0; | 89 if (pipeline_thread_) { |
| 29 } | 90 pipeline_thread_->Stop(); |
| 30 | 91 } |
| 31 int64 PipelineImpl::GetBufferedTime() const { | 92 ResetState(); |
| 32 // TODO(ralphl): implement GetBufferedTime. | 93 } |
| 33 NOTIMPLEMENTED(); | 94 |
| 34 return 0; | 95 |
| 35 } | 96 |
| 36 | 97 void PipelineImpl::SetPlaybackRate(float rate) { |
| 37 int64 PipelineImpl::GetTotalBytes() const { | 98 if (OkToCallThread() && rate >= 0.0f) { |
| 38 // TODO(ralphl): implement GetTotalBytes. | 99 pipeline_thread_->SetPlaybackRate(rate); |
| 39 NOTIMPLEMENTED(); | 100 } else { |
| 40 return 0; | 101 NOTREACHED(); |
| 41 } | 102 } |
| 42 | 103 } |
| 43 int64 PipelineImpl::GetBufferedBytes() const { | 104 |
| 44 // TODO(ralphl): implement GetBufferedBytes. | 105 void PipelineImpl::Seek(base::TimeDelta time) { |
| 45 NOTIMPLEMENTED(); | 106 if (OkToCallThread()) { |
| 46 return 0; | 107 pipeline_thread_->Seek(time); |
| 47 } | 108 } else { |
| 48 | 109 NOTREACHED(); |
| 49 void PipelineImpl::GetVideoSize(size_t* width_out, size_t* height_out) const { | 110 } |
| 50 // TODO(ralphl): implement GetVideoSize. | 111 } |
| 51 NOTIMPLEMENTED(); | 112 |
| 52 width_out = 0; | 113 void PipelineImpl::SetVolume(float volume) { |
| 53 height_out = 0; | 114 if (OkToCallThread() && volume >= 0.0f && volume <= 1.0f) { |
| 54 } | 115 pipeline_thread_->SetVolume(volume); |
| 55 | 116 } else { |
| 56 float PipelineImpl::GetVolume() const { | 117 NOTREACHED(); |
| 57 // TODO(ralphl): implement GetVolume. | 118 } |
| 58 NOTIMPLEMENTED(); | 119 } |
| 59 return 0; | 120 |
| 60 } | 121 void PipelineImpl::ResetState() { |
| 61 | 122 pipeline_thread_ = NULL; |
| 62 float PipelineImpl::GetPlaybackRate() const { | 123 initialized_ = false; |
| 63 // TODO(ralphl): implement GetPlaybackRate. | 124 duration_ = base::TimeDelta(); |
| 64 NOTIMPLEMENTED(); | 125 buffered_time_ = base::TimeDelta(); |
| 65 return 0; | 126 buffered_bytes_ = 0; |
| 66 } | 127 total_bytes_ = 0; |
| 67 | 128 video_width_ = 0; |
| 68 int64 PipelineImpl::GetTime() const { | 129 video_height_ = 0; |
| 69 // TODO(ralphl): implement GetTime. | 130 volume_ = 0.0f; |
| 70 NOTIMPLEMENTED(); | 131 playback_rate_ = 0.0f; |
| 71 return 0; | 132 time_ = base::TimeDelta(); |
| 72 } | 133 error_ = PIPELINE_OK; |
| 73 | 134 } |
| 74 PipelineError PipelineImpl::GetError() const { | 135 |
| 75 // TODO(ralphl): implement GetError. | 136 void PipelineImpl::SetVideoSize(size_t width, size_t height) { |
| 76 NOTIMPLEMENTED(); | 137 AutoLock auto_lock(video_size_access_lock_); |
| 77 return PIPELINE_ERROR_INITIALIZATION_FAILED; | 138 width = width; |
| 78 } | 139 height = height; |
| 79 | 140 } |
| 80 bool PipelineImpl::Start(FilterFactory* filter_factory, | 141 |
| 81 const std::string& uri, | 142 //----------------------------------------------------------------------------- |
| 82 Callback1<bool>::Type* init_complete_callback) { | 143 |
| 83 // TODO(ralphl): implement Start. | 144 PipelineThread::PipelineThread(PipelineImpl* pipeline) |
| 84 NOTIMPLEMENTED(); | 145 : pipeline_(pipeline), |
| 146 thread_("PipelineThread"), |
| 147 time_update_callback_scheduled_(false), |
| 148 host_initializing_(NULL) { |
| 149 } |
| 150 |
| 151 PipelineThread::~PipelineThread() { |
| 152 Stop(); |
| 153 } |
| 154 |
| 155 // This method is called on the client's thread. It starts the pipeline's |
| 156 // dedicated thread and posts a task to call the StartTask method on that |
| 157 // thread. |
| 158 bool PipelineThread::Start(FilterFactory* filter_factory, |
| 159 const std::string& url, |
| 160 Callback1<bool>::Type* init_complete_callback) { |
| 161 if (thread_.Start()) { |
| 162 filter_factory->AddRef(); |
| 163 PostTask(NewRunnableMethod(this, |
| 164 &PipelineThread::StartTask, |
| 165 filter_factory, |
| 166 url, |
| 167 // TODO(ralphl): what happens to this callback? |
| 168 // is it copied by NewRunnableTask? Just pointer |
| 169 // or is the callback itself copied? |
| 170 init_complete_callback)); |
| 171 return true; |
| 172 } |
| 85 return false; | 173 return false; |
| 86 } | 174 } |
| 87 | 175 |
| 88 void PipelineImpl::Stop() { | 176 // Called on the client's thread. If the thread has been started, then posts |
| 89 // TODO(ralphl): implement Stop. | 177 // a task to call the StopTask method, then waits until the thread has stopped. |
| 90 NOTIMPLEMENTED(); | 178 // There is a critical section that wraps the entire duration of the StartTask |
| 91 } | 179 // method. This method waits for that Lock to be released so that we know |
| 92 | 180 // that the thread is not executing a nested message loop. This way we know |
| 93 bool PipelineImpl::SetPlaybackRate(float rate) { | 181 // that that Thread::Stop call will quit the appropriate message loop. |
| 94 // TODO(ralphl): implement SetPlaybackRate. | 182 void PipelineThread::Stop() { |
| 95 NOTIMPLEMENTED(); | 183 if (thread_.IsRunning()) { |
| 184 PostTask(NewRunnableMethod(this, &PipelineThread::StopTask)); |
| 185 AutoLock lock_crit(initialization_lock_); |
| 186 thread_.Stop(); |
| 187 } |
| 188 DCHECK(filter_hosts_.empty()); |
| 189 } |
| 190 |
| 191 // Called on client's thread. |
| 192 void PipelineThread::SetPlaybackRate(float rate) { |
| 193 PostTask(NewRunnableMethod(this, &PipelineThread::SetPlaybackRateTask, rate)); |
| 194 } |
| 195 |
| 196 // Called on client's thread. |
| 197 void PipelineThread::Seek(base::TimeDelta time) { |
| 198 PostTask(NewRunnableMethod(this, &PipelineThread::SeekTask, time)); |
| 199 } |
| 200 |
| 201 // Called on client's thread. |
| 202 void PipelineThread::SetVolume(float volume) { |
| 203 PostTask(NewRunnableMethod(this, &PipelineThread::SetVolumeTask, volume)); |
| 204 } |
| 205 |
| 206 // May be called on any thread, and therefore we always assume the worst |
| 207 // possible race condition. This could, for example, be called from a filter's |
| 208 // thread just as the pipeline thread is exiting the call to the filter's |
| 209 // Initialize() method. Therefore, we make NO assumptions, and post work |
| 210 // in every case, even the trivial one of a thread calling this method from |
| 211 // within it's Initialize method. This means that we will always run a nested |
| 212 // message loop, and the InitializationCompleteTask will Quit that loop |
| 213 // immediately in the trivial case. |
| 214 void PipelineThread::InitializationComplete(FilterHostImpl* host) { |
| 215 DCHECK(host == host_initializing_); |
| 216 PostTask(NewRunnableMethod(this, |
| 217 &PipelineThread::InitializationCompleteTask, |
| 218 host)); |
| 219 } |
| 220 |
| 221 // Called from any thread. Updates the pipeline time and schedules a task to |
| 222 // call back to filters that have registered a callback for time updates. |
| 223 void PipelineThread::SetTime(base::TimeDelta time) { |
| 224 pipeline()->time_ = time; |
| 225 if (!time_update_callback_scheduled_) { |
| 226 time_update_callback_scheduled_ = true; |
| 227 PostTask(NewRunnableMethod(this, &PipelineThread::SetTimeTask)); |
| 228 } |
| 229 } |
| 230 |
| 231 // Called from any thread. Sets the pipeline error_ member and schedules a |
| 232 // task to stop all the filters in the pipeline. Note that the thread will |
| 233 // continue to run until the client calls Pipeline::Stop, but nothing will |
| 234 // be processed since filters will not be able to post tasks. |
| 235 void PipelineThread::Error(PipelineError error) { |
| 236 DCHECK(PIPELINE_OK != error); |
| 237 if (PIPELINE_OK == pipeline()->error_) { |
| 238 pipeline()->error_ = error; |
| 239 PostTask(NewRunnableMethod(this, &PipelineThread::StopTask)); |
| 240 } |
| 241 } |
| 242 |
| 243 // Called from any thread. Used by FilterHostImpl::PostTask method and used |
| 244 // internally. |
| 245 void PipelineThread::PostTask(Task* task) { |
| 246 message_loop()->PostTask(FROM_HERE, task); |
| 247 } |
| 248 |
| 249 |
| 250 // Main initialization method called on the pipeline thread. This code attempts |
| 251 // to use the specified filter factory to build a pipeline. It starts by |
| 252 // creating a DataSource, connects it to a Demuxer, and then connects the |
| 253 // Demuxer's audio stream to an AudioDecoder which is then connected to an |
| 254 // AudioRenderer. If the media has video, then it connects a VideoDecoder to |
| 255 // the Demuxer's video stream, and then connects the VideoDecoder to a |
| 256 // VideoRenderer. When all required filters have been created and have called |
| 257 // their FilterHost's InitializationComplete method, the pipeline's |
| 258 // initialized_ member is set to true, and, if the client provided an |
| 259 // init_complete_callback, it is called with "true". |
| 260 // If initializatoin fails, the client's callback will still be called, but |
| 261 // the bool parameter passed to it will be false. |
| 262 // |
| 263 // Note that at each step in this process, the initialization of any filter |
| 264 // may require running the pipeline thread's message loop recursively. This is |
| 265 // handled by the CreateFilter method. |
| 266 void PipelineThread::StartTask(FilterFactory* filter_factory, |
| 267 const std::string& url, |
| 268 Callback1<bool>::Type* init_complete_callback) { |
| 269 bool success = true; |
| 270 |
| 271 // During the entire StartTask we hold the initialization_lock_ so that |
| 272 // if the client calls the Pipeline::Stop method while we are running a |
| 273 // nested message loop, we can correctly unwind out of it before calling |
| 274 // the Thread::Stop method. |
| 275 AutoLock auto_lock(initialization_lock_); |
| 276 |
| 277 // Add ourselves as a destruction observer of the thread's message loop so |
| 278 // we can delete filters at an appropriate time (when all tasks have been |
| 279 // processed and the thread is about to be destroyed). |
| 280 message_loop()->AddDestructionObserver(this); |
| 281 success = CreateDataSource(filter_factory, url); |
| 282 if (success) { |
| 283 success = CreateAndConnect<Demuxer, DataSource>(filter_factory); |
| 284 } |
| 285 if (success) { |
| 286 success = CreateDecoder<AudioDecoder>(filter_factory); |
| 287 } |
| 288 if (success) { |
| 289 success = CreateAndConnect<AudioRenderer, AudioDecoder>(filter_factory); |
| 290 } |
| 291 if (success && HasVideo()) { |
| 292 success = CreateDecoder<VideoDecoder>(filter_factory); |
| 293 if (success) { |
| 294 success = CreateAndConnect<VideoRenderer, VideoDecoder>(filter_factory); |
| 295 } |
| 296 } |
| 297 if (success) { |
| 298 pipeline_->initialized_ = true; |
| 299 } else if (PIPELINE_OK == pipeline_->error_) { |
| 300 Error(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 301 } |
| 302 |
| 303 // No matter what, we're done with the filter factory, and |
| 304 // client callback so get rid of them. |
| 305 filter_factory->Release(); |
| 306 if (init_complete_callback) { |
| 307 init_complete_callback->Run(success); |
| 308 delete init_complete_callback; |
| 309 } |
| 310 } |
| 311 |
| 312 // This method is called as a result of the client calling Pipeline::Stop() or |
| 313 // as the result of an error condition. If there is no error, then set the |
| 314 // pipeline's error_ member to PIPELINE_STOPPING. We stop the filters in the |
| 315 // reverse order. |
| 316 void PipelineThread::StopTask() { |
| 317 if (PIPELINE_OK == pipeline_->error_) { |
| 318 pipeline_->error_ = PIPELINE_STOPPING; |
| 319 } |
| 320 FilterHostVector::reverse_iterator riter = filter_hosts_.rbegin(); |
| 321 while (riter != filter_hosts_.rend()) { |
| 322 (*riter)->Stop(); |
| 323 ++riter; |
| 324 } |
| 325 if (host_initializing_) { |
| 326 host_initializing_ = NULL; |
| 327 message_loop()->Quit(); |
| 328 } |
| 329 } |
| 330 |
| 331 // Task runs as a result of a filter calling InitializationComplete. If for |
| 332 // some reason StopTask has been executed prior to this, the host_initializing_ |
| 333 // member will be NULL, and the message loop will have been quit already, so |
| 334 // we don't want to do it again. |
| 335 void PipelineThread::InitializationCompleteTask(FilterHostImpl* host) { |
| 336 if (host == host_initializing_) { |
| 337 host_initializing_ = NULL; |
| 338 message_loop()->Quit(); |
| 339 } else { |
| 340 DCHECK(!host_initializing_); |
| 341 } |
| 342 } |
| 343 |
| 344 void PipelineThread::SetPlaybackRateTask(float rate) { |
| 345 pipeline_->playback_rate_ = rate; |
| 346 FilterHostVector::iterator iter = filter_hosts_.begin(); |
| 347 while (iter != filter_hosts_.end()) { |
| 348 (*iter)->media_filter()->SetPlaybackRate(rate); |
| 349 ++iter; |
| 350 } |
| 351 } |
| 352 |
| 353 void PipelineThread::SeekTask(base::TimeDelta time) { |
| 354 FilterHostVector::iterator iter = filter_hosts_.begin(); |
| 355 while (iter != filter_hosts_.end()) { |
| 356 (*iter)->media_filter()->Seek(time); |
| 357 ++iter; |
| 358 } |
| 359 } |
| 360 |
| 361 void PipelineThread::SetVolumeTask(float volume) { |
| 362 pipeline_->volume_ = volume; |
| 363 AudioRenderer* audio_renderer = GetFilter<AudioRenderer>(); |
| 364 if (audio_renderer) { |
| 365 audio_renderer->SetVolume(volume); |
| 366 } |
| 367 } |
| 368 |
| 369 void PipelineThread::SetTimeTask() { |
| 370 time_update_callback_scheduled_ = false; |
| 371 FilterHostVector::iterator iter = filter_hosts_.begin(); |
| 372 while (iter != filter_hosts_.end()) { |
| 373 (*iter)->RunTimeUpdateCallback(pipeline_->time_); |
| 374 ++iter; |
| 375 } |
| 376 } |
| 377 |
| 378 template <class Filter> |
| 379 Filter* PipelineThread::GetFilter() const { |
| 380 Filter* filter = NULL; |
| 381 FilterHostVector::const_iterator iter = filter_hosts_.begin(); |
| 382 while (iter != filter_hosts_.end() && NULL == filter) { |
| 383 filter = (*iter)->GetFilter<Filter>(); |
| 384 ++iter; |
| 385 } |
| 386 return filter; |
| 387 } |
| 388 |
| 389 template <class NewFilter, class Source> |
| 390 bool PipelineThread::CreateFilter(FilterFactory* filter_factory, |
| 391 Source source, |
| 392 const MediaFormat* source_media_format) { |
| 393 NewFilter* new_filter; |
| 394 bool success; |
| 395 success = filter_factory->Create(source_media_format, &new_filter); |
| 396 if (success) { |
| 397 DCHECK(!host_initializing_); |
| 398 host_initializing_ = new FilterHostImpl(this, new_filter); |
| 399 if (!host_initializing_) { |
| 400 success = false; |
| 401 new_filter->AddRef(); |
| 402 new_filter->Release(); |
| 403 } |
| 404 } |
| 405 if (success) { |
| 406 filter_hosts_.push_back(host_initializing_); |
| 407 new_filter->SetFilterHost(host_initializing_); |
| 408 |
| 409 // The filter must return true from initialize and there must still not |
| 410 // be an error or it's not successful. |
| 411 success = (new_filter->Initialize(source) && |
| 412 PIPELINE_OK == pipeline_->error_); |
| 413 } |
| 414 if (success) { |
| 415 // Now we run the thread's message loop recursively. We want all |
| 416 // pending tasks to be processed, so we set nestable tasks to be allowed |
| 417 // and then run the loop. The only way we exit the loop is as the result |
| 418 // of a call to FilterHost::InitializationComplete, FilterHost::Error, or |
| 419 // Pipeline::Stop. In each of these cases, the corresponding task method |
| 420 // sets host_initializing_ to NULL to signal that the message loop's Quit |
| 421 // method has already been called, and then calls message_loop()->Quit(). |
| 422 // The setting of |host_initializing_| to NULL in the task prevents a |
| 423 // subsequent task from accidentally quitting the wrong (non-nested) loop. |
| 424 message_loop()->SetNestableTasksAllowed(true); |
| 425 message_loop()->Run(); |
| 426 message_loop()->SetNestableTasksAllowed(false); |
| 427 DCHECK(!host_initializing_); |
| 428 |
| 429 // If an error occurred while we were in the nested Run state, then |
| 430 // not successful. When stopping, the |error_| member is set to a value of |
| 431 // PIPELINE_STOPPING so we will exit in that case also with false. |
| 432 success = (PIPELINE_OK == pipeline_->error_); |
| 433 } |
| 434 |
| 435 // This could still be set if we never ran the message loop (for example, |
| 436 // if the fiter returned false from it's Initialize method), so make sure |
| 437 // to reset it. |
| 438 host_initializing_ = NULL; |
| 439 |
| 440 // If this method fails, but no error set, then indicate a general |
| 441 // initialization failure. |
| 442 if (PIPELINE_OK == pipeline_->error_ && (!success)) { |
| 443 Error(PIPELINE_ERROR_INITIALIZATION_FAILED); |
| 444 } |
| 445 return success; |
| 446 } |
| 447 |
| 448 bool PipelineThread::CreateDataSource(FilterFactory* filter_factory, |
| 449 const std::string& url) { |
| 450 MediaFormat url_format; |
| 451 url_format.SetAsString(MediaFormat::kMimeType, mime_type::kURL); |
| 452 url_format.SetAsString(MediaFormat::kURL, url); |
| 453 return CreateFilter<DataSource>(filter_factory, url, &url_format); |
| 454 } |
| 455 |
| 456 template <class Decoder> |
| 457 bool PipelineThread::CreateDecoder(FilterFactory* filter_factory) { |
| 458 Demuxer* demuxer = GetFilter<Demuxer>(); |
| 459 if (demuxer) { |
| 460 int num_outputs = demuxer->GetNumberOfStreams(); |
| 461 for (int i = 0; i < num_outputs; ++i) { |
| 462 DemuxerStream* stream = demuxer->GetStream(i); |
| 463 const MediaFormat* stream_format = stream->GetMediaFormat(); |
| 464 if (IsMajorMimeType(stream_format, Decoder::major_mime_type())) { |
| 465 return CreateFilter<Decoder>(filter_factory, stream, stream_format); |
| 466 } |
| 467 } |
| 468 } |
| 96 return false; | 469 return false; |
| 97 } | 470 } |
| 98 | 471 |
| 99 bool PipelineImpl::Seek(int64 time) { | 472 template <class NewFilter, class SourceFilter> |
| 100 // TODO(ralphl): implement Seek. | 473 bool PipelineThread::CreateAndConnect(FilterFactory* filter_factory) { |
| 101 NOTIMPLEMENTED(); | 474 SourceFilter* source_filter = GetFilter<SourceFilter>(); |
| 475 bool success = (source_filter && |
| 476 CreateFilter<NewFilter>(filter_factory, |
| 477 source_filter, |
| 478 source_filter->GetMediaFormat())); |
| 479 return success; |
| 480 } |
| 481 |
| 482 // TODO(ralphl): Consider making this part of the demuxer interface. |
| 483 bool PipelineThread::HasVideo() const { |
| 484 Demuxer* demuxer = GetFilter<Demuxer>(); |
| 485 if (demuxer) { |
| 486 int num_outputs = demuxer->GetNumberOfStreams(); |
| 487 for (int i = 0; i < num_outputs; ++i) { |
| 488 if (IsMajorMimeType(demuxer->GetStream(i)->GetMediaFormat(), |
| 489 mime_type::kMajorTypeVideo)) { |
| 490 return true; |
| 491 } |
| 492 } |
| 493 } |
| 102 return false; | 494 return false; |
| 103 } | 495 } |
| 104 | 496 |
| 105 bool PipelineImpl::SetVolume(float volume) { | 497 bool PipelineThread::IsMajorMimeType(const MediaFormat* media_format, |
| 106 // TODO(ralphl): implement SetVolume. | 498 const std::string& major_mime_type) const { |
| 107 NOTIMPLEMENTED(); | 499 std::string value; |
| 500 if (media_format->GetAsString(MediaFormat::kMimeType, &value)) { |
| 501 return (0 == value.compare(0, major_mime_type.length(), major_mime_type)); |
| 502 } |
| 108 return false; | 503 return false; |
| 109 } | 504 } |
| 110 | 505 |
| 506 // Called as a result of destruction of the thread. |
| 507 void PipelineThread::WillDestroyCurrentMessageLoop() { |
| 508 while (!filter_hosts_.empty()) { |
| 509 delete filter_hosts_.back(); |
| 510 filter_hosts_.pop_back(); |
| 511 } |
| 512 } |
| 513 |
| 111 } // namespace media | 514 } // namespace media |
| OLD | NEW |