MP4Recorder.cpp 4.1 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
xiongziliang committed
3 4 5
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
xiongziliang committed
6 7 8
 * 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.
xiongziliang committed
9
 */
xzl committed
10

xiongziliang committed
11
#ifdef ENABLE_MP4
12
#include <ctime>
xzl committed
13
#include <sys/stat.h>
14
#include "Util/File.h"
xiongziliang committed
15
#include "Common/config.h"
xiongziliang committed
16
#include "MP4Recorder.h"
17
#include "Thread/WorkThreadPool.h"
18

19 20 21 22
using namespace toolkit;

namespace mediakit {

xiongziliang committed
23
MP4Recorder::MP4Recorder(const string& strPath,
24 25 26 27 28
                   const string &strVhost,
                   const string &strApp,
                   const string &strStreamId) {
    _strPath = strPath;
    /////record 业务逻辑//////
29 30 31 32
    _info.app = strApp;
    _info.stream = strStreamId;
    _info.vhost = strVhost;
    _info.folder = strPath;
xzl committed
33
}
xiongziliang committed
34
MP4Recorder::~MP4Recorder() {
35
    closeFile();
xzl committed
36 37
}

xiongziliang committed
38
void MP4Recorder::createFile() {
39 40 41 42 43 44 45
    closeFile();
    auto strDate = getTimeStr("%Y-%m-%d");
    auto strTime = getTimeStr("%H-%M-%S");
    auto strFileTmp = _strPath + strDate + "/." + strTime + ".mp4";
    auto strFile =	_strPath + strDate + "/" + strTime + ".mp4";

    /////record 业务逻辑//////
46 47 48
    _info.start_time = ::time(NULL);
    _info.file_name = strTime + ".mp4";
    _info.file_path = strFile;
49
    GET_CONFIG(string,appName,Record::kAppName);
50 51 52 53 54
    _info.url = appName + "/"
                + _info.app + "/"
                + _info.stream + "/"
                + strDate + "/"
                + strTime + ".mp4";
55

56
    try {
57 58 59
        _muxer = std::make_shared<MP4Muxer>();
        _muxer->openMP4(strFileTmp);
        for (auto &track :_tracks) {
60 61
            //添加track
            _muxer->addTrack(track);
62 63 64 65
        }
        _strFileTmp = strFileTmp;
        _strFile = strFile;
        _createFileTicker.resetTime();
66
    } catch (std::exception &ex) {
67 68
        WarnL << ex.what();
    }
xzl committed
69 70
}

xiongziliang committed
71
void MP4Recorder::asyncClose() {
72 73 74 75 76 77
    auto muxer = _muxer;
    auto strFileTmp = _strFileTmp;
    auto strFile = _strFile;
    auto info = _info;
    WorkThreadPool::Instance().getExecutor()->async([muxer,strFileTmp,strFile,info]() {
        //获取文件录制时间,放在关闭mp4之前是为了忽略关闭mp4执行时间
78
        const_cast<RecordInfo&>(info).time_len = ::time(NULL) - info.start_time;
79
        //关闭mp4非常耗时,所以要放在后台线程执行
80
        muxer->closeMP4();
81

82 83
        //获取文件大小
        struct stat fileData;
84 85 86 87 88 89 90 91 92 93
        stat(strFileTmp.data(), &fileData);
        const_cast<RecordInfo &>(info).file_size = fileData.st_size;
        if (fileData.st_size < 1024) {
            //录像文件太小,删除之
            File::delete_file(strFileTmp.data());
            return;
        }
        //临时文件名改成正式文件名,防止mp4未完成时被访问
        rename(strFileTmp.data(),strFile.data());

94 95 96
        /////record 业务逻辑//////
        NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastRecordMP4,info);
    });
97 98
}

xiongziliang committed
99
void MP4Recorder::closeFile() {
100 101 102 103
    if (_muxer) {
        asyncClose();
        _muxer = nullptr;
    }
xzl committed
104 105
}

xiongziliang committed
106
void MP4Recorder::inputFrame(const Frame::Ptr &frame) {
107 108 109 110 111 112 113 114 115 116 117 118 119 120
    GET_CONFIG(uint32_t,recordSec,Record::kFileSecond);
    if(!_muxer || ((_createFileTicker.elapsedTime() > recordSec * 1000) &&
                  (!_haveVideo || (_haveVideo && frame->keyFrame()))) ){
        //成立条件
        //1、_muxer为空
        //2、到了切片时间,并且只有音频
        //3、到了切片时间,有视频并且遇到视频的关键帧
        createFile();
    }

    if(_muxer){
        //生成mp4文件
        _muxer->inputFrame(frame);
    }
121 122
}

xiongziliang committed
123
void MP4Recorder::addTrack(const Track::Ptr & track){
124 125 126 127 128
    //保存所有的track,为创建MP4MuxerFile做准备
    _tracks.emplace_back(track);
    if(track->getTrackType() == TrackVideo){
        _haveVideo = true;
    }
129 130
}

131
void MP4Recorder::resetTracks() {
132 133 134 135
    closeFile();
    _tracks.clear();
    _haveVideo = false;
    _createFileTicker.resetTime();
136 137
}

xiongziliang committed
138
} /* namespace mediakit */
xzl committed
139 140


xiongziliang committed
141
#endif //ENABLE_MP4