Use VecDeque in recorder
The packets queued for recording were wrapped in a dynamically allocated structure with a "next" field. To avoid this additional layer of allocation and indirection, use a VecDeque.
This commit is contained in:
parent
33df484912
commit
efc15744da
2 changed files with 84 additions and 94 deletions
|
@ -33,41 +33,27 @@ find_muxer(const char *name) {
|
||||||
return oformat;
|
return oformat;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct sc_record_packet *
|
static AVPacket *
|
||||||
sc_record_packet_new(const AVPacket *packet) {
|
sc_recorder_packet_ref(const AVPacket *packet) {
|
||||||
struct sc_record_packet *rec = malloc(sizeof(*rec));
|
AVPacket *p = av_packet_alloc();
|
||||||
if (!rec) {
|
if (!p) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->packet = av_packet_alloc();
|
if (av_packet_ref(p, packet)) {
|
||||||
if (!rec->packet) {
|
av_packet_free(&p);
|
||||||
LOG_OOM();
|
|
||||||
free(rec);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (av_packet_ref(rec->packet, packet)) {
|
return p;
|
||||||
av_packet_free(&rec->packet);
|
|
||||||
free(rec);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
return rec;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
sc_record_packet_delete(struct sc_record_packet *rec) {
|
|
||||||
av_packet_free(&rec->packet);
|
|
||||||
free(rec);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sc_recorder_queue_clear(struct sc_recorder_queue *queue) {
|
sc_recorder_queue_clear(struct sc_recorder_queue *queue) {
|
||||||
while (!sc_queue_is_empty(queue)) {
|
while (!sc_vecdeque_is_empty(queue)) {
|
||||||
struct sc_record_packet *rec;
|
AVPacket *p = sc_vecdeque_pop(queue);
|
||||||
sc_queue_take(queue, next, &rec);
|
av_packet_free(&p);
|
||||||
sc_record_packet_delete(rec);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,12 +213,12 @@ sc_recorder_wait_audio_stream(struct sc_recorder *recorder) {
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
sc_recorder_has_empty_queues(struct sc_recorder *recorder) {
|
sc_recorder_has_empty_queues(struct sc_recorder *recorder) {
|
||||||
if (sc_queue_is_empty(&recorder->video_queue)) {
|
if (sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
// The video queue is empty
|
// The video queue is empty
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorder->audio && sc_queue_is_empty(&recorder->audio_queue)) {
|
if (recorder->audio && sc_vecdeque_is_empty(&recorder->audio_queue)) {
|
||||||
// The audio queue is empty (when audio is enabled)
|
// The audio queue is empty (when audio is enabled)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -249,28 +235,27 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
||||||
sc_cond_wait(&recorder->queue_cond, &recorder->mutex);
|
sc_cond_wait(&recorder->queue_cond, &recorder->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sc_queue_is_empty(&recorder->video_queue)) {
|
if (sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
assert(recorder->stopped);
|
assert(recorder->stopped);
|
||||||
// Don't process anything if there are not at least video packets (when
|
// If the recorder is stopped, don't process anything if there are not
|
||||||
// the recorder is stopped)
|
// at least video packets
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_record_packet *video_pkt;
|
AVPacket *video_pkt = sc_vecdeque_pop(&recorder->video_queue);
|
||||||
sc_queue_take(&recorder->video_queue, next, &video_pkt);
|
|
||||||
|
|
||||||
struct sc_record_packet *audio_pkt = NULL;
|
AVPacket *audio_pkt = NULL;
|
||||||
if (!sc_queue_is_empty(&recorder->audio_queue)) {
|
if (!sc_vecdeque_is_empty(&recorder->audio_queue)) {
|
||||||
assert(recorder->audio);
|
assert(recorder->audio);
|
||||||
sc_queue_take(&recorder->audio_queue, next, &audio_pkt);
|
audio_pkt = sc_vecdeque_pop(&recorder->audio_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
int ret = false;
|
int ret = false;
|
||||||
|
|
||||||
if (video_pkt->packet->pts != AV_NOPTS_VALUE) {
|
if (video_pkt->pts != AV_NOPTS_VALUE) {
|
||||||
LOGE("The first video packet is not a config packet");
|
LOGE("The first video packet is not a config packet");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -278,13 +263,13 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
||||||
assert(recorder->video_stream_index >= 0);
|
assert(recorder->video_stream_index >= 0);
|
||||||
AVStream *video_stream =
|
AVStream *video_stream =
|
||||||
recorder->ctx->streams[recorder->video_stream_index];
|
recorder->ctx->streams[recorder->video_stream_index];
|
||||||
bool ok = sc_recorder_set_extradata(video_stream, video_pkt->packet);
|
bool ok = sc_recorder_set_extradata(video_stream, video_pkt);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_pkt) {
|
if (audio_pkt) {
|
||||||
if (audio_pkt->packet->pts != AV_NOPTS_VALUE) {
|
if (audio_pkt->pts != AV_NOPTS_VALUE) {
|
||||||
LOGE("The first audio packet is not a config packet");
|
LOGE("The first audio packet is not a config packet");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -292,7 +277,7 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
||||||
assert(recorder->audio_stream_index >= 0);
|
assert(recorder->audio_stream_index >= 0);
|
||||||
AVStream *audio_stream =
|
AVStream *audio_stream =
|
||||||
recorder->ctx->streams[recorder->audio_stream_index];
|
recorder->ctx->streams[recorder->audio_stream_index];
|
||||||
ok = sc_recorder_set_extradata(audio_stream, audio_pkt->packet);
|
ok = sc_recorder_set_extradata(audio_stream, audio_pkt);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
@ -307,9 +292,9 @@ sc_recorder_process_header(struct sc_recorder *recorder) {
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
sc_record_packet_delete(video_pkt);
|
av_packet_free(&video_pkt);
|
||||||
if (audio_pkt) {
|
if (audio_pkt) {
|
||||||
sc_record_packet_delete(audio_pkt);
|
av_packet_free(&audio_pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -324,12 +309,12 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_record_packet *video_pkt = NULL;
|
AVPacket *video_pkt = NULL;
|
||||||
struct sc_record_packet *audio_pkt = NULL;
|
AVPacket *audio_pkt = NULL;
|
||||||
|
|
||||||
// We can write a video packet only once we received the next one so that
|
// We can write a video packet only once we received the next one so that
|
||||||
// we can set its duration (next_pts - current_pts)
|
// we can set its duration (next_pts - current_pts)
|
||||||
struct sc_record_packet *video_pkt_previous = NULL;
|
AVPacket *video_pkt_previous = NULL;
|
||||||
|
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
|
@ -337,12 +322,12 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
|
|
||||||
while (!recorder->stopped) {
|
while (!recorder->stopped) {
|
||||||
if (!video_pkt && !sc_queue_is_empty(&recorder->video_queue)) {
|
if (!video_pkt && !sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
// A new packet may be assigned to video_pkt and be processed
|
// A new packet may be assigned to video_pkt and be processed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (recorder->audio && !audio_pkt
|
if (recorder->audio && !audio_pkt
|
||||||
&& !sc_queue_is_empty(&recorder->audio_queue)) {
|
&& !sc_vecdeque_is_empty(&recorder->audio_queue)) {
|
||||||
// A new packet may be assigned to audio_pkt and be processed
|
// A new packet may be assigned to audio_pkt and be processed
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -354,20 +339,20 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
|
|
||||||
// If there is no audio, then the audio_queue will remain empty forever
|
// If there is no audio, then the audio_queue will remain empty forever
|
||||||
// and audio_pkt will always be NULL.
|
// and audio_pkt will always be NULL.
|
||||||
assert(recorder->audio
|
assert(recorder->audio || (!audio_pkt
|
||||||
|| (!audio_pkt && sc_queue_is_empty(&recorder->audio_queue)));
|
&& sc_vecdeque_is_empty(&recorder->audio_queue)));
|
||||||
|
|
||||||
if (!video_pkt && !sc_queue_is_empty(&recorder->video_queue)) {
|
if (!video_pkt && !sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
sc_queue_take(&recorder->video_queue, next, &video_pkt);
|
video_pkt = sc_vecdeque_pop(&recorder->video_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!audio_pkt && !sc_queue_is_empty(&recorder->audio_queue)) {
|
if (!audio_pkt && !sc_vecdeque_is_empty(&recorder->audio_queue)) {
|
||||||
sc_queue_take(&recorder->audio_queue, next, &audio_pkt);
|
audio_pkt = sc_vecdeque_pop(&recorder->audio_queue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorder->stopped && !video_pkt && !audio_pkt) {
|
if (recorder->stopped && !video_pkt && !audio_pkt) {
|
||||||
assert(sc_queue_is_empty(&recorder->video_queue));
|
assert(sc_vecdeque_is_empty(&recorder->video_queue));
|
||||||
assert(sc_queue_is_empty(&recorder->audio_queue));
|
assert(sc_vecdeque_is_empty(&recorder->audio_queue));
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -379,28 +364,27 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
// Ignore further config packets (e.g. on device orientation
|
// Ignore further config packets (e.g. on device orientation
|
||||||
// change). The next non-config packet will have the config packet
|
// change). The next non-config packet will have the config packet
|
||||||
// data prepended.
|
// data prepended.
|
||||||
if (video_pkt && video_pkt->packet->pts == AV_NOPTS_VALUE) {
|
if (video_pkt && video_pkt->pts == AV_NOPTS_VALUE) {
|
||||||
sc_record_packet_delete(video_pkt);
|
av_packet_free(&video_pkt);
|
||||||
video_pkt = NULL;
|
video_pkt = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_pkt && audio_pkt->packet->pts == AV_NOPTS_VALUE) {
|
if (audio_pkt && audio_pkt->pts == AV_NOPTS_VALUE) {
|
||||||
sc_record_packet_delete(audio_pkt);
|
av_packet_free(&audio_pkt);
|
||||||
audio_pkt= NULL;
|
audio_pkt = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pts_origin == AV_NOPTS_VALUE) {
|
if (pts_origin == AV_NOPTS_VALUE) {
|
||||||
if (!recorder->audio) {
|
if (!recorder->audio) {
|
||||||
assert(video_pkt);
|
assert(video_pkt);
|
||||||
pts_origin = video_pkt->packet->pts;
|
pts_origin = video_pkt->pts;
|
||||||
} else if (video_pkt && audio_pkt) {
|
} else if (video_pkt && audio_pkt) {
|
||||||
pts_origin =
|
pts_origin = MIN(video_pkt->pts, audio_pkt->pts);
|
||||||
MIN(video_pkt->packet->pts, audio_pkt->packet->pts);
|
|
||||||
} else if (recorder->stopped) {
|
} else if (recorder->stopped) {
|
||||||
if (video_pkt) {
|
if (video_pkt) {
|
||||||
// The recorder is stopped without audio, record the video
|
// The recorder is stopped without audio, record the video
|
||||||
// packets
|
// packets
|
||||||
pts_origin = video_pkt->packet->pts;
|
pts_origin = video_pkt->pts;
|
||||||
} else {
|
} else {
|
||||||
// Fail if there is no video
|
// Fail if there is no video
|
||||||
error = true;
|
error = true;
|
||||||
|
@ -415,17 +399,16 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
assert(pts_origin != AV_NOPTS_VALUE);
|
assert(pts_origin != AV_NOPTS_VALUE);
|
||||||
|
|
||||||
if (video_pkt) {
|
if (video_pkt) {
|
||||||
video_pkt->packet->pts -= pts_origin;
|
video_pkt->pts -= pts_origin;
|
||||||
video_pkt->packet->dts = video_pkt->packet->pts;
|
video_pkt->dts = video_pkt->pts;
|
||||||
|
|
||||||
if (video_pkt_previous) {
|
if (video_pkt_previous) {
|
||||||
// we now know the duration of the previous packet
|
// we now know the duration of the previous packet
|
||||||
video_pkt_previous->packet->duration =
|
video_pkt_previous->duration = video_pkt->pts
|
||||||
video_pkt->packet->pts - video_pkt_previous->packet->pts;
|
- video_pkt_previous->pts;
|
||||||
|
|
||||||
bool ok = sc_recorder_write_video(recorder,
|
bool ok = sc_recorder_write_video(recorder, video_pkt_previous);
|
||||||
video_pkt_previous->packet);
|
av_packet_free(&video_pkt_previous);
|
||||||
sc_record_packet_delete(video_pkt_previous);
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not record video packet");
|
LOGE("Could not record video packet");
|
||||||
error = true;
|
error = true;
|
||||||
|
@ -438,34 +421,34 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audio_pkt) {
|
if (audio_pkt) {
|
||||||
audio_pkt->packet->pts -= pts_origin;
|
audio_pkt->pts -= pts_origin;
|
||||||
audio_pkt->packet->dts = audio_pkt->packet->pts;
|
audio_pkt->dts = audio_pkt->pts;
|
||||||
|
|
||||||
bool ok = sc_recorder_write_audio(recorder, audio_pkt->packet);
|
bool ok = sc_recorder_write_audio(recorder, audio_pkt);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
LOGE("Could not record audio packet");
|
LOGE("Could not record audio packet");
|
||||||
error = true;
|
error = true;
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
sc_record_packet_delete(audio_pkt);
|
av_packet_free(&audio_pkt);
|
||||||
audio_pkt = NULL;
|
audio_pkt = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the last video packet
|
// Write the last video packet
|
||||||
struct sc_record_packet *last = video_pkt_previous;
|
AVPacket *last = video_pkt_previous;
|
||||||
if (last) {
|
if (last) {
|
||||||
// assign an arbitrary duration to the last packet
|
// assign an arbitrary duration to the last packet
|
||||||
last->packet->duration = 100000;
|
last->duration = 100000;
|
||||||
bool ok = sc_recorder_write_video(recorder, last->packet);
|
bool ok = sc_recorder_write_video(recorder, last);
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
// failing to write the last frame is not very serious, no
|
// failing to write the last frame is not very serious, no
|
||||||
// future frame may depend on it, so the resulting file
|
// future frame may depend on it, so the resulting file
|
||||||
// will still be valid
|
// will still be valid
|
||||||
LOGW("Could not record last packet");
|
LOGW("Could not record last packet");
|
||||||
}
|
}
|
||||||
sc_record_packet_delete(last);
|
av_packet_free(&last);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ret = av_write_trailer(recorder->ctx);
|
int ret = av_write_trailer(recorder->ctx);
|
||||||
|
@ -476,10 +459,10 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
|
|
||||||
end:
|
end:
|
||||||
if (video_pkt) {
|
if (video_pkt) {
|
||||||
sc_record_packet_delete(video_pkt);
|
av_packet_free(&video_pkt);
|
||||||
}
|
}
|
||||||
if (audio_pkt) {
|
if (audio_pkt) {
|
||||||
sc_record_packet_delete(audio_pkt);
|
av_packet_free(&audio_pkt);
|
||||||
}
|
}
|
||||||
|
|
||||||
return !error;
|
return !error;
|
||||||
|
@ -585,16 +568,22 @@ sc_recorder_video_packet_sink_push(struct sc_packet_sink *sink,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_record_packet *rec = sc_record_packet_new(packet);
|
AVPacket *rec = sc_recorder_packet_ref(packet);
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->packet->stream_index = recorder->video_stream_index;
|
rec->stream_index = recorder->video_stream_index;
|
||||||
|
|
||||||
|
bool ok = sc_vecdeque_push(&recorder->video_queue, rec);
|
||||||
|
if (!ok) {
|
||||||
|
LOG_OOM();
|
||||||
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sc_queue_push(&recorder->video_queue, next, rec);
|
|
||||||
sc_cond_signal(&recorder->queue_cond);
|
sc_cond_signal(&recorder->queue_cond);
|
||||||
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
@ -648,16 +637,22 @@ sc_recorder_audio_packet_sink_push(struct sc_packet_sink *sink,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sc_record_packet *rec = sc_record_packet_new(packet);
|
AVPacket *rec = sc_recorder_packet_ref(packet);
|
||||||
if (!rec) {
|
if (!rec) {
|
||||||
LOG_OOM();
|
LOG_OOM();
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
rec->packet->stream_index = recorder->audio_stream_index;
|
rec->stream_index = recorder->audio_stream_index;
|
||||||
|
|
||||||
|
bool ok = sc_vecdeque_push(&recorder->audio_queue, rec);
|
||||||
|
if (!ok) {
|
||||||
|
LOG_OOM();
|
||||||
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
sc_queue_push(&recorder->audio_queue, next, rec);
|
|
||||||
sc_cond_signal(&recorder->queue_cond);
|
sc_cond_signal(&recorder->queue_cond);
|
||||||
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
@ -708,8 +703,8 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
|
||||||
|
|
||||||
recorder->audio = audio;
|
recorder->audio = audio;
|
||||||
|
|
||||||
sc_queue_init(&recorder->video_queue);
|
sc_vecdeque_init(&recorder->video_queue);
|
||||||
sc_queue_init(&recorder->audio_queue);
|
sc_vecdeque_init(&recorder->audio_queue);
|
||||||
recorder->stopped = false;
|
recorder->stopped = false;
|
||||||
|
|
||||||
recorder->video_codec = NULL;
|
recorder->video_codec = NULL;
|
||||||
|
|
|
@ -9,15 +9,10 @@
|
||||||
#include "coords.h"
|
#include "coords.h"
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "trait/packet_sink.h"
|
#include "trait/packet_sink.h"
|
||||||
#include "util/queue.h"
|
|
||||||
#include "util/thread.h"
|
#include "util/thread.h"
|
||||||
|
#include "util/vecdeque.h"
|
||||||
|
|
||||||
struct sc_record_packet {
|
struct sc_recorder_queue SC_VECDEQUE(AVPacket *);
|
||||||
AVPacket *packet;
|
|
||||||
struct sc_record_packet *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sc_recorder_queue SC_QUEUE(struct sc_record_packet);
|
|
||||||
|
|
||||||
struct sc_recorder {
|
struct sc_recorder {
|
||||||
struct sc_packet_sink video_packet_sink;
|
struct sc_packet_sink video_packet_sink;
|
||||||
|
|
Loading…
Reference in a new issue