GUAC-236: Do not store buffer - encode directly. Flush frames at end.

This commit is contained in:
Michael Jumper 2016-03-11 15:23:03 -08:00
parent 1a3e1465fa
commit 1ce39306cb
2 changed files with 82 additions and 45 deletions

View File

@ -94,7 +94,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
/* Init properties of video */ /* Init properties of video */
video->context = context; video->context = context;
video->frame = frame; video->next_frame = frame;
video->width = width; video->width = width;
video->height = height; video->height = height;
video->bitrate = bitrate; video->bitrate = bitrate;
@ -102,7 +102,6 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
/* No frames have been written or prepared yet */ /* No frames have been written or prepared yet */
video->last_timestamp = 0; video->last_timestamp = 0;
video->next_pts = 0; video->next_pts = 0;
video->next_frame = NULL;
return video; return video;
@ -119,6 +118,63 @@ fail_context:
} }
/**
* Flushes the specied frame as a new frame of video, updating the internal
* video timestamp by one frame's worth of time. The pts member of the given
* frame structure will be updated with the current presentation timestamp of
* the video. If pending frames of the video are being flushed, the given frame
* may be NULL (as required by avcodec_encode_video2()).
*
* @param video
* The video to write the given frame to.
*
* @param frame
* The frame to write to the video, or NULL if previously-written frames
* are being flushed.
*
* @return
* A positive value if the frame was successfully written, zero if the
* frame has been saved for later writing / reordering, negative if an
* error occurs.
*/
static int guacenc_video_write_frame(guacenc_video* video, AVFrame* frame) {
/* Init video packet */
AVPacket packet;
av_init_packet(&packet);
/* Request that encoder allocate data for packet */
packet.data = NULL;
packet.size = 0;
/* Set timestamp of frame, if frame given */
if (frame != NULL)
frame->pts = video->next_pts;
/* Write frame to video */
int got_data;
if (avcodec_encode_video2(video->context, &packet, frame, &got_data) < 0) {
guacenc_log(GUAC_LOG_WARNING, "Error encoding frame #%" PRId64,
video->next_pts);
return -1;
}
/* Write corresponding data to file */
if (got_data) {
guacenc_log(GUAC_LOG_DEBUG, "Frame #%08" PRId64 ": wrote %i bytes",
video->next_pts, packet.size);
/* TODO: Write frame to file */
av_packet_unref(&packet);
}
/* Update presentation timestamp for next frame */
video->next_pts++;
/* Write was successful */
return got_data;
}
/** /**
* Flushes the frame previously specified by guacenc_video_prepare_frame() as a * Flushes the frame previously specified by guacenc_video_prepare_frame() as a
* new frame of video, updating the internal video timestamp by one frame's * new frame of video, updating the internal video timestamp by one frame's
@ -132,26 +188,8 @@ fail_context:
*/ */
static int guacenc_video_flush_frame(guacenc_video* video) { static int guacenc_video_flush_frame(guacenc_video* video) {
/* Ignore empty frames */ /* Write frame to video */
guacenc_buffer* buffer = video->next_frame; return guacenc_video_write_frame(video, video->next_frame) < 0;
if (buffer == NULL)
return 0;
/* Init video packet */
AVPacket packet;
av_init_packet(&packet);
/* Request that encoder allocate data for packet */
packet.data = NULL;
packet.size = 0;
/* TODO: Write frame to video */
guacenc_log(GUAC_LOG_DEBUG, "Encoding frame #%08" PRId64, video->next_pts);
/* Update presentation timestamp for next frame */
video->next_pts++;
return 0;
} }
@ -183,7 +221,9 @@ int guacenc_video_advance_timeline(guacenc_video* video,
} }
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer) { void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer) {
video->next_frame = buffer;
/* TODO: Convert frame buffer to video->next_frame */
} }
int guacenc_video_free(guacenc_video* video) { int guacenc_video_free(guacenc_video* video) {
@ -195,9 +235,19 @@ 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 */
int retval;
do {
retval = guacenc_video_write_frame(video, NULL);
} while (retval > 0);
/* Free frame encoding data */ /* Free frame encoding data */
av_freep(&video->frame->data[0]); av_freep(&video->next_frame->data[0]);
av_frame_free(&video->frame); av_frame_free(&video->next_frame);
/* Clean up encoding context */ /* Clean up encoding context */
avcodec_close(video->context); avcodec_close(video->context);

View File

@ -49,12 +49,6 @@ typedef struct guacenc_video {
*/ */
AVCodecContext* context; AVCodecContext* context;
/**
* An image data area, containing YCbCr image data in the format required
* by avcodec_encode_video2(), for use and re-use as frames are rendered.
*/
AVFrame* frame;
/** /**
* The width of the video, in pixels. * The width of the video, in pixels.
*/ */
@ -71,11 +65,11 @@ typedef struct guacenc_video {
int bitrate; int bitrate;
/** /**
* A pointer to the buffer containing the most recent frame submitted via * An image data area containing the next frame to be written, encoded as
* guacenc_video_prepare_frame(). This buffer MUST not be freed prior to * YCbCr image data in the format required by avcodec_encode_video2(), for
* the call to guacenc_video_free(). * use and re-use as frames are rendered.
*/ */
guacenc_buffer* next_frame; AVFrame* next_frame;
/** /**
* The presentation timestamp that should be used for the next frame. This * The presentation timestamp that should be used for the next frame. This
@ -95,9 +89,7 @@ typedef struct guacenc_video {
* Allocates a new guacenc_video which encodes video according to the given * Allocates a new guacenc_video which encodes video according to the given
* specifications, saving the output in the given file. The output file will be * specifications, saving the output in the given file. The output file will be
* created if necessary and truncated if it already exists. Frames will be * created if necessary and truncated if it already exists. Frames will be
* scaled up or down as necessary to fit the given width and height. Note that * scaled up or down as necessary to fit the given width and height.
* frames written to this guacenc_video may be buffered, and are not guaranteed
* to be written until guacenc_video_free() is called.
* *
* @param path * @param path
* The full path to the file in which encoded video should be written. * The full path to the file in which encoded video should be written.
@ -157,25 +149,20 @@ int guacenc_video_advance_timeline(guacenc_video* video,
* timeline or through reaching the end of the encoding process * timeline or through reaching the end of the encoding process
* (guacenc_video_free()). * (guacenc_video_free()).
* *
* Any given buffer MUST NOT be freed prior to the call to guacenc_video_free()
* which ultimately ends the encoding process.
*
* @param video * @param video
* The video in which the given buffer should be queued for possible * The video in which the given buffer should be queued for possible
* writing (depending on timing vs. video framerate). * writing (depending on timing vs. video framerate).
* *
* @param buffer * @param buffer
* The guacenc_buffer representing the image data of the frame that should * The guacenc_buffer representing the image data of the frame that should
* be queued. This buffer MUST NOT be freed prior to the call to * be queued.
* guacenc_video_free() which ultimately ends the encoding process.
*/ */
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer); void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer);
/** /**
* Frees all resources associated with the given video, finalizing the encoding * Frees all resources associated with the given video, finalizing the encoding
* process. Any buffered frames which have not yet been written will be written * process. Any buffered frames which have not yet been written will be written
* at this point. Once this function is invoked, it is safe to resume freeing * at this point.
* any buffers provided to guacenc_video_prepare_frame().
* *
* @return * @return
* Zero if the video was successfully written and freed, non-zero if the * Zero if the video was successfully written and freed, non-zero if the