GUACAMOLE-465: added dependency to libavformat as first step to supporting other types of codecs and containers in guacenc. migrated existing functionality to use the libavformat library for writing the files. there is not differnce to the user with this patch, but it provides a good base to finish this new feature from later
This commit is contained in:
parent
68a6285818
commit
bb825de73b
28
configure.ac
28
configure.ac
@ -202,6 +202,24 @@ fi
|
|||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"])
|
AM_CONDITIONAL([ENABLE_AVCODEC], [test "x${have_libavcodec}" = "xyes"])
|
||||||
|
|
||||||
|
#
|
||||||
|
# libavformat
|
||||||
|
#
|
||||||
|
|
||||||
|
have_libavformat=disabled
|
||||||
|
AC_ARG_WITH([libavformat],
|
||||||
|
[AS_HELP_STRING([--with-libavformat],
|
||||||
|
[use libavformat when encoding video @<:@default=check@:>@])],
|
||||||
|
[].
|
||||||
|
[with_libavformat=check])
|
||||||
|
if test "x$with_libavformat" != "xno"
|
||||||
|
then
|
||||||
|
have_libavformat=yes
|
||||||
|
PKG_CHECK_MODULES([AVFORMAT], [libavformat],, [have_libavformat=no]);
|
||||||
|
fi
|
||||||
|
|
||||||
|
AM_CONDITIONAL([ENABLE_AVFORMAT], [test "x${have_libavformat}" = "xyes"])
|
||||||
|
|
||||||
#
|
#
|
||||||
# libavutil
|
# libavutil
|
||||||
#
|
#
|
||||||
@ -980,10 +998,11 @@ AC_ARG_ENABLE([guacenc],
|
|||||||
[],
|
[],
|
||||||
[enable_guacenc=yes])
|
[enable_guacenc=yes])
|
||||||
|
|
||||||
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
|
AM_CONDITIONAL([ENABLE_GUACENC], [test "x${enable_guacenc}" = "xyes" \
|
||||||
-a "x${have_libavcodec}" = "xyes" \
|
-a "x${have_libavcodec}" = "xyes" \
|
||||||
-a "x${have_libavutil}" = "xyes" \
|
-a "x${have_libavutil}" = "xyes" \
|
||||||
-a "x${have_libswscale}" = "xyes"])
|
-a "x${have_libswscale}" = "xyes" \
|
||||||
|
-a "x${have_libavformat}" = "xyes"])
|
||||||
|
|
||||||
#
|
#
|
||||||
# guaclog
|
# guaclog
|
||||||
@ -1076,6 +1095,7 @@ $PACKAGE_NAME version $PACKAGE_VERSION
|
|||||||
freerdp2 ............ ${have_freerdp2}
|
freerdp2 ............ ${have_freerdp2}
|
||||||
pango ............... ${have_pango}
|
pango ............... ${have_pango}
|
||||||
libavcodec .......... ${have_libavcodec}
|
libavcodec .......... ${have_libavcodec}
|
||||||
|
libavformat.......... ${have_libavformat}
|
||||||
libavutil ........... ${have_libavutil}
|
libavutil ........... ${have_libavutil}
|
||||||
libssh2 ............. ${have_libssh2}
|
libssh2 ............. ${have_libssh2}
|
||||||
libssl .............. ${have_ssl}
|
libssl .............. ${have_ssl}
|
||||||
|
@ -90,6 +90,7 @@ endif
|
|||||||
guacenc_CFLAGS = \
|
guacenc_CFLAGS = \
|
||||||
-Werror -Wall \
|
-Werror -Wall \
|
||||||
@AVCODEC_CFLAGS@ \
|
@AVCODEC_CFLAGS@ \
|
||||||
|
@AVFORMAT_CFLAGS@ \
|
||||||
@AVUTIL_CFLAGS@ \
|
@AVUTIL_CFLAGS@ \
|
||||||
@LIBGUAC_INCLUDE@ \
|
@LIBGUAC_INCLUDE@ \
|
||||||
@SWSCALE_CFLAGS@
|
@SWSCALE_CFLAGS@
|
||||||
@ -97,12 +98,13 @@ guacenc_CFLAGS = \
|
|||||||
guacenc_LDADD = \
|
guacenc_LDADD = \
|
||||||
@LIBGUAC_LTLIB@
|
@LIBGUAC_LTLIB@
|
||||||
|
|
||||||
guacenc_LDFLAGS = \
|
guacenc_LDFLAGS = \
|
||||||
@AVCODEC_LIBS@ \
|
@AVCODEC_LIBS@ \
|
||||||
@AVUTIL_LIBS@ \
|
@AVFORMAT_LIBS@ \
|
||||||
@CAIRO_LIBS@ \
|
@AVUTIL_LIBS@ \
|
||||||
@JPEG_LIBS@ \
|
@CAIRO_LIBS@ \
|
||||||
@SWSCALE_LIBS@ \
|
@JPEG_LIBS@ \
|
||||||
|
@SWSCALE_LIBS@ \
|
||||||
@WEBP_LIBS@
|
@WEBP_LIBS@
|
||||||
|
|
||||||
EXTRA_DIST = \
|
EXTRA_DIST = \
|
||||||
|
@ -51,8 +51,41 @@
|
|||||||
*/
|
*/
|
||||||
static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
|
static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
|
||||||
|
|
||||||
/* Write data, logging any errors */
|
int ret;
|
||||||
if (fwrite(data, 1, size, video->output) == 0) {
|
AVPacket *pkt;
|
||||||
|
|
||||||
|
|
||||||
|
#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0)
|
||||||
|
|
||||||
|
pkt = malloc(sizeof(AVPacket));
|
||||||
|
/* have to create a packet around the encoded data we have */
|
||||||
|
av_init_packet(pkt);
|
||||||
|
|
||||||
|
if (video->context->coded_frame->pts != AV_NOPTS_VALUE) {
|
||||||
|
pkt->pts = av_rescale_q(video->context->coded_frame->pts,
|
||||||
|
video->context->time_base,
|
||||||
|
video->output_stream->time_base);
|
||||||
|
}
|
||||||
|
if (video->context->coded_frame->key_frame) {
|
||||||
|
pkt->flags |= AV_PKT_FLAG_KEY;
|
||||||
|
}
|
||||||
|
|
||||||
|
pkt->data = data;
|
||||||
|
pkt->size = size;
|
||||||
|
pkt->stream_index = video->output_stream->index;
|
||||||
|
ret = av_interleaved_write_frame(video->container_format_context, pkt);
|
||||||
|
free(pkt);
|
||||||
|
|
||||||
|
#else
|
||||||
|
/* we know data is already a packet if we're using a newer libavcodec */
|
||||||
|
pkt = (AVPacket*) data;
|
||||||
|
av_packet_rescale_ts(pkt, video->context->time_base, video->output_stream->time_base);
|
||||||
|
pkt->stream_index = video->output_stream->index;
|
||||||
|
ret = av_interleaved_write_frame(video->container_format_context, pkt);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
|
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
|
||||||
"#%" PRId64 ": %s", video->next_pts, strerror(errno));
|
"#%" PRId64 ": %s", video->next_pts, strerror(errno));
|
||||||
return -1;
|
return -1;
|
||||||
@ -62,8 +95,7 @@ static int guacenc_write_packet(guacenc_video* video, void* data, int size) {
|
|||||||
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
|
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
|
||||||
video->next_pts, size);
|
video->next_pts, size);
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
|
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
|
||||||
@ -123,7 +155,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
|
|||||||
|
|
||||||
/* Write corresponding data to file */
|
/* Write corresponding data to file */
|
||||||
if (got_data) {
|
if (got_data) {
|
||||||
guacenc_write_packet(video, packet.data, packet.size);
|
guacenc_write_packet(video, (void*) &packet, packet.size);
|
||||||
av_packet_unref(&packet);
|
av_packet_unref(&packet);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -149,7 +181,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
|
|||||||
got_data = 1;
|
got_data = 1;
|
||||||
|
|
||||||
/* Attempt to write data to output file */
|
/* Attempt to write data to output file */
|
||||||
guacenc_write_packet(video, packet.data, packet.size);
|
guacenc_write_packet(video, (void*) &packet, packet.size);
|
||||||
av_packet_unref(&packet);
|
av_packet_unref(&packet);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -165,3 +197,62 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream,
|
||||||
|
AVCodec* codec,
|
||||||
|
int bitrate,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int gop_size,
|
||||||
|
int qmax,
|
||||||
|
int qmin,
|
||||||
|
int pix_fmt,
|
||||||
|
AVRational time_base) {
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(57, 33, 100)
|
||||||
|
stream->codec->bit_rate = bitrate;
|
||||||
|
stream->codec->width = width;
|
||||||
|
stream->codec->height = height;
|
||||||
|
stream->codec->gop_size = gop_size;
|
||||||
|
stream->codec->qmax = qmax;
|
||||||
|
stream->codec->qmin = qmin;
|
||||||
|
stream->codec->pix_fmt = pix_fmt;
|
||||||
|
stream->codec->time_base = time_base;
|
||||||
|
#if LIBAVFORMAT_VERSION_INT < AV_VERSION_INT(55, 44, 100)
|
||||||
|
stream->codec->time_base = time_base;
|
||||||
|
#else
|
||||||
|
stream->time_base = time_base;
|
||||||
|
#endif
|
||||||
|
return stream->codec;
|
||||||
|
#else
|
||||||
|
AVCodecContext* context = avcodec_alloc_context3(codec);
|
||||||
|
if (context) {
|
||||||
|
context->bit_rate = bitrate;
|
||||||
|
context->width = width;
|
||||||
|
context->height = height;
|
||||||
|
context->gop_size = gop_size;
|
||||||
|
context->qmax = qmax;
|
||||||
|
context->qmin = qmin;
|
||||||
|
context->pix_fmt = pix_fmt;
|
||||||
|
context->time_base = time_base;
|
||||||
|
stream->time_base = time_base;
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
|
||||||
|
AVCodec *codec, AVDictionary **options,
|
||||||
|
AVStream* stream) {
|
||||||
|
int ret = 0;
|
||||||
|
ret = avcodec_open2(avcodec_context, codec, options);
|
||||||
|
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(57, 33, 100)
|
||||||
|
int codecpar_ret = 0;
|
||||||
|
/* copy stream parameters to the muxer */
|
||||||
|
codecpar_ret = avcodec_parameters_from_context(stream->codecpar,
|
||||||
|
avcodec_context);
|
||||||
|
if (codecpar_ret < 0) {
|
||||||
|
return codecpar_ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
@ -78,5 +78,77 @@
|
|||||||
*/
|
*/
|
||||||
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
|
int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates and sets up the AVCodecContext for the appropriate version
|
||||||
|
* of libavformat installed
|
||||||
|
*
|
||||||
|
* @param stream
|
||||||
|
* The open AVStream
|
||||||
|
*
|
||||||
|
* @param codec
|
||||||
|
* The codec used on the AVStream
|
||||||
|
*
|
||||||
|
* @param bitrate
|
||||||
|
* The target bitrate for the encoded video
|
||||||
|
*
|
||||||
|
* @param width
|
||||||
|
* The target width for the encoded video
|
||||||
|
*
|
||||||
|
* @param height
|
||||||
|
* The target width for the encoded video
|
||||||
|
*
|
||||||
|
* @param gop_size
|
||||||
|
* The size of the Group of Pictures
|
||||||
|
*
|
||||||
|
* @param qmax
|
||||||
|
* The max value of the quantizer
|
||||||
|
*
|
||||||
|
* @param qmin
|
||||||
|
* The min value of the quantizer
|
||||||
|
*
|
||||||
|
* @param pix_fmt
|
||||||
|
* The target pixel format for the encoded video
|
||||||
|
*
|
||||||
|
* @param time_base
|
||||||
|
* The target time base for the encoded video
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
AVCodecContext* guacenc_build_avcodeccontext(AVStream* stream,
|
||||||
|
AVCodec* codec,
|
||||||
|
int bitrate,
|
||||||
|
int width,
|
||||||
|
int height,
|
||||||
|
int gop_size,
|
||||||
|
int qmax,
|
||||||
|
int qmin,
|
||||||
|
int pix_fmt,
|
||||||
|
AVRational time_base);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A wrapper for avcodec_open2(). Because libavformat ver
|
||||||
|
* 57.33.100 and greater use stream->codecpar rather than
|
||||||
|
* stream->codec to handle information to the codec,
|
||||||
|
* there needs to be an additional step in that version.
|
||||||
|
* So this wrapper handles that. Otherwise, it's the
|
||||||
|
* same as avcodec_open2().
|
||||||
|
*
|
||||||
|
* @param avcodec_context The context to initialize.
|
||||||
|
* @param codec The codec to open this context for. If a non-NULL codec has
|
||||||
|
* been previously passed to avcodec_alloc_context3() or
|
||||||
|
* for this context, then this parameter MUST be either NULL or
|
||||||
|
* equal to the previously passed codec.
|
||||||
|
* @param options A dictionary filled with AVCodecContext and codec-private
|
||||||
|
* options. On return this object will be filled with options
|
||||||
|
* that were not found.
|
||||||
|
* @param stream The stream for the codec context.
|
||||||
|
* Only used in libavformat >= 57.33.100. Can be NULL in
|
||||||
|
* lower versions
|
||||||
|
*
|
||||||
|
* @return zero on success, a negative value on error
|
||||||
|
*/
|
||||||
|
int guacenc_open_avcodec(AVCodecContext *avcodec_context,
|
||||||
|
AVCodec *codec, AVDictionary **options,
|
||||||
|
AVStream* stream);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "parse.h"
|
#include "parse.h"
|
||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
@ -80,6 +81,9 @@ int main(int argc, char* argv[]) {
|
|||||||
avcodec_register_all();
|
avcodec_register_all();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Prepare libavformat */
|
||||||
|
av_register_all();
|
||||||
|
|
||||||
/* Track number of overall failures */
|
/* Track number of overall failures */
|
||||||
int total_files = argc - optind;
|
int total_files = argc - optind;
|
||||||
int failures = 0;
|
int failures = 0;
|
||||||
|
@ -25,6 +25,9 @@
|
|||||||
|
|
||||||
#include <cairo/cairo.h>
|
#include <cairo/cairo.h>
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
#ifndef AVFORMAT_AVFORMAT_H
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#endif
|
||||||
#include <libavutil/common.h>
|
#include <libavutil/common.h>
|
||||||
#include <libavutil/imgutils.h>
|
#include <libavutil/imgutils.h>
|
||||||
#include <libswscale/swscale.h>
|
#include <libswscale/swscale.h>
|
||||||
@ -43,6 +46,21 @@
|
|||||||
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
||||||
int width, int height, int bitrate) {
|
int width, int height, int bitrate) {
|
||||||
|
|
||||||
|
AVOutputFormat *container_format;
|
||||||
|
AVFormatContext *container_format_context;
|
||||||
|
AVStream *video_stream;
|
||||||
|
int ret;
|
||||||
|
int failed_header = 0;
|
||||||
|
|
||||||
|
/* allocate the output media context */
|
||||||
|
avformat_alloc_output_context2(&container_format_context, NULL, NULL, path);
|
||||||
|
if (container_format_context == NULL) {
|
||||||
|
guacenc_log(GUAC_LOG_ERROR, "Failed to determine container from output file name\n");
|
||||||
|
goto fail_codec;
|
||||||
|
}
|
||||||
|
|
||||||
|
container_format = container_format_context->oformat;
|
||||||
|
|
||||||
/* Pull codec based on name */
|
/* Pull codec based on name */
|
||||||
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
|
AVCodec* codec = avcodec_find_encoder_by_name(codec_name);
|
||||||
if (codec == NULL) {
|
if (codec == NULL) {
|
||||||
@ -51,25 +69,41 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
|||||||
goto fail_codec;
|
goto fail_codec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* create stream */
|
||||||
|
video_stream = NULL;
|
||||||
|
video_stream = avformat_new_stream(container_format_context, codec);
|
||||||
|
if (video_stream == NULL) {
|
||||||
|
guacenc_log(GUAC_LOG_ERROR, "Could not allocate encoder stream. Cannot continue.\n");
|
||||||
|
goto fail_format_context;
|
||||||
|
}
|
||||||
|
video_stream->id = container_format_context->nb_streams - 1;
|
||||||
|
|
||||||
/* Retrieve encoding context */
|
/* Retrieve encoding context */
|
||||||
AVCodecContext* context = avcodec_alloc_context3(codec);
|
AVCodecContext* avcodec_context =
|
||||||
if (context == NULL) {
|
guacenc_build_avcodeccontext(video_stream,
|
||||||
|
codec,
|
||||||
|
bitrate,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
/*gop size*/ 10,
|
||||||
|
/*qmax*/ 31,
|
||||||
|
/*qmin*/ 2,
|
||||||
|
/*pix fmt*/ AV_PIX_FMT_YUV420P,
|
||||||
|
/*time base*/ (AVRational) { 1, GUACENC_VIDEO_FRAMERATE });
|
||||||
|
|
||||||
|
if (avcodec_context == NULL) {
|
||||||
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for "
|
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate context for "
|
||||||
"codec \"%s\".", codec_name);
|
"codec \"%s\".", codec_name);
|
||||||
goto fail_context;
|
goto fail_context;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init context with encoding parameters */
|
//if format needs global headers, write them
|
||||||
context->bit_rate = bitrate;
|
if (container_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
|
||||||
context->width = width;
|
avcodec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
|
||||||
context->height = height;
|
}
|
||||||
context->time_base = (AVRational) { 1, GUACENC_VIDEO_FRAMERATE };
|
|
||||||
context->gop_size = 10;
|
|
||||||
context->max_b_frames = 1;
|
|
||||||
context->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
||||||
|
|
||||||
/* Open codec for use */
|
/* Open codec for use */
|
||||||
if (avcodec_open2(context, codec, NULL) < 0) {
|
if (guacenc_open_avcodec(avcodec_context, codec, NULL, video_stream) < 0) {
|
||||||
guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name);
|
guacenc_log(GUAC_LOG_ERROR, "Failed to open codec \"%s\".", codec_name);
|
||||||
goto fail_codec_open;
|
goto fail_codec_open;
|
||||||
}
|
}
|
||||||
@ -81,9 +115,9 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Copy necessary data for frame from context */
|
/* Copy necessary data for frame from context */
|
||||||
frame->format = context->pix_fmt;
|
frame->format = avcodec_context->pix_fmt;
|
||||||
frame->width = context->width;
|
frame->width = avcodec_context->width;
|
||||||
frame->height = context->height;
|
frame->height = avcodec_context->height;
|
||||||
|
|
||||||
/* Allocate actual backing data for frame */
|
/* Allocate actual backing data for frame */
|
||||||
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
if (av_image_alloc(frame->data, frame->linesize, frame->width,
|
||||||
@ -91,31 +125,32 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
|||||||
goto fail_frame_data;
|
goto fail_frame_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open output file */
|
/* Open output file, if the container needs it */
|
||||||
int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
|
if (!(container_format->flags & AVFMT_NOFILE)) {
|
||||||
if (fd == -1) {
|
ret = avio_open(&container_format_context->pb, path, AVIO_FLAG_WRITE);
|
||||||
guacenc_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
|
if (ret < 0) {
|
||||||
path, strerror(errno));
|
guacenc_log(GUAC_LOG_ERROR, "Error occurred while opening output file.\n");
|
||||||
goto fail_output_fd;
|
goto fail_output_avio;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create stream for output file */
|
/* write the stream header, if needed */
|
||||||
FILE* output = fdopen(fd, "wb");
|
ret = avformat_write_header(container_format_context, NULL);
|
||||||
if (output == NULL) {
|
if (ret < 0) {
|
||||||
guacenc_log(GUAC_LOG_ERROR, "Failed to allocate stream for output "
|
guacenc_log(GUAC_LOG_ERROR, "Error occurred while writing output file header.\n");
|
||||||
"file \"%s\": %s", path, strerror(errno));
|
failed_header = true;
|
||||||
goto fail_output_file;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate video structure */
|
/* Allocate video structure */
|
||||||
guacenc_video* video = malloc(sizeof(guacenc_video));
|
guacenc_video* video = malloc(sizeof(guacenc_video));
|
||||||
if (video == NULL) {
|
if (video == NULL) {
|
||||||
goto fail_video;
|
goto fail_output_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init properties of video */
|
/* Init properties of video */
|
||||||
video->output = output;
|
video->output_stream = video_stream;
|
||||||
video->context = context;
|
video->context = avcodec_context;
|
||||||
|
video->container_format_context = container_format_context;
|
||||||
video->next_frame = frame;
|
video->next_frame = frame;
|
||||||
video->width = width;
|
video->width = width;
|
||||||
video->height = height;
|
video->height = height;
|
||||||
@ -125,16 +160,24 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
|||||||
video->last_timestamp = 0;
|
video->last_timestamp = 0;
|
||||||
video->next_pts = 0;
|
video->next_pts = 0;
|
||||||
|
|
||||||
|
if (failed_header) {
|
||||||
|
guacenc_log(GUAC_LOG_ERROR, "An incompatible codec/container "
|
||||||
|
"combination was specified. Cannot encode.\n");
|
||||||
|
goto fail_output_file;
|
||||||
|
}
|
||||||
|
|
||||||
return video;
|
return video;
|
||||||
|
|
||||||
/* Free all allocated data in case of failure */
|
/* Free all allocated data in case of failure */
|
||||||
fail_video:
|
|
||||||
fclose(output);
|
|
||||||
|
|
||||||
fail_output_file:
|
fail_output_file:
|
||||||
close(fd);
|
avio_close(container_format_context->pb);
|
||||||
|
/* delete the file that was created if it was actually created */
|
||||||
|
if (access(path, F_OK) != -1) {
|
||||||
|
remove(path);
|
||||||
|
}
|
||||||
|
|
||||||
fail_output_fd:
|
fail_output_avio:
|
||||||
av_freep(&frame->data[0]);
|
av_freep(&frame->data[0]);
|
||||||
|
|
||||||
fail_frame_data:
|
fail_frame_data:
|
||||||
@ -142,7 +185,13 @@ fail_frame_data:
|
|||||||
|
|
||||||
fail_frame:
|
fail_frame:
|
||||||
fail_codec_open:
|
fail_codec_open:
|
||||||
avcodec_free_context(&context);
|
avcodec_free_context(&avcodec_context);
|
||||||
|
|
||||||
|
fail_format_context:
|
||||||
|
/* failing to write the container implicitly frees the context */
|
||||||
|
if (!failed_header) {
|
||||||
|
avformat_free_context(container_format_context);
|
||||||
|
}
|
||||||
|
|
||||||
fail_context:
|
fail_context:
|
||||||
fail_codec:
|
fail_codec:
|
||||||
@ -435,26 +484,34 @@ int guacenc_video_free(guacenc_video* video) {
|
|||||||
/* Write final frame */
|
/* Write final frame */
|
||||||
guacenc_video_flush_frame(video);
|
guacenc_video_flush_frame(video);
|
||||||
|
|
||||||
/* Init video packet for final flush of encoded data */
|
|
||||||
AVPacket packet;
|
|
||||||
av_init_packet(&packet);
|
|
||||||
|
|
||||||
/* Flush any unwritten frames */
|
/* Flush any unwritten frames */
|
||||||
int retval;
|
int retval;
|
||||||
do {
|
do {
|
||||||
retval = guacenc_video_write_frame(video, NULL);
|
retval = guacenc_video_write_frame(video, NULL);
|
||||||
} while (retval > 0);
|
} while (retval > 0);
|
||||||
|
|
||||||
|
/* write trailer, if needed */
|
||||||
|
if (video->container_format_context != NULL &&
|
||||||
|
video->output_stream != NULL) {
|
||||||
|
guacenc_log(GUAC_LOG_DEBUG, "Writing trailer: %d\n",
|
||||||
|
av_write_trailer(video->container_format_context) == 0 ?
|
||||||
|
"success" : "failure");
|
||||||
|
}
|
||||||
|
|
||||||
/* File is now completely written */
|
/* File is now completely written */
|
||||||
fclose(video->output);
|
if (video->container_format_context != NULL) {
|
||||||
|
avio_close(video->container_format_context->pb);
|
||||||
|
}
|
||||||
|
|
||||||
/* Free frame encoding data */
|
/* Free frame encoding data */
|
||||||
av_freep(&video->next_frame->data[0]);
|
av_freep(&video->next_frame->data[0]);
|
||||||
av_frame_free(&video->next_frame);
|
av_frame_free(&video->next_frame);
|
||||||
|
|
||||||
/* Clean up encoding context */
|
/* Clean up encoding context */
|
||||||
avcodec_close(video->context);
|
if (video->context != NULL) {
|
||||||
avcodec_free_context(&(video->context));
|
avcodec_close(video->context);
|
||||||
|
avcodec_free_context(&(video->context));
|
||||||
|
}
|
||||||
|
|
||||||
free(video);
|
free(video);
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -26,6 +26,14 @@
|
|||||||
#include <guacamole/timestamp.h>
|
#include <guacamole/timestamp.h>
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
|
||||||
|
#ifndef AVCODEC_AVCODEC_H
|
||||||
|
#include <libavcodec/avcodec.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef AVFORMAT_AVFORMAT_H
|
||||||
|
#include <libavformat/avformat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
@ -42,9 +50,10 @@
|
|||||||
typedef struct guacenc_video {
|
typedef struct guacenc_video {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output file stream.
|
* AVStream for video output.
|
||||||
|
* Persists via the AVFormatContext.
|
||||||
*/
|
*/
|
||||||
FILE* output;
|
AVStream* output_stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The open encoding context from libavcodec, created for the codec
|
* The open encoding context from libavcodec, created for the codec
|
||||||
@ -52,6 +61,12 @@ typedef struct guacenc_video {
|
|||||||
*/
|
*/
|
||||||
AVCodecContext* context;
|
AVCodecContext* context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The open format context from libavformat, created for the file
|
||||||
|
* container specified when this guacenc_video was created.
|
||||||
|
*/
|
||||||
|
AVFormatContext* container_format_context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The width of the video, in pixels.
|
* The width of the video, in pixels.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user