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}