webrtc的scoped_ptr

本文探讨了WebRTC中scoped_ptr的用法,包括其如何像普通指针一样操作、所有权管理、禁止赋值和复制的特点,以及在内存管理中的栈和堆思想。通过示例代码,解释了正确和错误的使用方式。

前几天看的webrtc实际上一直是libjingle的talk,base目录下的基础性东西,似乎是google在原来的webrtc的代码中又做了一些改进。现在由于工作的需要,所有的眼光都集中到webrtc上了,让我也不得不将精力向webrtc的代码靠拢。

最近几天的文章不会涉及到任何关于webrtc框架的东西,何况我也不懂框架,仅仅是对webrtc一些基础类的封装的使用的库,学鲁迅的拿来主义,读懂,然后据为己有,然后在自己的以后的编程中应用上面的思想进行改进。

今天主要讨论scoped_ptr,先上代码:

//  (C) Copyright Greg Colvin and Beman Dawes 1998, 1999.
//  Copyright (c) 2001, 2002 Peter Dimov
//
//  Permission to copy, use, modify, sell and distribute this software
//  is granted provided this copyright notice appears in all copies.
//  This software is provided "as is" without express or implied
//  warranty, and with no claim as to its suitability for any purpose.
//
//  See http://www.boost.org/libs/smart_ptr/scoped_ptr.htm for documentation.
//

//  scoped_ptr mimics a built-in pointer except that it guarantees deletion
//  of the object pointed to, either on destruction of the scoped_ptr or via
//  an explicit reset(). scoped_ptr is a simple solution for simple needs;
//  use shared_ptr or std::auto_ptr if your needs are more complex.

//  scoped_ptr_malloc added in by Google.  When one of
//  these goes out of scope, instead of doing a delete or delete[], it
//  calls free().  scoped_ptr_malloc<char> is likely to see much more
//  use than any other specializations.

//  release() added in by Google. Use this to conditionally
//  transfer ownership of a heap-allocated object to the caller, usually on
//  method success.
#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_PTR_H_
#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_PTR_H_

#include <assert.h>            // for assert
#include <stdlib.h>            // for free() decl

#include <cstddef>             // for std::ptrdiff_t

#ifdef _WIN32
namespace std { using ::ptrdiff_t; };
#endif // _WIN32

namespace webrtc {

template <typename T>
class scoped_ptr {
 private:

  T* ptr;

  scoped_ptr(scoped_ptr const &);
  scoped_ptr & operator=(scoped_ptr const &);

 public:

  typedef T element_type;

  explicit scoped_ptr(T* p = NULL): ptr(p) {}

  ~scoped_ptr() {
    typedef char type_must_be_complete[sizeof(T)];
    delete ptr;
  }

  void reset(T* p = NULL) {
    typedef char type_must_be_complete[sizeof(T)];

    if (ptr != p) {
      T* obj = ptr;
      ptr = p;
      // Delete last, in case obj destructor indirectly results in ~scoped_ptr
      delete obj;
    }
  }

  T& operator*() const {
    assert(ptr != NULL);
    return *ptr;
  }

  T* operator->() const  {
    assert(ptr != NULL);
    return ptr;
  }

  T* get() const  {
    return ptr;
  }

  void swap(scoped_ptr & b) {
    T* tmp = b.ptr;
    b.ptr = ptr;
    ptr = tmp;
  }

  T* release() {
    T* tmp = ptr;
    ptr = NULL;
    return tmp;
  }

  T** accept() {
    if (ptr) {
      delete ptr;
      ptr = NULL;
    }
    return &ptr;
  }

  T** use() {
    return &ptr;
  }
};

template<typename T> inline
void swap(scoped_ptr<T>& a, scoped_ptr<T>& b) {
  a.swap(b);
}




//  scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
//  is guaranteed, either on destruction of the scoped_array or via an explicit
//  reset(). Use shared_array or std::vector if your needs are more complex.

template<typename T>
class scoped_array {
 private:

  T* ptr;

  scoped_array(scoped_array const &);
  scoped_array & operator=(scoped_array const &);

 public:

  typedef T element_type;

  explicit scoped_array(T* p = NULL) : ptr(p) {}

  ~scoped_array() {
    typedef char type_must_be_complete[sizeof(T)];
    delete[] ptr;
  }

  void reset(T* p = NULL) {
    typedef char type_must_be_complete[sizeof(T)];

    if (ptr != p) {
      T* arr = ptr;
      ptr = p;
      // Delete last, in case arr destructor indirectly results in ~scoped_array
      delete [] arr;
    }
  }

  T& operator[](std::ptrdiff_t i) const {
    assert(ptr != NULL);
    assert(i >= 0);
    return ptr[i];
  }

  T* get() const {
    return ptr;
  }

  void swap(scoped_array & b) {
    T* tmp = b.ptr;
    b.ptr = ptr;
    ptr = tmp;
  }

  T* release() {
    T* tmp = ptr;
    ptr = NULL;
    return tmp;
  }

  T** accept() {
    if (ptr) {
      delete [] ptr;
      ptr = NULL;
    }
    return &ptr;
  }
};

template<class T> inline
void swap(scoped_array<T>& a, scoped_array<T>& b) {
  a.swap(b);
}

// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a
// second template argument, the function used to free the object.

template<typename T, void (*FF)(void*) = free> class scoped_ptr_malloc {
 private:

  T* ptr;

  scoped_ptr_malloc(scoped_ptr_malloc const &);
  scoped_ptr_malloc & operator=(scoped_ptr_malloc const &);

 public:

  typedef T element_type;

  explicit scoped_ptr_malloc(T* p = 0): ptr(p) {}

  ~scoped_ptr_malloc() {
    FF(static_cast<void*>(ptr));
  }

  void reset(T* p = 0) {
    if (ptr != p) {
      FF(static_cast<void*>(ptr));
      ptr = p;
    }
  }

  T& operator*() const {
    assert(ptr != 0);
    return *ptr;
  }

  T* operator->() const {
    assert(ptr != 0);
    return ptr;
  }

  T* get() const {
    return ptr;
  }

  void swap(scoped_ptr_malloc & b) {
    T* tmp = b.ptr;
    b.ptr = ptr;
    ptr = tmp;
  }

  T* release() {
    T* tmp = ptr;
    ptr = 0;
    return tmp;
  }

  T** accept() {
    if (ptr) {
      FF(static_cast<void*>(ptr));
      ptr = 0;
    }
    return &ptr;
  }
};

template<typename T, void (*FF)(void*)> inline
void swap(scoped_ptr_malloc<T,FF>& a, scoped_ptr_malloc<T,FF>& b) {
  a.swap(b);
}

} // namespace webrtc

#endif  // #ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_SCOPED_PTR_H_
scoped_ptr是为了方便的管理指针而使用的,它有一个数据成员ptr,它拥有者这个指针的所有权,并负责释放它,客户不用去管。

它可以像普通的指针一样使用,主要由于重载了operator*(),operator->(),运算符

它可以返回ptr供客户使用。通过get方法,但是一定要注意,它仅仅返回指针,而并不返回所有权。也就是说下面的这段代码:

scoped_ptr<int> sp(new int(100));

int *p = sp.get();

delete p;

这段代码是错误的,由于p并没有获得int变量的所有权,它也就无权释放它

它可以手工的去释放ptr,通过sp.reset().

它可以用于管理另一个之后指针,通过sp.reset(another_ptr);

它可以和另一个scoped_ptr交换所有权。

它可以自动释放管理的指针,通过析构函数
它不可以被赋值,被copy,是因为它的这两个函数私有声明了。

它采用的是栈内存管理堆内存的思想,这个思想在后面介绍锁的使用也有体现。


通常情况下我们会这样使用它:

class ViEExternalRendererImpl
{
	scoped_ptr<VideoFrame> converted_frame_;
}
ViEExternalRendererImpl::ViEExternalRendererImpl()
	:converted_frame_(new VideoFrame()) {
}

如上面所示,类的成员中有一个scoped_ptr对象,它管理者VideoFrame指针。

必须在构造函数的成员初始化列表中负责对scoped_ptr对象进行初始化。

### 问题分析 在 WebRTC 编译和运行过程中,调用 `SetLocalDescription` 时出现访问冲突异常(0xC0000005)通常意味着程序试图访问无效内存地址或访问了未初始化的对象。这种异常在原生 C++ 实现中较为常见,尤其是在多线程环境中或对象生命周期管理不当的情况下。 该方法的主要职责是将本地生成的 SDP 描述应用到 `RTCPeerConnection` 中,并触发后续的媒体流配置和 ICE 候选收集流程[^1]。若在此过程中访问了空指针、已释放的对象或未正确初始化的结构,就会导致崩溃。 ### 可能原因及解决方法 #### 1. 对象未正确初始化 在调用 `SetLocalDescription` 之前,必须确保 `RTCPeerConnection` 实例以及相关的媒体通道(如 `VoiceMediaChannel`)已经正确初始化。如果在对象尚未准备好时调用此方法,可能导致访问冲突。 - **解决方案**:确保 `PeerConnectionFactory` 和 `PeerConnection` 已成功创建,并且所有依赖资源(如音频/视频轨道)已正确添加。 ```cpp rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection = peer_connection_factory->CreatePeerConnection(rtc_config, nullptr, nullptr, observer); ``` #### 2. 多线程访问问题 WebRTC 的内部实现依赖于多个线程(如网络线程、工作线程等),若在非预期线程调用 `SetLocalDescription`,可能会导致线程安全问题。 - **解决方案**:确保所有对 `PeerConnection` 的操作都在创建该对象的线程中执行,或通过 `worker_thread_->Invoke` 显式切换线程。 ```cpp worker_thread_->Invoke<void>(RTC_FROM_HERE, [&] { peer_connection->SetLocalDescription(std::move(desc), nullptr); }); ``` #### 3. SDP 描述格式错误或内容不完整 如果传入的 `RTCSessionDescription` 对象内容不完整或格式错误,可能导致解析失败,进而引发非法内存访问。 - **解决方案**:确保 SDP 字符串格式正确,且包含必要的媒体信息和 ICE 参数。 ```cpp webrtc::SdpParseError error; std::unique_ptr<webrtc::SessionDescriptionInterface> session_description = webrtc::CreateSessionDescription(type, sdp, &error); if (!session_description) { // 处理解析错误 } ``` #### 4. 内部状态不一致 在某些情况下,`PeerConnection` 的内部状态可能不一致,例如在尚未完成 ICE 收集或媒体协商失败后调用 `SetLocalDescription`,也可能导致异常。 - **解决方案**:确保在调用前检查当前连接状态,并在必要时重置相关状态。 ```cpp if (peer_connection->signaling_state() == PeerConnectionInterface::SignalingState::kStable) { peer_connection->SetLocalDescription(std::move(desc), nullptr); } ``` #### 5. 内存泄漏或对象提前释放 若在异步操作中使用了已释放的对象指针(如 `RTCSessionDescription` 或 `PeerConnection`),也可能导致访问冲突。 - **解决方案**:使用智能指针管理对象生命周期,避免手动释放对象。 ```cpp std::unique_ptr<webrtc::SessionDescriptionInterface> desc = webrtc::CreateSessionDescription("offer", sdp, &error); peer_connection->SetLocalDescription(std::move(desc), nullptr); ``` ### 总结 访问冲突异常(0xC0000005)通常与对象生命周期、线程安全、SDP 格式或内部状态有关。在 WebRTC 编译和运行过程中,应确保所有资源正确初始化、线程访问安全、SDP 内容有效,并合理管理对象的生命周期。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值