GUAC-286: Actually write video output to file.

This commit is contained in:
Michael Jumper 2016-03-11 16:07:31 -08:00
parent 1ce39306cb
commit f9f08627ff
3 changed files with 50 additions and 11 deletions

View File

@ -88,7 +88,7 @@ cairo_operator_t guacenc_display_cairo_operator(guac_composite_mode mask) {
guacenc_display* guacenc_display_alloc(const char* path, const char* codec, guacenc_display* guacenc_display_alloc(const char* path, const char* codec,
int width, int height, int bitrate) { int width, int height, int bitrate) {
/* STUB: Prepare video encoding */ /* Prepare video encoding */
guacenc_video* video = guacenc_video_alloc(path, codec, width, height, bitrate); guacenc_video* video = guacenc_video_alloc(path, codec, width, height, bitrate);
if (video == NULL) if (video == NULL)
return NULL; return NULL;

View File

@ -43,7 +43,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
if (codec == NULL) { if (codec == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".", guacenc_log(GUAC_LOG_ERROR, "Failed to locate codec \"%s\".",
codec_name); codec_name);
return NULL; goto fail_codec;
} }
/* Retrieve encoding context */ /* Retrieve encoding context */
@ -51,7 +51,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
if (context == NULL) { if (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);
return NULL; goto fail_context;
} }
/* Init context with encoding parameters */ /* Init context with encoding parameters */
@ -66,13 +66,13 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
/* Open codec for use */ /* Open codec for use */
if (avcodec_open2(context, codec, NULL) < 0) { if (avcodec_open2(context, codec, NULL) < 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_context; goto fail_codec_open;
} }
/* Allocate corresponding frame */ /* Allocate corresponding frame */
AVFrame* frame = av_frame_alloc(); AVFrame* frame = av_frame_alloc();
if (frame == NULL) { if (frame == NULL) {
goto fail_context; goto fail_frame;
} }
/* Copy necessary data for frame from context */ /* Copy necessary data for frame from context */
@ -83,16 +83,25 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
/* 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,
frame->height, frame->format, 32) < 0) { frame->height, frame->format, 32) < 0) {
goto fail_frame; goto fail_frame_data;
}
/* Open output file */
FILE* output = fopen(path, "wb");
if (output == NULL) {
guacenc_log(GUAC_LOG_ERROR, "Failed to open output file \"%s\": %s",
path, strerror(errno));
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_frame_data; goto fail_video;
} }
/* Init properties of video */ /* Init properties of video */
video->output = output;
video->context = context; video->context = context;
video->next_frame = frame; video->next_frame = frame;
video->width = width; video->width = width;
@ -106,14 +115,21 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
return video; return video;
/* Free all allocated data in case of failure */ /* Free all allocated data in case of failure */
fail_frame_data: fail_video:
fclose(output);
fail_output_file:
av_freep(&frame->data[0]); av_freep(&frame->data[0]);
fail_frame: fail_frame_data:
av_frame_free(&frame); av_frame_free(&frame);
fail_context: fail_frame:
fail_codec_open:
avcodec_free_context(&context); avcodec_free_context(&context);
fail_context:
fail_codec:
return NULL; return NULL;
} }
@ -161,12 +177,26 @@ static int guacenc_video_write_frame(guacenc_video* video, AVFrame* frame) {
/* Write corresponding data to file */ /* Write corresponding data to file */
if (got_data) { if (got_data) {
/* Write data, logging any errors */
if (fwrite(packet.data, 1, packet.size, video->output) == 0) {
guacenc_log(GUAC_LOG_ERROR, "Unable to write frame "
"#%" PRId64 ": %s", video->next_pts, strerror(errno));
return -1;
}
/* Data was written successfully */
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, packet.size); video->next_pts, packet.size);
/* TODO: Write frame to file */
av_packet_unref(&packet); av_packet_unref(&packet);
} }
/* Frame may have been queued for later writing / reordering */
else
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": queued for later",
video->next_pts);
/* Update presentation timestamp for next frame */ /* Update presentation timestamp for next frame */
video->next_pts++; video->next_pts++;
@ -245,6 +275,9 @@ int guacenc_video_free(guacenc_video* video) {
retval = guacenc_video_write_frame(video, NULL); retval = guacenc_video_write_frame(video, NULL);
} while (retval > 0); } while (retval > 0);
/* File is now completely written */
fclose(video->output);
/* 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);

View File

@ -30,6 +30,7 @@
#include <libavcodec/avcodec.h> #include <libavcodec/avcodec.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h>
/** /**
* The framerate at which video should be encoded, in frames per second. * The framerate at which video should be encoded, in frames per second.
@ -43,6 +44,11 @@
*/ */
typedef struct guacenc_video { typedef struct guacenc_video {
/**
* Output file stream.
*/
FILE* output;
/** /**
* The open encoding context from libavcodec, created for the codec * The open encoding context from libavcodec, created for the codec
* specified when this guacenc_video was created. * specified when this guacenc_video was created.