/* * 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. */ #ifdef ENABLE_MP4 #include #include #include "Util/File.h" #include "Common/config.h" #include "MP4Recorder.h" #include "Thread/WorkThreadPool.h" using namespace toolkit; namespace mediakit { MP4Recorder::MP4Recorder(const string &path, const string &vhost, const string &app, const string &stream_id, size_t max_second) { _folder_path = path; /////record 业务逻辑////// _info.app = app; _info.stream = stream_id; _info.vhost = vhost; _info.folder = path; GET_CONFIG(size_t ,recordSec,Record::kFileSecond); _max_second = max_second ? max_second : recordSec; } MP4Recorder::~MP4Recorder() { closeFile(); } void MP4Recorder::createFile() { closeFile(); auto date = getTimeStr("%Y-%m-%d"); auto time = getTimeStr("%H-%M-%S"); auto full_path_tmp = _folder_path + date + "/." + time + ".mp4"; auto full_path = _folder_path + date + "/" + time + ".mp4"; /////record 业务逻辑////// _info.start_time = ::time(NULL); _info.file_name = time + ".mp4"; _info.file_path = full_path; GET_CONFIG(string, appName, Record::kAppName); _info.url = appName + "/" + _info.app + "/" + _info.stream + "/" + date + "/" + time + ".mp4"; try { _muxer = std::make_shared(); _muxer->openMP4(full_path_tmp); for (auto &track :_tracks) { //添加track _muxer->addTrack(track); } _full_path_tmp = full_path_tmp; _full_path = full_path; } catch (std::exception &ex) { WarnL << ex.what(); } } void MP4Recorder::asyncClose() { auto muxer = _muxer; auto full_path_tmp = _full_path_tmp; auto full_path = _full_path; auto info = _info; WorkThreadPool::Instance().getExecutor()->async([muxer, full_path_tmp, full_path, info]() mutable { //获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间 info.time_len = (float) (::time(NULL) - info.start_time); //关闭mp4非常耗时,所以要放在后台线程执行 muxer->closeMP4(); //获取文件大小 struct stat fileData; stat(full_path_tmp.data(), &fileData); info.file_size = fileData.st_size; if (fileData.st_size < 1024) { //录像文件太小,删除之 File::delete_file(full_path_tmp.data()); return; } //临时文件名改成正式文件名,防止mp4未完成时被访问 rename(full_path_tmp.data(), full_path.data()); /////record 业务逻辑////// NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4, info); }); } void MP4Recorder::closeFile() { if (_muxer) { asyncClose(); _muxer = nullptr; } } bool MP4Recorder::inputFrame(const Frame::Ptr &frame) { if (_last_dts == 0 || _last_dts > frame->dts()) { //极少情况下dts时间戳可能回退 _last_dts = frame->dts(); } auto duration = frame->dts() - _last_dts; if (!_muxer || ((duration > _max_second * 1000) && (!_have_video || (_have_video && frame->keyFrame())))) { //成立条件 //1、_muxer为空 //2、到了切片时间,并且只有音频 //3、到了切片时间,有视频并且遇到视频的关键帧 _last_dts = 0; createFile(); } if (_muxer) { //生成mp4文件 return _muxer->inputFrame(frame); } return false; } bool MP4Recorder::addTrack(const Track::Ptr &track) { //保存所有的track,为创建MP4MuxerFile做准备 _tracks.emplace_back(track); if (track->getTrackType() == TrackVideo) { _have_video = true; } return true; } void MP4Recorder::resetTracks() { closeFile(); _tracks.clear(); _have_video = false; } } /* namespace mediakit */ #endif //ENABLE_MP4