MPEG: 整合复用ts/ps生成代码

This commit is contained in:
ziyue
2021-12-28 21:04:53 +08:00
parent dce6b27f4f
commit 89870190e9
12 changed files with 425 additions and 481 deletions

View File

@@ -12,13 +12,15 @@
#define HLSRECORDER_H
#include "HlsMakerImp.h"
#include "TsMuxer.h"
#include "MPEG.h"
namespace mediakit {
class HlsRecorder : public MediaSourceEventInterceptor, public TsMuxer, public std::enable_shared_from_this<HlsRecorder> {
class HlsRecorder : public MediaSourceEventInterceptor, public MpegMuxer, public std::enable_shared_from_this<HlsRecorder> {
public:
typedef std::shared_ptr<HlsRecorder> Ptr;
HlsRecorder(const string &m3u8_file, const string &params){
using Ptr = std::shared_ptr<HlsRecorder>;
HlsRecorder(const string &m3u8_file, const string &params) : MpegMuxer(false) {
GET_CONFIG(uint32_t, hlsNum, Hls::kSegmentNum);
GET_CONFIG(uint32_t, hlsBufSize, Hls::kFileBufSize);
GET_CONFIG(float, hlsDuration, Hls::kSegmentDuration);
@@ -27,7 +29,7 @@ public:
_hls->clearCache();
}
~HlsRecorder(){}
~HlsRecorder() = default;
void setMediaSource(const string &vhost, const string &app, const string &stream_id) {
_hls->setMediaSource(vhost, app, stream_id);
@@ -68,17 +70,17 @@ public:
_hls->clearCache();
}
if (_enabled || !hls_demand) {
return TsMuxer::inputFrame(frame);
return MpegMuxer::inputFrame(frame);
}
return false;
}
private:
void onTs(std::shared_ptr<Buffer> buffer, uint32_t timestamp, bool is_idr_fast_packet) override {
void onWrite(std::shared_ptr<Buffer> buffer, uint32_t timestamp, bool key_pos) override {
if (!buffer) {
_hls->inputData(nullptr, 0, timestamp, is_idr_fast_packet);
_hls->inputData(nullptr, 0, timestamp, key_pos);
} else {
_hls->inputData(buffer->data(), buffer->size(), timestamp, is_idr_fast_packet);
_hls->inputData(buffer->data(), buffer->size(), timestamp, key_pos);
}
}

256
src/Record/MPEG.cpp Normal file
View File

@@ -0,0 +1,256 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#include <cstdlib>
#include <assert.h>
#include "MPEG.h"
#if defined(ENABLE_HLS) || defined(ENABLE_RTPPROXY)
#include "mpeg-ps.h"
#include "mpeg-ts.h"
#include "mpeg-ts-proto.h"
namespace mediakit{
struct mpeg_muxer_t {
int is_ps;
union {
struct {
void *ctx;
void *param;
mpeg_muxer_func_t func;
} ts;
ps_muxer_t *ps;
} u;
};
static void *on_mpeg_ts_alloc(void *param, size_t bytes) {
mpeg_muxer_t *mpeg = (mpeg_muxer_t *) param;
return mpeg->u.ts.func.alloc(mpeg->u.ts.param, bytes);
}
static void on_mpeg_ts_free(void *param, void *packet) {
mpeg_muxer_t *mpeg = (mpeg_muxer_t *) param;
mpeg->u.ts.func.free(mpeg->u.ts.param, packet);
}
static int on_mpeg_ts_write(void *param, const void *packet, size_t bytes) {
mpeg_muxer_t *mpeg = (mpeg_muxer_t *) param;
return mpeg->u.ts.func.write(mpeg->u.ts.param, 0, (void *) packet, bytes);
}
mpeg_muxer_t *mpeg_muxer_create(int is_ps, const mpeg_muxer_func_t *func, void *param) {
mpeg_muxer_t *mpeg = (mpeg_muxer_t *) malloc(sizeof(mpeg_muxer_t));
assert(mpeg);
mpeg->is_ps = is_ps;
if (is_ps) {
mpeg->u.ps = ps_muxer_create(func, param);
} else {
struct mpeg_ts_func_t ts_func{on_mpeg_ts_alloc, on_mpeg_ts_free, on_mpeg_ts_write};
mpeg->u.ts.func = *func;
mpeg->u.ts.param = param;
mpeg->u.ts.ctx = mpeg_ts_create(&ts_func, mpeg);
}
return mpeg;
}
int mpeg_muxer_destroy(mpeg_muxer_t *muxer) {
assert(muxer);
int ret = -1;
if (muxer->is_ps) {
ret = ps_muxer_destroy(muxer->u.ps);
} else {
ret = mpeg_ts_destroy(muxer->u.ts.ctx);
}
free(muxer);
return ret;
}
int mpeg_muxer_add_stream(mpeg_muxer_t *muxer, int codecid, const void *extradata, size_t extradata_size) {
assert(muxer);
if (muxer->is_ps) {
return ps_muxer_add_stream(muxer->u.ps, codecid, extradata, extradata_size);
}
return mpeg_ts_add_stream(muxer->u.ts.ctx, codecid, extradata, extradata_size);
}
int mpeg_muxer_input(mpeg_muxer_t *muxer, int stream, int flags, int64_t pts, int64_t dts, const void *data, size_t bytes) {
assert(muxer);
if (muxer->is_ps) {
return ps_muxer_input(muxer->u.ps, stream, flags, pts, dts, data, bytes);
}
return mpeg_ts_write(muxer->u.ts.ctx, stream, flags, pts, dts, data, bytes);
}
int mpeg_muxer_reset(mpeg_muxer_t *muxer) {
assert(muxer);
if (muxer->is_ps) {
return -1;
}
return mpeg_ts_reset(muxer->u.ts.ctx);
}
int mpeg_muxer_add_program(mpeg_muxer_t *muxer, uint16_t pn, const void *info, int bytes) {
assert(muxer);
if (muxer->is_ps) {
return -1;
}
return mpeg_ts_add_program(muxer->u.ts.ctx, pn, info, bytes);
}
int mpeg_muxer_remove_program(mpeg_muxer_t *muxer, uint16_t pn) {
assert(muxer);
if (muxer->is_ps) {
return -1;
}
return mpeg_ts_remove_program(muxer->u.ts.ctx, pn);
}
int peg_muxer_add_program_stream(mpeg_muxer_t *muxer, uint16_t pn, int codecid, const void *extra_data, size_t extra_data_size) {
assert(muxer);
if (muxer->is_ps) {
return -1;
}
return mpeg_ts_add_program_stream(muxer->u.ts.ctx, pn, codecid, extra_data, extra_data_size);
}
//////////////////////////////////////////////////////////////////////////////////////////
MpegMuxer::MpegMuxer(bool is_ps) {
_is_ps = is_ps;
_buffer = BufferRaw::create();
createContext();
}
MpegMuxer::~MpegMuxer() {
releaseContext();
}
#define XX(name, type, value, str, mpeg_id) \
case name : { \
if (mpeg_id == PSI_STREAM_RESERVED) { \
break; \
} \
_codec_to_trackid[track->getCodecId()] = mpeg_muxer_add_stream(_context, mpeg_id, nullptr, 0); \
return true; \
}
bool MpegMuxer::addTrack(const Track::Ptr &track) {
if (track->getTrackType() == TrackVideo) {
_have_video = true;
}
switch (track->getCodecId()) {
CODEC_MAP(XX)
default: break;
}
WarnL << "不支持该编码格式,已忽略:" << track->getCodecName();
return false;
}
#undef XX
bool MpegMuxer::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if (it == _codec_to_trackid.end()) {
return false;
}
auto track_id = it->second;
_key_pos = !_have_video;
switch (frame->getCodecId()) {
case CodecH264:
case CodecH265: {
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理
return _frame_merger.inputFrame(frame,[&](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_idr) {
_key_pos = have_idr;
//取视频时间戳为TS的时间戳
_timestamp = (uint32_t) dts;
mpeg_muxer_input(_context, track_id, have_idr ? 0x0001 : 0, pts * 90LL,dts * 90LL, buffer->data(), buffer->size());
flushCache();
});
}
case CodecAAC: {
if (frame->prefixSize() == 0) {
WarnL << "必须提供adts头才能mpeg-ts打包";
return false;
}
}
default: {
if (!_have_video) {
//没有视频时才以音频时间戳为TS的时间戳
_timestamp = (uint32_t) frame->dts();
}
mpeg_muxer_input(_context, track_id, frame->keyFrame() ? 0x0001 : 0, frame->pts() * 90LL, frame->dts() * 90LL, frame->data(), frame->size());
flushCache();
return true;
}
}
}
void MpegMuxer::resetTracks() {
_have_video = false;
//通知片段中断
onWrite(nullptr, _timestamp, false);
releaseContext();
createContext();
}
void MpegMuxer::createContext() {
static mpeg_muxer_func_t func = {
/*alloc*/
[](void *param, size_t bytes) {
MpegMuxer *thiz = (MpegMuxer *) param;
thiz->_buffer->setCapacity(bytes + 1);
return (void *) thiz->_buffer->data();
},
/*free*/
[](void *param, void *packet) {
//什么也不做
},
/*wtite*/
[](void *param, int stream, void *packet, size_t bytes) {
MpegMuxer *thiz = (MpegMuxer *) param;
thiz->onWrite_l(packet, bytes);
return 0;
}
};
if (_context == nullptr) {
_context = mpeg_muxer_create(_is_ps, &func, this);
}
}
void MpegMuxer::onWrite_l(const void *packet, size_t bytes) {
if (!_cache) {
_cache = std::make_shared<BufferLikeString>();
}
_cache->append((char *) packet, bytes);
}
void MpegMuxer::flushCache() {
if (!_cache || _cache->empty()) {
return;
}
onWrite(std::move(_cache), _timestamp, _key_pos);
_key_pos = false;
}
void MpegMuxer::releaseContext() {
if (_context) {
mpeg_muxer_destroy(_context);
_context = nullptr;
}
_codec_to_trackid.clear();
_frame_merger.clear();
}
}//mediakit
#endif

114
src/Record/MPEG.h Normal file
View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef ZLMEDIAKIT_MPEG_H
#define ZLMEDIAKIT_MPEG_H
#if defined(ENABLE_HLS) || defined(ENABLE_RTPPROXY)
#include <cstdio>
#include <cstdint>
#include <unordered_map>
#include "mpeg-ps.h"
#include "Extension/Frame.h"
#include "Extension/Track.h"
#include "Util/File.h"
#include "Common/MediaSink.h"
#include "Common/Stamp.h"
namespace mediakit {
typedef struct mpeg_muxer_t mpeg_muxer_t;
typedef struct ps_muxer_func_t mpeg_muxer_func_t;
mpeg_muxer_t *mpeg_muxer_create(int is_ps, const mpeg_muxer_func_t *func, void *param);
int mpeg_muxer_destroy(mpeg_muxer_t *muxer);
int mpeg_muxer_add_stream(mpeg_muxer_t *muxer, int codecid, const void *extradata, size_t extradata_size);
int mpeg_muxer_input(mpeg_muxer_t *muxer, int stream, int flags, int64_t pts, int64_t dts, const void *data, size_t bytes);
int mpeg_muxer_reset(mpeg_muxer_t *muxer);
int mpeg_muxer_add_program(mpeg_muxer_t *muxer, uint16_t pn, const void *info, int bytes);
int mpeg_muxer_remove_program(mpeg_muxer_t *muxer, uint16_t pn);
int peg_muxer_add_program_stream(mpeg_muxer_t *muxer, uint16_t pn, int codecid, const void *extra_data, size_t extra_data_size);
///////////////////////////////////////////////////////////////
//该类用于产生MPEG-TS/MPEG-PS
class MpegMuxer : public MediaSinkInterface {
public:
MpegMuxer(bool is_ps);
~MpegMuxer() override;
/**
* 添加音视频轨道
*/
bool addTrack(const Track::Ptr &track) override;
/**
* 重置音视频轨道
*/
void resetTracks() override;
/**
* 输入帧数据
*/
bool inputFrame(const Frame::Ptr &frame) override;
protected:
/**
* 输出ts/ps数据回调
* @param buffer ts/ps数据包
* @param timestamp 时间戳,单位毫秒
* @param key_pos 是否为关键帧的第一个ts/ps包用于确保ts切片第一帧为关键帧
*/
virtual void onWrite(std::shared_ptr<Buffer> buffer, uint32_t timestamp, bool key_pos) = 0;
private:
void createContext();
void releaseContext();
void flushCache();
void onWrite_l(const void *packet, size_t bytes);
private:
bool _is_ps = false;
bool _have_video = false;
bool _key_pos = false;
uint32_t _timestamp = 0;
mpeg_muxer_t *_context = nullptr;
BufferRaw::Ptr _buffer;
unordered_map<int, int/*track_id*/> _codec_to_trackid;
FrameMerger _frame_merger{FrameMerger::h264_prefix};
std::shared_ptr<BufferLikeString> _cache;
};
}//mediakit
#else
#include "Common/MediaSink.h"
namespace mediakit {
class MpegMuxer : public MediaSinkInterface {
public:
MpegMuxer(bool is_ps) {};
~MpegMuxer() override = default;
bool addTrack(const Track::Ptr &track) override { return false; }
void resetTracks() override {}
bool inputFrame(const Frame::Ptr &frame) override { return false; }
protected:
virtual void onWrite(std::shared_ptr<Buffer> buffer, uint32_t timestamp, bool key_pos) = 0;
};
}//namespace mediakit
#endif
#endif //ZLMEDIAKIT_MPEG_H

View File

@@ -1,159 +0,0 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#if defined(ENABLE_HLS)
#include "TsMuxer.h"
#include "mpeg-ts-proto.h"
#include "mpeg-ts.h"
#include "Extension/H264.h"
namespace mediakit {
TsMuxer::TsMuxer() {
init();
}
TsMuxer::~TsMuxer() {
uninit();
}
bool TsMuxer::addTrack(const Track::Ptr &track) {
switch (track->getCodecId()) {
case CodecH264: {
_have_video = true;
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_H264, nullptr, 0);
break;
}
case CodecH265: {
_have_video = true;
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_H265, nullptr, 0);
break;
}
case CodecAAC: {
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_AAC, nullptr, 0);
break;
}
case CodecG711A: {
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_G711A,nullptr, 0);
break;
}
case CodecG711U: {
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_G711U,nullptr, 0);
break;
}
case CodecOpus: {
_codec_to_trackid[track->getCodecId()] = mpeg_ts_add_stream(_context, PSI_STREAM_AUDIO_OPUS,nullptr, 0);
break;
}
default: WarnL << "mpeg-ts 不支持该编码格式,已忽略:" << track->getCodecName(); return false;
}
return true;
}
bool TsMuxer::inputFrame(const Frame::Ptr &frame) {
auto it = _codec_to_trackid.find(frame->getCodecId());
if (it == _codec_to_trackid.end()) {
return false;
}
auto track_id = it->second;
_is_idr_fast_packet = !_have_video;
switch (frame->getCodecId()) {
case CodecH264:
case CodecH265: {
//这里的代码逻辑是让SPS、PPS、IDR这些时间戳相同的帧打包到一起当做一个帧处理
return _frame_merger.inputFrame(frame,[&](uint32_t dts, uint32_t pts, const Buffer::Ptr &buffer, bool have_idr) {
//取视频时间戳为TS的时间戳
_timestamp = (uint32_t) dts;
_is_idr_fast_packet = have_idr;
mpeg_ts_write(_context, track_id, have_idr ? 0x0001 : 0, pts * 90LL,dts * 90LL, buffer->data(), buffer->size());
flushCache();
});
}
case CodecAAC: {
if (frame->prefixSize() == 0) {
WarnL << "必须提供adts头才能mpeg-ts打包";
return false;
}
}
default: {
if (!_have_video) {
//没有视频时才以音频时间戳为TS的时间戳
_timestamp = (uint32_t) frame->dts();
}
mpeg_ts_write(_context, track_id, frame->keyFrame() ? 0x0001 : 0, frame->pts() * 90LL, frame->dts() * 90LL, frame->data(), frame->size());
flushCache();
return true;
}
}
}
void TsMuxer::resetTracks() {
_have_video = false;
//通知片段中断
onTs(nullptr, _timestamp, 0);
uninit();
init();
}
void TsMuxer::init() {
static mpeg_ts_func_t s_func = {
[](void *param, size_t bytes) {
TsMuxer *muxer = (TsMuxer *) param;
assert(sizeof(muxer->_tsbuf) >= bytes);
return (void *) muxer->_tsbuf;
},
[](void *param, void *packet) {
//do nothing
},
[](void *param, const void *packet, size_t bytes) {
TsMuxer *muxer = (TsMuxer *) param;
muxer->onTs_l(packet, bytes);
return 0;
}
};
if (_context == nullptr) {
_context = mpeg_ts_create(&s_func, this);
}
}
void TsMuxer::onTs_l(const void *packet, size_t bytes) {
if (!_cache) {
_cache = std::make_shared<BufferLikeString>();
}
_cache->append((char *) packet, bytes);
}
void TsMuxer::flushCache() {
onTs(std::move(_cache), _timestamp, _is_idr_fast_packet);
_is_idr_fast_packet = false;
}
void TsMuxer::uninit() {
if (_context) {
mpeg_ts_destroy(_context);
_context = nullptr;
}
_codec_to_trackid.clear();
_frame_merger.clear();
}
}//namespace mediakit
#endif// defined(ENABLE_HLS)

View File

@@ -1,96 +0,0 @@
/*
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
*
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
*
* Use of this source code is governed by MIT license that can be found in the
* LICENSE file in the root of the source tree. All contributing project authors
* may be found in the AUTHORS file in the root of the source tree.
*/
#ifndef TSMUXER_H
#define TSMUXER_H
#if defined(ENABLE_HLS)
#include <unordered_map>
#include "Extension/Frame.h"
#include "Extension/Track.h"
#include "Util/File.h"
#include "Common/MediaSink.h"
#include "Common/Stamp.h"
using namespace toolkit;
namespace mediakit {
//该类用于产生MPEG-TS
class TsMuxer : public MediaSinkInterface {
public:
TsMuxer();
virtual ~TsMuxer();
/**
* 添加音视频轨道
*/
bool addTrack(const Track::Ptr &track) override;
/**
* 重置音视频轨道
*/
void resetTracks() override;
/**
* 输入帧数据
*/
bool inputFrame(const Frame::Ptr &frame) override;
protected:
/**
* 输出mpegts数据回调
* @param buffer mpegts数据包
* @param timestamp 时间戳,单位毫秒
* @param is_idr_fast_packet 是否为关键帧的第一个TS包用于确保ts切片第一帧为关键帧
*/
virtual void onTs(std::shared_ptr<Buffer> buffer, uint32_t timestamp, bool is_idr_fast_packet) = 0;
private:
void init();
void uninit();
void flushCache();
void onTs_l(const void *packet, size_t bytes);
private:
bool _have_video = false;
bool _is_idr_fast_packet = false;
void *_context = nullptr;
char _tsbuf[188];
uint32_t _timestamp = 0;
unordered_map<int, int/*track_id*/> _codec_to_trackid;
FrameMerger _frame_merger{FrameMerger::h264_prefix};
std::shared_ptr<BufferLikeString> _cache;
};
}//namespace mediakit
#else
#include "Common/MediaSink.h"
namespace mediakit {
class TsMuxer : public MediaSinkInterface {
public:
TsMuxer() {}
~TsMuxer() override {}
bool addTrack(const Track::Ptr &track) override { return false; }
void resetTracks() override {}
bool inputFrame(const Frame::Ptr &frame) override { return false; }
protected:
virtual void onTs(std::shared_ptr<Buffer> buffer, uint32_t timestamp,bool is_idr_fast_packet) = 0;
};
}//namespace mediakit
#endif// defined(ENABLE_HLS)
#endif //TSMUXER_H