media_encoder.cpp 28.3 KB
Newer Older
1
/*
2
 *  Copyright (C) 2013-2019 Savoir-faire Linux Inc.
3
 *
4
 *  Author: Guillaume Roguez <Guillaume.Roguez@savoirfairelinux.com>
5
 *  Author: Eloi Bail <Eloi.Bail@savoirfairelinux.com>
6
 *  Author: Philippe Gorley <philippe.gorley@savoirfairelinux.com>
7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *  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
20
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
21 22
 */

23
#include "libav_deps.h" // MUST BE INCLUDED FIRST
Guillaume Roguez's avatar
Guillaume Roguez committed
24
#include "media_codec.h"
25
#include "media_encoder.h"
26
#include "media_buffer.h"
Guillaume Roguez's avatar
Guillaume Roguez committed
27

28
#include "client/ring_signal.h"
29
#include "fileutils.h"
Tristan Matthews's avatar
Tristan Matthews committed
30
#include "logger.h"
31 32
#include "manager.h"
#include "string_utils.h"
33
#include "system_codec_container.h"
34 35 36 37

#ifdef RING_ACCEL
#include "video/accel.h"
#endif
38

39 40 41 42
extern "C" {
#include <libavutil/parseutils.h>
}

43 44
#include <algorithm>
#include <fstream>
45
#include <iostream>
46
#include <json/json.h>
47
#include <sstream>
48
#include <thread> // hardware_concurrency
49

50 51
// Define following line if you need to debug libav SDP
//#define DEBUG_SDP 1
52

Adrien Béraud's avatar
Adrien Béraud committed
53
namespace jami {
54

Pierre Lespagnol's avatar
Pierre Lespagnol committed
55 56 57
constexpr double LOGREG_PARAM_A {114.40432};
constexpr double LOGREG_PARAM_B {-6.049181};

58 59
MediaEncoder::MediaEncoder()
    : outputCtx_(avformat_alloc_context())
60
{}
61

62
MediaEncoder::~MediaEncoder()
63
{
64
    if (outputCtx_) {
65 66
        if (outputCtx_->priv_data)
            av_write_trailer(outputCtx_);
67
        for (auto encoderCtx : encoders_) {
68 69 70 71 72 73 74
            if (encoderCtx) {
#ifndef _MSC_VER
                avcodec_free_context(&encoderCtx);
#else
                avcodec_close(encoderCtx);
#endif
            }
75
        }
76 77 78
        avformat_free_context(outputCtx_);
    }
    av_dict_free(&options_);
79 80
}

81
void
82
MediaEncoder::setOptions(const MediaStream& opts)
83
{
84
    if (!opts.isValid()) {
Adrien Béraud's avatar
Adrien Béraud committed
85
        JAMI_ERR() << "Invalid options";
86 87 88 89 90 91 92 93 94 95 96
        return;
    }

    if (opts.isVideo) {
        videoOpts_ = opts;
        // Make sure width and height are even (required by x264)
        // This is especially for image/gif streaming, as video files and cameras usually have even resolutions
        videoOpts_.width -= videoOpts_.width % 2;
        videoOpts_.height -= videoOpts_.height % 2;
        if (not videoOpts_.frameRate)
            videoOpts_.frameRate = 30;
97 98
        if (opts.bitrate)
            libav_utils::setDictValue(&options_, "max_rate", std::to_string(opts.bitrate));
99 100 101
    } else {
        audioOpts_ = opts;
    }
102 103
}

104 105
void
MediaEncoder::setOptions(const MediaDescription& args)
106
{
107 108 109
    libav_utils::setDictValue(&options_, "payload_type", std::to_string(args.payload_type));
    libav_utils::setDictValue(&options_, "max_rate", std::to_string(args.codec->bitrate));
    libav_utils::setDictValue(&options_, "crf", std::to_string(args.codec->quality));
110

Guillaume Roguez's avatar
Guillaume Roguez committed
111
    if (not args.parameters.empty())
112
        libav_utils::setDictValue(&options_, "parameters", args.parameters);
113 114
}

115
void
116
MediaEncoder::setMetadata(const std::string& title, const std::string& description)
117
{
118 119 120 121
    if (not title.empty())
        libav_utils::setDictValue(&outputCtx_->metadata, "title", title);
    if (not description.empty())
        libav_utils::setDictValue(&outputCtx_->metadata, "description", description);
122 123
}

124 125 126 127 128
void
MediaEncoder::setInitSeqVal(uint16_t seqVal)
{
    //only set not default value (!=0)
    if (seqVal != 0)
129
        av_opt_set_int(outputCtx_, "seq", seqVal, AV_OPT_SEARCH_CHILDREN);
130 131 132 133 134
}

uint16_t
MediaEncoder::getLastSeqValue()
{
135 136 137
    int64_t retVal;
    if (av_opt_get_int(outputCtx_, "seq", AV_OPT_SEARCH_CHILDREN, &retVal) >= 0)
        return (uint16_t)retVal;
138 139
    else
        return 0;
140 141 142
}

void
143
MediaEncoder::openOutput(const std::string& filename, const std::string& format)
144 145
{
    avformat_free_context(outputCtx_);
146 147 148 149
    if (format.empty())
        avformat_alloc_output_context2(&outputCtx_, nullptr, nullptr, filename.c_str());
    else
        avformat_alloc_output_context2(&outputCtx_, nullptr, format.c_str(), filename.c_str());
150 151 152 153

#ifdef RING_ACCEL
    enableAccel_ = Manager::instance().videoPreferences.getEncodingAccelerated();
#endif
154 155 156
}

int
157
MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo)
158 159 160
{
    if (systemCodecInfo.mediaType == MEDIA_AUDIO) {
        audioCodec_ = systemCodecInfo.name;
161
        return initStream(systemCodecInfo, nullptr);
162 163 164 165 166 167 168 169 170 171 172
    } else {
        videoCodec_ = systemCodecInfo.name;
        // TODO only support 1 audio stream and 1 video stream per encoder
        if (audioOpts_.isValid())
            return 1; // stream will be added to AVFormatContext after audio stream
        else
            return 0; // only a video stream
    }
}

int
173
MediaEncoder::initStream(const std::string& codecName, AVBufferRef* framesCtx)
174 175 176
{
    const auto codecInfo = getSystemCodecContainer()->searchCodecByName(codecName, MEDIA_ALL);
    if (codecInfo)
177
        return initStream(*codecInfo, framesCtx);
178 179 180 181 182
    else
        return -1;
}

int
183
MediaEncoder::initStream(const SystemCodecInfo& systemCodecInfo, AVBufferRef* framesCtx)
184 185 186
{
    AVCodec* outputCodec = nullptr;
    AVCodecContext* encoderCtx = nullptr;
187 188 189 190
#ifdef RING_ACCEL
    if (systemCodecInfo.mediaType == MEDIA_VIDEO) {
        if (enableAccel_) {
            if (accel_ = video::HardwareAccel::setupEncoder(
191 192
                static_cast<AVCodecID>(systemCodecInfo.avcodecId),
                videoOpts_.width, videoOpts_.height, framesCtx)) {
193 194 195
                outputCodec = avcodec_find_encoder_by_name(accel_->getCodecName().c_str());
            }
        } else {
Adrien Béraud's avatar
Adrien Béraud committed
196
            JAMI_WARN() << "Hardware encoding disabled";
197 198 199 200
        }
    }
#endif

201
    if (!outputCodec) {
202 203 204 205 206 207 208 209 210
        /* find the video encoder */
        if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263)
            // For H263 encoding, we force the use of AV_CODEC_ID_H263P (H263-1998)
            // H263-1998 can manage all frame sizes while H263 don't
            // AV_CODEC_ID_H263 decoder will be used for decoding
            outputCodec = avcodec_find_encoder(AV_CODEC_ID_H263P);
        else
            outputCodec = avcodec_find_encoder(static_cast<AVCodecID>(systemCodecInfo.avcodecId));
        if (!outputCodec) {
Adrien Béraud's avatar
Adrien Béraud committed
211
            JAMI_ERR("Encoder \"%s\" not found!", systemCodecInfo.name.c_str());
212 213
            throw MediaEncoderException("No output encoder");
        }
214 215
    }

216 217
    encoderCtx = prepareEncoderContext(outputCodec, systemCodecInfo.mediaType == MEDIA_VIDEO);
    encoders_.push_back(encoderCtx);
218 219 220

#ifdef RING_ACCEL
    if (accel_) {
221
        accel_->setDetails(encoderCtx);
222 223 224 225
        encoderCtx->opaque = accel_.get();
    }
#endif

Pierre Lespagnol's avatar
Pierre Lespagnol committed
226 227 228
    uint64_t maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate"));
    uint8_t crf = (uint8_t) std::round(LOGREG_PARAM_A + log(pow(maxBitrate, LOGREG_PARAM_B)));     // CRF = A + B*ln(maxBitrate)
    uint64_t bufSize = 2 * maxBitrate;
229

230
    /* let x264 preset override our encoder settings */
231
    if (systemCodecInfo.avcodecId == AV_CODEC_ID_H264) {
232 233
        auto profileLevelId = libav_utils::getDictValue(options_, "parameters");
        extractProfileLevelID(profileLevelId, encoderCtx);
234
#ifdef RING_ACCEL
235 236 237 238 239 240 241
#ifdef ENABLE_VIDEOTOOLBOX
        if (accel_) {
            maxBitrate = 2000 * std::atoi(libav_utils::getDictValue(options_, "max_rate"));
            bufSize = 2 * maxBitrate;
            crf = 20;
        }
#endif
242 243 244 245 246
        if (accel_)
            // limit the bitrate else it will easily go up to a few MiB/s
            encoderCtx->bit_rate = maxBitrate;
        else
#endif
247
        forcePresetX264(encoderCtx);
248
        // For H264 :
249 250 251
        // Streaming => VBV (constrained encoding) + CRF (Constant Rate Factor)
        if (crf == SystemCodecInfo::DEFAULT_NO_QUALITY)
            crf = 30; // good value for H264-720p@30
252
        JAMI_DBG("H264 encoder setup: crf=%u, maxrate=%lu, bufsize=%lu", crf, maxBitrate, bufSize);
Pierre Lespagnol's avatar
Pierre Lespagnol committed
253
        libav_utils::setDictValue(&options_, "crf", std::to_string(crf));
254
        av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN);
255 256 257
        encoderCtx->rc_buffer_size = bufSize;
        encoderCtx->rc_max_rate = maxBitrate;
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_VP8) {
258 259 260 261
        // For VP8 :
        // 1- if quality is set use it
        // bitrate need to be set. The target bitrate becomes the maximum allowed bitrate
        // 2- otherwise set rc_max_rate and rc_buffer_size
262 263
        // Using information given on this page:
        // http://www.webmproject.org/docs/encoder-parameters/
264 265 266 267
        av_opt_set(encoderCtx, "quality", "realtime", AV_OPT_SEARCH_CHILDREN);
        av_opt_set_int(encoderCtx, "error-resilient", 1, AV_OPT_SEARCH_CHILDREN);
        av_opt_set_int(encoderCtx, "cpu-used", 7, AV_OPT_SEARCH_CHILDREN); // value obtained from testing
        av_opt_set_int(encoderCtx, "lag-in-frames", 0, AV_OPT_SEARCH_CHILDREN);
268 269
        // allow encoder to drop frames if buffers are full and
        // to undershoot target bitrate to lessen strain on resources
270 271
        av_opt_set_int(encoderCtx, "drop-frame", 25, AV_OPT_SEARCH_CHILDREN);
        av_opt_set_int(encoderCtx, "undershoot-pct", 95, AV_OPT_SEARCH_CHILDREN);
272 273 274 275 276 277
        // don't set encoderCtx->gop_size: let libvpx decide when to insert a keyframe
        encoderCtx->slices = 2; // VP8E_SET_TOKEN_PARTITIONS
        encoderCtx->qmin = 4;
        encoderCtx->qmax = 56;
        encoderCtx->rc_buffer_size = maxBitrate;
        encoderCtx->bit_rate = maxBitrate;
278
        if (crf != SystemCodecInfo::DEFAULT_NO_QUALITY) {
279
            av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN);
Adrien Béraud's avatar
Adrien Béraud committed
280
            JAMI_DBG("Using quality factor %d", crf);
281
        } else {
282
            JAMI_DBG("Using Max bitrate %lu", maxBitrate);
283
        }
284
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_MPEG4) {
285 286 287
        // For MPEG4 :
        // No CRF avaiable.
        // Use CBR (set bitrate)
288 289
        encoderCtx->rc_buffer_size = maxBitrate;
        encoderCtx->bit_rate = encoderCtx->rc_min_rate = encoderCtx->rc_max_rate =  maxBitrate;
290
        JAMI_DBG("Using Max bitrate %lu", maxBitrate);
291 292 293
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263) {
        encoderCtx->bit_rate = encoderCtx->rc_max_rate =  maxBitrate;
        encoderCtx->rc_buffer_size = maxBitrate;
294
        JAMI_DBG("Using Max bitrate %lu", maxBitrate);
295 296 297
    }

    // add video stream to outputformat context
298 299
    AVStream* stream = avformat_new_stream(outputCtx_, outputCodec);
    if (!stream)
300
        throw MediaEncoderException("Could not allocate stream");
301

302 303
    currentStreamIdx_ = stream->index;

304
    readConfig(&options_, encoderCtx);
305
    if (avcodec_open2(encoderCtx, outputCodec, &options_) < 0)
306 307
        throw MediaEncoderException("Could not open encoder");

308
#ifndef _WIN32
309
    avcodec_parameters_from_context(stream->codecpar, encoderCtx);
310
#else
311
    stream->codec = encoderCtx;
312
#endif
313 314
    // framerate is not copied from encoderCtx to stream
    stream->avg_frame_rate = encoderCtx->framerate;
Adrien Béraud's avatar
Adrien Béraud committed
315
#ifdef ENABLE_VIDEO
316
    if (systemCodecInfo.mediaType == MEDIA_VIDEO) {
317
        // allocate buffers for both scaled (pre-encoder) and encoded frames
318 319
        const int width = encoderCtx->width;
        const int height = encoderCtx->height;
320 321 322 323 324 325 326 327 328
        int format = encoderCtx->pix_fmt;
#ifdef RING_ACCEL
        if (accel_) {
            // hardware encoders require a specific pixel format
            auto desc = av_pix_fmt_desc_get(encoderCtx->pix_fmt);
            if (desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL))
                format = accel_->getSoftwareFormat();
        }
#endif
329
        scaledFrameBufferSize_ = videoFrameSize(format, width, height);
330 331 332
        if (scaledFrameBufferSize_ < 0)
            throw MediaEncoderException(("Could not compute buffer size: " + libav_utils::getError(scaledFrameBufferSize_)).c_str());
        else if (scaledFrameBufferSize_ <= AV_INPUT_BUFFER_MIN_SIZE)
333
            throw MediaEncoderException("buffer too small");
334

335 336
        scaledFrameBuffer_.reserve(scaledFrameBufferSize_);
        scaledFrame_.setFromMemory(scaledFrameBuffer_.data(), format, width, height);
337
    }
Adrien Béraud's avatar
Adrien Béraud committed
338
#endif // ENABLE_VIDEO
339 340

    return stream->index;
341 342
}

343
void
344
MediaEncoder::openIOContext()
345
{
346 347
    if (ioCtx_) {
        outputCtx_->pb = ioCtx_;
348 349 350 351 352 353 354 355 356 357
        outputCtx_->packet_size = outputCtx_->pb->buffer_size;
    } else {
        int ret = 0;
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
        const char* filename = outputCtx_->url;
#else
        const char* filename = outputCtx_->filename;
#endif
        if (!(outputCtx_->oformat->flags & AVFMT_NOFILE)) {
            if ((ret = avio_open(&outputCtx_->pb, filename, AVIO_FLAG_WRITE)) < 0) {
358
                std::stringstream ss;
359
                ss << "Could not open IO context for '" << filename << "': " << libav_utils::getError(ret);
360
                throw MediaEncoderException(ss.str().c_str());
361 362 363
            }
        }
    }
364 365
}

366
void
367
MediaEncoder::startIO()
368
{
369 370
    if (!outputCtx_->pb)
        openIOContext();
371
    if (avformat_write_header(outputCtx_, options_ ? &options_ : nullptr)) {
Adrien Béraud's avatar
Adrien Béraud committed
372
        JAMI_ERR("Could not write header for output file... check codec parameters");
373
        throw MediaEncoderException("Failed to write output file header");
374 375
    }

376 377 378
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
    av_dump_format(outputCtx_, 0, outputCtx_->url, 1);
#else
379
    av_dump_format(outputCtx_, 0, outputCtx_->filename, 1);
380
#endif
381
    initialized_ = true;
382 383
}

Adrien Béraud's avatar
Adrien Béraud committed
384
#ifdef ENABLE_VIDEO
Guillaume Roguez's avatar
Guillaume Roguez committed
385
int
Adrien Béraud's avatar
Adrien Béraud committed
386
MediaEncoder::encode(VideoFrame& input, bool is_keyframe, int64_t frame_number)
387
{
388
    if (!initialized_) {
389
        initStream(videoCodec_, input.pointer()->hw_frames_ctx);
390 391 392
        startIO();
    }

393 394 395 396
    AVFrame* frame;
#ifdef RING_ACCEL
    auto desc = av_pix_fmt_desc_get(static_cast<AVPixelFormat>(input.format()));
    bool isHardware = desc && (desc->flags & AV_PIX_FMT_FLAG_HWACCEL);
397 398 399 400 401 402 403 404 405
#ifdef ENABLE_VIDEOTOOLBOX
    //Videotoolbox handles frames allocations itself and do not need creating frame context manually.
    //Now videotoolbox supports only fully accelerated pipeline
    bool isVideotoolbox = static_cast<AVPixelFormat>(input.format()) == AV_PIX_FMT_VIDEOTOOLBOX;
    if (accel_ &&  isVideotoolbox) {
        // Fully accelerated pipeline, skip main memory
        frame = input.pointer();
    } else {
#else
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428
    std::unique_ptr<VideoFrame> framePtr;
    if (accel_ && accel_->isLinked()) {
        // Fully accelerated pipeline, skip main memory
        frame = input.pointer();
    } else if (isHardware) {
        // Hardware decoded frame, transfer back to main memory
        // Transfer to GPU if we have a hardware encoder
        AVPixelFormat pix = (accel_ ? accel_->getSoftwareFormat() : AV_PIX_FMT_YUV420P);
        framePtr = video::HardwareAccel::transferToMainMemory(input, pix);
        if (accel_)
            framePtr = accel_->transfer(*framePtr);
        frame = framePtr->pointer();
    } else if (accel_) {
        // Software decoded frame with a hardware encoder, convert to accepted format first
        auto pix = accel_->getSoftwareFormat();
        if (input.format() != pix) {
            framePtr = scaler_.convertFormat(input, pix);
            framePtr = accel_->transfer(*framePtr);
        } else {
            framePtr = accel_->transfer(input);
        }
        frame = framePtr->pointer();
    } else {
429
#endif //ENABLE_VIDEOTOOLBOX
430 431 432 433 434 435 436
#endif
        libav_utils::fillWithBlack(scaledFrame_.pointer());
        scaler_.scale_with_aspect(input, scaledFrame_);
        frame = scaledFrame_.pointer();
#ifdef RING_ACCEL
    }
#endif
437

438
    AVCodecContext* enc = encoders_[currentStreamIdx_];
439 440 441
    frame->pts = frame_number;
    if (enc->framerate.num != enc->time_base.den || enc->framerate.den != enc->time_base.num)
        frame->pts /= (rational<int64_t>(enc->framerate) * rational<int64_t>(enc->time_base)).real<int64_t>();
442 443

    if (is_keyframe) {
444
        frame->pict_type = AV_PICTURE_TYPE_I;
445
        frame->key_frame = 1;
446
    } else {
447
        frame->pict_type = AV_PICTURE_TYPE_NONE;
448
        frame->key_frame = 0;
449
    }
450

451
    return encode(frame, currentStreamIdx_);
452
}
Adrien Béraud's avatar
Adrien Béraud committed
453
#endif // ENABLE_VIDEO
454

455 456
int
MediaEncoder::encodeAudio(AudioFrame& frame)
457
{
458 459 460 461 462 463 464
    if (!initialized_) {
        // Initialize on first video frame, or first audio frame if no video stream
        if (not videoOpts_.isValid())
            startIO();
        else
            return 0;
    }
465
    frame.pointer()->pts = sent_samples;
466 467
    sent_samples += frame.pointer()->nb_samples;
    encode(frame.pointer(), currentStreamIdx_);
468 469 470
    return 0;
}

471 472
int
MediaEncoder::encode(AVFrame* frame, int streamIdx)
473
{
Philippe Gorley's avatar
Philippe Gorley committed
474
    if (!initialized_ && frame) {
475 476
        // Initialize on first video frame, or first audio frame if no video stream
        bool isVideo = (frame->width > 0 && frame->height > 0);
477 478
        if (isVideo and videoOpts_.isValid()) {
            // Has video stream, so init with video frame
479
            streamIdx = initStream(videoCodec_, frame->hw_frames_ctx);
480
            startIO();
481 482 483
        } else if (!isVideo and !videoOpts_.isValid()) {
            // Only audio, for MediaRecorder, which doesn't use encodeAudio
            startIO();
484 485 486 487
        } else {
            return 0;
        }
    }
488
    int ret = 0;
489
    AVCodecContext* encoderCtx = encoders_[streamIdx];
490
    AVPacket pkt;
491
    av_init_packet(&pkt);
492
    pkt.data = nullptr; // packet data will be allocated by the encoder
493
    pkt.size = 0;
494

495
    ret = avcodec_send_frame(encoderCtx, frame);
496 497 498
    if (ret < 0)
        return -1;

499
    while (ret >= 0) {
500
        ret = avcodec_receive_packet(encoderCtx, &pkt);
501 502
        if (ret == AVERROR(EAGAIN))
            break;
503
        if (ret < 0 && ret != AVERROR_EOF) { // we still want to write our frame on EOF
Adrien Béraud's avatar
Adrien Béraud committed
504
            JAMI_ERR() << "Failed to encode frame: " << libav_utils::getError(ret);
505 506
            return ret;
        }
507 508

        if (pkt.size) {
Philippe Gorley's avatar
Philippe Gorley committed
509
            if (send(pkt, streamIdx))
510 511 512
                break;
        }
    }
513

514
    av_packet_unref(&pkt);
515 516
    return 0;
}
517

518
bool
Philippe Gorley's avatar
Philippe Gorley committed
519
MediaEncoder::send(AVPacket& pkt, int streamIdx)
520
{
521 522 523 524
    if (!initialized_) {
        streamIdx = initStream(videoCodec_, nullptr);
        startIO();
    }
Philippe Gorley's avatar
Philippe Gorley committed
525 526
    if (streamIdx < 0)
        streamIdx = currentStreamIdx_;
527
    if (streamIdx >= 0 and static_cast<size_t>(streamIdx) < encoders_.size()) {
528 529 530 531 532 533 534 535 536
        auto encoderCtx = encoders_[streamIdx];
        pkt.stream_index = streamIdx;
        if (pkt.pts != AV_NOPTS_VALUE)
            pkt.pts = av_rescale_q(pkt.pts, encoderCtx->time_base,
                                outputCtx_->streams[streamIdx]->time_base);
        if (pkt.dts != AV_NOPTS_VALUE)
            pkt.dts = av_rescale_q(pkt.dts, encoderCtx->time_base,
                                outputCtx_->streams[streamIdx]->time_base);
    }
537 538 539
    // write the compressed frame
    auto ret = av_write_frame(outputCtx_, &pkt);
    if (ret < 0) {
Adrien Béraud's avatar
Adrien Béraud committed
540
        JAMI_ERR() << "av_write_frame failed: " << libav_utils::getError(ret);
541 542 543 544
    }
    return ret >= 0;
}

545 546 547
int
MediaEncoder::flush()
{
548 549 550
    int ret = 0;
    for (size_t i = 0; i < outputCtx_->nb_streams; ++i) {
        if (encode(nullptr, i) < 0) {
Adrien Béraud's avatar
Adrien Béraud committed
551
            JAMI_ERR() << "Could not flush stream #" << i;
552 553 554 555
            ret |= 1u << i; // provide a way for caller to know which streams failed
        }
    }
    return -ret;
556 557
}

558 559
std::string
MediaEncoder::print_sdp()
560 561
{
    /* theora sdp can be huge */
562
#ifndef _WIN32
563
    const auto sdp_size = outputCtx_->streams[currentStreamIdx_]->codecpar->extradata_size + 2048;
564
#else
565
    const auto sdp_size = outputCtx_->streams[currentStreamIdx_]->codec->extradata_size + 2048;
566
#endif
567
    std::string result;
Guillaume Roguez's avatar
Guillaume Roguez committed
568
    std::string sdp(sdp_size, '\0');
569 570
    av_sdp_create(&outputCtx_, 1, &(*sdp.begin()), sdp_size);
    std::istringstream iss(sdp);
571
    std::string line;
572 573 574
    while (std::getline(iss, line)) {
        /* strip windows line ending */
        line = line.substr(0, line.length() - 1);
575
        result += line + "\n";
576
    }
577
#ifdef DEBUG_SDP
Adrien Béraud's avatar
Adrien Béraud committed
578
    JAMI_DBG("Sending SDP:\n%s", result.c_str());
579 580
#endif
    return result;
581 582
}

583 584
AVCodecContext*
MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video)
585
{
586
    AVCodecContext* encoderCtx = avcodec_alloc_context3(outputCodec);
587

588
    auto encoderName = outputCodec->name; // guaranteed to be non null if AVCodec is not null
589

590
    encoderCtx->thread_count = std::min(std::thread::hardware_concurrency(), is_video ? 16u : 4u);
Adrien Béraud's avatar
Adrien Béraud committed
591
    JAMI_DBG("[%s] Using %d threads", encoderName, encoderCtx->thread_count);
592

593 594
    if (is_video) {
        // resolution must be a multiple of two
595 596
        encoderCtx->width = videoOpts_.width;
        encoderCtx->height = videoOpts_.height;
597

598
        // satisfy ffmpeg: denominator must be 16bit or less value
599
        // time base = 1/FPS
600 601 602 603
        av_reduce(&encoderCtx->framerate.num, &encoderCtx->framerate.den,
                  videoOpts_.frameRate.numerator(), videoOpts_.frameRate.denominator(),
                  (1U << 16) - 1);
        encoderCtx->time_base = av_inv_q(encoderCtx->framerate);
604

605
        // emit one intra frame every gop_size frames
606
        encoderCtx->max_b_frames = 0;
607 608 609 610 611
        encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P;
#ifdef RING_ACCEL
        if (accel_)
            encoderCtx->pix_fmt = accel_->getFormat();
#endif
612 613 614 615 616

        // Fri Jul 22 11:37:59 EDT 2011:tmatth:XXX: DON'T set this, we want our
        // pps and sps to be sent in-band for RTP
        // This is to place global headers in extradata instead of every
        // keyframe.
617
        // encoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
618
    } else {
619
        encoderCtx->sample_fmt = AV_SAMPLE_FMT_S16;
620 621 622 623
        encoderCtx->sample_rate = std::max(8000, audioOpts_.sampleRate);
        encoderCtx->time_base = AVRational{1, encoderCtx->sample_rate};
        if (audioOpts_.nbChannels > 2 || audioOpts_.nbChannels < 1) {
            encoderCtx->channels = std::max(std::min(audioOpts_.nbChannels, 1), 2);
Adrien Béraud's avatar
Adrien Béraud committed
624
            JAMI_ERR() << "[" << encoderName << "] Clamping invalid channel count: "
625
                << audioOpts_.nbChannels << " -> " << encoderCtx->channels;
626
        } else {
627
            encoderCtx->channels = audioOpts_.nbChannels;
628
        }
629 630 631
        encoderCtx->channel_layout = av_get_default_channel_layout(encoderCtx->channels);
        if (audioOpts_.frameSize) {
            encoderCtx->frame_size = audioOpts_.frameSize;
Adrien Béraud's avatar
Adrien Béraud committed
632
            JAMI_DBG() << "[" << encoderName << "] Frame size " << encoderCtx->frame_size;
633
        } else {
Adrien Béraud's avatar
Adrien Béraud committed
634
            JAMI_WARN() << "[" << encoderName << "] Frame size not set";
635 636
        }
    }
637 638

    return encoderCtx;
639 640
}

641 642
void
MediaEncoder::forcePresetX264(AVCodecContext* encoderCtx)
643 644
{
    const char *speedPreset = "ultrafast";
645
    if (av_opt_set(encoderCtx, "preset", speedPreset, AV_OPT_SEARCH_CHILDREN))
Adrien Béraud's avatar
Adrien Béraud committed
646
        JAMI_WARN("Failed to set x264 preset '%s'", speedPreset);
647
    const char *tune = "zerolatency";
648
    if (av_opt_set(encoderCtx, "tune", tune, AV_OPT_SEARCH_CHILDREN))
Adrien Béraud's avatar
Adrien Béraud committed
649
        JAMI_WARN("Failed to set x264 tune '%s'", tune);
650 651
}

652 653
void
MediaEncoder::extractProfileLevelID(const std::string &parameters,
654 655 656 657 658
                                         AVCodecContext *ctx)
{
    // From RFC3984:
    // If no profile-level-id is present, the Baseline Profile without
    // additional constraints at Level 1 MUST be implied.
Pierre Lespagnol's avatar
Pierre Lespagnol committed
659
    ctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684
    ctx->level = 0x0d;
    // ctx->level = 0x0d; // => 13 aka 1.3
    if (parameters.empty())
        return;

    const std::string target("profile-level-id=");
    size_t needle = parameters.find(target);
    if (needle == std::string::npos)
        return;

    needle += target.length();
    const size_t id_length = 6; /* digits */
    const std::string profileLevelID(parameters.substr(needle, id_length));
    if (profileLevelID.length() != id_length)
        return;

    int result;
    std::stringstream ss;
    ss << profileLevelID;
    ss >> std::hex >> result;
    // profile-level id consists of three bytes
    const unsigned char profile_idc = result >> 16;             // 42xxxx -> 42
    const unsigned char profile_iop = ((result >> 8) & 0xff);   // xx80xx -> 80
    ctx->level = result & 0xff;                                 // xxxx0d -> 0d
    switch (profile_idc) {
685 686 687 688 689 690 691 692 693 694 695 696
        case FF_PROFILE_H264_BASELINE:
            // check constraint_set_1_flag
            if ((profile_iop & 0x40) >> 6)
                ctx->profile |= FF_PROFILE_H264_CONSTRAINED;
            break;
        case FF_PROFILE_H264_HIGH_10:
        case FF_PROFILE_H264_HIGH_422:
        case FF_PROFILE_H264_HIGH_444_PREDICTIVE:
            // check constraint_set_3_flag
            if ((profile_iop & 0x10) >> 4)
                ctx->profile |= FF_PROFILE_H264_INTRA;
            break;
697
    }
698
    JAMI_DBG("Using profile %s (%x) and level %d", avcodec_profile_name(AV_CODEC_ID_H264, ctx->profile), ctx->profile, ctx->level);
699
}
700

701 702 703 704 705 706 707 708 709 710 711 712 713 714
#ifdef RING_ACCEL
void
MediaEncoder::enableAccel(bool enableAccel)
{
    enableAccel_ = enableAccel;
    emitSignal<DRing::ConfigurationSignal::HardwareEncodingChanged>(enableAccel_);
    if (!enableAccel_) {
        accel_.reset();
        for (auto enc : encoders_)
            enc->opaque = nullptr;
    }
}
#endif

715 716 717
unsigned
MediaEncoder::getStreamCount() const
{
718
    return (audioOpts_.isValid() + videoOpts_.isValid());
719 720
}

721 722 723 724 725 726 727 728 729 730 731
MediaStream
MediaEncoder::getStream(const std::string& name, int streamIdx) const
{
    // if streamIdx is negative, use currentStreamIdx_
    if (streamIdx < 0)
        streamIdx = currentStreamIdx_;
    // make sure streamIdx is valid
    if (getStreamCount() <= 0 || streamIdx < 0 || encoders_.size() < (unsigned)(streamIdx + 1))
        return {};
    auto enc = encoders_[streamIdx];
    // TODO set firstTimestamp
732 733 734 735 736 737
    auto ms = MediaStream(name, enc);
#ifdef RING_ACCEL
    if (accel_)
        ms.format = accel_->getSoftwareFormat();
#endif
    return ms;
738 739
}

740
void
741
MediaEncoder::readConfig(AVDictionary** dict, AVCodecContext* encoderCtx)
742 743
{
    std::string path = fileutils::get_config_dir() + DIR_SEPARATOR_STR + "encoder.json";
744
    std::string name = encoderCtx->codec->name;
745 746 747 748 749 750
    if (fileutils::isFile(path)) {
        try {
            Json::Value root;
            std::ifstream file(path);
            file >> root;
            if (!root.isObject()) {
Adrien Béraud's avatar
Adrien Béraud committed
751
                JAMI_ERR() << "Invalid encoder configuration: root is not an object";
752 753
                return;
            }
754
            const auto& config = root[name];
755
            if (config.isNull()) {
Adrien Béraud's avatar
Adrien Béraud committed
756
                JAMI_WARN() << "Encoder '" << name << "' not found in configuration file";
757 758 759
                return;
            }
            if (!config.isObject()) {
Adrien Béraud's avatar
Adrien Béraud committed
760
                JAMI_ERR() << "Invalid encoder configuration: '" << name << "' is not an object";
761 762 763 764 765 766 767
                return;
            }
            // If users want to change these, they should use the settings page.
            for (Json::Value::const_iterator it = config.begin(); it != config.end(); ++it) {
                Json::Value v = *it;
                if (!it.key().isConvertibleTo(Json::ValueType::stringValue)
                    || !v.isConvertibleTo(Json::ValueType::stringValue)) {
Adrien Béraud's avatar
Adrien Béraud committed
768
                    JAMI_ERR() << "Invalid configuration for '" << name << "'";
769 770 771 772
                    return;
                }
                const auto& key = it.key().asString();
                const auto& value = v.asString();
773
                // provides a way to override all AVCodecContext fields MediaEncoder sets
774
                if (key == "parameters") // Used by MediaEncoder for profile-level-id, ignore
775
                    continue;
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793
                else if (value.empty())
                    libav_utils::setDictValue(dict, key, nullptr);
                else if (key == "profile")
                    encoderCtx->profile = v.asInt();
                else if (key == "level")
                    encoderCtx->level = v.asInt();
                else if (key == "bit_rate")
                    encoderCtx->bit_rate = v.asInt();
                else if (key == "rc_buffer_size")
                    encoderCtx->rc_buffer_size = v.asInt();
                else if (key == "rc_min_rate")
                    encoderCtx->rc_min_rate = v.asInt();
                else if (key == "rc_max_rate")
                    encoderCtx->rc_max_rate = v.asInt();
                else if (key == "qmin")
                    encoderCtx->qmin = v.asInt();
                else if (key == "qmax")
                    encoderCtx->qmax = v.asInt();
794 795 796 797
                else
                    libav_utils::setDictValue(dict, key, value);
            }
        } catch (const Json::Exception& e) {
Adrien Béraud's avatar
Adrien Béraud committed
798
            JAMI_ERR() << "Failed to load encoder configuration file: " << e.what();
799 800 801 802
        }
    }
}

Adrien Béraud's avatar
Adrien Béraud committed
803
} // namespace jami