You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用live555提取H264帧?求完整实现示例

终于找到一个懂行的需求了!确实,live555自带的testRTSPClient.cpp用DummySink只是打酱油,根本没处理实际的H264数据,而且全网确实缺一套用官方H264专用Sink提取帧的完整示例。我给你整理一套可直接落地的方案:

替换DummySink实现H264帧提取的完整方案

核心思路

live555针对H264专门做了H264VideoRTPSink和配套的接收端解析逻辑,咱们不用自己啃原始RTP包,直接基于官方的H264处理组件,自定义一个MediaSink子类替换掉DummySink,就能优雅地解析出完整的H264 NAL单元(也就是帧数据)。

具体实现步骤

1. 自定义H264FrameExtractorSink类

先创建一个继承自MediaSink的子类,把H264的解析逻辑集成进去:

#include "H264VideoRTPSink.hh"
#include "H264VideoStreamFramer.hh"

class H264FrameExtractorSink : public MediaSink {
public:
  static H264FrameExtractorSink* createNew(UsageEnvironment& env,
                                           MediaSubsession& subsession,
                                           char const* streamId = NULL);

protected:
  H264FrameExtractorSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId);
  virtual ~H264FrameExtractorSink();

private:
  // 重写MediaSink的核心播放控制方法
  virtual Boolean continuePlaying();
  // 回调函数:收到帧数据后触发
  static void afterGettingFrame(void* clientData, unsigned frameSize,
                                unsigned numTruncatedBytes,
                                struct timeval presentationTime,
                                unsigned durationInMicroseconds);
  // 实际处理帧数据的方法
  void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
                         struct timeval presentationTime, unsigned durationInMicroseconds);

private:
  MediaSubsession& fSubsession;
  char* fStreamId;
  unsigned char* fReceiveBuffer;
  H264VideoStreamFramer* fH264Framer; // 官方自带的H264 NAL解析器
};

2. 实现Sink的核心方法

重点是在afterGettingFrame里处理解析后的H264数据——H264VideoStreamFramer已经帮我们把RTP分片拼好了,直接拿完整NAL就行:

H264FrameExtractorSink* H264FrameExtractorSink::createNew(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
  return new H264FrameExtractorSink(env, subsession, streamId);
}

H264FrameExtractorSink::H264FrameExtractorSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)
  : MediaSink(env), fSubsession(subsession), fStreamId(strDup(streamId) ? strDup(streamId) : NULL) {
  // 初始化接收缓冲区,按MTU设置大小(1500足够应付大部分场景)
  fReceiveBuffer = new unsigned char[1500];
  // 创建官方的H264帧解析器,自动处理RTP分片
  fH264Framer = H264VideoStreamFramer::createNew(env, subsession.source());
}

H264FrameExtractorSink::~H264FrameExtractorSink() {
  delete[] fReceiveBuffer;
  delete[] fStreamId;
  Medium::close(fH264Framer);
}

Boolean H264FrameExtractorSink::continuePlaying() {
  if (fH264Framer == NULL) return False;

  // 向数据源请求下一个帧
  fH264Framer->getNextFrame(fReceiveBuffer, 1500,
                             afterGettingFrame, this,
                             onSourceClosure, this);
  return True;
}

void H264FrameExtractorSink::afterGettingFrame(void* clientData, unsigned frameSize,
                                               unsigned numTruncatedBytes,
                                               struct timeval presentationTime,
                                               unsigned durationInMicroseconds) {
  H264FrameExtractorSink* sink = (H264FrameExtractorSink*)clientData;
  sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);
}

void H264FrameExtractorSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
                                              struct timeval presentationTime, unsigned durationInMicroseconds) {
  // 这里就是你要的完整H264 NAL单元!
  // fReceiveBuffer存的是NAL数据,frameSize是NAL长度
  // 你可以在这里把数据写入.h264文件、传给解码器,或者做其他处理
  printf("拿到H264 NAL单元,大小:%u 字节\n", frameSize);
  
  // 别忘了继续请求下一个帧,不然流就断了
  continuePlaying();
}

3. 替换testRTSPClient里的DummySink

找到testRTSPClient.cpp里的createNewMediaSink函数,把原来创建DummySink的逻辑改成我们的自定义Sink:

MediaSink* createNewMediaSink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId) {
  // 判断当前流是不是H264,是的话用我们的自定义Sink
  if (subsession.codecName() != NULL && strcmp(subsession.codecName(), "H264") == 0) {
    return H264FrameExtractorSink::createNew(env, subsession, streamId);
  } else {
    // 非H264流还是用原来的DummySink兜底
    return DummySink::createNew(env, subsession, streamId);
  }
}

4. 编译测试

确保你的项目链接了live555的库文件,编译后运行修改后的testRTSPClient,输入RTSP地址就能看到NAL单元的输出了。如果要生成可播放的H264文件,就在afterGettingFrame里给每个NAL单元加上0x00 0x00 0x00 0x01的起始码,然后写入文件就行。

关键注意点

  • 一定要确认RTSP流是H264编码的,不然会自动回退到DummySink
  • H264VideoStreamFramer已经帮你处理了RTP分片的拼接,不用自己写分片逻辑
  • 接收缓冲区大小建议设为MTU的1.5倍,避免数据截断
  • 如果需要获取SPS/PPS等关键参数,可以在Sink初始化时从subsession的SDP信息里提取

内容的提问来源于stack exchange,提问作者PPP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:18:09