media_recorder.h 4.56 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 *  Copyright (C) 2018 Savoir-faire Linux Inc.
 *
 *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */

#pragma once

#include "config.h"
24
#include "media_buffer.h"
25 26 27 28
#include "media_encoder.h"
#include "media_filter.h"
#include "media_stream.h"
#include "noncopyable.h"
29
#include "observer.h"
30 31 32 33

#include <map>
#include <memory>
#include <mutex>
34
#include <queue>
35 36 37 38 39 40
#include <stdexcept>
#include <string>
#include <utility>

namespace ring {

41
class MediaRecorder : public std::enable_shared_from_this<MediaRecorder>
42 43 44 45 46
{
public:
    MediaRecorder();
    ~MediaRecorder();

47 48 49 50
    /**
     * Gets whether or not the recorder is active.
     */
    bool isRecording() const;
51

52 53 54 55 56 57 58
    /**
     * Get file path of file to be recorded. Same path as sent to @setPath, but with
     * file extension appended.
     *
     * NOTE @audioOnly must be called to have the right extension.
     */
    std::string getPath() const;
59

60 61 62 63
    /**
     * Sets whether or not output file will have audio. Determines the extension of the
     * output file (.ogg or .webm).
     */
64 65
    void audioOnly(bool audioOnly);

66 67 68 69 70 71 72 73 74 75 76 77 78 79
    /**
     * Sets output file path.
     *
     * NOTE An empty path will put the output file in the working directory.
     */
    void setPath(const std::string& path);

    /**
     * Sets title and description metadata for the file. Uses default if either is empty.
     * Default title is "Conversation at %Y-%m-%d %H:%M:%S".
     * Default description is "Recorded with Jami https://jami.net".
     *
     * NOTE replaces %TIMESTAMP with time at start of recording
     */
80 81
    void setMetadata(const std::string& title, const std::string& desc);

82
    /**
83
     * Adds a stream to the recorder. Caller must then attach this to the media source.
84
     */
85 86 87 88 89 90
    Observer<std::shared_ptr<MediaFrame>>* addStream(const MediaStream& ms);

    /**
     * Gets the stream observer so the caller can detach it from the media source.
     */
    Observer<std::shared_ptr<MediaFrame>>* getStream(const std::string& name) const;
91

92 93 94 95
    /**
     * Starts the record. Streams must have been added using Observable::attach and
     * @addStream.
     */
96 97
    int startRecording();

98 99 100
    /**
     * Stops the record. Streams must be removed using Observable::detach afterwards.
     */
101 102 103 104 105
    void stopRecording();

private:
    NON_COPYABLE(MediaRecorder);

106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
    struct StreamObserver : public Observer<std::shared_ptr<MediaFrame>> {
        const MediaStream info;

        StreamObserver(const MediaStream& ms, std::function<void(const std::shared_ptr<MediaFrame>&)> func)
            : info(ms), cb_(func)
        {};

        ~StreamObserver() {};

        void update(Observable<std::shared_ptr<MediaFrame>>* /*ob*/, const std::shared_ptr<MediaFrame>& m) override
        {
            cb_(m);
        }

    private:
        std::function<void(const std::shared_ptr<MediaFrame>&)> cb_;
    };

    void onFrame(const std::string& name, const std::shared_ptr<MediaFrame>& frame);

126
    void flush();
127
    void reset();
128 129 130 131 132 133 134 135 136

    int initRecord();
    MediaStream setupVideoOutput();
    std::string buildVideoFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const;
    MediaStream setupAudioOutput();
    std::string buildAudioFilter(const std::vector<MediaStream>& peers, const MediaStream& local) const;

    std::mutex mutex_; // protect against concurrent file writes

137
    std::map<std::string, std::unique_ptr<StreamObserver>> streams_;
138

139
    std::string path_;
140 141 142 143
    std::tm startTime_;
    std::string title_;
    std::string description_;

144 145 146
    std::unique_ptr<MediaEncoder> encoder_;
    std::unique_ptr<MediaFilter> videoFilter_;
    std::unique_ptr<MediaFilter> audioFilter_;
147

148 149
    bool hasAudio_ {false};
    bool hasVideo_ {false};
150 151 152 153 154
    int videoIdx_ = -1;
    int audioIdx_ = -1;
    bool isRecording_ = false;
    bool audioOnly_ = false;

155
    void filterAndEncode(MediaFilter* filter, int streamIdx);
156 157 158
};

}; // namespace ring