mirror of
https://github.com/Genymobile/scrcpy.git
synced 2025-08-02 22:29:25 +00:00
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
abeaa9ae07
commit
fc4ece9d6a
2 changed files with 83 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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,12 +218,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;
|
||||||
}
|
}
|
||||||
|
@ -254,27 +240,26 @@ 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 (recorder->stopped && sc_queue_is_empty(&recorder->video_queue)) {
|
if (recorder->stopped && sc_vecdeque_is_empty(&recorder->video_queue)) {
|
||||||
// If the recorder is stopped, don't process anything if there are not
|
// If the recorder is stopped, don't process anything if there are not
|
||||||
// at least video packets
|
// 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;
|
||||||
}
|
}
|
||||||
|
@ -282,13 +267,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;
|
||||||
}
|
}
|
||||||
|
@ -296,7 +281,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;
|
||||||
}
|
}
|
||||||
|
@ -311,9 +296,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;
|
||||||
|
@ -328,12 +313,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;
|
||||||
|
|
||||||
|
@ -341,12 +326,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;
|
||||||
}
|
}
|
||||||
|
@ -358,20 +343,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;
|
||||||
}
|
}
|
||||||
|
@ -383,28 +368,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;
|
||||||
|
@ -413,8 +397,7 @@ sc_recorder_process_packets(struct sc_recorder *recorder) {
|
||||||
// If the recorder is stopped while one of the streams has no
|
// If the recorder is stopped while one of the streams has no
|
||||||
// packets, then we must avoid a live-loop and correctly record
|
// packets, then we must avoid a live-loop and correctly record
|
||||||
// the stream having packets.
|
// the stream having packets.
|
||||||
pts_origin = video_pkt ? video_pkt->packet->pts
|
pts_origin = video_pkt ? video_pkt->pts : audio_pkt->pts;
|
||||||
: audio_pkt->packet->pts;
|
|
||||||
} else {
|
} else {
|
||||||
// We need both video and audio packets to initialize pts_origin
|
// We need both video and audio packets to initialize pts_origin
|
||||||
continue;
|
continue;
|
||||||
|
@ -424,17 +407,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;
|
||||||
|
@ -447,34 +429,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);
|
||||||
|
@ -485,10 +467,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;
|
||||||
|
@ -594,16 +576,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 = 0;
|
rec->stream_index = 0;
|
||||||
|
|
||||||
|
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);
|
||||||
|
@ -657,16 +645,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 = 1;
|
rec->stream_index = 1;
|
||||||
|
|
||||||
|
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);
|
||||||
|
@ -717,8 +711,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…
Add table
Add a link
Reference in a new issue