基于RTMP推送实时AAC+H264流(三)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/scnu20142005027/article/details/60623670

推送

流程:初始化、连接服务器、发送数据
这一部分主要用的是librtmp,由于是使用别人封装好的库,总的来说比较简单

初始化

简单地调用一下函数

rtmp = RTMP_Alloc();
RTMP_Init(rtmp);

连接服务器

四个函数分别代表:设置URL、使自己成为发送端、建立NetConnection,建立NetStream

RTMP_SetupURL(rtmp, url);
RTMP_EnableWrite(rtmp);
RTMP_Connect(rtmp, NULL);
RTMP_ConnectStream(mRTMP, 0);

发送数据

不断从队列中取出RTMP包,加上时间戳和流ID,然后往服务器发送,帧率的控制位于编码线程,所以这里不用计算时间,直接加上时间戳发送即可,而且由于这里也不好计算时间,还有就是这里的队列是阻塞队列

while (true) {
    RTMPPacket& packet = queue.front();
    packet.m_nInfoField2 = rtmp->m_stream_id;
    packet.m_nTimeStamp = RTMP_GetTime();

    RTMP_SendPacket(rtmp, &packet, 1);
    queue.pop();
}

主程序

主要分为:视频编码线程、音频编码线程、主线程(发送线程)

视频编码线程

根据帧率算出间隔时间,然后把视频元数据的rtmp包存下来以便多次发送,从视频源获取帧,判断是否是关键帧,如果是则先发送元数据,经过编码封装后发送数据,最后比较花费时间与间隔时间决定是否需要等待

const int interval = 1000 / fps;
int bytes;
char *buf, *frame;
RTMPPacket packet, meta;
H264RTMPPackager packager;
std::chrono::milliseconds duration;
std::pair<int, char*> result;

meta = encoder.getMetadata();
buf = pool.get(1024);
packet = packager.metadata(buf, result.second, result.first);

auto last = std::chrono::system_clock::now();

while ((frame = source.getNextFrame()) != NULL) {
    result = source.encode(frame);

    if (H264RTMPPackager::isKeyFrame(result.second)) {
        queue.push(meta, true);
    }

    bytes = packager.getBodyLength(result.first);
    buf = pool.get(bytes);
    packet = packager.pack(buf, result.second, result.first)
    queue.push(packet);

    duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - last);

    if (duration < interval) {
        msleep((interval - duration).count());
    }
    last = std::chrono::system_clock::now();
}

音频编码线程

步骤与视频编码线程相似,只是这里无需等待,因为音频源是根据采样率来输出帧的,这里由于元数据只需要发送一次,所以可以不用保存下来

int bytes;
char *buf;
RTMPPacket packet;
AACRTMPPackager packager;
std::pair<int, char*> frame, result;

result = encoder.getMetadata();
buf = pool.get(128);
packet = packager.metadata(buf, result.second, result.first);
queue.push(packet, true);

while ((frame = source.getNextFrames()).second != NULL) {
    result = encoder.encode(frame.first, frame.second);

    if (result.first != 0) {
        bytes = packager.getBodyLength(result.first);
        buf = pool.get(bytes);
        packet = packager.pack(buf, result.second, result.first);
        queue.push(packet);
    }
}

主线程

创建H264流以及AAC流,然后开启视频编码线程和音频编码线程

PacketQueue queue;
MemoryPool pool;
V4L2Source v4l2(width, height);
PCMSource pcm;
YUY2Converter converter(YUY2_CVT_RGB24);
MotionDetector detector;

FilteredVideoSource videoSource(v4l2);
videoSource.addFilter(&converter);
videoSource.addFilter(&detector);

RTMPPublisher publisher(queue, pool);
publisher.connect(url);

H264Stream videoStream(videoSource, queue, pool, fps, bitrate);
AACStream audioStream(pcm, queue, pool);

std::thread videoEncodeThread(&H264Stream::run, &videoStream);
std::thread audioEncodeThread(&AACStream::run, &audioStream);

publisher.run();

后记

至此,简单的推送程序就完成了,项目整体比较简单,基本上也就是在调用第三方库,但是总的来说学到的东西不少,对于以前不懂的视音频编码,也有了一些了解,而且也是第一次遇到生产者-消费者的情景,虽然这里的情景比较简单,只有一位消费者
自己的测试环境是电脑Ubuntu和树莓派Raspbian,关于使用到的库,opencv3应该是需要编译安装,树莓派上libfaac可能也需要编译安装
rtmp服务器用的是SRS,配置方法再网上可以很简单地搜索到

猜你喜欢

转载自blog.csdn.net/scnu20142005027/article/details/60623670