media_encoder.cpp 27.9 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

55 56
MediaEncoder::MediaEncoder()
    : outputCtx_(avformat_alloc_context())
57
{}
58

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

78
void
79
MediaEncoder::setOptions(const MediaStream& opts)
80
{
81
    if (!opts.isValid()) {
Adrien Béraud's avatar
Adrien Béraud committed
82
        JAMI_ERR() << "Invalid options";
83 84 85 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;
    } else {
        audioOpts_ = opts;
    }
97 98
}

99 100
void
MediaEncoder::setOptions(const MediaDescription& args)
101
{
102 103 104
    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));
105

Guillaume Roguez's avatar
Guillaume Roguez committed
106
    if (not args.parameters.empty())
107
        libav_utils::setDictValue(&options_, "parameters", args.parameters);
108 109
}

110
void
111
MediaEncoder::setMetadata(const std::string& title, const std::string& description)
112
{
113 114 115 116
    if (not title.empty())
        libav_utils::setDictValue(&outputCtx_->metadata, "title", title);
    if (not description.empty())
        libav_utils::setDictValue(&outputCtx_->metadata, "description", description);
117 118
}

119 120 121 122 123
void
MediaEncoder::setInitSeqVal(uint16_t seqVal)
{
    //only set not default value (!=0)
    if (seqVal != 0)
124
        av_opt_set_int(outputCtx_, "seq", seqVal, AV_OPT_SEARCH_CHILDREN);
125 126 127 128 129
}

uint16_t
MediaEncoder::getLastSeqValue()
{
130 131 132
    int64_t retVal;
    if (av_opt_get_int(outputCtx_, "seq", AV_OPT_SEARCH_CHILDREN, &retVal) >= 0)
        return (uint16_t)retVal;
133 134
    else
        return 0;
135 136 137
}

void
138
MediaEncoder::openOutput(const std::string& filename, const std::string& format)
139 140
{
    avformat_free_context(outputCtx_);
141 142 143 144
    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());
145 146 147 148

#ifdef RING_ACCEL
    enableAccel_ = Manager::instance().videoPreferences.getEncodingAccelerated();
#endif
149 150 151
}

int
152
MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo)
153 154 155
{
    if (systemCodecInfo.mediaType == MEDIA_AUDIO) {
        audioCodec_ = systemCodecInfo.name;
156
        return initStream(systemCodecInfo, nullptr);
157 158 159 160 161 162 163 164 165 166 167
    } 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
168
MediaEncoder::initStream(const std::string& codecName, AVBufferRef* framesCtx)
169 170 171
{
    const auto codecInfo = getSystemCodecContainer()->searchCodecByName(codecName, MEDIA_ALL);
    if (codecInfo)
172
        return initStream(*codecInfo, framesCtx);
173 174 175 176 177
    else
        return -1;
}

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

196
    if (!outputCodec) {
197 198 199 200 201 202 203 204 205
        /* 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
206
            JAMI_ERR("Encoder \"%s\" not found!", systemCodecInfo.name.c_str());
207 208
            throw MediaEncoderException("No output encoder");
        }
209 210
    }

211 212
    encoderCtx = prepareEncoderContext(outputCodec, systemCodecInfo.mediaType == MEDIA_VIDEO);
    encoders_.push_back(encoderCtx);
213 214 215

#ifdef RING_ACCEL
    if (accel_) {
216
        accel_->setDetails(encoderCtx);
217 218 219 220
        encoderCtx->opaque = accel_.get();
    }
#endif

221
    auto maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate"));
222
    auto bufSize = 2 * maxBitrate; // as recommended (TODO: make it customizable)
223
    auto crf = std::atoi(libav_utils::getDictValue(options_, "crf"));
224

225
    /* let x264 preset override our encoder settings */
226
    if (systemCodecInfo.avcodecId == AV_CODEC_ID_H264) {
227 228
        auto profileLevelId = libav_utils::getDictValue(options_, "parameters");
        extractProfileLevelID(profileLevelId, encoderCtx);
229
#ifdef RING_ACCEL
230 231 232 233 234 235 236
#ifdef ENABLE_VIDEOTOOLBOX
        if (accel_) {
            maxBitrate = 2000 * std::atoi(libav_utils::getDictValue(options_, "max_rate"));
            bufSize = 2 * maxBitrate;
            crf = 20;
        }
#endif
237 238 239 240 241
        if (accel_)
            // limit the bitrate else it will easily go up to a few MiB/s
            encoderCtx->bit_rate = maxBitrate;
        else
#endif
242
        forcePresetX264(encoderCtx);
243
        // For H264 :
244 245 246
        // Streaming => VBV (constrained encoding) + CRF (Constant Rate Factor)
        if (crf == SystemCodecInfo::DEFAULT_NO_QUALITY)
            crf = 30; // good value for H264-720p@30
Adrien Béraud's avatar
Adrien Béraud committed
247
        JAMI_DBG("H264 encoder setup: crf=%u, maxrate=%u, bufsize=%u", crf, maxBitrate, bufSize);
248

249
        av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN);
250 251 252
        encoderCtx->rc_buffer_size = bufSize;
        encoderCtx->rc_max_rate = maxBitrate;
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_VP8) {
253 254 255 256
        // 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
257 258
        // Using information given on this page:
        // http://www.webmproject.org/docs/encoder-parameters/
259 260 261 262
        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);
263 264
        // allow encoder to drop frames if buffers are full and
        // to undershoot target bitrate to lessen strain on resources
265 266
        av_opt_set_int(encoderCtx, "drop-frame", 25, AV_OPT_SEARCH_CHILDREN);
        av_opt_set_int(encoderCtx, "undershoot-pct", 95, AV_OPT_SEARCH_CHILDREN);
267 268 269 270 271 272
        // 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;
273
        if (crf != SystemCodecInfo::DEFAULT_NO_QUALITY) {
274
            av_opt_set_int(encoderCtx, "crf", crf, AV_OPT_SEARCH_CHILDREN);
Adrien Béraud's avatar
Adrien Béraud committed
275
            JAMI_DBG("Using quality factor %d", crf);
276
        } else {
Adrien Béraud's avatar
Adrien Béraud committed
277
            JAMI_DBG("Using Max bitrate %d", maxBitrate);
278
        }
279
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_MPEG4) {
280 281 282
        // For MPEG4 :
        // No CRF avaiable.
        // Use CBR (set bitrate)
283 284
        encoderCtx->rc_buffer_size = maxBitrate;
        encoderCtx->bit_rate = encoderCtx->rc_min_rate = encoderCtx->rc_max_rate =  maxBitrate;
Adrien Béraud's avatar
Adrien Béraud committed
285
        JAMI_DBG("Using Max bitrate %d", maxBitrate);
286 287 288
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263) {
        encoderCtx->bit_rate = encoderCtx->rc_max_rate =  maxBitrate;
        encoderCtx->rc_buffer_size = maxBitrate;
Adrien Béraud's avatar
Adrien Béraud committed
289
        JAMI_DBG("Using Max bitrate %d", maxBitrate);
290 291 292
    }

    // add video stream to outputformat context
293 294
    AVStream* stream = avformat_new_stream(outputCtx_, outputCodec);
    if (!stream)
295
        throw MediaEncoderException("Could not allocate stream");
296

297 298
    currentStreamIdx_ = stream->index;

299
    readConfig(&options_, encoderCtx);
300
    if (avcodec_open2(encoderCtx, outputCodec, &options_) < 0)
301 302
        throw MediaEncoderException("Could not open encoder");

303
#ifndef _WIN32
304
    avcodec_parameters_from_context(stream->codecpar, encoderCtx);
305
#else
306
    stream->codec = encoderCtx;
307
#endif
308 309
    // framerate is not copied from encoderCtx to stream
    stream->avg_frame_rate = encoderCtx->framerate;
Adrien Béraud's avatar
Adrien Béraud committed
310
#ifdef ENABLE_VIDEO
311
    if (systemCodecInfo.mediaType == MEDIA_VIDEO) {
312
        // allocate buffers for both scaled (pre-encoder) and encoded frames
313 314
        const int width = encoderCtx->width;
        const int height = encoderCtx->height;
315 316 317 318 319 320 321 322 323
        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
324
        scaledFrameBufferSize_ = videoFrameSize(format, width, height);
325 326 327
        if (scaledFrameBufferSize_ < 0)
            throw MediaEncoderException(("Could not compute buffer size: " + libav_utils::getError(scaledFrameBufferSize_)).c_str());
        else if (scaledFrameBufferSize_ <= AV_INPUT_BUFFER_MIN_SIZE)
328
            throw MediaEncoderException("buffer too small");
329

330 331
        scaledFrameBuffer_.reserve(scaledFrameBufferSize_);
        scaledFrame_.setFromMemory(scaledFrameBuffer_.data(), format, width, height);
332
    }
Adrien Béraud's avatar
Adrien Béraud committed
333
#endif // ENABLE_VIDEO
334 335

    return stream->index;
336 337
}

338
void
339
MediaEncoder::openIOContext()
340
{
341 342
    if (ioCtx_) {
        outputCtx_->pb = ioCtx_;
343 344 345 346 347 348 349 350 351 352
        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) {
353
                std::stringstream ss;
354
                ss << "Could not open IO context for '" << filename << "': " << libav_utils::getError(ret);
355
                throw MediaEncoderException(ss.str().c_str());
356 357 358
            }
        }
    }
359 360
}

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

371 372 373
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
    av_dump_format(outputCtx_, 0, outputCtx_->url, 1);
#else
374
    av_dump_format(outputCtx_, 0, outputCtx_->filename, 1);
375
#endif
376
    initialized_ = true;
377 378
}

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

388 389 390 391
    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);
392 393 394 395 396 397 398 399 400
#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
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
    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 {
424
#endif //ENABLE_VIDEOTOOLBOX
425 426 427 428 429 430 431
#endif
        libav_utils::fillWithBlack(scaledFrame_.pointer());
        scaler_.scale_with_aspect(input, scaledFrame_);
        frame = scaledFrame_.pointer();
#ifdef RING_ACCEL
    }
#endif
432

433
    AVCodecContext* enc = encoders_[currentStreamIdx_];
434 435 436
    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>();
437 438

    if (is_keyframe) {
439
        frame->pict_type = AV_PICTURE_TYPE_I;
440
        frame->key_frame = 1;
441
    } else {
442
        frame->pict_type = AV_PICTURE_TYPE_NONE;
443
        frame->key_frame = 0;
444
    }
445

446
    return encode(frame, currentStreamIdx_);
447
}
Adrien Béraud's avatar
Adrien Béraud committed
448
#endif // ENABLE_VIDEO
449

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

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

490
    ret = avcodec_send_frame(encoderCtx, frame);
491 492 493
    if (ret < 0)
        return -1;

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

        if (pkt.size) {
Philippe Gorley's avatar
Philippe Gorley committed
504
            if (send(pkt, streamIdx))
505 506 507
                break;
        }
    }
508

509
    av_packet_unref(&pkt);
510 511
    return 0;
}
512

513
bool
Philippe Gorley's avatar
Philippe Gorley committed
514
MediaEncoder::send(AVPacket& pkt, int streamIdx)
515
{
516 517 518 519
    if (!initialized_) {
        streamIdx = initStream(videoCodec_, nullptr);
        startIO();
    }
Philippe Gorley's avatar
Philippe Gorley committed
520 521
    if (streamIdx < 0)
        streamIdx = currentStreamIdx_;
522 523 524 525 526 527 528 529 530 531
    if (streamIdx >= 0 and streamIdx < encoders_.size()) {
        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);
    }
532 533 534
    // write the compressed frame
    auto ret = av_write_frame(outputCtx_, &pkt);
    if (ret < 0) {
Adrien Béraud's avatar
Adrien Béraud committed
535
        JAMI_ERR() << "av_write_frame failed: " << libav_utils::getError(ret);
536 537 538 539
    }
    return ret >= 0;
}

540 541 542
int
MediaEncoder::flush()
{
543 544 545
    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
546
            JAMI_ERR() << "Could not flush stream #" << i;
547 548 549 550
            ret |= 1u << i; // provide a way for caller to know which streams failed
        }
    }
    return -ret;
551 552
}

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

578 579
AVCodecContext*
MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video)
580
{
581
    AVCodecContext* encoderCtx = avcodec_alloc_context3(outputCodec);
582

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

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

588 589
    if (is_video) {
        // resolution must be a multiple of two
590 591
        encoderCtx->width = videoOpts_.width;
        encoderCtx->height = videoOpts_.height;
592

593
        // satisfy ffmpeg: denominator must be 16bit or less value
594
        // time base = 1/FPS
595 596 597 598
        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);
599

600
        // emit one intra frame every gop_size frames
601
        encoderCtx->max_b_frames = 0;
602 603 604 605 606
        encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P;
#ifdef RING_ACCEL
        if (accel_)
            encoderCtx->pix_fmt = accel_->getFormat();
#endif
607 608 609 610 611

        // 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.
612
        // encoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
613
    } else {
614
        encoderCtx->sample_fmt = AV_SAMPLE_FMT_S16;
615 616 617 618
        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
619
            JAMI_ERR() << "[" << encoderName << "] Clamping invalid channel count: "
620
                << audioOpts_.nbChannels << " -> " << encoderCtx->channels;
621
        } else {
622
            encoderCtx->channels = audioOpts_.nbChannels;
623
        }
624 625 626
        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
627
            JAMI_DBG() << "[" << encoderName << "] Frame size " << encoderCtx->frame_size;
628
        } else {
Adrien Béraud's avatar
Adrien Béraud committed
629
            JAMI_WARN() << "[" << encoderName << "] Frame size not set";
630 631
        }
    }
632 633

    return encoderCtx;
634 635
}

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

647 648
void
MediaEncoder::extractProfileLevelID(const std::string &parameters,
649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
                                         AVCodecContext *ctx)
{
    // From RFC3984:
    // If no profile-level-id is present, the Baseline Profile without
    // additional constraints at Level 1 MUST be implied.
    ctx->profile = FF_PROFILE_H264_BASELINE;
    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) {
680 681 682 683 684 685 686 687 688 689 690 691
        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;
692
    }
Adrien Béraud's avatar
Adrien Béraud committed
693
    JAMI_DBG("Using profile %x and level %d", ctx->profile, ctx->level);
694
}
695

696 697 698 699 700 701 702 703 704 705 706 707 708 709
#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

710 711 712
unsigned
MediaEncoder::getStreamCount() const
{
713
    return (audioOpts_.isValid() + videoOpts_.isValid());
714 715
}

716 717 718 719 720 721 722 723 724 725 726
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
727 728 729 730 731 732
    auto ms = MediaStream(name, enc);
#ifdef RING_ACCEL
    if (accel_)
        ms.format = accel_->getSoftwareFormat();
#endif
    return ms;
733 734
}

735
void
736
MediaEncoder::readConfig(AVDictionary** dict, AVCodecContext* encoderCtx)
737 738
{
    std::string path = fileutils::get_config_dir() + DIR_SEPARATOR_STR + "encoder.json";
739
    std::string name = encoderCtx->codec->name;
740 741 742 743 744 745
    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
746
                JAMI_ERR() << "Invalid encoder configuration: root is not an object";
747 748
                return;
            }
749
            const auto& config = root[name];
750
            if (config.isNull()) {
Adrien Béraud's avatar
Adrien Béraud committed
751
                JAMI_WARN() << "Encoder '" << name << "' not found in configuration file";
752 753 754
                return;
            }
            if (!config.isObject()) {
Adrien Béraud's avatar
Adrien Béraud committed
755
                JAMI_ERR() << "Invalid encoder configuration: '" << name << "' is not an object";
756 757 758 759 760 761 762
                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
763
                    JAMI_ERR() << "Invalid configuration for '" << name << "'";
764 765 766 767
                    return;
                }
                const auto& key = it.key().asString();
                const auto& value = v.asString();
768
                // provides a way to override all AVCodecContext fields MediaEncoder sets
769
                if (key == "parameters") // Used by MediaEncoder for profile-level-id, ignore
770
                    continue;
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
                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();
789 790 791 792
                else
                    libav_utils::setDictValue(dict, key, value);
            }
        } catch (const Json::Exception& e) {
Adrien Béraud's avatar
Adrien Béraud committed
793
            JAMI_ERR() << "Failed to load encoder configuration file: " << e.what();
794 795 796 797
        }
    }
}

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