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

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

28
#include "audio/audiobuffer.h"
Guillaume Roguez's avatar
Guillaume Roguez committed
29
#include "string_utils.h"
Tristan Matthews's avatar
Tristan Matthews committed
30
#include "logger.h"
31

32 33 34 35
extern "C" {
#include <libavutil/parseutils.h>
}

36 37
#include <iostream>
#include <sstream>
38
#include <algorithm>
39
#include <thread> // hardware_concurrency
40

41 42
// Define following line if you need to debug libav SDP
//#define DEBUG_SDP 1
43

44
namespace ring {
45

46 47
MediaEncoder::MediaEncoder()
    : outputCtx_(avformat_alloc_context())
48
{}
49

50
MediaEncoder::~MediaEncoder()
51
{
52 53 54
    if (outputCtx_) {
        if (outputCtx_->priv_data)
            av_write_trailer(outputCtx_);
55

56
        for (auto encoderCtx : encoders_)
57 58 59 60 61 62 63
            if (encoderCtx) {
#ifndef _MSC_VER
                avcodec_free_context(&encoderCtx);
#else
                avcodec_close(encoderCtx);
#endif
            }
64

65 66
        avformat_free_context(outputCtx_);
    }
67

68
    av_dict_free(&options_);
69 70
}

Guillaume Roguez's avatar
Guillaume Roguez committed
71
void MediaEncoder::setDeviceOptions(const DeviceParams& args)
72
{
73 74 75 76 77 78 79 80
    device_ = args;
    if (device_.width)
        av_dict_set(&options_, "width", ring::to_string(device_.width).c_str(), 0);
    if (device_.height)
        av_dict_set(&options_, "height", ring::to_string(device_.height).c_str(), 0);
    if (not device_.framerate)
        device_.framerate = 30;
    av_dict_set(&options_, "framerate", ring::to_string(device_.framerate.real()).c_str(), 0);
81 82
}

Guillaume Roguez's avatar
Guillaume Roguez committed
83
void MediaEncoder::setOptions(const MediaDescription& args)
84
{
85 86
    codec_ = args.codec;

87
    av_dict_set(&options_, "payload_type", ring::to_string(args.payload_type).c_str(), 0);
88
    av_dict_set(&options_, "max_rate", ring::to_string(args.codec->bitrate).c_str(), 0);
89
    av_dict_set(&options_, "crf", ring::to_string(args.codec->quality).c_str(), 0);
90

91 92 93 94 95 96 97 98 99 100 101 102 103 104
    if (args.codec->systemCodecInfo.mediaType == MEDIA_AUDIO) {
        auto accountAudioCodec = std::static_pointer_cast<AccountAudioCodecInfo>(args.codec);
        if (accountAudioCodec->audioformat.sample_rate)
            av_dict_set(&options_, "sample_rate",
                        ring::to_string(accountAudioCodec->audioformat.sample_rate).c_str(), 0);

        if (accountAudioCodec->audioformat.nb_channels)
            av_dict_set(&options_, "channels",
                        ring::to_string(accountAudioCodec->audioformat.nb_channels).c_str(), 0);

        if (accountAudioCodec->audioformat.sample_rate && accountAudioCodec->audioformat.nb_channels)
            av_dict_set(&options_, "frame_size",
                        ring::to_string(static_cast<unsigned>(0.02 * accountAudioCodec->audioformat.sample_rate)).c_str(), 0);
    }
105

Guillaume Roguez's avatar
Guillaume Roguez committed
106 107
    if (not args.parameters.empty())
        av_dict_set(&options_, "parameters", args.parameters.c_str(), 0);
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
}

void
MediaEncoder::setInitSeqVal(uint16_t seqVal)
{
    //only set not default value (!=0)
    if (seqVal != 0)
        av_dict_set(&options_, "seq", ring::to_string(seqVal).c_str(), 0);
}

uint16_t
MediaEncoder::getLastSeqValue()
{
    int64_t  retVal;
    auto ret = av_opt_get_int(outputCtx_->priv_data, "seq", AV_OPT_SEARCH_CHILDREN, &retVal);
    if (ret == 0)
        return (uint16_t) retVal;
    else
        return 0;
127 128
}

Gasuleg's avatar
Gasuleg committed
129 130 131
std::string
MediaEncoder::getEncoderName() const
{
132
    return encoders_[currentStreamIdx_]->codec->name;
Gasuleg's avatar
Gasuleg committed
133 134
}

135
void
136 137
MediaEncoder::openLiveOutput(const std::string& filename,
                             const ring::MediaDescription& args)
138
{
Guillaume Roguez's avatar
Guillaume Roguez committed
139
    setOptions(args);
140
    AVOutputFormat *oformat = av_guess_format("rtp", filename.c_str(), nullptr);
141 142

    if (!oformat) {
143
        RING_ERR("Unable to find a suitable output format for %s", filename.c_str());
144
        throw MediaEncoderException("No output format");
145 146 147
    }

    outputCtx_->oformat = oformat;
148
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
149
    // c_str guarantees null termination
150 151 152 153
    outputCtx_->url = av_strdup(filename.c_str()); // must be compatible with av_free
#else
    strncpy(outputCtx_->filename, filename.c_str(), sizeof(outputCtx_->filename));
    // in case our filename is longer than the space reserved for AVFormatContext.filename
154
    outputCtx_->filename[sizeof(outputCtx_->filename) - 1] = '\0';
155
#endif
156

157 158 159 160 161 162 163 164
    addStream(args.codec->systemCodecInfo, args.parameters);
}

void
MediaEncoder::openFileOutput(const std::string& filename, std::map<std::string, std::string> options)
{
    avformat_free_context(outputCtx_);
    avformat_alloc_output_context2(&outputCtx_, nullptr, nullptr, filename.c_str());
165 166 167 168 169 170

    if (!options["title"].empty())
        av_dict_set(&outputCtx_->metadata, "title", options["title"].c_str(), 0);
    if (!options["description"].empty())
        av_dict_set(&outputCtx_->metadata, "description", options["description"].c_str(), 0);

171 172 173 174 175 176 177 178 179 180 181
    auto bitrate = SystemCodecInfo::DEFAULT_MAX_BITRATE;
    auto quality = SystemCodecInfo::DEFAULT_CODEC_QUALITY;
    // ensure all options retrieved later on are in options_ (insert does nothing if key exists)
    options.insert({"max_rate", ring::to_string(bitrate)});
    options.insert({"crf", ring::to_string(quality)});
    options.insert({"sample_rate", "8000"});
    options.insert({"channels", "2"});
    int sampleRate = atoi(options["sample_rate"].c_str());
    options.insert({"frame_size", ring::to_string(static_cast<unsigned>(0.02*sampleRate))});
    options.insert({"width", "320"});
    options.insert({"height", "240"});
182
    options.insert({"framerate", "30.00"});
183 184 185 186 187 188 189 190 191 192
    for (const auto& it : options)
        av_dict_set(&options_, it.first.c_str(), it.second.c_str(), 0);
    // for a file output, addStream is done by the caller, as there may be multiple streams
}

int
MediaEncoder::addStream(const SystemCodecInfo& systemCodecInfo, std::string parameters)
{
    AVCodec* outputCodec = nullptr;
    AVCodecContext* encoderCtx = nullptr;
193
    /* find the video encoder */
194
    if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263)
Éloi Bail's avatar
Éloi Bail committed
195 196 197
        // 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
198
        outputCodec = avcodec_find_encoder(AV_CODEC_ID_H263P);
Éloi Bail's avatar
Éloi Bail committed
199
    else
200 201 202
        outputCodec = avcodec_find_encoder(static_cast<AVCodecID>(systemCodecInfo.avcodecId));
    if (!outputCodec) {
        RING_ERR("Encoder \"%s\" not found!", systemCodecInfo.name.c_str());
203
        throw MediaEncoderException("No output encoder");
204 205
    }

206 207
    encoderCtx = prepareEncoderContext(outputCodec, systemCodecInfo.mediaType == MEDIA_VIDEO);
    encoders_.push_back(encoderCtx);
208
    auto maxBitrate = 1000 * std::atoi(libav_utils::getDictValue(options_, "max_rate"));
209
    auto bufSize = 2 * maxBitrate; // as recommended (TODO: make it customizable)
210
    auto crf = std::atoi(libav_utils::getDictValue(options_, "crf"));
211

212
    /* let x264 preset override our encoder settings */
213 214 215
    if (systemCodecInfo.avcodecId == AV_CODEC_ID_H264) {
        extractProfileLevelID(parameters, encoderCtx);
        forcePresetX264(encoderCtx);
216
        // For H264 :
217 218 219 220 221
        // Streaming => VBV (constrained encoding) + CRF (Constant Rate Factor)
        if (crf == SystemCodecInfo::DEFAULT_NO_QUALITY)
            crf = 30; // good value for H264-720p@30
        RING_DBG("H264 encoder setup: crf=%u, maxrate=%u, bufsize=%u", crf, maxBitrate, bufSize);

222
        av_opt_set_int(encoderCtx->priv_data, "crf", crf, 0);
223 224 225
        encoderCtx->rc_buffer_size = bufSize;
        encoderCtx->rc_max_rate = maxBitrate;
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_VP8) {
226 227 228 229
        // 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
230 231
        // Using information given on this page:
        // http://www.webmproject.org/docs/encoder-parameters/
232 233 234 235
        av_opt_set(encoderCtx->priv_data, "quality", "realtime", 0);
        av_opt_set_int(encoderCtx->priv_data, "error-resilient", 1, 0);
        av_opt_set_int(encoderCtx->priv_data, "cpu-used", 7, 0); // value obtained from testing
        av_opt_set_int(encoderCtx->priv_data, "lag-in-frames", 0, 0);
236 237
        // allow encoder to drop frames if buffers are full and
        // to undershoot target bitrate to lessen strain on resources
238 239 240 241 242 243 244 245
        av_opt_set_int(encoderCtx->priv_data, "drop-frame", 25, 0);
        av_opt_set_int(encoderCtx->priv_data, "undershoot-pct", 95, 0);
        // 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;
246
        if (crf != SystemCodecInfo::DEFAULT_NO_QUALITY) {
247
            av_opt_set_int(encoderCtx->priv_data, "crf", crf, 0);
248 249 250 251
            RING_DBG("Using quality factor %d", crf);
        } else {
            RING_DBG("Using Max bitrate %d", maxBitrate);
        }
252
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_MPEG4) {
253 254 255
        // For MPEG4 :
        // No CRF avaiable.
        // Use CBR (set bitrate)
256 257
        encoderCtx->rc_buffer_size = maxBitrate;
        encoderCtx->bit_rate = encoderCtx->rc_min_rate = encoderCtx->rc_max_rate =  maxBitrate;
258
        RING_DBG("Using Max bitrate %d", maxBitrate);
259 260 261
    } else if (systemCodecInfo.avcodecId == AV_CODEC_ID_H263) {
        encoderCtx->bit_rate = encoderCtx->rc_max_rate =  maxBitrate;
        encoderCtx->rc_buffer_size = maxBitrate;
Éloi Bail's avatar
Éloi Bail committed
262
        RING_DBG("Using Max bitrate %d", maxBitrate);
263 264 265
    }

    // add video stream to outputformat context
266 267
    AVStream* stream = avformat_new_stream(outputCtx_, outputCodec);
    if (!stream)
268
        throw MediaEncoderException("Could not allocate stream");
269

270 271
    currentStreamIdx_ = stream->index;

272 273 274
    if (avcodec_open2(encoderCtx, outputCodec, nullptr) < 0)
        throw MediaEncoderException("Could not open encoder");

275
#ifndef _WIN32
276
    avcodec_parameters_from_context(stream->codecpar, encoderCtx);
277
#else
278
    stream->codec = encoderCtx;
279
#endif
280 281
    // framerate is not copied from encoderCtx to stream
    stream->avg_frame_rate = encoderCtx->framerate;
282
#ifdef RING_VIDEO
283
    if (systemCodecInfo.mediaType == MEDIA_VIDEO) {
284
        // allocate buffers for both scaled (pre-encoder) and encoded frames
285 286
        const int width = encoderCtx->width;
        const int height = encoderCtx->height;
287
        const int format = encoderCtx->pix_fmt;
288
        scaledFrameBufferSize_ = videoFrameSize(format, width, height);
289
        if (scaledFrameBufferSize_ <= AV_INPUT_BUFFER_MIN_SIZE)
290
            throw MediaEncoderException("buffer too small");
291

292 293
        scaledFrameBuffer_.reserve(scaledFrameBufferSize_);
        scaledFrame_.setFromMemory(scaledFrameBuffer_.data(), format, width, height);
294
    }
295
#endif // RING_VIDEO
296 297

    return stream->index;
298 299
}

300
void MediaEncoder::setInterruptCallback(int (*cb)(void*), void *opaque)
301 302
{
    if (cb) {
303 304
        outputCtx_->interrupt_callback.callback = cb;
        outputCtx_->interrupt_callback.opaque = opaque;
305 306 307 308 309
    } else {
        outputCtx_->interrupt_callback.callback = 0;
    }
}

310
void MediaEncoder::setIOContext(const std::unique_ptr<MediaIOHandle> &ioctx)
311
{
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
    if (ioctx) {
        outputCtx_->pb = ioctx->getContext();
        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) {
                throw MediaEncoderException(
                    std::string("Could not set IO context" + libav_utils::getError(ret)).c_str());
            }
        }
    }
329 330
}

331
void
332
MediaEncoder::startIO()
333
{
334
    if (avformat_write_header(outputCtx_, options_ ? &options_ : nullptr)) {
Adrien Béraud's avatar
Adrien Béraud committed
335
        RING_ERR("Could not write header for output file... check codec parameters");
336
        throw MediaEncoderException("Failed to write output file header");
337 338
    }

339 340 341
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(58, 7, 100)
    av_dump_format(outputCtx_, 0, outputCtx_->url, 1);
#else
342
    av_dump_format(outputCtx_, 0, outputCtx_->filename, 1);
343
#endif
344 345
}

346 347 348
// seq: frame number for video, sent samples audio
// sampleFreq: fps for video, sample rate for audio
// clock: stream time base (packetization interval times)
349
static int64_t
350 351 352 353 354
getNextTimestamp(int64_t seq, rational<int64_t> sampleFreq, rational<int64_t> clock)
{
    return (seq / (sampleFreq * clock)).real<int64_t>();
}

355
#ifdef RING_VIDEO
Guillaume Roguez's avatar
Guillaume Roguez committed
356 357 358
int
MediaEncoder::encode(VideoFrame& input, bool is_keyframe,
                     int64_t frame_number)
359
{
360 361 362
    /* Prepare a frame suitable to our encoder frame format,
     * keeping also the input aspect ratio.
     */
363
    libav_utils::fillWithBlack(scaledFrame_.pointer());
364

365
    scaler_.scale_with_aspect(input, scaledFrame_);
366

367
    auto frame = scaledFrame_.pointer();
368 369
    AVCodecContext* enc = encoders_[currentStreamIdx_];
    frame->pts = getNextTimestamp(frame_number, enc->framerate, enc->time_base);
370 371

    if (is_keyframe) {
372
        frame->pict_type = AV_PICTURE_TYPE_I;
373
        frame->key_frame = 1;
374
    } else {
375
        frame->pict_type = AV_PICTURE_TYPE_NONE;
376
        frame->key_frame = 0;
377
    }
378

379
    return encode(frame, currentStreamIdx_);
380
}
381
#endif // RING_VIDEO
382

383
int MediaEncoder::encodeAudio(AVFrame* frame)
384
{
385 386 387 388 389
    auto enc = encoders_[currentStreamIdx_];
    frame->pts = getNextTimestamp(sent_samples, enc->sample_rate, enc->time_base);
    sent_samples += frame->nb_samples;
    encode(frame, currentStreamIdx_);
    av_frame_free(&frame);
390 391 392
    return 0;
}

393 394
int
MediaEncoder::encode(AVFrame* frame, int streamIdx)
395
{
396
    int ret = 0;
397
    AVCodecContext* encoderCtx = encoders_[streamIdx];
398
    AVPacket pkt;
399
    av_init_packet(&pkt);
400
    pkt.data = nullptr; // packet data will be allocated by the encoder
401
    pkt.size = 0;
402

403
    ret = avcodec_send_frame(encoderCtx, frame);
404 405 406
    if (ret < 0)
        return -1;

407
    while (ret >= 0) {
408
        ret = avcodec_receive_packet(encoderCtx, &pkt);
409 410
        if (ret == AVERROR(EAGAIN))
            break;
411
        if (ret < 0 && ret != AVERROR_EOF) { // we still want to write our frame on EOF
412 413 414
            RING_ERR() << "Failed to encode frame: " << libav_utils::getError(ret);
            return ret;
        }
415 416

        if (pkt.size) {
417
            pkt.stream_index = streamIdx;
418 419 420 421 422 423
            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);
424 425 426

            // write the compressed frame
            ret = av_write_frame(outputCtx_, &pkt);
427
            if (ret < 0) {
428
                RING_ERR() << "av_write_frame failed: " << libav_utils::getError(ret);
429
            } else
430 431 432
                break;
        }
    }
433

434
    av_packet_unref(&pkt);
435 436
    return 0;
}
437

438 439 440
int
MediaEncoder::flush()
{
441 442 443 444 445 446 447 448
    int ret = 0;
    for (size_t i = 0; i < outputCtx_->nb_streams; ++i) {
        if (encode(nullptr, i) < 0) {
            RING_ERR() << "Could not flush stream #" << i;
            ret |= 1u << i; // provide a way for caller to know which streams failed
        }
    }
    return -ret;
449 450
}

451 452
std::string
MediaEncoder::print_sdp()
453 454
{
    /* theora sdp can be huge */
455
#ifndef _WIN32
456
    const auto sdp_size = outputCtx_->streams[currentStreamIdx_]->codecpar->extradata_size + 2048;
457
#else
458
    const auto sdp_size = outputCtx_->streams[currentStreamIdx_]->codec->extradata_size + 2048;
459
#endif
460
    std::string result;
Guillaume Roguez's avatar
Guillaume Roguez committed
461
    std::string sdp(sdp_size, '\0');
462 463
    av_sdp_create(&outputCtx_, 1, &(*sdp.begin()), sdp_size);
    std::istringstream iss(sdp);
464
    std::string line;
465 466 467
    while (std::getline(iss, line)) {
        /* strip windows line ending */
        line = line.substr(0, line.length() - 1);
468
        result += line + "\n";
469
    }
470 471 472 473
#ifdef DEBUG_SDP
    RING_DBG("Sending SDP:\n%s", result.c_str());
#endif
    return result;
474 475
}

476
AVCodecContext* MediaEncoder::prepareEncoderContext(AVCodec* outputCodec, bool is_video)
477
{
478
    AVCodecContext* encoderCtx = avcodec_alloc_context3(outputCodec);
479

480 481
    auto encoderName = encoderCtx->av_class->item_name ?
        encoderCtx->av_class->item_name(encoderCtx) : nullptr;
482 483 484
    if (encoderName == nullptr)
        encoderName = "encoder?";

485 486
    encoderCtx->thread_count = std::min(std::thread::hardware_concurrency(), is_video ? 16u : 4u);
    RING_DBG("[%s] Using %d threads", encoderName, encoderCtx->thread_count);
487

488 489
    if (is_video) {
        // resolution must be a multiple of two
490 491 492 493
        if (device_.width && device_.height) {
            encoderCtx->width = device_.width;
            encoderCtx->height = device_.height;
        } else {
494 495
            encoderCtx->width = std::atoi(libav_utils::getDictValue(options_, "width"));
            encoderCtx->height = std::atoi(libav_utils::getDictValue(options_, "height"));
496
        }
497

498
        // satisfy ffmpeg: denominator must be 16bit or less value
499
        // time base = 1/FPS
500 501
        if (device_.framerate) {
            av_dict_set(&options_, "width", ring::to_string(device_.width).c_str(), 0);
502
            av_reduce(&encoderCtx->framerate.num, &encoderCtx->framerate.den,
503 504
                      device_.framerate.numerator(), device_.framerate.denominator(),
                      (1U << 16) - 1);
505
            encoderCtx->time_base = av_inv_q(encoderCtx->framerate);
506 507 508 509 510 511 512 513
        } else {
            // get from options_, else default to 30 fps
            auto v = av_dict_get(options_, "framerate", nullptr, 0);
            AVRational framerate = AVRational{30, 1};
            if (v)
                av_parse_ratio_quiet(&framerate, v->value, 120);
            if (framerate.den == 0)
                framerate.den = 1;
514
            av_reduce(&encoderCtx->framerate.num, &encoderCtx->framerate.den,
515 516
                      framerate.num, framerate.den,
                      (1U << 16) - 1);
517
            encoderCtx->time_base = av_inv_q(encoderCtx->framerate);
518
        }
519

520
        // emit one intra frame every gop_size frames
521 522
        encoderCtx->max_b_frames = 0;
        encoderCtx->pix_fmt = AV_PIX_FMT_YUV420P; // TODO: option me !
523 524 525 526 527

        // 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.
528
        // encoderCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
529
    } else {
530 531
        encoderCtx->sample_fmt = AV_SAMPLE_FMT_S16;
        auto v = av_dict_get(options_, "sample_rate", nullptr, 0);
532
        if (v) {
533 534
            encoderCtx->sample_rate = atoi(v->value);
            encoderCtx->time_base = AVRational{1, encoderCtx->sample_rate};
535
        } else {
536
            RING_WARN("[%s] No sample rate set", encoderName);
537
            encoderCtx->sample_rate = 8000;
538 539
        }

540
        v = av_dict_get(options_, "channels", nullptr, 0);
541 542 543
        if (v) {
            auto c = std::atoi(v->value);
            if (c > 2 or c < 1) {
544
                RING_WARN("[%s] Clamping invalid channel value %d", encoderName, c);
545 546
                c = 1;
            }
547
            encoderCtx->channels = c;
548
        } else {
549
            RING_WARN("[%s] Channels not set", encoderName);
550
            encoderCtx->channels = 1;
551 552
        }

553
        encoderCtx->channel_layout = encoderCtx->channels == 2 ? AV_CH_LAYOUT_STEREO : AV_CH_LAYOUT_MONO;
554

555
        v = av_dict_get(options_, "frame_size", nullptr, 0);
556
        if (v) {
557 558
            encoderCtx->frame_size = atoi(v->value);
            RING_DBG("[%s] Frame size %d", encoderName, encoderCtx->frame_size);
559
        } else {
560
            RING_WARN("[%s] Frame size not set", encoderName);
561 562
        }
    }
563 564

    return encoderCtx;
565 566
}

567
void MediaEncoder::forcePresetX264(AVCodecContext* encoderCtx)
568 569
{
    const char *speedPreset = "ultrafast";
570
    if (av_opt_set(encoderCtx->priv_data, "preset", speedPreset, 0))
Adrien Béraud's avatar
Adrien Béraud committed
571
        RING_WARN("Failed to set x264 preset '%s'", speedPreset);
572
    const char *tune = "zerolatency";
573
    if (av_opt_set(encoderCtx->priv_data, "tune", tune, 0))
Adrien Béraud's avatar
Adrien Béraud committed
574
        RING_WARN("Failed to set x264 tune '%s'", tune);
575 576
}

577
void MediaEncoder::extractProfileLevelID(const std::string &parameters,
578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608
                                         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) {
609 610 611 612 613 614 615 616 617 618 619 620
        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;
621
    }
Adrien Béraud's avatar
Adrien Béraud committed
622
    RING_DBG("Using profile %x and level %d", ctx->profile, ctx->level);
623
}
624

625 626 627 628 629 630
void
MediaEncoder::setMuted(bool isMuted)
{
    is_muted = isMuted;
}

631 632 633 634 635 636
bool
MediaEncoder::useCodec(const ring::AccountCodecInfo* codec) const noexcept
{
    return codec_.get() == codec;
}

637 638 639 640 641 642 643 644 645
unsigned
MediaEncoder::getStreamCount() const
{
    if (outputCtx_)
        return outputCtx_->nb_streams;
    else
        return 0;
}

646
} // namespace ring