mirror of
https://github.com/ZLMediaKit/ZLMediaKit.git
synced 2024-11-25 12:11:36 +08:00
MPEG: 整合复用ts/ps生成代码
This commit is contained in:
parent
dce6b27f4f
commit
89870190e9
@ -187,10 +187,7 @@ endif ()
|
||||
set(VS_FALGS "/wd4819")
|
||||
|
||||
#添加mpeg用于支持ts生成
|
||||
if (ENABLE_HLS)
|
||||
message(STATUS "ENABLE_HLS defined")
|
||||
add_definitions(-DENABLE_HLS)
|
||||
|
||||
if (ENABLE_RTPPROXY OR ENABLE_HLS)
|
||||
include_directories(${MediaServer_Root}/libmpeg/include)
|
||||
aux_source_directory(${MediaServer_Root}/libmpeg/include src_mpeg)
|
||||
aux_source_directory(${MediaServer_Root}/libmpeg/source src_mpeg)
|
||||
@ -228,12 +225,16 @@ if (ENABLE_MP4)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
#添加rtp库用于rtp转ps/ts
|
||||
if (ENABLE_RTPPROXY AND ENABLE_HLS)
|
||||
if (ENABLE_RTPPROXY)
|
||||
message(STATUS "ENABLE_RTPPROXY defined")
|
||||
add_definitions(-DENABLE_RTPPROXY)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_HLS)
|
||||
message(STATUS "ENABLE_HLS defined")
|
||||
add_definitions(-DENABLE_HLS)
|
||||
endif ()
|
||||
|
||||
#收集源代码
|
||||
file(GLOB ToolKit_src_list ${ToolKit_Root}/*/*.cpp ${ToolKit_Root}/*/*.h ${ToolKit_Root}/*/*.c)
|
||||
if (IOS)
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "Util/onceToken.h"
|
||||
#include "Util/CMD.h"
|
||||
#include "Network/TcpServer.h"
|
||||
#include "Network/UdpServer.h"
|
||||
#include "Poller/EventPoller.h"
|
||||
#include "Common/config.h"
|
||||
#include "Rtsp/RtspSession.h"
|
||||
|
@ -92,7 +92,7 @@ Frame::Ptr Frame::getCacheAbleFrame(const Frame::Ptr &frame){
|
||||
|
||||
TrackType getTrackType(CodecId codecId) {
|
||||
switch (codecId) {
|
||||
#define XX(name, type, value, str) case name : return type;
|
||||
#define XX(name, type, value, str, mpeg_id) case name : return type;
|
||||
CODEC_MAP(XX)
|
||||
#undef XX
|
||||
default : return TrackInvalid;
|
||||
@ -101,14 +101,14 @@ TrackType getTrackType(CodecId codecId) {
|
||||
|
||||
const char *getCodecName(CodecId codec) {
|
||||
switch (codec) {
|
||||
#define XX(name, type, value, str) case name : return str;
|
||||
#define XX(name, type, value, str, mpeg_id) case name : return str;
|
||||
CODEC_MAP(XX)
|
||||
#undef XX
|
||||
default : return "invalid";
|
||||
}
|
||||
}
|
||||
|
||||
#define XX(name, type, value, str) {str, name},
|
||||
#define XX(name, type, value, str, mpeg_id) {str, name},
|
||||
static map<string, CodecId, StrCaseCompare> codec_map = {CODEC_MAP(XX)};
|
||||
#undef XX
|
||||
|
||||
|
@ -31,20 +31,20 @@ typedef enum {
|
||||
} TrackType;
|
||||
|
||||
#define CODEC_MAP(XX) \
|
||||
XX(CodecH264, TrackVideo, 0, "H264") \
|
||||
XX(CodecH265, TrackVideo, 1, "H265") \
|
||||
XX(CodecAAC, TrackAudio, 2, "mpeg4-generic") \
|
||||
XX(CodecG711A, TrackAudio, 3, "PCMA") \
|
||||
XX(CodecG711U, TrackAudio, 4, "PCMU") \
|
||||
XX(CodecOpus, TrackAudio, 5, "opus") \
|
||||
XX(CodecL16, TrackAudio, 6, "L16") \
|
||||
XX(CodecVP8, TrackVideo, 7, "VP8") \
|
||||
XX(CodecVP9, TrackVideo, 8, "VP9") \
|
||||
XX(CodecAV1, TrackVideo, 9, "AV1X")
|
||||
XX(CodecH264, TrackVideo, 0, "H264", PSI_STREAM_H264) \
|
||||
XX(CodecH265, TrackVideo, 1, "H265", PSI_STREAM_H265) \
|
||||
XX(CodecAAC, TrackAudio, 2, "mpeg4-generic", PSI_STREAM_AAC) \
|
||||
XX(CodecG711A, TrackAudio, 3, "PCMA", PSI_STREAM_AUDIO_G711A) \
|
||||
XX(CodecG711U, TrackAudio, 4, "PCMU", PSI_STREAM_AUDIO_G711U) \
|
||||
XX(CodecOpus, TrackAudio, 5, "opus", PSI_STREAM_AUDIO_OPUS) \
|
||||
XX(CodecL16, TrackAudio, 6, "L16", PSI_STREAM_RESERVED) \
|
||||
XX(CodecVP8, TrackVideo, 7, "VP8", PSI_STREAM_VP8) \
|
||||
XX(CodecVP9, TrackVideo, 8, "VP9", PSI_STREAM_VP9) \
|
||||
XX(CodecAV1, TrackVideo, 9, "AV1X", PSI_STREAM_AV1)
|
||||
|
||||
typedef enum {
|
||||
CodecInvalid = -1,
|
||||
#define XX(name, type, value, str) name = value,
|
||||
#define XX(name, type, value, str, mpeg_id) name = value,
|
||||
CODEC_MAP(XX)
|
||||
#undef XX
|
||||
CodecMax
|
||||
|
@ -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 ¶ms){
|
||||
using Ptr = std::shared_ptr<HlsRecorder>;
|
||||
|
||||
HlsRecorder(const string &m3u8_file, const string ¶ms) : 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
256
src/Record/MPEG.cpp
Normal 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
114
src/Record/MPEG.h
Normal 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
|
@ -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)
|
@ -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
|
@ -9,148 +9,14 @@
|
||||
*/
|
||||
|
||||
#if defined(ENABLE_RTPPROXY)
|
||||
|
||||
#include "PSEncoder.h"
|
||||
#include "Extension/H264.h"
|
||||
#include "Rtsp/RtspMuxer.h"
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
PSEncoder::PSEncoder() {
|
||||
_buffer = BufferRaw::create();
|
||||
init();
|
||||
}
|
||||
|
||||
PSEncoder::~PSEncoder() {
|
||||
|
||||
}
|
||||
|
||||
void PSEncoder::init() {
|
||||
static struct ps_muxer_func_t func = {
|
||||
/*alloc*/
|
||||
[](void *param, size_t bytes) {
|
||||
PSEncoder *thiz = (PSEncoder *) 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) {
|
||||
PSEncoder *thiz = (PSEncoder *) param;
|
||||
thiz->onPS(thiz->_timestamp, packet, bytes);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
_muxer.reset(ps_muxer_create(&func, this), [](struct ps_muxer_t *ptr) {
|
||||
ps_muxer_destroy(ptr);
|
||||
});
|
||||
}
|
||||
|
||||
bool PSEncoder::addTrack(const Track::Ptr &track) {
|
||||
switch (track->getCodecId()) {
|
||||
case CodecH264: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_VIDEO_H264, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CodecH265: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_VIDEO_H265, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CodecAAC: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_AAC, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CodecG711A: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_G711A, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CodecG711U: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_G711U, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
case CodecOpus: {
|
||||
_codec_to_trackid[track->getCodecId()].track_id = ps_muxer_add_stream(_muxer.get(), STREAM_AUDIO_OPUS, nullptr, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
default: WarnL << "mpeg-ps 不支持该编码格式,已忽略:" << track->getCodecName(); return false;
|
||||
}
|
||||
//尝试音视频同步
|
||||
stampSync();
|
||||
return true;
|
||||
}
|
||||
|
||||
void PSEncoder::stampSync(){
|
||||
if(_codec_to_trackid.size() < 2){
|
||||
return;
|
||||
}
|
||||
|
||||
Stamp *audio = nullptr, *video = nullptr;
|
||||
for(auto &pr : _codec_to_trackid){
|
||||
switch (getTrackType((CodecId) pr.first)){
|
||||
case TrackAudio : audio = &pr.second.stamp; break;
|
||||
case TrackVideo : video = &pr.second.stamp; break;
|
||||
default : break;
|
||||
}
|
||||
}
|
||||
|
||||
if(audio && video){
|
||||
//音频时间戳同步于视频,因为音频时间戳被修改后不影响播放
|
||||
audio->syncTo(*video);
|
||||
}
|
||||
}
|
||||
|
||||
void PSEncoder::resetTracks() {
|
||||
init();
|
||||
}
|
||||
|
||||
bool PSEncoder::inputFrame(const Frame::Ptr &frame) {
|
||||
auto it = _codec_to_trackid.find(frame->getCodecId());
|
||||
if (it == _codec_to_trackid.end()) {
|
||||
return false;
|
||||
}
|
||||
auto &track_info = it->second;
|
||||
int64_t dts_out, pts_out;
|
||||
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) {
|
||||
track_info.stamp.revise(dts, pts, dts_out, pts_out);
|
||||
//取视频时间戳为TS的时间戳
|
||||
_timestamp = (uint32_t) pts_out;
|
||||
ps_muxer_input(_muxer.get(), track_info.track_id, have_idr ? 0x0001 : 0,
|
||||
pts_out * 90LL, dts_out * 90LL, buffer->data(), buffer->size());
|
||||
});
|
||||
}
|
||||
|
||||
case CodecAAC: {
|
||||
if (frame->prefixSize() == 0) {
|
||||
WarnL << "必须提供adts头才能mpeg-ps打包";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
default: {
|
||||
track_info.stamp.revise(frame->dts(), frame->pts(), dts_out, pts_out);
|
||||
_timestamp = (uint32_t) dts_out;
|
||||
ps_muxer_input(_muxer.get(), track_info.track_id, frame->keyFrame() ? 0x0001 : 0, pts_out * 90LL,
|
||||
dts_out * 90LL, frame->data(), frame->size());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
PSEncoderImp::PSEncoderImp(uint32_t ssrc, uint8_t payload_type) {
|
||||
PSEncoderImp::PSEncoderImp(uint32_t ssrc, uint8_t payload_type) : MpegMuxer(true) {
|
||||
GET_CONFIG(uint32_t,video_mtu,Rtp::kVideoMtuSize);
|
||||
_rtp_encoder = std::make_shared<CommonRtpEncoder>(CodecInvalid, ssrc, video_mtu, 90000, payload_type, 0);
|
||||
_rtp_encoder->setRtpRing(std::make_shared<RtpRing::RingType>());
|
||||
@ -164,9 +30,13 @@ PSEncoderImp::~PSEncoderImp() {
|
||||
InfoL << this << " " << printSSRC(_rtp_encoder->getSsrc());
|
||||
}
|
||||
|
||||
void PSEncoderImp::onPS(uint32_t stamp, void *packet, size_t bytes) {
|
||||
_rtp_encoder->inputFrame(std::make_shared<FrameFromPtr>((char *) packet, bytes, stamp, stamp));
|
||||
void PSEncoderImp::onWrite(std::shared_ptr<Buffer> buffer, uint32_t stamp, bool key_pos) {
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
_rtp_encoder->inputFrame(std::make_shared<FrameFromPtr>(buffer->data(), buffer->size(), stamp, stamp));
|
||||
}
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
#endif//defined(ENABLE_RTPPROXY)
|
||||
|
@ -10,63 +10,17 @@
|
||||
|
||||
#ifndef ZLMEDIAKIT_PSENCODER_H
|
||||
#define ZLMEDIAKIT_PSENCODER_H
|
||||
|
||||
#if defined(ENABLE_RTPPROXY)
|
||||
#include "mpeg-ps.h"
|
||||
|
||||
#include "Record/MPEG.h"
|
||||
#include "Common/MediaSink.h"
|
||||
#include "Common/Stamp.h"
|
||||
#include "Extension/CommonRtp.h"
|
||||
|
||||
namespace mediakit{
|
||||
|
||||
//该类实现mpeg-ps容器格式的打包
|
||||
class PSEncoder : public MediaSinkInterface {
|
||||
public:
|
||||
PSEncoder();
|
||||
~PSEncoder() override;
|
||||
|
||||
/**
|
||||
* 添加音视频轨道
|
||||
*/
|
||||
bool addTrack(const Track::Ptr &track) override;
|
||||
|
||||
/**
|
||||
* 重置音视频轨道
|
||||
*/
|
||||
void resetTracks() override;
|
||||
|
||||
/**
|
||||
* 输入帧数据
|
||||
*/
|
||||
bool inputFrame(const Frame::Ptr &frame) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 输出mpeg-ps的回调函数
|
||||
* @param stamp 时间戳,毫秒
|
||||
* @param packet 数据指针
|
||||
* @param bytes 数据长度
|
||||
*/
|
||||
virtual void onPS(uint32_t stamp, void *packet, size_t bytes) = 0;
|
||||
|
||||
private:
|
||||
void init();
|
||||
//音视频时间戳同步用
|
||||
void stampSync();
|
||||
|
||||
private:
|
||||
struct track_info {
|
||||
int track_id = -1;
|
||||
Stamp stamp;
|
||||
};
|
||||
|
||||
private:
|
||||
uint32_t _timestamp = 0;
|
||||
BufferRaw::Ptr _buffer;
|
||||
std::shared_ptr<struct ps_muxer_t> _muxer;
|
||||
unordered_map<int, track_info> _codec_to_trackid;
|
||||
FrameMerger _frame_merger{FrameMerger::h264_prefix};
|
||||
};
|
||||
|
||||
class PSEncoderImp : public PSEncoder{
|
||||
class PSEncoderImp : public MpegMuxer{
|
||||
public:
|
||||
PSEncoderImp(uint32_t ssrc, uint8_t payload_type = 96);
|
||||
~PSEncoderImp() override;
|
||||
@ -76,12 +30,13 @@ protected:
|
||||
virtual void onRTP(Buffer::Ptr rtp) = 0;
|
||||
|
||||
protected:
|
||||
void onPS(uint32_t stamp, void *packet, size_t bytes) override;
|
||||
void onWrite(std::shared_ptr<Buffer> buffer, uint32_t stamp, bool key_pos) override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<CommonRtpEncoder> _rtp_encoder;
|
||||
};
|
||||
|
||||
}//namespace mediakit
|
||||
|
||||
#endif //ENABLE_RTPPROXY
|
||||
#endif //ZLMEDIAKIT_PSENCODER_H
|
||||
|
@ -12,18 +12,18 @@
|
||||
#define ZLMEDIAKIT_TSMEDIASOURCEMUXER_H
|
||||
|
||||
#include "TSMediaSource.h"
|
||||
#include "Record/TsMuxer.h"
|
||||
#include "Record/MPEG.h"
|
||||
|
||||
namespace mediakit {
|
||||
|
||||
class TSMediaSourceMuxer : public TsMuxer, public MediaSourceEventInterceptor,
|
||||
class TSMediaSourceMuxer : public MpegMuxer, public MediaSourceEventInterceptor,
|
||||
public std::enable_shared_from_this<TSMediaSourceMuxer> {
|
||||
public:
|
||||
using Ptr = std::shared_ptr<TSMediaSourceMuxer>;
|
||||
|
||||
TSMediaSourceMuxer(const string &vhost,
|
||||
const string &app,
|
||||
const string &stream_id) {
|
||||
const string &stream_id) : MpegMuxer(false) {
|
||||
_media_src = std::make_shared<TSMediaSource>(vhost, app, stream_id);
|
||||
}
|
||||
|
||||
@ -54,7 +54,7 @@ public:
|
||||
_media_src->clearCache();
|
||||
}
|
||||
if (_enabled || !ts_demand) {
|
||||
return TsMuxer::inputFrame(frame);
|
||||
return MpegMuxer::inputFrame(frame);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -66,13 +66,13 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
auto packet = std::make_shared<TSPacket>(std::move(buffer));
|
||||
packet->time_stamp = timestamp;
|
||||
_media_src->onWrite(std::move(packet), is_idr_fast_packet);
|
||||
_media_src->onWrite(std::move(packet), key_pos);
|
||||
}
|
||||
|
||||
private:
|
||||
|
Loading…
Reference in New Issue
Block a user