cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

ffmpeg-compat.c (7979B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "config.h"
     21#include "ffmpeg-compat.h"
     22#include "log.h"
     23#include "video.h"
     24
     25#include <libavcodec/avcodec.h>
     26#include <libavutil/common.h>
     27#include <libavutil/imgutils.h>
     28#include <guacamole/client.h>
     29#include <guacamole/mem.h>
     30
     31#include <stdio.h>
     32#include <stdint.h>
     33#include <stdlib.h>
     34
     35/**
     36 * Writes a single packet of video data to the current output file. If an error
     37 * occurs preventing the packet from being written, messages describing the
     38 * error are logged.
     39 *
     40 * @param video
     41 *     The video associated with the output file that the given packet should
     42 *     be written to.
     43 *
     44 * @param data
     45 *     The buffer of data containing the video packet which should be written.
     46 *
     47 * @param size
     48 *     The number of bytes within the video packet.
     49 *
     50 * @return
     51 *     Zero if the packet was written successfully, non-zero otherwise.
     52 */
     53static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
     54
     55    int ret;
     56
     57#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0)
     58
     59    AVPacket pkt;
     60
     61    /* Have to create a packet around the encoded data we have */
     62    av_init_packet(&pkt);
     63
     64    if (video->context->coded_frame->pts != AV_NOPTS_VALUE) {
     65        pkt.pts = av_rescale_q(video->context->coded_frame->pts,
     66                video->context->time_base,
     67                video->output_stream->time_base);
     68    }
     69    if (video->context->coded_frame->key_frame) {
     70        pkt->flags |= AV_PKT_FLAG_KEY;
     71    }
     72
     73    pkt.data = data;
     74    pkt.size = size;
     75    pkt.stream_index = video->output_stream->index;
     76    ret = av_interleaved_write_frame(video->container_format_context, &pkt);
     77
     78#else
     79
     80    /* We know data is already a packet if we're using a newer libavcodec */
     81    AVPacket* pkt = (AVPacket*) data;
     82    av_packet_rescale_ts(pkt, video->context->time_base, video->output_stream->time_base);
     83    pkt->stream_index = video->output_stream->index;
     84    ret = av_interleaved_write_frame(video->container_format_context, pkt);
     85
     86#endif
     87
     88
     89    if (ret != 0) {
     90        guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
     91                "#%" PRId64 ": %s", video->next_pts, strerror(errno));
     92        return -1;
     93    }
     94
     95    /* Data was written successfully */
     96    guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
     97            video->next_pts, size);
     98
     99    return ret;
    100}
    101
    102int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
    103
    104/* For libavcodec < 54.1.0: packets were handled as raw malloc'd buffers */
    105#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0)
    106
    107    AVCodecContext* context = video->context;
    108
    109    /* Calculate appropriate buffer size */
    110    size_t length = guac_mem_ckd_add_or_die(FF_MIN_BUFFER_SIZE,
    111            guac_mem_ckd_mul_or_die(12, context->width, context->height));
    112
    113    /* Allocate space for output */
    114    uint8_t* data = guac_mem_alloc(length);
    115    if (data == NULL)
    116        return -1;
    117
    118    /* Encode packet of video data */
    119    int used = avcodec_encode_video(context, data, length, frame);
    120    if (used < 0) {
    121        guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64,
    122                video->next_pts);
    123        guac_mem_free(data);
    124        return -1;
    125    }
    126
    127    /* Report if no data needs to be written */
    128    if (used == 0) {
    129        guac_mem_free(data);
    130        return 0;
    131    }
    132
    133    /* Write data, logging any errors */
    134    guacenc_write_packet(video, data, used);
    135    guac_mem_free(data);
    136    return 1;
    137
    138#else
    139
    140/* For libavcodec < 57.37.100: input/output was not decoupled and static
    141 * allocation of AVPacket was supported.
    142 *
    143 * NOTE: Since dynamic allocation of AVPacket was added before this point (in
    144 * 57.12.100) and static allocation was deprecated later (in 58.133.100), it is
    145 * convenient to tie static vs. dynamic allocation to the old vs. new I/O
    146 * mechanism and avoid further complicating the version comparison logic. */
    147#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57, 37, 100)
    148
    149    /* Init video packet */
    150    AVPacket packet;
    151    av_init_packet(&packet);
    152
    153    /* Request that encoder allocate data for packet */
    154    packet.data = NULL;
    155    packet.size = 0;
    156
    157    /* Write frame to video */
    158    int got_data;
    159    if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) {
    160        guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64,
    161                video->next_pts);
    162        return -1;
    163    }
    164
    165    /* Write corresponding data to file */
    166    if (got_data) {
    167        guacenc_write_packet(video, (void*) &packet, packet.size);
    168        av_packet_unref(&packet);
    169    }
    170
    171#else
    172
    173    /* Write frame to video */
    174    int result = avcodec_send_frame(video->context, frame);
    175
    176    /* Stop once encoded has been flushed */
    177    if (result == AVERROR_EOF)
    178        return 0;
    179
    180    /* Abort on error */
    181    else if (result < 0) {
    182        guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64,
    183                video->next_pts);
    184        return -1;
    185    }
    186
    187    AVPacket* packet = av_packet_alloc();
    188    if (packet == NULL)
    189        return -1;
    190
    191    /* Flush all available packets */
    192    int got_data = 0;
    193    while (avcodec_receive_packet(video->context, packet) == 0) {
    194
    195        /* Data was received */
    196        got_data = 1;
    197
    198        /* Attempt to write data to output file */
    199        guacenc_write_packet(video, (void*) packet, packet->size);
    200        av_packet_unref(packet);
    201
    202    }
    203
    204    av_packet_free(&packet);
    205
    206#endif
    207
    208    /* Frame may have been queued for later writing / reordering */
    209    if (!got_data)
    210        guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": queued for later",
    211                video->next_pts);
    212
    213    return got_data;
    214
    215#endif
    216}
    217
    218AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream, AVCodec* codec, 
    219        int bitrate, int width, int height, int gop_size, int qmax, int qmin,
    220        int pix_fmt, AVRational time_base) {
    221
    222#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 33, 100)
    223    stream->codec->bit_rate = bitrate;
    224    stream->codec->width = width;
    225    stream->codec->height = height;
    226    stream->codec->gop_size = gop_size;
    227    stream->codec->qmax = qmax;
    228    stream->codec->qmin = qmin;
    229    stream->codec->pix_fmt = pix_fmt;
    230    stream->codec->time_base = time_base;
    231#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 44, 100)
    232    stream->time_base = time_base;
    233#endif
    234    return stream->codec;
    235#else
    236    AVCodecContext* context = avcodec_alloc_context3(codec);
    237    if (context) {
    238        context->bit_rate = bitrate;
    239        context->width = width;
    240        context->height = height;
    241        context->gop_size = gop_size;
    242        context->qmax = qmax;
    243        context->qmin = qmin;
    244        context->pix_fmt = pix_fmt;
    245        context->time_base = time_base;
    246        stream->time_base = time_base;
    247    }
    248    return context;
    249#endif
    250
    251}
    252
    253int guacenc_open_avcodec(AVCodecContext *avcodec_context,
    254        AVCodec *codec, AVDictionary **options,
    255        AVStream* stream) {
    256
    257    int ret = avcodec_open2(avcodec_context, codec, options);
    258
    259#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
    260    /* Copy stream parameters to the muxer */
    261    int codecpar_ret = avcodec_parameters_from_context(stream->codecpar, avcodec_context);
    262    if (codecpar_ret < 0)
    263        return codecpar_ret;
    264#endif
    265
    266    return ret;
    267
    268}