OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "content/renderer/media/rtc_video_decoder.h" | 5 #include "content/renderer/media/rtc_video_decoder.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/ref_counted.h" | 9 #include "base/memory/ref_counted.h" |
10 #include "base/metrics/histogram.h" | 10 #include "base/metrics/histogram.h" |
11 #include "base/numerics/safe_conversions.h" | 11 #include "base/numerics/safe_conversions.h" |
12 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
13 #include "base/synchronization/waitable_event.h" | 13 #include "base/synchronization/waitable_event.h" |
14 #include "base/task_runner_util.h" | 14 #include "base/task_runner_util.h" |
15 #include "content/renderer/media/webrtc/webrtc_video_frame_adapter.h" | 15 #include "content/renderer/media/webrtc/webrtc_video_frame_adapter.h" |
16 #include "gpu/command_buffer/common/mailbox_holder.h" | 16 #include "gpu/command_buffer/common/mailbox_holder.h" |
17 #include "media/base/bind_to_current_loop.h" | 17 #include "media/base/bind_to_current_loop.h" |
18 #include "media/renderers/gpu_video_accelerator_factories.h" | 18 #include "media/renderers/gpu_video_accelerator_factories.h" |
19 #include "third_party/skia/include/core/SkBitmap.h" | 19 #include "third_party/skia/include/core/SkBitmap.h" |
20 #include "third_party/webrtc/base/bind.h" | 20 #include "third_party/webrtc/base/bind.h" |
21 #include "third_party/webrtc/system_wrappers/include/ref_count.h" | 21 #include "third_party/webrtc/system_wrappers/include/ref_count.h" |
22 #include "third_party/webrtc/video_frame.h" | 22 #include "third_party/webrtc/video_frame.h" |
23 | 23 |
24 namespace content { | 24 namespace content { |
25 | 25 |
26 const int32 RTCVideoDecoder::ID_LAST = 0x3FFFFFFF; | 26 const int32_t RTCVideoDecoder::ID_LAST = 0x3FFFFFFF; |
27 const int32 RTCVideoDecoder::ID_HALF = 0x20000000; | 27 const int32_t RTCVideoDecoder::ID_HALF = 0x20000000; |
28 const int32 RTCVideoDecoder::ID_INVALID = -1; | 28 const int32_t RTCVideoDecoder::ID_INVALID = -1; |
29 | 29 |
30 // Maximum number of concurrent VDA::Decode() operations RVD will maintain. | 30 // Maximum number of concurrent VDA::Decode() operations RVD will maintain. |
31 // Higher values allow better pipelining in the GPU, but also require more | 31 // Higher values allow better pipelining in the GPU, but also require more |
32 // resources. | 32 // resources. |
33 static const size_t kMaxInFlightDecodes = 8; | 33 static const size_t kMaxInFlightDecodes = 8; |
34 | 34 |
35 // Number of allocated shared memory segments. | 35 // Number of allocated shared memory segments. |
36 static const size_t kNumSharedMemorySegments = 16; | 36 static const size_t kNumSharedMemorySegments = 16; |
37 | 37 |
38 // Maximum number of pending WebRTC buffers that are waiting for shared memory. | 38 // Maximum number of pending WebRTC buffers that are waiting for shared memory. |
39 static const size_t kMaxNumOfPendingBuffers = 8; | 39 static const size_t kMaxNumOfPendingBuffers = 8; |
40 | 40 |
41 RTCVideoDecoder::BufferData::BufferData(int32 bitstream_buffer_id, | 41 RTCVideoDecoder::BufferData::BufferData(int32_t bitstream_buffer_id, |
42 uint32_t timestamp, | 42 uint32_t timestamp, |
43 size_t size, | 43 size_t size, |
44 const gfx::Rect& visible_rect) | 44 const gfx::Rect& visible_rect) |
45 : bitstream_buffer_id(bitstream_buffer_id), | 45 : bitstream_buffer_id(bitstream_buffer_id), |
46 timestamp(timestamp), | 46 timestamp(timestamp), |
47 size(size), | 47 size(size), |
48 visible_rect(visible_rect) {} | 48 visible_rect(visible_rect) {} |
49 | 49 |
50 RTCVideoDecoder::BufferData::BufferData() {} | 50 RTCVideoDecoder::BufferData::BufferData() {} |
51 | 51 |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 if (state_ != RESETTING) { | 278 if (state_ != RESETTING) { |
279 state_ = RESETTING; | 279 state_ = RESETTING; |
280 factories_->GetTaskRunner()->PostTask( | 280 factories_->GetTaskRunner()->PostTask( |
281 FROM_HERE, | 281 FROM_HERE, |
282 base::Bind(&RTCVideoDecoder::ResetInternal, | 282 base::Bind(&RTCVideoDecoder::ResetInternal, |
283 weak_factory_.GetWeakPtr())); | 283 weak_factory_.GetWeakPtr())); |
284 } | 284 } |
285 return WEBRTC_VIDEO_CODEC_OK; | 285 return WEBRTC_VIDEO_CODEC_OK; |
286 } | 286 } |
287 | 287 |
288 void RTCVideoDecoder::ProvidePictureBuffers(uint32 count, | 288 void RTCVideoDecoder::ProvidePictureBuffers(uint32_t count, |
289 const gfx::Size& size, | 289 const gfx::Size& size, |
290 uint32 texture_target) { | 290 uint32_t texture_target) { |
291 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 291 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
292 DVLOG(3) << "ProvidePictureBuffers. texture_target=" << texture_target; | 292 DVLOG(3) << "ProvidePictureBuffers. texture_target=" << texture_target; |
293 | 293 |
294 if (!vda_) | 294 if (!vda_) |
295 return; | 295 return; |
296 | 296 |
297 std::vector<uint32> texture_ids; | 297 std::vector<uint32_t> texture_ids; |
298 std::vector<gpu::Mailbox> texture_mailboxes; | 298 std::vector<gpu::Mailbox> texture_mailboxes; |
299 decoder_texture_target_ = texture_target; | 299 decoder_texture_target_ = texture_target; |
300 if (!factories_->CreateTextures(count, | 300 if (!factories_->CreateTextures(count, |
301 size, | 301 size, |
302 &texture_ids, | 302 &texture_ids, |
303 &texture_mailboxes, | 303 &texture_mailboxes, |
304 decoder_texture_target_)) { | 304 decoder_texture_target_)) { |
305 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | 305 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); |
306 return; | 306 return; |
307 } | 307 } |
308 DCHECK_EQ(count, texture_ids.size()); | 308 DCHECK_EQ(count, texture_ids.size()); |
309 DCHECK_EQ(count, texture_mailboxes.size()); | 309 DCHECK_EQ(count, texture_mailboxes.size()); |
310 | 310 |
311 std::vector<media::PictureBuffer> picture_buffers; | 311 std::vector<media::PictureBuffer> picture_buffers; |
312 for (size_t i = 0; i < texture_ids.size(); ++i) { | 312 for (size_t i = 0; i < texture_ids.size(); ++i) { |
313 picture_buffers.push_back(media::PictureBuffer( | 313 picture_buffers.push_back(media::PictureBuffer( |
314 next_picture_buffer_id_++, size, texture_ids[i], texture_mailboxes[i])); | 314 next_picture_buffer_id_++, size, texture_ids[i], texture_mailboxes[i])); |
315 bool inserted = assigned_picture_buffers_.insert(std::make_pair( | 315 bool inserted = assigned_picture_buffers_.insert(std::make_pair( |
316 picture_buffers.back().id(), picture_buffers.back())).second; | 316 picture_buffers.back().id(), picture_buffers.back())).second; |
317 DCHECK(inserted); | 317 DCHECK(inserted); |
318 } | 318 } |
319 vda_->AssignPictureBuffers(picture_buffers); | 319 vda_->AssignPictureBuffers(picture_buffers); |
320 } | 320 } |
321 | 321 |
322 void RTCVideoDecoder::DismissPictureBuffer(int32 id) { | 322 void RTCVideoDecoder::DismissPictureBuffer(int32_t id) { |
323 DVLOG(3) << "DismissPictureBuffer. id=" << id; | 323 DVLOG(3) << "DismissPictureBuffer. id=" << id; |
324 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 324 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
325 | 325 |
326 std::map<int32, media::PictureBuffer>::iterator it = | 326 std::map<int32_t, media::PictureBuffer>::iterator it = |
327 assigned_picture_buffers_.find(id); | 327 assigned_picture_buffers_.find(id); |
328 if (it == assigned_picture_buffers_.end()) { | 328 if (it == assigned_picture_buffers_.end()) { |
329 NOTREACHED() << "Missing picture buffer: " << id; | 329 NOTREACHED() << "Missing picture buffer: " << id; |
330 return; | 330 return; |
331 } | 331 } |
332 | 332 |
333 media::PictureBuffer buffer_to_dismiss = it->second; | 333 media::PictureBuffer buffer_to_dismiss = it->second; |
334 assigned_picture_buffers_.erase(it); | 334 assigned_picture_buffers_.erase(it); |
335 | 335 |
336 if (!picture_buffers_at_display_.count(id)) { | 336 if (!picture_buffers_at_display_.count(id)) { |
337 // We can delete the texture immediately as it's not being displayed. | 337 // We can delete the texture immediately as it's not being displayed. |
338 factories_->DeleteTexture(buffer_to_dismiss.texture_id()); | 338 factories_->DeleteTexture(buffer_to_dismiss.texture_id()); |
339 return; | 339 return; |
340 } | 340 } |
341 // Not destroying a texture in display in |picture_buffers_at_display_|. | 341 // Not destroying a texture in display in |picture_buffers_at_display_|. |
342 // Postpone deletion until after it's returned to us. | 342 // Postpone deletion until after it's returned to us. |
343 } | 343 } |
344 | 344 |
345 void RTCVideoDecoder::PictureReady(const media::Picture& picture) { | 345 void RTCVideoDecoder::PictureReady(const media::Picture& picture) { |
346 DVLOG(3) << "PictureReady"; | 346 DVLOG(3) << "PictureReady"; |
347 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 347 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
348 | 348 |
349 std::map<int32, media::PictureBuffer>::iterator it = | 349 std::map<int32_t, media::PictureBuffer>::iterator it = |
350 assigned_picture_buffers_.find(picture.picture_buffer_id()); | 350 assigned_picture_buffers_.find(picture.picture_buffer_id()); |
351 if (it == assigned_picture_buffers_.end()) { | 351 if (it == assigned_picture_buffers_.end()) { |
352 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); | 352 NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id(); |
353 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | 353 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); |
354 return; | 354 return; |
355 } | 355 } |
356 | 356 |
357 uint32_t timestamp = 0; | 357 uint32_t timestamp = 0; |
358 gfx::Rect visible_rect; | 358 gfx::Rect visible_rect; |
359 GetBufferData(picture.bitstream_buffer_id(), ×tamp, &visible_rect); | 359 GetBufferData(picture.bitstream_buffer_id(), ×tamp, &visible_rect); |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
413 &RTCVideoDecoder::ReleaseMailbox, weak_factory_.GetWeakPtr(), | 413 &RTCVideoDecoder::ReleaseMailbox, weak_factory_.GetWeakPtr(), |
414 factories_, picture.picture_buffer_id(), pb.texture_id())), | 414 factories_, picture.picture_buffer_id(), pb.texture_id())), |
415 pb.size(), visible_rect, visible_rect.size(), timestamp_ms)); | 415 pb.size(), visible_rect, visible_rect.size(), timestamp_ms)); |
416 if (picture.allow_overlay()) { | 416 if (picture.allow_overlay()) { |
417 frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY, | 417 frame->metadata()->SetBoolean(media::VideoFrameMetadata::ALLOW_OVERLAY, |
418 true); | 418 true); |
419 } | 419 } |
420 return frame; | 420 return frame; |
421 } | 421 } |
422 | 422 |
423 void RTCVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) { | 423 void RTCVideoDecoder::NotifyEndOfBitstreamBuffer(int32_t id) { |
424 DVLOG(3) << "NotifyEndOfBitstreamBuffer. id=" << id; | 424 DVLOG(3) << "NotifyEndOfBitstreamBuffer. id=" << id; |
425 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 425 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
426 | 426 |
427 std::map<int32, base::SharedMemory*>::iterator it = | 427 std::map<int32_t, base::SharedMemory*>::iterator it = |
428 bitstream_buffers_in_decoder_.find(id); | 428 bitstream_buffers_in_decoder_.find(id); |
429 if (it == bitstream_buffers_in_decoder_.end()) { | 429 if (it == bitstream_buffers_in_decoder_.end()) { |
430 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); | 430 NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE); |
431 NOTREACHED() << "Missing bitstream buffer: " << id; | 431 NOTREACHED() << "Missing bitstream buffer: " << id; |
432 return; | 432 return; |
433 } | 433 } |
434 | 434 |
435 { | 435 { |
436 base::AutoLock auto_lock(lock_); | 436 base::AutoLock auto_lock(lock_); |
437 PutSHM_Locked(scoped_ptr<base::SharedMemory>(it->second)); | 437 PutSHM_Locked(scoped_ptr<base::SharedMemory>(it->second)); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
515 << " existed already in bitstream_buffers_in_decoder_"; | 515 << " existed already in bitstream_buffers_in_decoder_"; |
516 RecordBufferData(buffer_data); | 516 RecordBufferData(buffer_data); |
517 vda_->Decode(bitstream_buffer); | 517 vda_->Decode(bitstream_buffer); |
518 } | 518 } |
519 } | 519 } |
520 | 520 |
521 bool RTCVideoDecoder::CanMoreDecodeWorkBeDone() { | 521 bool RTCVideoDecoder::CanMoreDecodeWorkBeDone() { |
522 return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; | 522 return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes; |
523 } | 523 } |
524 | 524 |
525 bool RTCVideoDecoder::IsBufferAfterReset(int32 id_buffer, int32 id_reset) { | 525 bool RTCVideoDecoder::IsBufferAfterReset(int32_t id_buffer, int32_t id_reset) { |
526 if (id_reset == ID_INVALID) | 526 if (id_reset == ID_INVALID) |
527 return true; | 527 return true; |
528 int32 diff = id_buffer - id_reset; | 528 int32_t diff = id_buffer - id_reset; |
529 if (diff <= 0) | 529 if (diff <= 0) |
530 diff += ID_LAST + 1; | 530 diff += ID_LAST + 1; |
531 return diff < ID_HALF; | 531 return diff < ID_HALF; |
532 } | 532 } |
533 | 533 |
534 bool RTCVideoDecoder::IsFirstBufferAfterReset(int32 id_buffer, int32 id_reset) { | 534 bool RTCVideoDecoder::IsFirstBufferAfterReset(int32_t id_buffer, |
| 535 int32_t id_reset) { |
535 if (id_reset == ID_INVALID) | 536 if (id_reset == ID_INVALID) |
536 return id_buffer == 0; | 537 return id_buffer == 0; |
537 return id_buffer == ((id_reset + 1) & ID_LAST); | 538 return id_buffer == ((id_reset + 1) & ID_LAST); |
538 } | 539 } |
539 | 540 |
540 void RTCVideoDecoder::SaveToDecodeBuffers_Locked( | 541 void RTCVideoDecoder::SaveToDecodeBuffers_Locked( |
541 const webrtc::EncodedImage& input_image, | 542 const webrtc::EncodedImage& input_image, |
542 scoped_ptr<base::SharedMemory> shm_buffer, | 543 scoped_ptr<base::SharedMemory> shm_buffer, |
543 const BufferData& buffer_data) { | 544 const BufferData& buffer_data) { |
544 memcpy(shm_buffer->memory(), input_image._buffer, input_image._length); | 545 memcpy(shm_buffer->memory(), input_image._buffer, input_image._length); |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
606 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 607 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
607 DVLOG(2) << "ResetInternal"; | 608 DVLOG(2) << "ResetInternal"; |
608 if (vda_) | 609 if (vda_) |
609 vda_->Reset(); | 610 vda_->Reset(); |
610 } | 611 } |
611 | 612 |
612 // static | 613 // static |
613 void RTCVideoDecoder::ReleaseMailbox( | 614 void RTCVideoDecoder::ReleaseMailbox( |
614 base::WeakPtr<RTCVideoDecoder> decoder, | 615 base::WeakPtr<RTCVideoDecoder> decoder, |
615 media::GpuVideoAcceleratorFactories* factories, | 616 media::GpuVideoAcceleratorFactories* factories, |
616 int64 picture_buffer_id, | 617 int64_t picture_buffer_id, |
617 uint32 texture_id, | 618 uint32_t texture_id, |
618 const gpu::SyncToken& release_sync_token) { | 619 const gpu::SyncToken& release_sync_token) { |
619 DCHECK(factories->GetTaskRunner()->BelongsToCurrentThread()); | 620 DCHECK(factories->GetTaskRunner()->BelongsToCurrentThread()); |
620 factories->WaitSyncToken(release_sync_token); | 621 factories->WaitSyncToken(release_sync_token); |
621 | 622 |
622 if (decoder) { | 623 if (decoder) { |
623 decoder->ReusePictureBuffer(picture_buffer_id); | 624 decoder->ReusePictureBuffer(picture_buffer_id); |
624 return; | 625 return; |
625 } | 626 } |
626 // It's the last chance to delete the texture after display, | 627 // It's the last chance to delete the texture after display, |
627 // because RTCVideoDecoder was destructed. | 628 // because RTCVideoDecoder was destructed. |
628 factories->DeleteTexture(texture_id); | 629 factories->DeleteTexture(texture_id); |
629 } | 630 } |
630 | 631 |
631 void RTCVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id) { | 632 void RTCVideoDecoder::ReusePictureBuffer(int64_t picture_buffer_id) { |
632 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); | 633 DCheckGpuVideoAcceleratorFactoriesTaskRunnerIsCurrent(); |
633 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; | 634 DVLOG(3) << "ReusePictureBuffer. id=" << picture_buffer_id; |
634 | 635 |
635 DCHECK(!picture_buffers_at_display_.empty()); | 636 DCHECK(!picture_buffers_at_display_.empty()); |
636 PictureBufferTextureMap::iterator display_iterator = | 637 PictureBufferTextureMap::iterator display_iterator = |
637 picture_buffers_at_display_.find(picture_buffer_id); | 638 picture_buffers_at_display_.find(picture_buffer_id); |
638 uint32 texture_id = display_iterator->second; | 639 uint32_t texture_id = display_iterator->second; |
639 DCHECK(display_iterator != picture_buffers_at_display_.end()); | 640 DCHECK(display_iterator != picture_buffers_at_display_.end()); |
640 picture_buffers_at_display_.erase(display_iterator); | 641 picture_buffers_at_display_.erase(display_iterator); |
641 | 642 |
642 if (!assigned_picture_buffers_.count(picture_buffer_id)) { | 643 if (!assigned_picture_buffers_.count(picture_buffer_id)) { |
643 // This picture was dismissed while in display, so we postponed deletion. | 644 // This picture was dismissed while in display, so we postponed deletion. |
644 factories_->DeleteTexture(texture_id); | 645 factories_->DeleteTexture(texture_id); |
645 return; | 646 return; |
646 } | 647 } |
647 | 648 |
648 // DestroyVDA() might already have been called. | 649 // DestroyVDA() might already have been called. |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
771 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but | 772 // Why this value? Because why not. avformat.h:MAX_REORDER_DELAY is 16, but |
772 // that's too small for some pathological B-frame test videos. The cost of | 773 // that's too small for some pathological B-frame test videos. The cost of |
773 // using too-high a value is low (192 bits per extra slot). | 774 // using too-high a value is low (192 bits per extra slot). |
774 static const size_t kMaxInputBufferDataSize = 128; | 775 static const size_t kMaxInputBufferDataSize = 128; |
775 // Pop from the back of the list, because that's the oldest and least likely | 776 // Pop from the back of the list, because that's the oldest and least likely |
776 // to be useful in the future data. | 777 // to be useful in the future data. |
777 if (input_buffer_data_.size() > kMaxInputBufferDataSize) | 778 if (input_buffer_data_.size() > kMaxInputBufferDataSize) |
778 input_buffer_data_.pop_back(); | 779 input_buffer_data_.pop_back(); |
779 } | 780 } |
780 | 781 |
781 void RTCVideoDecoder::GetBufferData(int32 bitstream_buffer_id, | 782 void RTCVideoDecoder::GetBufferData(int32_t bitstream_buffer_id, |
782 uint32_t* timestamp, | 783 uint32_t* timestamp, |
783 gfx::Rect* visible_rect) { | 784 gfx::Rect* visible_rect) { |
784 for (const auto& buffer_data : input_buffer_data_) { | 785 for (const auto& buffer_data : input_buffer_data_) { |
785 if (buffer_data.bitstream_buffer_id != bitstream_buffer_id) | 786 if (buffer_data.bitstream_buffer_id != bitstream_buffer_id) |
786 continue; | 787 continue; |
787 *timestamp = buffer_data.timestamp; | 788 *timestamp = buffer_data.timestamp; |
788 *visible_rect = buffer_data.visible_rect; | 789 *visible_rect = buffer_data.visible_rect; |
789 return; | 790 return; |
790 } | 791 } |
791 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; | 792 NOTREACHED() << "Missing bitstream buffer id: " << bitstream_buffer_id; |
(...skipping 13 matching lines...) Expand all Loading... |
805 } | 806 } |
806 | 807 |
807 void RTCVideoDecoder::ClearPendingBuffers() { | 808 void RTCVideoDecoder::ClearPendingBuffers() { |
808 // Delete WebRTC input buffers. | 809 // Delete WebRTC input buffers. |
809 for (const auto& pending_buffer : pending_buffers_) | 810 for (const auto& pending_buffer : pending_buffers_) |
810 delete[] pending_buffer.first._buffer; | 811 delete[] pending_buffer.first._buffer; |
811 pending_buffers_.clear(); | 812 pending_buffers_.clear(); |
812 } | 813 } |
813 | 814 |
814 } // namespace content | 815 } // namespace content |
OLD | NEW |