| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // The bulk of this file is support code; sorry about that. Here's an overview | 5 // The bulk of this file is support code; sorry about that. Here's an overview |
| 6 // to hopefully help readers of this code: | 6 // to hopefully help readers of this code: |
| 7 // - RenderingHelper is charged with interacting with X11, EGL, and GLES2. | 7 // - RenderingHelper is charged with interacting with X11, EGL, and GLES2. |
| 8 // - ClientState is an enum for the state of the decode client used by the test. | 8 // - ClientState is an enum for the state of the decode client used by the test. |
| 9 // - ClientStateNotification is a barrier abstraction that allows the test code | 9 // - ClientStateNotification is a barrier abstraction that allows the test code |
| 10 // to be written sequentially and wait for the decode client to see certain | 10 // to be written sequentially and wait for the decode client to see certain |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 193 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs)) | 193 CHECK(eglChooseConfig(egl_display_, rgba8888, &egl_config, 1, &num_configs)) |
| 194 << eglGetError(); | 194 << eglGetError(); |
| 195 CHECK_GE(num_configs, 1); | 195 CHECK_GE(num_configs, 1); |
| 196 static EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; | 196 static EGLint context_attribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; |
| 197 egl_context_ = eglCreateContext( | 197 egl_context_ = eglCreateContext( |
| 198 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); | 198 egl_display_, egl_config, EGL_NO_CONTEXT, context_attribs); |
| 199 CHECK_NE(egl_context_, EGL_NO_CONTEXT) << eglGetError(); | 199 CHECK_NE(egl_context_, EGL_NO_CONTEXT) << eglGetError(); |
| 200 | 200 |
| 201 // Per-window/surface X11 & EGL initialization. | 201 // Per-window/surface X11 & EGL initialization. |
| 202 for (int i = 0; i < num_windows; ++i) { | 202 for (int i = 0; i < num_windows; ++i) { |
| 203 // Arrange X windows side by side whimsically. | 203 // Arrange X windows whimsically, with some padding. |
| 204 int top_left_x = (width + 50) * i; | 204 int top_left_x = (width + 20) * (i % 4); |
| 205 int top_left_y = 100 + (i % 2) * 250; | 205 int top_left_y = (height + 12) * (i % 3); |
| 206 Window x_window = XCreateWindow( | 206 Window x_window = XCreateWindow( |
| 207 x_display_, DefaultRootWindow(x_display_), | 207 x_display_, DefaultRootWindow(x_display_), |
| 208 top_left_x, top_left_y, width_, height_, | 208 top_left_x, top_left_y, width_, height_, |
| 209 0 /* border width */, | 209 0 /* border width */, |
| 210 depth, CopyFromParent /* class */, CopyFromParent /* visual */, | 210 depth, CopyFromParent /* class */, CopyFromParent /* visual */, |
| 211 (CWBackPixel | CWOverrideRedirect), &window_attributes); | 211 (CWBackPixel | CWOverrideRedirect), &window_attributes); |
| 212 x_windows_.push_back(x_window); | 212 x_windows_.push_back(x_window); |
| 213 XStoreName(x_display_, x_window, "OmxVideoDecodeAcceleratorTest"); | 213 XStoreName(x_display_, x_window, "OmxVideoDecodeAcceleratorTest"); |
| 214 XSelectInput(x_display_, x_window, ExposureMask); | 214 XSelectInput(x_display_, x_window, ExposureMask); |
| 215 XMapWindow(x_display_, x_window); | 215 XMapWindow(x_display_, x_window); |
| (...skipping 198 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 414 const gfx::Size& dimensions, | 414 const gfx::Size& dimensions, |
| 415 VideoDecodeAccelerator::MemoryType type); | 415 VideoDecodeAccelerator::MemoryType type); |
| 416 virtual void DismissPictureBuffer(int32 picture_buffer_id); | 416 virtual void DismissPictureBuffer(int32 picture_buffer_id); |
| 417 virtual void PictureReady(const media::Picture& picture); | 417 virtual void PictureReady(const media::Picture& picture); |
| 418 // Simple state changes. | 418 // Simple state changes. |
| 419 virtual void NotifyInitializeDone(); | 419 virtual void NotifyInitializeDone(); |
| 420 virtual void NotifyEndOfStream(); | 420 virtual void NotifyEndOfStream(); |
| 421 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); | 421 virtual void NotifyEndOfBitstreamBuffer(int32 bitstream_buffer_id); |
| 422 virtual void NotifyFlushDone(); | 422 virtual void NotifyFlushDone(); |
| 423 virtual void NotifyResetDone(); | 423 virtual void NotifyResetDone(); |
| 424 virtual void NotifyDestroyDone(); | |
| 425 virtual void NotifyError(VideoDecodeAccelerator::Error error); | 424 virtual void NotifyError(VideoDecodeAccelerator::Error error); |
| 426 | 425 |
| 427 // Simple getters for inspecting the state of the Client. | 426 // Simple getters for inspecting the state of the Client. |
| 428 ClientState state() { return state_; } | 427 ClientState state() { return state_; } |
| 429 VideoDecodeAccelerator::Error error() { return error_; } | 428 VideoDecodeAccelerator::Error error() { return error_; } |
| 430 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } | 429 int num_done_bitstream_buffers() { return num_done_bitstream_buffers_; } |
| 431 int num_decoded_frames() { return num_decoded_frames_; } | 430 int num_decoded_frames() { return num_decoded_frames_; } |
| 432 EGLDisplay egl_display() { return rendering_helper_->egl_display(); } | 431 EGLDisplay egl_display() { return rendering_helper_->egl_display(); } |
| 433 EGLContext egl_context() { return rendering_helper_->egl_context(); } | 432 EGLContext egl_context() { return rendering_helper_->egl_context(); } |
| 434 double frames_per_second(); | 433 double frames_per_second(); |
| 435 bool decoder_deleted() { return !decoder_.get(); } | 434 bool decoder_deleted() { return !decoder_; } |
| 436 | 435 |
| 437 private: | 436 private: |
| 438 typedef std::map<int, media::GLESBuffer*> PictureBufferById; | 437 typedef std::map<int, media::GLESBuffer*> PictureBufferById; |
| 439 | 438 |
| 440 void SetState(ClientState new_state); | 439 void SetState(ClientState new_state); |
| 441 | 440 |
| 442 // Delete the associated OMX decoder helper. | 441 // Delete the associated OMX decoder helper. |
| 443 void DeleteDecoder(); | 442 void DeleteDecoder(); |
| 444 | 443 |
| 445 // Compute & return in |*end_pos| the end position for the next batch of NALUs | 444 // Compute & return in |*end_pos| the end position for the next batch of NALUs |
| 446 // to ship to the decoder (based on |start_pos| & |num_NALUs_per_decode_|). | 445 // to ship to the decoder (based on |start_pos| & |num_NALUs_per_decode_|). |
| 447 void GetRangeForNextNALUs(size_t start_pos, size_t* end_pos); | 446 void GetRangeForNextNALUs(size_t start_pos, size_t* end_pos); |
| 448 | 447 |
| 449 // Request decode of the next batch of NALUs in the encoded data. | 448 // Request decode of the next batch of NALUs in the encoded data. |
| 450 void DecodeNextNALUs(); | 449 void DecodeNextNALUs(); |
| 451 | 450 |
| 452 RenderingHelper* rendering_helper_; | 451 RenderingHelper* rendering_helper_; |
| 453 int rendering_window_id_; | 452 int rendering_window_id_; |
| 454 const std::string* encoded_data_; | 453 const std::string* encoded_data_; |
| 455 const int num_NALUs_per_decode_; | 454 const int num_NALUs_per_decode_; |
| 456 size_t encoded_data_next_pos_to_decode_; | 455 size_t encoded_data_next_pos_to_decode_; |
| 457 int next_bitstream_buffer_id_; | 456 int next_bitstream_buffer_id_; |
| 458 ClientStateNotification* note_; | 457 ClientStateNotification* note_; |
| 459 scoped_ptr<OmxVideoDecodeAccelerator> decoder_; | 458 scoped_refptr<OmxVideoDecodeAccelerator> decoder_; |
| 460 int delete_decoder_state_; | 459 int delete_decoder_state_; |
| 461 ClientState state_; | 460 ClientState state_; |
| 462 VideoDecodeAccelerator::Error error_; | 461 VideoDecodeAccelerator::Error error_; |
| 463 int num_decoded_frames_; | 462 int num_decoded_frames_; |
| 464 int num_done_bitstream_buffers_; | 463 int num_done_bitstream_buffers_; |
| 465 PictureBufferById picture_buffers_by_id_; | 464 PictureBufferById picture_buffers_by_id_; |
| 466 base::TimeTicks initialize_done_ticks_; | 465 base::TimeTicks initialize_done_ticks_; |
| 467 base::TimeTicks last_frame_delivered_ticks_; | 466 base::TimeTicks last_frame_delivered_ticks_; |
| 468 }; | 467 }; |
| 469 | 468 |
| 470 EglRenderingVDAClient::EglRenderingVDAClient(RenderingHelper* rendering_helper, | 469 EglRenderingVDAClient::EglRenderingVDAClient(RenderingHelper* rendering_helper, |
| 471 int rendering_window_id, | 470 int rendering_window_id, |
| 472 ClientStateNotification* note, | 471 ClientStateNotification* note, |
| 473 std::string* encoded_data, | 472 std::string* encoded_data, |
| 474 int num_NALUs_per_decode, | 473 int num_NALUs_per_decode, |
| 475 int delete_decoder_state) | 474 int delete_decoder_state) |
| 476 : rendering_helper_(rendering_helper), | 475 : rendering_helper_(rendering_helper), |
| 477 rendering_window_id_(rendering_window_id), | 476 rendering_window_id_(rendering_window_id), |
| 478 encoded_data_(encoded_data), num_NALUs_per_decode_(num_NALUs_per_decode), | 477 encoded_data_(encoded_data), num_NALUs_per_decode_(num_NALUs_per_decode), |
| 479 encoded_data_next_pos_to_decode_(0), next_bitstream_buffer_id_(0), | 478 encoded_data_next_pos_to_decode_(0), next_bitstream_buffer_id_(0), |
| 480 note_(note), delete_decoder_state_(delete_decoder_state), | 479 note_(note), delete_decoder_state_(delete_decoder_state), |
| 481 state_(CS_CREATED), | 480 state_(CS_CREATED), |
| 482 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE), | 481 error_(VideoDecodeAccelerator::VIDEODECODERERROR_NONE), |
| 483 num_decoded_frames_(0), num_done_bitstream_buffers_(0) { | 482 num_decoded_frames_(0), num_done_bitstream_buffers_(0) { |
| 484 CHECK_GT(num_NALUs_per_decode, 0); | 483 CHECK_GT(num_NALUs_per_decode, 0); |
| 485 } | 484 } |
| 486 | 485 |
| 487 EglRenderingVDAClient::~EglRenderingVDAClient() { | 486 EglRenderingVDAClient::~EglRenderingVDAClient() { |
| 488 CHECK(!decoder_.get()); | 487 DeleteDecoder(); // Clean up in case of expected error. |
| 488 CHECK(decoder_deleted()); |
| 489 STLDeleteValues(&picture_buffers_by_id_); | 489 STLDeleteValues(&picture_buffers_by_id_); |
| 490 SetState(CS_DESTROYED); | 490 SetState(CS_DESTROYED); |
| 491 } | 491 } |
| 492 | 492 |
| 493 void EglRenderingVDAClient::CreateDecoder() { | 493 void EglRenderingVDAClient::CreateDecoder() { |
| 494 CHECK(!decoder_.get()); | 494 CHECK(decoder_deleted()); |
| 495 decoder_.reset(new OmxVideoDecodeAccelerator(this)); | 495 decoder_ = new OmxVideoDecodeAccelerator(this); |
| 496 decoder_->SetEglState(egl_display(), egl_context()); | 496 decoder_->SetEglState(egl_display(), egl_context()); |
| 497 SetState(CS_DECODER_SET); | 497 SetState(CS_DECODER_SET); |
| 498 if (!decoder_.get()) | 498 if (decoder_deleted()) |
| 499 return; | 499 return; |
| 500 | 500 |
| 501 // Configure the decoder. | 501 // Configure the decoder. |
| 502 int32 config_array[] = { | 502 int32 config_array[] = { |
| 503 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, | 503 media::VIDEOATTRIBUTEKEY_BITSTREAMFORMAT_FOURCC, |
| 504 media::VIDEOCODECFOURCC_H264, | 504 media::VIDEOCODECFOURCC_H264, |
| 505 media::VIDEOATTRIBUTEKEY_VIDEOCOLORFORMAT, media::VIDEOCOLORFORMAT_RGBA, | 505 media::VIDEOATTRIBUTEKEY_VIDEOCOLORFORMAT, media::VIDEOCOLORFORMAT_RGBA, |
| 506 }; | 506 }; |
| 507 std::vector<uint32> config( | 507 std::vector<uint32> config( |
| 508 config_array, config_array + arraysize(config_array)); | 508 config_array, config_array + arraysize(config_array)); |
| 509 CHECK(decoder_->Initialize(config)); | 509 CHECK(decoder_->Initialize(config)); |
| 510 } | 510 } |
| 511 | 511 |
| 512 | 512 |
| 513 void EglRenderingVDAClient::ProvidePictureBuffers( | 513 void EglRenderingVDAClient::ProvidePictureBuffers( |
| 514 uint32 requested_num_of_buffers, | 514 uint32 requested_num_of_buffers, |
| 515 const gfx::Size& dimensions, | 515 const gfx::Size& dimensions, |
| 516 VideoDecodeAccelerator::MemoryType type) { | 516 VideoDecodeAccelerator::MemoryType type) { |
| 517 if (!decoder_.get()) | 517 if (decoder_deleted()) |
| 518 return; | 518 return; |
| 519 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); | 519 CHECK_EQ(type, VideoDecodeAccelerator::PICTUREBUFFER_MEMORYTYPE_GL_TEXTURE); |
| 520 std::vector<media::GLESBuffer> buffers; | 520 std::vector<media::GLESBuffer> buffers; |
| 521 CHECK_EQ(dimensions.width(), kFrameWidth); | 521 CHECK_EQ(dimensions.width(), kFrameWidth); |
| 522 CHECK_EQ(dimensions.height(), kFrameHeight); | 522 CHECK_EQ(dimensions.height(), kFrameHeight); |
| 523 | 523 |
| 524 for (uint32 i = 0; i < requested_num_of_buffers; ++i) { | 524 for (uint32 i = 0; i < requested_num_of_buffers; ++i) { |
| 525 uint32 id = picture_buffers_by_id_.size(); | 525 uint32 id = picture_buffers_by_id_.size(); |
| 526 GLuint texture_id; | 526 GLuint texture_id; |
| 527 base::WaitableEvent done(false, false); | 527 base::WaitableEvent done(false, false); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 543 DCHECK(it != picture_buffers_by_id_.end()); | 543 DCHECK(it != picture_buffers_by_id_.end()); |
| 544 rendering_helper_->DeleteTexture(it->second->texture_id()); | 544 rendering_helper_->DeleteTexture(it->second->texture_id()); |
| 545 delete it->second; | 545 delete it->second; |
| 546 picture_buffers_by_id_.erase(it); | 546 picture_buffers_by_id_.erase(it); |
| 547 } | 547 } |
| 548 | 548 |
| 549 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { | 549 void EglRenderingVDAClient::PictureReady(const media::Picture& picture) { |
| 550 // We shouldn't be getting pictures delivered after Reset has completed. | 550 // We shouldn't be getting pictures delivered after Reset has completed. |
| 551 DCHECK_LT(state_, CS_RESET); | 551 DCHECK_LT(state_, CS_RESET); |
| 552 | 552 |
| 553 if (!decoder_.get()) | 553 if (decoder_deleted()) |
| 554 return; | 554 return; |
| 555 last_frame_delivered_ticks_ = base::TimeTicks::Now(); | 555 last_frame_delivered_ticks_ = base::TimeTicks::Now(); |
| 556 | 556 |
| 557 // Because we feed the decoder a limited number of NALUs at a time, we can be | 557 // Because we feed the decoder a limited number of NALUs at a time, we can be |
| 558 // sure that the bitstream buffer from which a frame comes has a limited | 558 // sure that the bitstream buffer from which a frame comes has a limited |
| 559 // range. Assert that. | 559 // range. Assert that. |
| 560 CHECK_GE((picture.bitstream_buffer_id() + 1) * num_NALUs_per_decode_, | 560 CHECK_GE((picture.bitstream_buffer_id() + 1) * num_NALUs_per_decode_, |
| 561 num_decoded_frames_); | 561 num_decoded_frames_); |
| 562 CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_); | 562 CHECK_LE(picture.bitstream_buffer_id(), next_bitstream_buffer_id_); |
| 563 ++num_decoded_frames_; | 563 ++num_decoded_frames_; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 580 SetState(CS_DONE); | 580 SetState(CS_DONE); |
| 581 } | 581 } |
| 582 | 582 |
| 583 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer( | 583 void EglRenderingVDAClient::NotifyEndOfBitstreamBuffer( |
| 584 int32 bitstream_buffer_id) { | 584 int32 bitstream_buffer_id) { |
| 585 ++num_done_bitstream_buffers_; | 585 ++num_done_bitstream_buffers_; |
| 586 DecodeNextNALUs(); | 586 DecodeNextNALUs(); |
| 587 } | 587 } |
| 588 | 588 |
| 589 void EglRenderingVDAClient::NotifyFlushDone() { | 589 void EglRenderingVDAClient::NotifyFlushDone() { |
| 590 if (!decoder_.get()) | 590 if (decoder_deleted()) |
| 591 return; | 591 return; |
| 592 SetState(CS_FLUSHED); | 592 SetState(CS_FLUSHED); |
| 593 if (!decoder_.get()) | 593 if (decoder_deleted()) |
| 594 return; | 594 return; |
| 595 decoder_->Reset(); | 595 decoder_->Reset(); |
| 596 } | 596 } |
| 597 | 597 |
| 598 void EglRenderingVDAClient::NotifyResetDone() { | 598 void EglRenderingVDAClient::NotifyResetDone() { |
| 599 if (!decoder_.get()) | 599 if (decoder_deleted()) |
| 600 return; | 600 return; |
| 601 SetState(CS_RESET); | 601 SetState(CS_RESET); |
| 602 if (!decoder_.get()) | 602 if (!decoder_deleted()) |
| 603 return; | 603 DeleteDecoder(); |
| 604 decoder_->Destroy(); | |
| 605 } | |
| 606 | |
| 607 void EglRenderingVDAClient::NotifyDestroyDone() { | |
| 608 SetState(CS_DESTROYED); | |
| 609 } | 604 } |
| 610 | 605 |
| 611 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { | 606 void EglRenderingVDAClient::NotifyError(VideoDecodeAccelerator::Error error) { |
| 612 SetState(CS_ERROR); | 607 SetState(CS_ERROR); |
| 613 error_ = error; | 608 error_ = error; |
| 614 } | 609 } |
| 615 | 610 |
| 616 static bool LookingAtNAL(const std::string& encoded, size_t pos) { | 611 static bool LookingAtNAL(const std::string& encoded, size_t pos) { |
| 617 return pos + 3 < encoded.size() && | 612 return pos + 3 < encoded.size() && |
| 618 encoded[pos] == 0 && encoded[pos + 1] == 0 && | 613 encoded[pos] == 0 && encoded[pos + 1] == 0 && |
| 619 encoded[pos + 2] == 0 && encoded[pos + 3] == 1; | 614 encoded[pos + 2] == 0 && encoded[pos + 3] == 1; |
| 620 } | 615 } |
| 621 | 616 |
| 622 void EglRenderingVDAClient::SetState(ClientState new_state) { | 617 void EglRenderingVDAClient::SetState(ClientState new_state) { |
| 623 note_->Notify(new_state); | 618 note_->Notify(new_state); |
| 624 state_ = new_state; | 619 state_ = new_state; |
| 625 if (new_state == delete_decoder_state_) | 620 if (new_state == delete_decoder_state_) { |
| 621 CHECK(!decoder_deleted()); |
| 626 DeleteDecoder(); | 622 DeleteDecoder(); |
| 623 } |
| 627 } | 624 } |
| 628 | 625 |
| 629 void EglRenderingVDAClient::DeleteDecoder() { | 626 void EglRenderingVDAClient::DeleteDecoder() { |
| 630 decoder_.reset(); | 627 if (decoder_deleted()) |
| 628 return; |
| 629 decoder_->Destroy(); |
| 630 decoder_ = NULL; |
| 631 // Cascade through the rest of the states to simplify test code below. | 631 // Cascade through the rest of the states to simplify test code below. |
| 632 for (int i = state_ + 1; i < CS_MAX; ++i) | 632 for (int i = state_ + 1; i < CS_MAX; ++i) |
| 633 SetState(static_cast<ClientState>(i)); | 633 SetState(static_cast<ClientState>(i)); |
| 634 } | 634 } |
| 635 | 635 |
| 636 void EglRenderingVDAClient::GetRangeForNextNALUs( | 636 void EglRenderingVDAClient::GetRangeForNextNALUs( |
| 637 size_t start_pos, size_t* end_pos) { | 637 size_t start_pos, size_t* end_pos) { |
| 638 *end_pos = start_pos; | 638 *end_pos = start_pos; |
| 639 CHECK(LookingAtNAL(*encoded_data_, start_pos)); | 639 CHECK(LookingAtNAL(*encoded_data_, start_pos)); |
| 640 for (int i = 0; i < num_NALUs_per_decode_; ++i) { | 640 for (int i = 0; i < num_NALUs_per_decode_; ++i) { |
| 641 *end_pos += 4; | 641 *end_pos += 4; |
| 642 while (*end_pos + 3 < encoded_data_->size() && | 642 while (*end_pos + 3 < encoded_data_->size() && |
| 643 !LookingAtNAL(*encoded_data_, *end_pos)) { | 643 !LookingAtNAL(*encoded_data_, *end_pos)) { |
| 644 ++*end_pos; | 644 ++*end_pos; |
| 645 } | 645 } |
| 646 if (*end_pos + 3 >= encoded_data_->size()) { | 646 if (*end_pos + 3 >= encoded_data_->size()) { |
| 647 *end_pos = encoded_data_->size(); | 647 *end_pos = encoded_data_->size(); |
| 648 return; | 648 return; |
| 649 } | 649 } |
| 650 } | 650 } |
| 651 } | 651 } |
| 652 | 652 |
| 653 void EglRenderingVDAClient::DecodeNextNALUs() { | 653 void EglRenderingVDAClient::DecodeNextNALUs() { |
| 654 if (!decoder_.get()) | 654 if (decoder_deleted()) |
| 655 return; | 655 return; |
| 656 if (encoded_data_next_pos_to_decode_ == encoded_data_->size()) { | 656 if (encoded_data_next_pos_to_decode_ == encoded_data_->size()) { |
| 657 decoder_->Flush(); | 657 decoder_->Flush(); |
| 658 return; | 658 return; |
| 659 } | 659 } |
| 660 size_t start_pos = encoded_data_next_pos_to_decode_; | 660 size_t start_pos = encoded_data_next_pos_to_decode_; |
| 661 size_t end_pos; | 661 size_t end_pos; |
| 662 GetRangeForNextNALUs(start_pos, &end_pos); | 662 GetRangeForNextNALUs(start_pos, &end_pos); |
| 663 | 663 |
| 664 // Populate the shared memory buffer w/ the NALU, duplicate its handle, and | 664 // Populate the shared memory buffer w/ the NALU, duplicate its handle, and |
| 665 // hand it off to the decoder. | 665 // hand it off to the decoder. |
| 666 base::SharedMemory shm; | 666 base::SharedMemory shm; |
| 667 CHECK(shm.CreateAndMapAnonymous(end_pos - start_pos)) | 667 CHECK(shm.CreateAndMapAnonymous(end_pos - start_pos)) |
| 668 << start_pos << ", " << end_pos; | 668 << start_pos << ", " << end_pos; |
| 669 memcpy(shm.memory(), encoded_data_->data() + start_pos, end_pos - start_pos); | 669 memcpy(shm.memory(), encoded_data_->data() + start_pos, end_pos - start_pos); |
| 670 base::SharedMemoryHandle dup_handle; | 670 base::SharedMemoryHandle dup_handle; |
| 671 CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle)); | 671 CHECK(shm.ShareToProcess(base::Process::Current().handle(), &dup_handle)); |
| 672 media::BitstreamBuffer bitstream_buffer( | 672 media::BitstreamBuffer bitstream_buffer( |
| 673 next_bitstream_buffer_id_++, dup_handle, end_pos - start_pos); | 673 next_bitstream_buffer_id_++, dup_handle, end_pos - start_pos); |
| 674 decoder_->Decode(bitstream_buffer); | 674 decoder_->Decode(bitstream_buffer); |
| 675 encoded_data_next_pos_to_decode_ = end_pos; | 675 encoded_data_next_pos_to_decode_ = end_pos; |
| 676 | 676 |
| 677 if (-delete_decoder_state_ == next_bitstream_buffer_id_) | 677 if (-delete_decoder_state_ == next_bitstream_buffer_id_) |
| 678 DeleteDecoder(); | 678 DeleteDecoder(); |
| 679 } | 679 } |
| 680 | 680 |
| 681 double EglRenderingVDAClient::frames_per_second() { | 681 double EglRenderingVDAClient::frames_per_second() { |
| 682 base::TimeDelta delta = last_frame_delivered_ticks_ - initialize_done_ticks_; | 682 base::TimeDelta delta = last_frame_delivered_ticks_ - initialize_done_ticks_; |
| 683 CHECK_GT(delta.InSecondsF(), 0); | 683 if (delta.InSecondsF() == 0) |
| 684 return 0; |
| 684 return num_decoded_frames_ / delta.InSecondsF(); | 685 return num_decoded_frames_ / delta.InSecondsF(); |
| 685 } | 686 } |
| 686 | 687 |
| 687 // Test parameters: | 688 // Test parameters: |
| 688 // - Number of NALUs per Decode() call. | 689 // - Number of NALUs per Decode() call. |
| 689 // - Number of concurrent decoders. | 690 // - Number of concurrent decoders. |
| 690 // - When to delete the decoder. N<0 means after -N Decode() calls have been | 691 // - When to delete the decoder. N<0 means after -N Decode() calls have been |
| 691 // made, N>=0 means interpret as ClientState. | 692 // made, N>=0 means interpret as ClientState. |
| 692 class OmxVideoDecodeAcceleratorTest | 693 class OmxVideoDecodeAcceleratorTest |
| 693 : public ::testing::TestWithParam<Tuple3<int, int, ClientState> > { | 694 : public ::testing::TestWithParam<Tuple3<int, int, ClientState> > { |
| 694 }; | 695 }; |
| 695 | 696 |
| 696 // Wait for |note| to report a state and if it's not |expected_state| then | 697 // Wait for |note| to report a state and if it's not |expected_state| then |
| 697 // assert |client| has deleted its decoder. | 698 // assert |client| has deleted its decoder. |
| 698 static void AssertWaitForStateOrDeleted(ClientStateNotification* note, | 699 static void AssertWaitForStateOrDeleted(ClientStateNotification* note, |
| 699 EglRenderingVDAClient* client, | 700 EglRenderingVDAClient* client, |
| 700 ClientState expected_state) { | 701 ClientState expected_state) { |
| 701 ClientState state = note->Wait(); | 702 ClientState state = note->Wait(); |
| 702 if (state == expected_state) return; | 703 if (state == expected_state) return; |
| 703 ASSERT_TRUE(client->decoder_deleted()) | 704 ASSERT_TRUE(client->decoder_deleted()) |
| 704 << "Decoder not deleted but Wait() returned " << state | 705 << "Decoder not deleted but Wait() returned " << state |
| 705 << ", instead of " << expected_state; | 706 << ", instead of " << expected_state; |
| 706 } | 707 } |
| 707 | 708 |
| 709 // We assert the exact number of concurrent decoders we expect to succeed and |
| 710 // that one more than that fails initialization. |
| 711 enum { kMaxSupportedNumConcurrentDecoders = 5 }; |
| 712 |
| 708 // Test the most straightforward case possible: data is decoded from a single | 713 // Test the most straightforward case possible: data is decoded from a single |
| 709 // chunk and rendered to the screen. | 714 // chunk and rendered to the screen. |
| 710 TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { | 715 TEST_P(OmxVideoDecodeAcceleratorTest, TestSimpleDecode) { |
| 711 // Can be useful for debugging VLOGs from OVDA. | 716 // Can be useful for debugging VLOGs from OVDA. |
| 712 // logging::SetMinLogLevel(-1); | 717 // logging::SetMinLogLevel(-1); |
| 713 | 718 |
| 714 // Required for Thread to work. Not used otherwise. | 719 // Required for Thread to work. Not used otherwise. |
| 715 base::ShadowingAtExitManager at_exit_manager; | 720 base::ShadowingAtExitManager at_exit_manager; |
| 716 | 721 |
| 717 const int num_NALUs_per_decode = GetParam().a; | 722 const int num_NALUs_per_decode = GetParam().a; |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 756 clients[index] = client; | 761 clients[index] = client; |
| 757 | 762 |
| 758 rendering_thread.message_loop()->PostTask( | 763 rendering_thread.message_loop()->PostTask( |
| 759 FROM_HERE, | 764 FROM_HERE, |
| 760 base::Bind(&EglRenderingVDAClient::CreateDecoder, | 765 base::Bind(&EglRenderingVDAClient::CreateDecoder, |
| 761 base::Unretained(client))); | 766 base::Unretained(client))); |
| 762 | 767 |
| 763 ASSERT_EQ(note->Wait(), CS_DECODER_SET); | 768 ASSERT_EQ(note->Wait(), CS_DECODER_SET); |
| 764 } | 769 } |
| 765 // Then wait for all the decodes to finish. | 770 // Then wait for all the decodes to finish. |
| 771 bool saw_init_failure = false; |
| 766 for (size_t i = 0; i < num_concurrent_decoders; ++i) { | 772 for (size_t i = 0; i < num_concurrent_decoders; ++i) { |
| 767 ClientStateNotification* note = notes[i]; | 773 ClientStateNotification* note = notes[i]; |
| 768 ASSERT_EQ(note->Wait(), CS_INITIALIZED); | 774 ClientState state = note->Wait(); |
| 775 if (state != CS_INITIALIZED) { |
| 776 saw_init_failure = true; |
| 777 // We expect initialization to fail only when more than the supported |
| 778 // number of decoders is instantiated. Assert here that something else |
| 779 // didn't trigger failure. |
| 780 ASSERT_GT(num_concurrent_decoders, kMaxSupportedNumConcurrentDecoders); |
| 781 continue; |
| 782 } |
| 783 ASSERT_EQ(state, CS_INITIALIZED); |
| 769 // InitializeDone kicks off decoding inside the client, so we just need to | 784 // InitializeDone kicks off decoding inside the client, so we just need to |
| 770 // wait for Flush. | 785 // wait for Flush. |
| 771 ASSERT_NO_FATAL_FAILURE( | 786 ASSERT_NO_FATAL_FAILURE( |
| 772 AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHED)); | 787 AssertWaitForStateOrDeleted(note, clients[i], CS_FLUSHED)); |
| 773 // FlushDone requests Reset(). | 788 // FlushDone requests Reset(). |
| 774 ASSERT_NO_FATAL_FAILURE( | 789 ASSERT_NO_FATAL_FAILURE( |
| 775 AssertWaitForStateOrDeleted(note, clients[i], CS_RESET)); | 790 AssertWaitForStateOrDeleted(note, clients[i], CS_RESET)); |
| 776 // ResetDone requests Destroy(). | 791 // ResetDone requests Destroy(). |
| 777 ASSERT_NO_FATAL_FAILURE( | 792 ASSERT_NO_FATAL_FAILURE( |
| 778 AssertWaitForStateOrDeleted(note, clients[i], CS_DESTROYED)); | 793 AssertWaitForStateOrDeleted(note, clients[i], CS_DESTROYED)); |
| 779 } | 794 } |
| 795 ASSERT_EQ(saw_init_failure, |
| 796 num_concurrent_decoders > kMaxSupportedNumConcurrentDecoders) |
| 797 << num_concurrent_decoders; |
| 780 // Finally assert that decoding went as expected. | 798 // Finally assert that decoding went as expected. |
| 781 for (size_t i = 0; i < num_concurrent_decoders; ++i) { | 799 for (size_t i = 0; i < num_concurrent_decoders && !saw_init_failure; ++i) { |
| 782 // We can only make performance/correctness assertions if the decoder was | 800 // We can only make performance/correctness assertions if the decoder was |
| 783 // allowed to finish. | 801 // allowed to finish. |
| 784 if (delete_decoder_state < CS_FLUSHED) | 802 if (delete_decoder_state < CS_FLUSHED) |
| 785 continue; | 803 continue; |
| 786 EglRenderingVDAClient* client = clients[i]; | 804 EglRenderingVDAClient* client = clients[i]; |
| 787 EXPECT_EQ(client->num_decoded_frames(), 25 /* fps */ * 10 /* seconds */); | 805 EXPECT_EQ(client->num_decoded_frames(), 25 /* fps */ * 10 /* seconds */); |
| 788 // Hard-coded the number of NALUs in the stream. Icky. | 806 // Hard-coded the number of NALUs in the stream. Icky. |
| 789 EXPECT_EQ(client->num_done_bitstream_buffers(), | 807 EXPECT_EQ(client->num_done_bitstream_buffers(), |
| 790 ceil(258.0 / num_NALUs_per_decode)); | 808 ceil(258.0 / num_NALUs_per_decode)); |
| 791 // These numbers are pulled out of a hat, but seem to be safe with current | 809 // These numbers are pulled out of a hat, but seem to be safe with current |
| (...skipping 14 matching lines...) Expand all Loading... |
| 806 ¬es)); | 824 ¬es)); |
| 807 rendering_thread.message_loop()->PostTask( | 825 rendering_thread.message_loop()->PostTask( |
| 808 FROM_HERE, | 826 FROM_HERE, |
| 809 base::Bind(&RenderingHelper::UnInitialize, | 827 base::Bind(&RenderingHelper::UnInitialize, |
| 810 base::Unretained(&rendering_helper), | 828 base::Unretained(&rendering_helper), |
| 811 &done)); | 829 &done)); |
| 812 done.Wait(); | 830 done.Wait(); |
| 813 rendering_thread.Stop(); | 831 rendering_thread.Stop(); |
| 814 }; | 832 }; |
| 815 | 833 |
| 816 // TODO(fischman): Remove DISABLED_ prefix when ~OVDA can tear down OMX | |
| 817 // synchronously (http://crosbug.com/p/4869). | |
| 818 INSTANTIATE_TEST_CASE_P( | 834 INSTANTIATE_TEST_CASE_P( |
| 819 DISABLED_TearDownTiming, OmxVideoDecodeAcceleratorTest, | 835 TearDownTiming, OmxVideoDecodeAcceleratorTest, |
| 820 ::testing::Values( | 836 ::testing::Values( |
| 821 MakeTuple(1, 1, CS_DECODER_SET), MakeTuple(1, 1, CS_INITIALIZED), | 837 MakeTuple(1, 1, CS_DECODER_SET), MakeTuple(1, 1, CS_INITIALIZED), |
| 822 MakeTuple(1, 1, CS_FLUSHED), MakeTuple(1, 1, CS_RESET), | 838 MakeTuple(1, 1, CS_FLUSHED), MakeTuple(1, 1, CS_RESET), |
| 823 MakeTuple(1, 1, static_cast<ClientState>(-1)), | 839 MakeTuple(1, 1, static_cast<ClientState>(-1)), |
| 824 MakeTuple(1, 1, static_cast<ClientState>(-10)), | 840 MakeTuple(1, 1, static_cast<ClientState>(-10)), |
| 825 MakeTuple(1, 1, static_cast<ClientState>(-100)))); | 841 MakeTuple(1, 1, static_cast<ClientState>(-100)))); |
| 826 | 842 |
| 827 // TODO(fischman): using 2nd param of 16 and higher below breaks decode - visual | 843 // TODO(fischman): using 1st param of 16 and higher below breaks decode - visual |
| 828 // artifacts are introduced as well as spurious frames are delivered (more | 844 // artifacts are introduced as well as spurious frames are delivered (more |
| 829 // pictures are returned than NALUs are fed to the decoder). Increase the "15" | 845 // pictures are returned than NALUs are fed to the decoder). Increase the "15" |
| 830 // below when http://code.google.com/p/chrome-os-partner/issues/detail?id=4378 | 846 // below when http://code.google.com/p/chrome-os-partner/issues/detail?id=4378 |
| 831 // is fixed. | 847 // is fixed. |
| 832 INSTANTIATE_TEST_CASE_P( | 848 INSTANTIATE_TEST_CASE_P( |
| 833 DecodeVariations, OmxVideoDecodeAcceleratorTest, | 849 DecodeVariations, OmxVideoDecodeAcceleratorTest, |
| 834 ::testing::Values( | 850 ::testing::Values( |
| 835 MakeTuple(1, 1, CS_DESTROYED), MakeTuple(1, 3, CS_DESTROYED), | 851 MakeTuple(1, 1, CS_RESET), MakeTuple(1, 3, CS_RESET), |
| 836 MakeTuple(2, 1, CS_DESTROYED), MakeTuple(3, 1, CS_DESTROYED), | 852 MakeTuple(2, 1, CS_RESET), MakeTuple(3, 1, CS_RESET), |
| 837 MakeTuple(5, 1, CS_DESTROYED), MakeTuple(8, 1, CS_DESTROYED), | 853 MakeTuple(5, 1, CS_RESET), MakeTuple(8, 1, CS_RESET), |
| 838 MakeTuple(15, 1, CS_DESTROYED))); | 854 MakeTuple(15, 1, CS_RESET))); |
| 855 |
| 856 // Find out how many concurrent decoders can go before we exhaust system |
| 857 // resources. |
| 858 INSTANTIATE_TEST_CASE_P( |
| 859 ResourceExhaustion, OmxVideoDecodeAcceleratorTest, |
| 860 ::testing::Values( |
| 861 // +0 hack below to promote enum to int. |
| 862 MakeTuple(1, kMaxSupportedNumConcurrentDecoders + 0, CS_RESET), |
| 863 MakeTuple(1, kMaxSupportedNumConcurrentDecoders + 1, CS_RESET))); |
| 839 | 864 |
| 840 // TODO(fischman, vrk): add more tests! In particular: | 865 // TODO(fischman, vrk): add more tests! In particular: |
| 841 // - Test life-cycle: Seek/Stop/Pause/Play/RePlay for a single decoder. | 866 // - Test life-cycle: Seek/Stop/Pause/Play/RePlay for a single decoder. |
| 842 // - Test alternate configurations | 867 // - Test alternate configurations |
| 843 // - Test failure conditions. | 868 // - Test failure conditions. |
| 844 // - Test frame size changes mid-stream | 869 // - Test frame size changes mid-stream |
| 845 | 870 |
| 846 } // namespace | 871 } // namespace |
| OLD | NEW |