rework codec choice between h264 and h265

This commit is contained in:
F. Duncanh
2025-02-17 09:24:47 -05:00
parent 4260d052b6
commit 79ff1411d8
5 changed files with 85 additions and 53 deletions

View File

@@ -90,7 +90,7 @@ struct raop_callbacks_s {
void (*register_client) (void *cls, const char *device_id, const char *pk_str, const char *name);
bool (*check_register) (void *cls, const char *pk_str);
void (*export_dacp) (void *cls, const char *active_remote, const char *dacp_id);
void (*video_set_codec)(void *cls, video_codec_t codec);
int (*video_set_codec)(void *cls, video_codec_t codec);
/* for HLS video player controls */
void (*on_video_play) (void *cls, const char *location, const float start_position);
void (*on_video_scrub) (void *cls, const float position);

View File

@@ -196,7 +196,7 @@ raop_rtp_mirror_thread(void *arg)
unsigned char nal_start_code[4] = { 0x00, 0x00, 0x00, 0x01 };
bool logger_debug = (logger_get_level(raop_rtp_mirror->logger) >= LOGGER_DEBUG);
bool h265_video = false;
video_codec_t codec;
video_codec_t codec = VIDEO_CODEC_UNKNOWN;
const char h264[] = "h264";
const char h265[] = "h265";
bool unsupported_codec = false;
@@ -578,7 +578,6 @@ raop_rtp_mirror_thread(void *arg)
video_stream_suspended = false;
}
codec = VIDEO_CODEC_UNKNOWN;
assert (raop_rtp_mirror->callbacks.video_set_codec);
ntp_timestamp_nal = ntp_timestamp_raw;
@@ -619,9 +618,21 @@ raop_rtp_mirror_thread(void *arg)
if (!memcmp(payload + 4, hvc1, 4)) {
/* hvc1 HECV detected */
codec = VIDEO_CODEC_H265;
h265_video = true;
raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec);
if (codec == VIDEO_CODEC_UNKNOWN) {
codec = VIDEO_CODEC_H265;
h265_video = true;
if (raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec) < 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "failed to set video codec as H265 ");
/* drop connection */
conn_reset = true;
break;
}
} else if (codec != VIDEO_CODEC_H265) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "invalid video codec change to H265: codec was set previously");
/* drop connection */
conn_reset = true;
break;
}
unsigned char vps_start_code[] = { 0xa0, 0x00, 0x01, 0x00 };
unsigned char sps_start_code[] = { 0xa1, 0x00, 0x01, 0x00 };
unsigned char pps_start_code[] = { 0xa2, 0x00, 0x01, 0x00 };
@@ -692,9 +703,21 @@ raop_rtp_mirror_thread(void *arg)
ptr += 4;
memcpy(ptr, pps, pps_size);
} else {
codec = VIDEO_CODEC_H264;
h265_video = false;
raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec);
if (codec == VIDEO_CODEC_UNKNOWN) {
codec = VIDEO_CODEC_H264;
h265_video = false;
if (raop_rtp_mirror->callbacks.video_set_codec(raop_rtp_mirror->callbacks.cls, codec) < 0) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "failed to set video codec as H264 ");
/* drop connection */
conn_reset = true;
break;
}
} else if (codec != VIDEO_CODEC_H264) {
logger_log(raop_rtp_mirror->logger, LOGGER_ERR, "invalid codec change to H264: codec was set previously");
/* drop connection */
conn_reset = true;
break;
}
short sps_size = byteutils_get_short_be(payload,6);
unsigned char *sequence_parameter_set = payload + 8;
short pps_size = byteutils_get_short_be(payload, sps_size + 9);

View File

@@ -54,7 +54,7 @@ struct video_renderer_s {
GstElement *appsrc, *pipeline;
GstBus *bus;
const char *codec;
bool autovideo, state_pending;
bool autovideo;
int id;
gboolean terminate;
gint64 duration;
@@ -158,7 +158,7 @@ GstElement *make_video_sink(const char *videosink, const char *videosink_options
return NULL;
}
/* process the video_sink_optons */
/* process the video_sink_options */
size_t len = strlen(videosink_options);
if (!len) {
return video_sink;
@@ -398,23 +398,23 @@ void video_renderer_resume() {
}
void video_renderer_start() {
GstState state;
const gchar *state_name;
if (hls_video) {
GstState state;
renderer->bus = gst_element_get_bus(renderer->pipeline);
gst_element_set_state (renderer->pipeline, GST_STATE_PAUSED);
gst_element_get_state(renderer->pipeline, &state, NULL, 1000 * GST_MSECOND);
const gchar *state_name;
state_name= gst_element_state_get_name(state);
logger_log(logger, LOGGER_DEBUG, "video renderer_start: state %s", state_name);
return;
}
/* when not hls, start both h264 and h265 pipelines; will shut down the "wrong" one when we know the codec */
for (int i = 0; i < n_renderers; i++) {
gst_element_set_state (renderer_type[i]->pipeline, GST_STATE_PLAYING);
if (renderer_type[i]->appsrc) {
gst_video_pipeline_base_time = gst_element_get_base_time(renderer_type[i]->appsrc);
}
renderer_type[i]->bus = gst_element_get_bus(renderer_type[i]->pipeline);
gst_element_set_state (renderer_type[i]->pipeline, GST_STATE_PAUSED);
gst_element_get_state(renderer_type[i]->pipeline, &state, NULL, 1000 * GST_MSECOND);
state_name= gst_element_state_get_name(state);
logger_log(logger, LOGGER_DEBUG, "video renderer_start: renderer %d state %s", i, state_name);
}
renderer = NULL;
first_packet = true;
@@ -505,15 +505,21 @@ void video_renderer_stop() {
}
}
static void video_renderer_destroy_h26x(video_renderer_t *renderer) {
static void video_renderer_destroy_instance(video_renderer_t *renderer) {
if (renderer) {
logger_log(logger, LOGGER_DEBUG,"destroying renderer instance %p", renderer);
GstState state;
GstStateChangeReturn ret;
gst_element_get_state(renderer->pipeline, &state, NULL, 100 * GST_MSECOND);
logger_log(logger, LOGGER_DEBUG,"pipeline state is %s", gst_element_state_get_name(state));
if (state != GST_STATE_NULL) {
if (!hls_video) {
gst_app_src_end_of_stream (GST_APP_SRC(renderer->appsrc));
}
gst_element_set_state (renderer->pipeline, GST_STATE_NULL);
ret = gst_element_set_state (renderer->pipeline, GST_STATE_NULL);
logger_log(logger, LOGGER_DEBUG,"pipeline state change to NULL: %s",
gst_element_state_change_return_get_name(ret));
gst_element_get_state(renderer->pipeline, NULL, NULL, 1000 * GST_MSECOND);
}
gst_object_unref(renderer->bus);
if (renderer->appsrc) {
@@ -534,9 +540,9 @@ static void video_renderer_destroy_h26x(video_renderer_t *renderer) {
void video_renderer_destroy() {
for (int i = 0; i < n_renderers; i++) {
if (renderer_type[i]) {
video_renderer_destroy_h26x(renderer_type[i]);
video_renderer_destroy_instance(renderer_type[i]);
}
}
}
}
gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void *loop) {
@@ -642,15 +648,6 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void
gst_element_state_get_name (old_state),
gst_element_state_get_name (new_state));
}
if (renderer_type[type]->state_pending && strstr(GST_MESSAGE_SRC_NAME(message), "pipeline")) {
GstState state;
gst_element_get_state(renderer_type[type]->pipeline, &state, NULL, 100 * GST_MSECOND);
if (state == GST_STATE_NULL) {
gst_element_set_state(renderer_type[type]->pipeline, GST_STATE_PLAYING);
} else if (state == GST_STATE_PLAYING) {
renderer_type[type]->state_pending = false;
}
}
if (renderer_type[type]->autovideo) {
char *sink = strstr(GST_MESSAGE_SRC_NAME(message), "-actual-sink-");
if (sink) {
@@ -711,27 +708,41 @@ gboolean gstreamer_pipeline_bus_callback(GstBus *bus, GstMessage *message, void
return TRUE;
}
void video_renderer_choose_codec (bool video_is_h265) {
int video_renderer_choose_codec (bool video_is_h265) {
video_renderer_t *renderer_used = NULL;
video_renderer_t *renderer_unused = NULL;
g_assert(!hls_video);
/* set renderer to h264 or h265, depending on pps/sps received by raop_rtp_mirror */
video_renderer_t *renderer_new = video_is_h265 ? renderer_type[1] : renderer_type[0];
if (renderer == renderer_new) {
return;
if (n_renderers == 1) {
if (video_is_h265) {
logger_log(logger, LOGGER_ERR, "video is h265 but the -h265 option was not used");
return -1;
}
renderer_used = renderer_type[0];
} else {
renderer_used = video_is_h265 ? renderer_type[1] : renderer_type[0];
renderer_unused = video_is_h265 ? renderer_type[0] : renderer_type[1];
}
video_renderer_t *renderer_prev = renderer;
renderer = renderer_new;
if (renderer_used == NULL) {
return -1;
} else if (renderer_used == renderer) {
return 0;
} else if (renderer) {
return -1;
}
renderer = renderer_used;
gst_element_set_state (renderer->pipeline, GST_STATE_PLAYING);
GstState old_state, new_state;
if (gst_element_get_state(renderer->pipeline, &old_state, &new_state, 100 * GST_MSECOND) == GST_STATE_CHANGE_FAILURE) {
g_error("video pipeline failed to go into playing state");
return -1;
}
logger_log(logger, LOGGER_DEBUG, "video_pipeline state change from %s to %s\n",
gst_element_state_get_name (old_state),gst_element_state_get_name (new_state));
gst_video_pipeline_base_time = gst_element_get_base_time(renderer->appsrc);
/* it seems unlikely that the codec will change between h264 and h265 during a connection,
* but in case it does, we set the previous renderer to GST_STATE_NULL, detect
* when this is finished by listening for the bus message, and then reset it to
* GST_STATE_READY, so it can be reused if the codec changes again. */
if (renderer_prev) {
gst_app_src_end_of_stream (GST_APP_SRC(renderer_prev->appsrc));
gst_bus_set_flushing(renderer_prev->bus, TRUE);
/* set state of previous renderer to GST_STATE_NULL to (hopefully?) close its video window */
gst_element_set_state (renderer_prev->pipeline, GST_STATE_NULL);
renderer_prev->state_pending = true; // will set state to PLAYING once state is NULL
if (renderer_unused) {
gst_element_set_state (renderer_unused->pipeline, GST_STATE_NULL);
}
return 0;
}
unsigned int video_reset_callback(void * loop) {

View File

@@ -64,7 +64,7 @@ void video_renderer_destroy ();
void video_renderer_size(float *width_source, float *height_source, float *width, float *height);
bool waiting_for_x11_window();
bool video_get_playback_info(double *duration, double *position, float *rate);
void video_renderer_choose_codec(bool is_h265);
int video_renderer_choose_codec(bool is_h265);
unsigned int video_renderer_listen(void *loop, int id);
unsigned int video_reset_callback(void *loop);
#ifdef __cplusplus

View File

@@ -1579,11 +1579,9 @@ extern "C" void video_reset(void *cls) {
reset_loop = true;
}
extern "C" void video_set_codec(void *cls, video_codec_t codec) {
if (use_video) {
bool video_is_h265 = (codec == VIDEO_CODEC_H265);
video_renderer_choose_codec(video_is_h265);
}
extern "C" int video_set_codec(void *cls, video_codec_t codec) {
bool video_is_h265 = (codec == VIDEO_CODEC_H265);
return video_renderer_choose_codec(video_is_h265);
}
extern "C" void display_pin(void *cls, char *pin) {