Create recorder streams from packet sinks ops
Previously, the packet sink push() implementation just set the codec and notified a wait condition. Then the recorder thread read the codec and created the AVStream. But this was racy: an AVFrame could be pushed before the creation of the AVStream, causing its video_stream_index or audio_stream_index to be initialized to -1. Also, in the future, the AVStream initialization might need data provided by the packet sink open(), so initialize it there (with a mutex).
This commit is contained in:
parent
4bdf632dfa
commit
5052e15f7f
2 changed files with 57 additions and 78 deletions
|
@ -150,70 +150,22 @@ sc_recorder_close_output_file(struct sc_recorder *recorder) {
|
||||||
avformat_free_context(recorder->ctx);
|
avformat_free_context(recorder->ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static void
|
||||||
sc_recorder_wait_video_stream(struct sc_recorder *recorder) {
|
sc_recorder_wait_video_stream(struct sc_recorder *recorder) {
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
while (!recorder->video_codec && !recorder->stopped) {
|
while (!recorder->video_init && !recorder->stopped) {
|
||||||
sc_cond_wait(&recorder->stream_cond, &recorder->mutex);
|
sc_cond_wait(&recorder->stream_cond, &recorder->mutex);
|
||||||
}
|
}
|
||||||
const AVCodec *codec = recorder->video_codec;
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
if (codec) {
|
|
||||||
AVStream *stream = avformat_new_stream(recorder->ctx, codec);
|
|
||||||
if (!stream) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
static void
|
||||||
stream->codecpar->codec_id = codec->id;
|
|
||||||
stream->codecpar->format = AV_PIX_FMT_YUV420P;
|
|
||||||
stream->codecpar->width = recorder->declared_frame_size.width;
|
|
||||||
stream->codecpar->height = recorder->declared_frame_size.height;
|
|
||||||
|
|
||||||
recorder->video_stream_index = stream->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
sc_recorder_wait_audio_stream(struct sc_recorder *recorder) {
|
sc_recorder_wait_audio_stream(struct sc_recorder *recorder) {
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
while (!recorder->audio_codec && !recorder->audio_disabled
|
while (!recorder->audio_init && !recorder->stopped) {
|
||||||
&& !recorder->stopped) {
|
|
||||||
sc_cond_wait(&recorder->stream_cond, &recorder->mutex);
|
sc_cond_wait(&recorder->stream_cond, &recorder->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (recorder->audio_disabled) {
|
|
||||||
// Reset audio flag. From there, the recorder thread may access this
|
|
||||||
// flag without any mutex.
|
|
||||||
recorder->audio = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const AVCodec *codec = recorder->audio_codec;
|
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
if (codec) {
|
|
||||||
AVStream *stream = avformat_new_stream(recorder->ctx, codec);
|
|
||||||
if (!stream) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
|
|
||||||
stream->codecpar->codec_id = codec->id;
|
|
||||||
#ifdef SCRCPY_LAVU_HAS_CHLAYOUT
|
|
||||||
stream->codecpar->ch_layout.nb_channels = 2;
|
|
||||||
#else
|
|
||||||
stream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
|
|
||||||
stream->codecpar->channels = 2;
|
|
||||||
#endif
|
|
||||||
stream->codecpar->sample_rate = 48000;
|
|
||||||
|
|
||||||
recorder->audio_stream_index = stream->index;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
|
@ -480,18 +432,10 @@ sc_recorder_record(struct sc_recorder *recorder) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ok = sc_recorder_wait_video_stream(recorder);
|
sc_recorder_wait_video_stream(recorder);
|
||||||
if (!ok) {
|
|
||||||
sc_recorder_close_output_file(recorder);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (recorder->audio) {
|
if (recorder->audio) {
|
||||||
ok = sc_recorder_wait_audio_stream(recorder);
|
sc_recorder_wait_audio_stream(recorder);
|
||||||
if (!ok) {
|
|
||||||
sc_recorder_close_output_file(recorder);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If recorder->stopped, process any queued packet anyway
|
// If recorder->stopped, process any queued packet anyway
|
||||||
|
@ -538,6 +482,8 @@ static bool
|
||||||
sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink,
|
sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink,
|
||||||
AVCodecContext *ctx) {
|
AVCodecContext *ctx) {
|
||||||
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
||||||
|
// only written from this thread, no need to lock
|
||||||
|
assert(!recorder->video_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
if (recorder->stopped) {
|
if (recorder->stopped) {
|
||||||
|
@ -545,7 +491,21 @@ sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
recorder->video_codec = ctx->codec;
|
AVStream *stream = avformat_new_stream(recorder->ctx, ctx->codec);
|
||||||
|
if (!stream) {
|
||||||
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
|
||||||
|
stream->codecpar->codec_id = ctx->codec->id;
|
||||||
|
stream->codecpar->format = AV_PIX_FMT_YUV420P;
|
||||||
|
stream->codecpar->width = recorder->declared_frame_size.width;
|
||||||
|
stream->codecpar->height = recorder->declared_frame_size.height;
|
||||||
|
|
||||||
|
recorder->video_stream_index = stream->index;
|
||||||
|
|
||||||
|
recorder->video_init = true;
|
||||||
sc_cond_signal(&recorder->stream_cond);
|
sc_cond_signal(&recorder->stream_cond);
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
|
@ -555,6 +515,8 @@ sc_recorder_video_packet_sink_open(struct sc_packet_sink *sink,
|
||||||
static void
|
static void
|
||||||
sc_recorder_video_packet_sink_close(struct sc_packet_sink *sink) {
|
sc_recorder_video_packet_sink_close(struct sc_packet_sink *sink) {
|
||||||
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
||||||
|
// only written from this thread, no need to lock
|
||||||
|
assert(recorder->video_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
// EOS also stops the recorder
|
// EOS also stops the recorder
|
||||||
|
@ -567,6 +529,8 @@ static bool
|
||||||
sc_recorder_video_packet_sink_push(struct sc_packet_sink *sink,
|
sc_recorder_video_packet_sink_push(struct sc_packet_sink *sink,
|
||||||
const AVPacket *packet) {
|
const AVPacket *packet) {
|
||||||
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
struct sc_recorder *recorder = DOWNCAST_VIDEO(sink);
|
||||||
|
// only written from this thread, no need to lock
|
||||||
|
assert(recorder->video_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
|
|
||||||
|
@ -604,10 +568,29 @@ sc_recorder_audio_packet_sink_open(struct sc_packet_sink *sink,
|
||||||
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
||||||
assert(recorder->audio);
|
assert(recorder->audio);
|
||||||
// only written from this thread, no need to lock
|
// only written from this thread, no need to lock
|
||||||
assert(!recorder->audio_disabled);
|
assert(!recorder->audio_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
recorder->audio_codec = ctx->codec;
|
|
||||||
|
AVStream *stream = avformat_new_stream(recorder->ctx, ctx->codec);
|
||||||
|
if (!stream) {
|
||||||
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
|
||||||
|
stream->codecpar->codec_id = ctx->codec->id;
|
||||||
|
#ifdef SCRCPY_LAVU_HAS_CHLAYOUT
|
||||||
|
stream->codecpar->ch_layout.nb_channels = 2;
|
||||||
|
#else
|
||||||
|
stream->codecpar->channel_layout = AV_CH_LAYOUT_STEREO;
|
||||||
|
stream->codecpar->channels = 2;
|
||||||
|
#endif
|
||||||
|
stream->codecpar->sample_rate = 48000;
|
||||||
|
|
||||||
|
recorder->audio_stream_index = stream->index;
|
||||||
|
|
||||||
|
recorder->audio_init = true;
|
||||||
sc_cond_signal(&recorder->stream_cond);
|
sc_cond_signal(&recorder->stream_cond);
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
|
|
||||||
|
@ -619,7 +602,7 @@ sc_recorder_audio_packet_sink_close(struct sc_packet_sink *sink) {
|
||||||
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
||||||
assert(recorder->audio);
|
assert(recorder->audio);
|
||||||
// only written from this thread, no need to lock
|
// only written from this thread, no need to lock
|
||||||
assert(!recorder->audio_disabled);
|
assert(recorder->audio_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
// EOS also stops the recorder
|
// EOS also stops the recorder
|
||||||
|
@ -634,7 +617,7 @@ sc_recorder_audio_packet_sink_push(struct sc_packet_sink *sink,
|
||||||
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
||||||
assert(recorder->audio);
|
assert(recorder->audio);
|
||||||
// only written from this thread, no need to lock
|
// only written from this thread, no need to lock
|
||||||
assert(!recorder->audio_disabled);
|
assert(recorder->audio_init);
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
|
|
||||||
|
@ -671,13 +654,13 @@ sc_recorder_audio_packet_sink_disable(struct sc_packet_sink *sink) {
|
||||||
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
struct sc_recorder *recorder = DOWNCAST_AUDIO(sink);
|
||||||
assert(recorder->audio);
|
assert(recorder->audio);
|
||||||
// only written from this thread, no need to lock
|
// only written from this thread, no need to lock
|
||||||
assert(!recorder->audio_disabled);
|
assert(!recorder->audio_init);
|
||||||
assert(!recorder->audio_codec);
|
|
||||||
|
|
||||||
LOGW("Audio stream recording disabled");
|
LOGW("Audio stream recording disabled");
|
||||||
|
|
||||||
sc_mutex_lock(&recorder->mutex);
|
sc_mutex_lock(&recorder->mutex);
|
||||||
recorder->audio_disabled = true;
|
recorder->audio = false;
|
||||||
|
recorder->audio_init = true;
|
||||||
sc_cond_signal(&recorder->stream_cond);
|
sc_cond_signal(&recorder->stream_cond);
|
||||||
sc_mutex_unlock(&recorder->mutex);
|
sc_mutex_unlock(&recorder->mutex);
|
||||||
}
|
}
|
||||||
|
@ -714,9 +697,8 @@ sc_recorder_init(struct sc_recorder *recorder, const char *filename,
|
||||||
sc_vecdeque_init(&recorder->audio_queue);
|
sc_vecdeque_init(&recorder->audio_queue);
|
||||||
recorder->stopped = false;
|
recorder->stopped = false;
|
||||||
|
|
||||||
recorder->video_codec = NULL;
|
recorder->video_init = false;
|
||||||
recorder->audio_codec = NULL;
|
recorder->audio_init = false;
|
||||||
recorder->audio_disabled = false;
|
|
||||||
|
|
||||||
recorder->video_stream_index = -1;
|
recorder->video_stream_index = -1;
|
||||||
recorder->audio_stream_index = -1;
|
recorder->audio_stream_index = -1;
|
||||||
|
|
|
@ -43,11 +43,8 @@ struct sc_recorder {
|
||||||
|
|
||||||
// wake up the recorder thread once the video or audio codec is known
|
// wake up the recorder thread once the video or audio codec is known
|
||||||
sc_cond stream_cond;
|
sc_cond stream_cond;
|
||||||
const AVCodec *video_codec;
|
bool video_init;
|
||||||
const AVCodec *audio_codec;
|
bool audio_init;
|
||||||
// Instead of providing an audio_codec, the demuxer may notify that the
|
|
||||||
// stream is disabled if the device could not capture audio
|
|
||||||
bool audio_disabled;
|
|
||||||
|
|
||||||
int video_stream_index;
|
int video_stream_index;
|
||||||
int audio_stream_index;
|
int audio_stream_index;
|
||||||
|
|
Loading…
Reference in a new issue