diff --git a/src/guacenc/ffmpeg-compat.c b/src/guacenc/ffmpeg-compat.c index fb280a37..218d450d 100644 --- a/src/guacenc/ffmpeg-compat.c +++ b/src/guacenc/ffmpeg-compat.c @@ -31,9 +31,44 @@ #include #include +/** + * Writes a single packet of video data to the current output file. If an error + * occurs preventing the packet from being written, messages describing the + * error are logged. + * + * @param video + * The video associated with the output file that the given packet should + * be written to. + * + * @param data + * The buffer of data containing the video packet which should be written. + * + * @param size + * The number of bytes within the video packet. + * + * @return + * Zero if the packet was written successfully, non-zero otherwise. + */ +static int guacenc_write_packet(guacenc_video* video, void* data, int size) { + + /* Write data, logging any errors */ + if (fwrite(data, 1, 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", + video->next_pts, size); + + return 0; + +} + int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) { -/* For libavcodec < 54.1.0: avcodec_encode_video2() did not exist */ +/* For libavcodec < 54.1.0: packets were handled as raw malloc'd buffers */ #if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(54,1,0) AVCodecContext* context = video->context; @@ -62,14 +97,7 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) { } /* Write data, logging any errors */ - if (fwrite(data, 1, used, video->output) == 0) { - guacenc_log(GUAC_LOG_ERROR, "Unable to write frame " - "#%" PRId64 ": %s", video->next_pts, strerror(errno)); - free(data); - return -1; - } - - /* Data was written successfully */ + guacenc_write_packet(video, data, used); free(data); return 1; @@ -83,6 +111,8 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) { packet.data = NULL; packet.size = 0; +/* For libavcodec < 57.16.0: input/output was not decoupled */ +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(57,16,0) /* Write frame to video */ int got_data; if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) { @@ -93,23 +123,40 @@ int guacenc_avcodec_encode_video(guacenc_video* video, AVFrame* frame) { /* Write corresponding data to file */ if (got_data) { + guacenc_write_packet(video, packet.data, packet.size); + av_packet_unref(&packet); + } +#else + /* Write frame to video */ + int result = avcodec_send_frame(video->context, frame); - /* 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; - } + /* Stop once encoded has been flushed */ + if (result == AVERROR_EOF) + return 0; - /* Data was written successfully */ - guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes", - video->next_pts, packet.size); + /* Abort on error */ + else if (result < 0) { + guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64, + video->next_pts); + return -1; + } + + /* Flush all available packets */ + int got_data = 0; + while (avcodec_receive_packet(video->context, &packet) == 0) { + + /* Data was received */ + got_data = 1; + + /* Attempt to write data to output file */ + guacenc_write_packet(video, packet.data, packet.size); av_packet_unref(&packet); } +#endif /* Frame may have been queued for later writing / reordering */ - else + if (!got_data) guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": queued for later", video->next_pts);