Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 <dlfcn.h> | 5 #include <dlfcn.h> |
| 6 #include <errno.h> | 6 #include <errno.h> |
| 7 #include <fcntl.h> | 7 #include <fcntl.h> |
| 8 #include <linux/videodev2.h> | 8 #include <linux/videodev2.h> |
| 9 #include <poll.h> | 9 #include <poll.h> |
| 10 #include <string.h> | 10 #include <string.h> |
| 11 #include <sys/eventfd.h> | 11 #include <sys/eventfd.h> |
| 12 #include <sys/ioctl.h> | 12 #include <sys/ioctl.h> |
| 13 #include <sys/mman.h> | 13 #include <sys/mman.h> |
| 14 | 14 |
| 15 #include "base/bind.h" | 15 #include "base/bind.h" |
| 16 #include "base/command_line.h" | 16 #include "base/command_line.h" |
| 17 #include "base/macros.h" | 17 #include "base/macros.h" |
| 18 #include "base/memory/shared_memory.h" | |
| 19 #include "base/message_loop/message_loop.h" | 18 #include "base/message_loop/message_loop.h" |
| 20 #include "base/numerics/safe_conversions.h" | 19 #include "base/numerics/safe_conversions.h" |
| 21 #include "base/thread_task_runner_handle.h" | 20 #include "base/thread_task_runner_handle.h" |
| 22 #include "base/trace_event/trace_event.h" | 21 #include "base/trace_event/trace_event.h" |
| 23 #include "build/build_config.h" | 22 #include "build/build_config.h" |
| 23 #include "content/common/gpu/media/shared_memory_region.h" | |
| 24 #include "content/common/gpu/media/v4l2_video_decode_accelerator.h" | 24 #include "content/common/gpu/media/v4l2_video_decode_accelerator.h" |
| 25 #include "media/base/media_switches.h" | 25 #include "media/base/media_switches.h" |
| 26 #include "media/filters/h264_parser.h" | 26 #include "media/filters/h264_parser.h" |
| 27 #include "ui/gfx/geometry/rect.h" | 27 #include "ui/gfx/geometry/rect.h" |
| 28 #include "ui/gl/scoped_binders.h" | 28 #include "ui/gl/scoped_binders.h" |
| 29 | 29 |
| 30 #define NOTIFY_ERROR(x) \ | 30 #define NOTIFY_ERROR(x) \ |
| 31 do { \ | 31 do { \ |
| 32 LOG(ERROR) << "Setting error state:" << x; \ | 32 LOG(ERROR) << "Setting error state:" << x; \ |
| 33 SetErrorState(x); \ | 33 SetErrorState(x); \ |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 58 | 58 |
| 59 // static | 59 // static |
| 60 const uint32_t V4L2VideoDecodeAccelerator::supported_input_fourccs_[] = { | 60 const uint32_t V4L2VideoDecodeAccelerator::supported_input_fourccs_[] = { |
| 61 V4L2_PIX_FMT_H264, V4L2_PIX_FMT_VP8, V4L2_PIX_FMT_VP9, | 61 V4L2_PIX_FMT_H264, V4L2_PIX_FMT_VP8, V4L2_PIX_FMT_VP9, |
| 62 }; | 62 }; |
| 63 | 63 |
| 64 struct V4L2VideoDecodeAccelerator::BitstreamBufferRef { | 64 struct V4L2VideoDecodeAccelerator::BitstreamBufferRef { |
| 65 BitstreamBufferRef( | 65 BitstreamBufferRef( |
| 66 base::WeakPtr<Client>& client, | 66 base::WeakPtr<Client>& client, |
| 67 scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner, | 67 scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner, |
| 68 base::SharedMemory* shm, | 68 SharedMemoryRegion* shm, |
|
dcheng
2016/03/01 01:48:43
scoped_ptr<SharedMemoryRegion> to have the compile
Owen Lin
2016/03/02 02:50:53
Done.
| |
| 69 size_t size, | |
| 70 int32_t input_id); | 69 int32_t input_id); |
| 71 ~BitstreamBufferRef(); | 70 ~BitstreamBufferRef(); |
| 72 const base::WeakPtr<Client> client; | 71 const base::WeakPtr<Client> client; |
| 73 const scoped_refptr<base::SingleThreadTaskRunner> client_task_runner; | 72 const scoped_refptr<base::SingleThreadTaskRunner> client_task_runner; |
| 74 const scoped_ptr<base::SharedMemory> shm; | 73 const scoped_ptr<SharedMemoryRegion> shm; |
| 75 const size_t size; | |
| 76 size_t bytes_used; | 74 size_t bytes_used; |
| 77 const int32_t input_id; | 75 const int32_t input_id; |
| 78 }; | 76 }; |
| 79 | 77 |
| 80 struct V4L2VideoDecodeAccelerator::EGLSyncKHRRef { | 78 struct V4L2VideoDecodeAccelerator::EGLSyncKHRRef { |
| 81 EGLSyncKHRRef(EGLDisplay egl_display, EGLSyncKHR egl_sync); | 79 EGLSyncKHRRef(EGLDisplay egl_display, EGLSyncKHR egl_sync); |
| 82 ~EGLSyncKHRRef(); | 80 ~EGLSyncKHRRef(); |
| 83 EGLDisplay const egl_display; | 81 EGLDisplay const egl_display; |
| 84 EGLSyncKHR egl_sync; | 82 EGLSyncKHR egl_sync; |
| 85 }; | 83 }; |
| 86 | 84 |
| 87 struct V4L2VideoDecodeAccelerator::PictureRecord { | 85 struct V4L2VideoDecodeAccelerator::PictureRecord { |
| 88 PictureRecord(bool cleared, const media::Picture& picture); | 86 PictureRecord(bool cleared, const media::Picture& picture); |
| 89 ~PictureRecord(); | 87 ~PictureRecord(); |
| 90 bool cleared; // Whether the texture is cleared and safe to render from. | 88 bool cleared; // Whether the texture is cleared and safe to render from. |
| 91 media::Picture picture; // The decoded picture. | 89 media::Picture picture; // The decoded picture. |
| 92 }; | 90 }; |
| 93 | 91 |
| 94 V4L2VideoDecodeAccelerator::BitstreamBufferRef::BitstreamBufferRef( | 92 V4L2VideoDecodeAccelerator::BitstreamBufferRef::BitstreamBufferRef( |
| 95 base::WeakPtr<Client>& client, | 93 base::WeakPtr<Client>& client, |
| 96 scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner, | 94 scoped_refptr<base::SingleThreadTaskRunner>& client_task_runner, |
| 97 base::SharedMemory* shm, | 95 SharedMemoryRegion* shm, |
| 98 size_t size, | |
| 99 int32_t input_id) | 96 int32_t input_id) |
| 100 : client(client), | 97 : client(client), |
| 101 client_task_runner(client_task_runner), | 98 client_task_runner(client_task_runner), |
| 102 shm(shm), | 99 shm(shm), |
| 103 size(size), | |
| 104 bytes_used(0), | 100 bytes_used(0), |
| 105 input_id(input_id) {} | 101 input_id(input_id) {} |
| 106 | 102 |
| 107 V4L2VideoDecodeAccelerator::BitstreamBufferRef::~BitstreamBufferRef() { | 103 V4L2VideoDecodeAccelerator::BitstreamBufferRef::~BitstreamBufferRef() { |
| 108 if (input_id >= 0) { | 104 if (input_id >= 0) { |
| 109 client_task_runner->PostTask( | 105 client_task_runner->PostTask( |
| 110 FROM_HERE, | 106 FROM_HERE, |
| 111 base::Bind(&Client::NotifyEndOfBitstreamBuffer, client, input_id)); | 107 base::Bind(&Client::NotifyEndOfBitstreamBuffer, client, input_id)); |
| 112 } | 108 } |
| 113 } | 109 } |
| (...skipping 361 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 475 void V4L2VideoDecodeAccelerator::DecodeTask( | 471 void V4L2VideoDecodeAccelerator::DecodeTask( |
| 476 const media::BitstreamBuffer& bitstream_buffer) { | 472 const media::BitstreamBuffer& bitstream_buffer) { |
| 477 DVLOG(3) << "DecodeTask(): input_id=" << bitstream_buffer.id(); | 473 DVLOG(3) << "DecodeTask(): input_id=" << bitstream_buffer.id(); |
| 478 DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); | 474 DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); |
| 479 DCHECK_NE(decoder_state_, kUninitialized); | 475 DCHECK_NE(decoder_state_, kUninitialized); |
| 480 TRACE_EVENT1("Video Decoder", "V4L2VDA::DecodeTask", "input_id", | 476 TRACE_EVENT1("Video Decoder", "V4L2VDA::DecodeTask", "input_id", |
| 481 bitstream_buffer.id()); | 477 bitstream_buffer.id()); |
| 482 | 478 |
| 483 scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef( | 479 scoped_ptr<BitstreamBufferRef> bitstream_record(new BitstreamBufferRef( |
| 484 io_client_, io_task_runner_, | 480 io_client_, io_task_runner_, |
| 485 new base::SharedMemory(bitstream_buffer.handle(), true), | 481 new SharedMemoryRegion(bitstream_buffer, true), bitstream_buffer.id())); |
| 486 bitstream_buffer.size(), bitstream_buffer.id())); | 482 if (!bitstream_record->shm->Map()) { |
| 487 if (!bitstream_record->shm->Map(bitstream_buffer.size())) { | |
| 488 LOG(ERROR) << "Decode(): could not map bitstream_buffer"; | 483 LOG(ERROR) << "Decode(): could not map bitstream_buffer"; |
| 489 NOTIFY_ERROR(UNREADABLE_INPUT); | 484 NOTIFY_ERROR(UNREADABLE_INPUT); |
| 490 return; | 485 return; |
| 491 } | 486 } |
| 492 DVLOG(3) << "DecodeTask(): mapped at=" << bitstream_record->shm->memory(); | 487 DVLOG(3) << "DecodeTask(): mapped at=" << bitstream_record->shm->memory(); |
| 493 | 488 |
| 494 if (decoder_state_ == kResetting || decoder_flushing_) { | 489 if (decoder_state_ == kResetting || decoder_flushing_) { |
| 495 // In the case that we're resetting or flushing, we need to delay decoding | 490 // In the case that we're resetting or flushing, we need to delay decoding |
| 496 // the BitstreamBuffers that come after the Reset() or Flush() call. When | 491 // the BitstreamBuffers that come after the Reset() or Flush() call. When |
| 497 // we're here, we know that this DecodeTask() was scheduled by a Decode() | 492 // we're here, we know that this DecodeTask() was scheduled by a Decode() |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 536 } | 531 } |
| 537 linked_ptr<BitstreamBufferRef>& buffer_ref = decoder_input_queue_.front(); | 532 linked_ptr<BitstreamBufferRef>& buffer_ref = decoder_input_queue_.front(); |
| 538 if (decoder_delay_bitstream_buffer_id_ == buffer_ref->input_id) { | 533 if (decoder_delay_bitstream_buffer_id_ == buffer_ref->input_id) { |
| 539 // We're asked to delay decoding on this and subsequent buffers. | 534 // We're asked to delay decoding on this and subsequent buffers. |
| 540 return; | 535 return; |
| 541 } | 536 } |
| 542 | 537 |
| 543 // Setup to use the next buffer. | 538 // Setup to use the next buffer. |
| 544 decoder_current_bitstream_buffer_.reset(buffer_ref.release()); | 539 decoder_current_bitstream_buffer_.reset(buffer_ref.release()); |
| 545 decoder_input_queue_.pop(); | 540 decoder_input_queue_.pop(); |
| 546 DVLOG(3) << "DecodeBufferTask(): reading input_id=" | 541 const auto& shm = decoder_current_bitstream_buffer_->shm; |
| 547 << decoder_current_bitstream_buffer_->input_id | 542 if (shm) { |
| 548 << ", addr=" << (decoder_current_bitstream_buffer_->shm ? | 543 DVLOG(3) << "DecodeBufferTask(): reading input_id=" |
| 549 decoder_current_bitstream_buffer_->shm->memory() : | 544 << decoder_current_bitstream_buffer_->input_id |
| 550 NULL) | 545 << ", addr=" << shm->memory() << ", size=" << shm->size(); |
| 551 << ", size=" << decoder_current_bitstream_buffer_->size; | 546 } else { |
| 547 DCHECK_EQ(decoder_current_bitstream_buffer_->input_id, kFlushBufferId); | |
| 548 DVLOG(3) << "DecodeBufferTask(): reading input_id=kFlushBufferId"; | |
| 549 } | |
| 552 } | 550 } |
| 553 bool schedule_task = false; | 551 bool schedule_task = false; |
| 554 const size_t size = decoder_current_bitstream_buffer_->size; | |
| 555 size_t decoded_size = 0; | 552 size_t decoded_size = 0; |
| 556 if (size == 0) { | 553 const auto& shm = decoder_current_bitstream_buffer_->shm; |
| 557 const int32_t input_id = decoder_current_bitstream_buffer_->input_id; | 554 if (!shm) { |
| 558 if (input_id >= 0) { | 555 // This is a dummy buffer, queued to flush the pipe. Flush. |
| 559 // This is a buffer queued from the client that has zero size. Skip. | 556 DCHECK_EQ(decoder_current_bitstream_buffer_->input_id, kFlushBufferId); |
| 557 // Enqueue a buffer guaranteed to be empty. To do that, we flush the | |
| 558 // current input, enqueue no data to the next frame, then flush that down. | |
| 559 schedule_task = true; | |
| 560 if (decoder_current_input_buffer_ != -1 && | |
| 561 input_buffer_map_[decoder_current_input_buffer_].input_id != | |
| 562 kFlushBufferId) | |
| 563 schedule_task = FlushInputFrame(); | |
| 564 | |
| 565 if (schedule_task && AppendToInputFrame(NULL, 0) && FlushInputFrame()) { | |
| 566 DVLOG(2) << "DecodeBufferTask(): enqueued flush buffer"; | |
| 567 decoder_partial_frame_pending_ = false; | |
| 560 schedule_task = true; | 568 schedule_task = true; |
| 561 } else { | 569 } else { |
| 562 // This is a buffer of zero size, queued to flush the pipe. Flush. | 570 // If we failed to enqueue the empty buffer (due to pipeline |
| 563 DCHECK_EQ(decoder_current_bitstream_buffer_->shm.get(), | 571 // backpressure), don't advance the bitstream buffer queue, and don't |
| 564 static_cast<base::SharedMemory*>(NULL)); | 572 // schedule the next task. This bitstream buffer queue entry will get |
| 565 // Enqueue a buffer guaranteed to be empty. To do that, we flush the | 573 // reprocessed when the pipeline frees up. |
| 566 // current input, enqueue no data to the next frame, then flush that down. | 574 schedule_task = false; |
| 567 schedule_task = true; | |
| 568 if (decoder_current_input_buffer_ != -1 && | |
| 569 input_buffer_map_[decoder_current_input_buffer_].input_id != | |
| 570 kFlushBufferId) | |
| 571 schedule_task = FlushInputFrame(); | |
| 572 | |
| 573 if (schedule_task && AppendToInputFrame(NULL, 0) && FlushInputFrame()) { | |
| 574 DVLOG(2) << "DecodeBufferTask(): enqueued flush buffer"; | |
| 575 decoder_partial_frame_pending_ = false; | |
| 576 schedule_task = true; | |
| 577 } else { | |
| 578 // If we failed to enqueue the empty buffer (due to pipeline | |
| 579 // backpressure), don't advance the bitstream buffer queue, and don't | |
| 580 // schedule the next task. This bitstream buffer queue entry will get | |
| 581 // reprocessed when the pipeline frees up. | |
| 582 schedule_task = false; | |
| 583 } | |
| 584 } | 575 } |
| 576 } else if (shm->size() == 0) { | |
| 577 // This is a buffer queued from the client that has zero size. Skip. | |
| 578 schedule_task = true; | |
| 585 } else { | 579 } else { |
| 586 // This is a buffer queued from the client, with actual contents. Decode. | 580 // This is a buffer queued from the client, with actual contents. Decode. |
| 587 const uint8_t* const data = | 581 const uint8_t* const data = |
| 588 reinterpret_cast<const uint8_t*>( | 582 reinterpret_cast<const uint8_t*>(shm->memory()) + |
| 589 decoder_current_bitstream_buffer_->shm->memory()) + | |
| 590 decoder_current_bitstream_buffer_->bytes_used; | 583 decoder_current_bitstream_buffer_->bytes_used; |
| 591 const size_t data_size = | 584 const size_t data_size = |
| 592 decoder_current_bitstream_buffer_->size - | 585 shm->size() - decoder_current_bitstream_buffer_->bytes_used; |
| 593 decoder_current_bitstream_buffer_->bytes_used; | |
| 594 if (!AdvanceFrameFragment(data, data_size, &decoded_size)) { | 586 if (!AdvanceFrameFragment(data, data_size, &decoded_size)) { |
| 595 NOTIFY_ERROR(UNREADABLE_INPUT); | 587 NOTIFY_ERROR(UNREADABLE_INPUT); |
| 596 return; | 588 return; |
| 597 } | 589 } |
| 598 // AdvanceFrameFragment should not return a size larger than the buffer | 590 // AdvanceFrameFragment should not return a size larger than the buffer |
| 599 // size, even on invalid data. | 591 // size, even on invalid data. |
| 600 CHECK_LE(decoded_size, data_size); | 592 CHECK_LE(decoded_size, data_size); |
| 601 | 593 |
| 602 switch (decoder_state_) { | 594 switch (decoder_state_) { |
| 603 case kInitialized: | 595 case kInitialized: |
| 604 case kAfterReset: | 596 case kAfterReset: |
| 605 schedule_task = DecodeBufferInitial(data, decoded_size, &decoded_size); | 597 schedule_task = DecodeBufferInitial(data, decoded_size, &decoded_size); |
| 606 break; | 598 break; |
| 607 case kDecoding: | 599 case kDecoding: |
| 608 schedule_task = DecodeBufferContinue(data, decoded_size); | 600 schedule_task = DecodeBufferContinue(data, decoded_size); |
| 609 break; | 601 break; |
| 610 default: | 602 default: |
| 611 NOTIFY_ERROR(ILLEGAL_STATE); | 603 NOTIFY_ERROR(ILLEGAL_STATE); |
| 612 return; | 604 return; |
| 613 } | 605 } |
| 614 } | 606 } |
| 615 if (decoder_state_ == kError) { | 607 if (decoder_state_ == kError) { |
| 616 // Failed during decode. | 608 // Failed during decode. |
| 617 return; | 609 return; |
| 618 } | 610 } |
| 619 | 611 |
| 620 if (schedule_task) { | 612 if (schedule_task) { |
| 621 decoder_current_bitstream_buffer_->bytes_used += decoded_size; | 613 decoder_current_bitstream_buffer_->bytes_used += decoded_size; |
| 622 if (decoder_current_bitstream_buffer_->bytes_used == | 614 if ((shm ? shm->size() : 0) == |
| 623 decoder_current_bitstream_buffer_->size) { | 615 decoder_current_bitstream_buffer_->bytes_used) { |
| 624 // Our current bitstream buffer is done; return it. | 616 // Our current bitstream buffer is done; return it. |
| 625 int32_t input_id = decoder_current_bitstream_buffer_->input_id; | 617 int32_t input_id = decoder_current_bitstream_buffer_->input_id; |
| 626 DVLOG(3) << "DecodeBufferTask(): finished input_id=" << input_id; | 618 DVLOG(3) << "DecodeBufferTask(): finished input_id=" << input_id; |
| 627 // BitstreamBufferRef destructor calls NotifyEndOfBitstreamBuffer(). | 619 // BitstreamBufferRef destructor calls NotifyEndOfBitstreamBuffer(). |
| 628 decoder_current_bitstream_buffer_.reset(); | 620 decoder_current_bitstream_buffer_.reset(); |
| 629 } | 621 } |
| 630 ScheduleDecodeBufferTaskIfNeeded(); | 622 ScheduleDecodeBufferTaskIfNeeded(); |
| 631 } | 623 } |
| 632 } | 624 } |
| 633 | 625 |
| (...skipping 635 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1269 DVLOG(2) << "FlushTask(): early out: kError state"; | 1261 DVLOG(2) << "FlushTask(): early out: kError state"; |
| 1270 return; | 1262 return; |
| 1271 } | 1263 } |
| 1272 | 1264 |
| 1273 // We don't support stacked flushing. | 1265 // We don't support stacked flushing. |
| 1274 DCHECK(!decoder_flushing_); | 1266 DCHECK(!decoder_flushing_); |
| 1275 | 1267 |
| 1276 // Queue up an empty buffer -- this triggers the flush. | 1268 // Queue up an empty buffer -- this triggers the flush. |
| 1277 decoder_input_queue_.push( | 1269 decoder_input_queue_.push( |
| 1278 linked_ptr<BitstreamBufferRef>(new BitstreamBufferRef( | 1270 linked_ptr<BitstreamBufferRef>(new BitstreamBufferRef( |
| 1279 io_client_, io_task_runner_, NULL, 0, kFlushBufferId))); | 1271 io_client_, io_task_runner_, nullptr, kFlushBufferId))); |
| 1280 decoder_flushing_ = true; | 1272 decoder_flushing_ = true; |
| 1281 SendPictureReady(); // Send all pending PictureReady. | 1273 SendPictureReady(); // Send all pending PictureReady. |
| 1282 | 1274 |
| 1283 ScheduleDecodeBufferTaskIfNeeded(); | 1275 ScheduleDecodeBufferTaskIfNeeded(); |
| 1284 } | 1276 } |
| 1285 | 1277 |
| 1286 void V4L2VideoDecodeAccelerator::NotifyFlushDoneIfNeeded() { | 1278 void V4L2VideoDecodeAccelerator::NotifyFlushDoneIfNeeded() { |
| 1287 if (!decoder_flushing_) | 1279 if (!decoder_flushing_) |
| 1288 return; | 1280 return; |
| 1289 | 1281 |
| (...skipping 743 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2033 | 2025 |
| 2034 void V4L2VideoDecodeAccelerator::PictureCleared() { | 2026 void V4L2VideoDecodeAccelerator::PictureCleared() { |
| 2035 DVLOG(3) << "PictureCleared(). clearing count=" << picture_clearing_count_; | 2027 DVLOG(3) << "PictureCleared(). clearing count=" << picture_clearing_count_; |
| 2036 DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); | 2028 DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current()); |
| 2037 DCHECK_GT(picture_clearing_count_, 0); | 2029 DCHECK_GT(picture_clearing_count_, 0); |
| 2038 picture_clearing_count_--; | 2030 picture_clearing_count_--; |
| 2039 SendPictureReady(); | 2031 SendPictureReady(); |
| 2040 } | 2032 } |
| 2041 | 2033 |
| 2042 } // namespace content | 2034 } // namespace content |
| OLD | NEW |