Commit 383a14a7 by xiongziliang

重新mp4录制

parent 12e1068a
Subproject commit a32fe104c0290bc01a7ee73d50b568e8d9ded931
Subproject commit 6df71e01c174cdfe69e597cc4acb766a20b28620
......@@ -79,6 +79,10 @@ public:
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
public:
unsigned int syncword = 0; //12 bslbf 同步字The bit string ‘1111 1111 1111’,说明一个ADTS帧的开始
unsigned int id; //1 bslbf MPEG 标示符, 设置为1
......@@ -127,6 +131,10 @@ public:
bool keyFrame() const override {
return false;
}
bool configFrame() const override{
return false;
}
} ;
......
......@@ -118,6 +118,12 @@ public:
virtual bool keyFrame() const = 0;
/**
* 是否为配置帧,譬如sps pps vps
* @return
*/
virtual bool configFrame() const = 0;
/**
* 是否可以缓存
*/
virtual bool cacheAble() const { return true; }
......@@ -371,6 +377,7 @@ public:
_trackType = frame->getTrackType();
_codec = frame->getCodecId();
_key = frame->keyFrame();
_config = frame->configFrame();
}
virtual ~FrameCacheAble() = default;
......@@ -394,12 +401,17 @@ public:
bool keyFrame() const override{
return _key;
}
bool configFrame() const override{
return _config;
}
private:
Frame::Ptr _frame;
BufferRaw::Ptr _buffer;
TrackType _trackType;
CodecId _codec;
bool _key;
bool _config;
};
......
......@@ -82,6 +82,16 @@ public:
bool keyFrame() const override {
return H264_TYPE(buffer[iPrefixSize]) == H264Frame::NAL_IDR;
}
bool configFrame() const override{
switch(H264_TYPE(buffer[iPrefixSize]) ){
case H264Frame::NAL_SPS:
case H264Frame::NAL_PPS:
return true;
default:
return false;
}
}
public:
uint32_t timeStamp;
uint32_t ptsStamp = 0;
......@@ -118,6 +128,16 @@ public:
bool keyFrame() const override {
return H264_TYPE(_ptr[_prefixSize]) == H264Frame::NAL_IDR;
}
bool configFrame() const override{
switch(H264_TYPE(_ptr[_prefixSize])){
case H264Frame::NAL_SPS:
case H264Frame::NAL_PPS:
return true;
default:
return false;
}
}
};
/**
......
......@@ -86,6 +86,10 @@ public:
return timeStamp;
}
uint32_t pts() const override {
return ptsStamp ? ptsStamp : timeStamp;
}
uint32_t prefixSize() const override {
return iPrefixSize;
}
......@@ -99,7 +103,18 @@ public:
}
bool keyFrame() const override {
return isKeyFrame(type);
return isKeyFrame(H265_TYPE(buffer[iPrefixSize]));
}
bool configFrame() const override{
switch(H265_TYPE(buffer[iPrefixSize])){
case H265Frame::NAL_VPS:
case H265Frame::NAL_SPS:
case H265Frame::NAL_PPS:
return true;
default:
return false;
}
}
static bool isKeyFrame(int type) {
......@@ -117,9 +132,8 @@ public:
}
public:
uint16_t sequence;
uint32_t timeStamp;
unsigned char type;
uint32_t ptsStamp = 0;
string buffer;
uint32_t iPrefixSize = 4;
};
......@@ -146,8 +160,18 @@ public:
}
bool keyFrame() const override {
int type = H265_TYPE(((uint8_t *) _ptr)[_prefixSize]);
return H265Frame::isKeyFrame(type);
return H265Frame::isKeyFrame(H265_TYPE(((uint8_t *) _ptr)[_prefixSize]));
}
bool configFrame() const override{
switch(H265_TYPE(((uint8_t *) _ptr)[_prefixSize])){
case H265Frame::NAL_VPS:
case H265Frame::NAL_SPS:
case H265Frame::NAL_PPS:
return true;
default:
return false;
}
}
};
......@@ -328,7 +352,6 @@ private:
}
if(!_vps.empty()){
auto vpsFrame = std::make_shared<H265Frame>();
vpsFrame->type = H265Frame::NAL_VPS;
vpsFrame->iPrefixSize = 4;
vpsFrame->buffer.assign("\x0\x0\x0\x1", 4);
vpsFrame->buffer.append(_vps);
......@@ -337,7 +360,6 @@ private:
}
if (!_sps.empty()) {
auto spsFrame = std::make_shared<H265Frame>();
spsFrame->type = H265Frame::NAL_SPS;
spsFrame->iPrefixSize = 4;
spsFrame->buffer.assign("\x0\x0\x0\x1", 4);
spsFrame->buffer.append(_sps);
......@@ -347,7 +369,6 @@ private:
if (!_pps.empty()) {
auto ppsFrame = std::make_shared<H265Frame>();
ppsFrame->type = H265Frame::NAL_PPS;
ppsFrame->iPrefixSize = 4;
ppsFrame->buffer.assign("\x0\x0\x0\x1", 4);
ppsFrame->buffer.append(_pps);
......
......@@ -99,56 +99,56 @@ bool H265RtpDecoder::decodeRtp(const RtpPacket::Ptr &rtppack) {
// fragmentation unit (FU)
FU fu;
MakeFU(frame[2], fu);
if (fu.S == 1) {
//FU-A start
if (fu.S) {
//该帧的第一个rtp包
_h265frame->buffer.assign("\x0\x0\x0\x1", 4);
_h265frame->buffer.push_back(fu.type << 1);
_h265frame->buffer.push_back(0x01);
_h265frame->buffer.append((char *) frame + 3, length - 3);
_h265frame->type = fu.type;
_h265frame->timeStamp = rtppack->timeStamp;
_h265frame->sequence = rtppack->sequence;
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
_lastSeq = rtppack->sequence;
return (_h265frame->keyFrame()); //i frame
}
if (rtppack->sequence != (uint16_t) (_h265frame->sequence + 1)) {
if (rtppack->sequence != _lastSeq + 1 && rtppack->sequence != 0) {
//中间的或末尾的rtp包,其seq必须连续(如果回环了则判定为连续),否则说明rtp丢包,那么该帧不完整,必须得丢弃
_h265frame->buffer.clear();
WarnL << "丢包,帧废弃:" << rtppack->sequence << "," << _h265frame->sequence;
WarnL << "rtp sequence不连续: " << rtppack->sequence << " != " << _lastSeq << " + 1,该帧被废弃";
return false;
}
_h265frame->sequence = rtppack->sequence;
if (fu.E == 1) {
//FU-A end
if (!fu.E) {
//该帧的中间rtp包
_h265frame->buffer.append((char *) frame + 3, length - 3);
_h265frame->timeStamp = rtppack->timeStamp;
auto isIDR = _h265frame->keyFrame();
onGetH265(_h265frame);
return isIDR;
//该函数return时,保存下当前sequence,以便下次对比seq是否连续
_lastSeq = rtppack->sequence;
return false;
}
//FU-A mid
//该帧最后一个rtp包
_h265frame->buffer.append((char *) frame + 3, length - 3);
return false;
_h265frame->timeStamp = rtppack->timeStamp;
auto key = _h265frame->keyFrame();
onGetH265(_h265frame);
return key;
}
default: // 4.4.1. Single NAL Unit Packets (p24)
//a full frame
_h265frame->buffer.assign("\x0\x0\x0\x1", 4);
_h265frame->buffer.append((char *)frame, length);
_h265frame->type = nal;
_h265frame->timeStamp = rtppack->timeStamp;
_h265frame->sequence = rtppack->sequence;
auto isIDR = _h265frame->keyFrame();
auto key = _h265frame->keyFrame();
onGetH265(_h265frame);
return (isIDR); //i frame
return key;
}
}
void H265RtpDecoder::onGetH265(const H265Frame::Ptr &frame) {
//写入环形缓存
auto lastSeq = _h265frame->sequence;
RtpCodec::inputFrame(frame);
_h265frame = obtainFrame();
_h265frame->sequence = lastSeq;
}
......
......@@ -65,6 +65,7 @@ private:
H265Frame::Ptr obtainFrame();
private:
H265Frame::Ptr _h265frame;
int _lastSeq = 0;
};
/**
......
/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifdef ENABLE_MP4RECORD
#include "MP4Muxer.h"
#include "Util/File.h"
namespace mediakit{
#if defined(_WIN32) || defined(_WIN64)
#define fseek64 _fseeki64
#define ftell64 _ftelli64
#else
#define fseek64 fseek
#define ftell64 ftell
#endif
MP4MuxerBase::MP4MuxerBase(int flags) {
static struct mov_buffer_t s_io = {
[](void* ctx, void* data, uint64_t bytes) {
MP4MuxerBase *thiz = (MP4MuxerBase *)ctx;
return thiz->onRead(data,bytes);
},
[](void* ctx, const void* data, uint64_t bytes){
MP4MuxerBase *thiz = (MP4MuxerBase *)ctx;
return thiz->onWrite(data,bytes);
},
[](void* ctx, uint64_t offset) {
MP4MuxerBase *thiz = (MP4MuxerBase *)ctx;
return thiz->onSeek(offset);
},
[](void* ctx){
MP4MuxerBase *thiz = (MP4MuxerBase *)ctx;
return thiz->onTell();
}
};
_mov_writter.reset(mov_writer_create(&s_io,this,flags),[](mov_writer_t *ptr){
if(ptr){
mov_writer_destroy(ptr);
}
});
}
///////////////////////////////////
MP4Muxer::MP4Muxer(int flags) : MP4MuxerBase(flags) {
}
void MP4Muxer::onTrackFrame(const Frame::Ptr &frame) {
if(frame->configFrame()){
//忽略配置帧
return;
}
auto it = _codec_to_trackid.find(frame->getCodecId());
if(it == _codec_to_trackid.end()){
return;
}
mov_writer_write_l(_mov_writter.get(),
it->second,
frame->data() + frame->prefixSize(),
frame->size() - frame->prefixSize(),
frame->pts(),
frame->dts(),
frame->keyFrame() ? MOV_AV_FLAG_KEYFREAME : 0,
0);
}
void MP4Muxer::onTrackReady(const Track::Ptr &track) {
switch (track->getCodecId()) {
case CodecAAC: {
auto aac_track = dynamic_pointer_cast<AACTrack>(track);
if (!aac_track) {
WarnL << "不是AAC Track";
return;
}
auto track_id = mov_writer_add_audio(_mov_writter.get(),
MOV_OBJECT_AAC,
aac_track->getAudioChannel(),
aac_track->getAudioSampleBit(),
aac_track->getAudioSampleRate(),
aac_track->getAacCfg().data(), 2);
_codec_to_trackid[track->getCodecId()] = track_id;
}
break;
case CodecH264: {
auto h264_track = dynamic_pointer_cast<H264Track>(track);
if (!h264_track) {
WarnL << "不是H264 Track";
return;
}
struct mpeg4_avc_t avc;
string sps_pps = string("\x00\x00\x00\x01", 4) + h264_track->getSps() +
string("\x00\x00\x00\x01", 4) + h264_track->getPps();
h264_annexbtomp4(&avc, sps_pps.data(), sps_pps.size(), NULL, 0, NULL);
uint8_t extra_data[1024];
int extra_data_size = mpeg4_avc_decoder_configuration_record_save(&avc, extra_data, sizeof(extra_data));
if (extra_data_size == -1) {
WarnL << "生成H264 extra_data 失败";
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
MOV_OBJECT_H264,
h264_track->getVideoWidth(),
h264_track->getVideoHeight(),
extra_data,
extra_data_size);
_codec_to_trackid[track->getCodecId()] = track_id;
}
break;
case CodecH265: {
auto h265_track = dynamic_pointer_cast<H265Track>(track);
if (!h265_track) {
WarnL << "不是H265 Track";
return;
}
struct mpeg4_hevc_t hevc;
string vps_sps_pps = string("\x00\x00\x00\x01", 4) + h265_track->getVps() +
string("\x00\x00\x00\x01", 4) + h265_track->getSps() +
string("\x00\x00\x00\x01", 4) + h265_track->getPps();
h265_annexbtomp4(&hevc, vps_sps_pps.data(), vps_sps_pps.size(), NULL, 0, NULL);
uint8_t extra_data[1024];
int extra_data_size = mpeg4_hevc_decoder_configuration_record_save(&hevc, extra_data, sizeof(extra_data));
if (extra_data_size == -1) {
WarnL << "生成H265 extra_data 失败";
return;
}
auto track_id = mov_writer_add_video(_mov_writter.get(),
MOV_OBJECT_HEVC,
h265_track->getVideoWidth(),
h265_track->getVideoHeight(),
extra_data,
extra_data_size);
_codec_to_trackid[track->getCodecId()] = track_id;
}
break;
default:
WarnL << "MP4录制不支持该编码格式:" << track->getCodecId();
break;
}
}
#define FILE_BUF_SIZE (64 * 1024)
MP4MuxerFile::MP4MuxerFile(const char *file) {
//创建文件
auto fp = File::createfile_file(file,"wb");
if(!fp){
throw std::runtime_error(string("打开文件失败:") + file);
}
//新建文件io缓存
std::shared_ptr<char> file_buf(new char[FILE_BUF_SIZE],[](char *ptr){
if(ptr){
delete [] ptr;
}
});
if(file_buf){
//设置文件io缓存
setvbuf(fp, file_buf.get(), _IOFBF, FILE_BUF_SIZE);
}
//创建智能指针
_file.reset(fp,[file_buf](FILE *fp) {
fclose(fp);
});
}
int MP4MuxerFile::onRead(void *data, uint64_t bytes) {
return fread(data, 1, bytes, _file.get());
}
int MP4MuxerFile::onWrite(const void *data, uint64_t bytes) {
return fwrite(data, 1, bytes, _file.get());
}
#if defined(_WIN32) || defined(_WIN64)
#define fseek64 _fseeki64
#define ftell64 _ftelli64
#else
#define fseek64 fseek
#define ftell64 ftell
#endif
int MP4MuxerFile::onSeek(uint64_t offset) {
return fseek64(_file.get(), offset, SEEK_SET);
}
uint64_t MP4MuxerFile::onTell() {
return ftell64(_file.get());
}
}//namespace mediakit
#endif//#ifdef ENABLE_MP4RECORD
/*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
*
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef ZLMEDIAKIT_MP4MUXER_H
#define ZLMEDIAKIT_MP4MUXER_H
#ifdef ENABLE_MP4RECORD
#include "Common/MediaSink.h"
#include "mov-writer.h"
#include "mpeg4-hevc.h"
#include "mpeg4-avc.h"
#include "mpeg4-aac.h"
#include "mov-buffer.h"
#include "mov-format.h"
#include "Extension/AAC.h"
#include "Extension/H264.h"
#include "Extension/H265.h"
namespace mediakit{
class MP4MuxerBase{
public:
MP4MuxerBase(int flags = MOV_FLAG_FASTSTART);
virtual ~MP4MuxerBase() = default;
protected:
virtual int onRead(void* data, uint64_t bytes) = 0;
virtual int onWrite(const void* data, uint64_t bytes) = 0;
virtual int onSeek( uint64_t offset) = 0;
virtual uint64_t onTell() = 0;
protected:
std::shared_ptr<mov_writer_t> _mov_writter;
};
class MP4Muxer : public MediaSink , public MP4MuxerBase{
public:
MP4Muxer(int flags = MOV_FLAG_FASTSTART) ;
~MP4Muxer() override = default;
protected:
/**
* 某track已经准备好,其ready()状态返回true,
* 此时代表可以获取其例如sps pps等相关信息了
* @param track
*/
void onTrackReady(const Track::Ptr & track) override;
/**
* 某Track输出frame,在onAllTrackReady触发后才会调用此方法
* @param frame
*/
void onTrackFrame(const Frame::Ptr &frame) override;
private:
map<CodecId,int> _codec_to_trackid;
};
class MP4MuxerFile : public MP4Muxer {
public:
typedef std::shared_ptr<MP4MuxerFile> Ptr;
MP4MuxerFile(const char *file);
~MP4MuxerFile() = default;
protected:
int onRead(void* data, uint64_t bytes) override;
int onWrite(const void* data, uint64_t bytes) override;
int onSeek( uint64_t offset) override;
uint64_t onTell() override ;
private:
std::shared_ptr<FILE> _file;
};
}//namespace mediakit
#endif//#ifdef ENABLE_MP4RECORD
#endif //ZLMEDIAKIT_MP4MUXER_H
......@@ -106,11 +106,11 @@ void MediaRecorder::addTrack(const Track::Ptr &track) {
}
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2)
#if defined(ENABLE_MP4RECORD)
if (_mp4Maker) {
_mp4Maker->addTrack(track);
}
#endif //defined(ENABLE_MP4V2)
#endif //defined(ENABLE_MP4RECORD)
}
} /* namespace mediakit */
......@@ -64,9 +64,9 @@ private:
std::shared_ptr<HlsRecorder> _hlsMaker;
#endif //defined(ENABLE_HLS)
#if defined(ENABLE_MP4V2)
#if defined(ENABLE_MP4RECORD)
std::shared_ptr<Mp4Maker> _mp4Maker;
#endif //defined(ENABLE_MP4V2)
#endif //defined(ENABLE_MP4RECORD)
};
} /* namespace mediakit */
......
/*
 /*
* MIT License
*
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
......@@ -37,14 +37,7 @@
#include "Util/TimeTicker.h"
#include "Util/TimeTicker.h"
#include "Common/MediaSink.h"
#include "Extension/Track.h"
#include "mov-writer.h"
#include "mpeg4-hevc.h"
#include "mpeg4-avc.h"
#include "MP4Muxer.h"
using namespace toolkit;
namespace mediakit {
......@@ -62,20 +55,6 @@ public:
string strStreamId;//流ID
string strVhost;//vhost
};
class MovH265Info {
public:
mov_writer_t* pMov;
struct mpeg4_hevc_t hevc;
struct mpeg4_avc_t avc;
int videoTrack;
int audioTrack;
int width;
int height;
uint32_t startPts;
FILE * pFile;
};
class Mp4Maker : public MediaSink{
public:
typedef std::shared_ptr<Mp4Maker> Ptr;
......@@ -99,38 +78,15 @@ private:
void createFile();
void closeFile();
void asyncClose();
//时间戳:参考频率1000
void inputH264(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
void inputH265(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
//时间戳:参考频率1000
void inputAAC(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
void inputH264_l(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
void inputH265_l(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
void inputAAC_l(void *pData, uint32_t ui32Length, uint32_t ui32TimeStamp);
private:
MovH265Info _movH265info;
int _h265Record = 0;
uint32_t _startPts;
uint8_t _sBbuffer[2 * 1024 * 1024];
string _strPath;
string _strFile;
string _strFileTmp;
Ticker _ticker;
string _strLastVideo;
string _strLastAudio;
uint32_t _ui32LastVideoTime = 0;
uint32_t _ui32LastAudioTime = 0;
Ticker _createFileTicker;
Mp4Info _info;
bool _haveVideo = false;
int _audioSampleRate;
int _audioChannel;
MP4MuxerFile::Ptr _muxer;
list<Track::Ptr> _tracks;
};
} /* namespace mediakit */
......
......@@ -31,16 +31,17 @@
#include "Extension/Frame.h"
#include "Extension/Track.h"
#include "Util/File.h"
#include "Common/MediaSink.h"
using namespace toolkit;
namespace mediakit {
class TsMuxer {
class TsMuxer : public MediaSink{
public:
TsMuxer();
virtual ~TsMuxer();
void addTrack(const Track::Ptr &track);
void inputFrame(const Frame::Ptr &frame);
void addTrack(const Track::Ptr &track) override;
void inputFrame(const Frame::Ptr &frame) override ;
protected:
virtual void onTs(const void *packet, int bytes,uint32_t timestamp,int flags) = 0;
void resetTracks();
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论