GUAC-236: Do not store buffer - encode directly. Flush frames at end.
This commit is contained in:
parent
1a3e1465fa
commit
1ce39306cb
@ -94,7 +94,7 @@ guacenc_video* guacenc_video_alloc(const char* path, const char* codec_name,
|
||||
|
||||
/* Init properties of video */
|
||||
video->context = context;
|
||||
video->frame = frame;
|
||||
video->next_frame = frame;
|
||||
video->width = width;
|
||||
video->height = height;
|
||||
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 */
|
||||
video->last_timestamp = 0;
|
||||
video->next_pts = 0;
|
||||
video->next_frame = NULL;
|
||||
|
||||
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
|
||||
* 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) {
|
||||
|
||||
/* Ignore empty frames */
|
||||
guacenc_buffer* buffer = video->next_frame;
|
||||
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;
|
||||
/* Write frame to video */
|
||||
return guacenc_video_write_frame(video, video->next_frame) < 0;
|
||||
|
||||
}
|
||||
|
||||
@ -183,7 +221,9 @@ int guacenc_video_advance_timeline(guacenc_video* video,
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -195,9 +235,19 @@ int guacenc_video_free(guacenc_video* video) {
|
||||
/* Write final frame */
|
||||
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 */
|
||||
av_freep(&video->frame->data[0]);
|
||||
av_frame_free(&video->frame);
|
||||
av_freep(&video->next_frame->data[0]);
|
||||
av_frame_free(&video->next_frame);
|
||||
|
||||
/* Clean up encoding context */
|
||||
avcodec_close(video->context);
|
||||
|
@ -49,12 +49,6 @@ typedef struct guacenc_video {
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -71,11 +65,11 @@ typedef struct guacenc_video {
|
||||
int bitrate;
|
||||
|
||||
/**
|
||||
* A pointer to the buffer containing the most recent frame submitted via
|
||||
* guacenc_video_prepare_frame(). This buffer MUST not be freed prior to
|
||||
* the call to guacenc_video_free().
|
||||
* An image data area containing the next frame to be written, encoded as
|
||||
* YCbCr image data in the format required by avcodec_encode_video2(), for
|
||||
* 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
|
||||
@ -95,9 +89,7 @@ typedef struct guacenc_video {
|
||||
* 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
|
||||
* 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
|
||||
* frames written to this guacenc_video may be buffered, and are not guaranteed
|
||||
* to be written until guacenc_video_free() is called.
|
||||
* scaled up or down as necessary to fit the given width and height.
|
||||
*
|
||||
* @param path
|
||||
* 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
|
||||
* (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
|
||||
* The video in which the given buffer should be queued for possible
|
||||
* writing (depending on timing vs. video framerate).
|
||||
*
|
||||
* @param buffer
|
||||
* 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
|
||||
* guacenc_video_free() which ultimately ends the encoding process.
|
||||
* be queued.
|
||||
*/
|
||||
void guacenc_video_prepare_frame(guacenc_video* video, guacenc_buffer* buffer);
|
||||
|
||||
/**
|
||||
* Frees all resources associated with the given video, finalizing the encoding
|
||||
* 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
|
||||
* any buffers provided to guacenc_video_prepare_frame().
|
||||
* at this point.
|
||||
*
|
||||
* @return
|
||||
* Zero if the video was successfully written and freed, non-zero if the
|
||||
|
Loading…
Reference in New Issue
Block a user