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 */
|
/* 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);
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user