From ca5a19cf66859a826b2166fadd53bb5514274877 Mon Sep 17 00:00:00 2001 From: Richard Stanway Date: Sat, 30 Mar 2019 15:45:51 +0100 Subject: [PATCH] obs-ffmpeg: Add logging of last error for passing to UI --- plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c | 18 ++-- plugins/obs-ffmpeg/obs-ffmpeg-mux.c | 16 +++- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 102 +++++++++++++++------ 3 files changed, 97 insertions(+), 39 deletions(-) diff --git a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c index b3c5c8857..728555d23 100644 --- a/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/ffmpeg-mux/ffmpeg-mux.c @@ -268,7 +268,7 @@ static bool new_stream(struct ffmpeg_mux *ffm, AVStream **stream, AVCodec *codec; if (!desc) { - printf("Couldn't find encoder '%s'\n", name); + fprintf(stderr, "Couldn't find encoder '%s'\n", name); return false; } @@ -276,13 +276,13 @@ static bool new_stream(struct ffmpeg_mux *ffm, AVStream **stream, codec = avcodec_find_encoder(desc->id); if (!codec) { - printf("Couldn't create encoder"); + fprintf(stderr, "Couldn't create encoder"); return false; } *stream = avformat_new_stream(ffm->output, codec); if (!*stream) { - printf("Couldn't create stream for encoder '%s'\n", name); + fprintf(stderr, "Couldn't create stream for encoder '%s'\n", name); return false; } @@ -469,7 +469,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) ret = avio_open(&ffm->output->pb, ffm->params.file, AVIO_FLAG_WRITE); if (ret < 0) { - printf("Couldn't open '%s', %s", + fprintf(stderr, "Couldn't open '%s', %s", ffm->params.file, av_err2str(ret)); return FFM_ERROR; } @@ -482,7 +482,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) AVDictionary *dict = NULL; if ((ret = av_dict_parse_string(&dict, ffm->params.muxer_settings, "=", " ", 0))) { - printf("Failed to parse muxer settings: %s\n%s", + fprintf(stderr, "Failed to parse muxer settings: %s\n%s", av_err2str(ret), ffm->params.muxer_settings); av_dict_free(&dict); @@ -501,7 +501,7 @@ static inline int open_output_file(struct ffmpeg_mux *ffm) ret = avformat_write_header(ffm->output, &dict); if (ret < 0) { - printf("Error opening '%s': %s", + fprintf(stderr, "Error opening '%s': %s", ffm->params.file, av_err2str(ret)); av_dict_free(&dict); @@ -521,7 +521,7 @@ static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) output_format = av_guess_format(NULL, ffm->params.file, NULL); if (output_format == NULL) { - printf("Couldn't find an appropriate muxer for '%s'\n", + fprintf(stderr, "Couldn't find an appropriate muxer for '%s'\n", ffm->params.file); return FFM_ERROR; } @@ -529,7 +529,7 @@ static int ffmpeg_mux_init_context(struct ffmpeg_mux *ffm) ret = avformat_alloc_output_context2(&ffm->output, output_format, NULL, NULL); if (ret < 0) { - printf("Couldn't initialize output context: %s\n", + fprintf(stderr, "Couldn't initialize output context: %s\n", av_err2str(ret)); return FFM_ERROR; } @@ -679,7 +679,7 @@ int main(int argc, char *argv[]) ret = ffmpeg_mux_init(&ffm, argc, argv); if (ret != FFM_SUCCESS) { - puts("Couldn't initialize muxer"); + fprintf(stderr, "Couldn't initialize muxer\n"); return ret; } diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c index 9025730f4..466fa1a37 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-mux.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-mux.c @@ -369,9 +369,23 @@ static void ffmpeg_mux_stop(void *data, uint64_t ts) static void signal_failure(struct ffmpeg_muxer *stream) { - int ret = deactivate(stream); + char error[1024]; + int ret; int code; + size_t len; + + len = os_process_pipe_read_err(stream->pipe, (uint8_t *)error, + sizeof(error) - 1); + + if (len > 0) { + error[len] = 0; + warn ("ffmpeg-mux: %s", error); + obs_output_set_last_error (stream->output, error); + } + + ret = deactivate(stream); + switch (ret) { case FFM_UNSUPPORTED: code = OBS_OUTPUT_UNSUPPORTED; break; default: code = OBS_OUTPUT_ERROR; diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index d301cf501..7f687eeb4 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -86,6 +86,8 @@ struct ffmpeg_data { struct ffmpeg_cfg config; bool initialized; + + char *last_error; }; struct ffmpeg_output { @@ -114,6 +116,30 @@ struct ffmpeg_output { /* ------------------------------------------------------------------------- */ +static void ffmpeg_output_set_last_error(struct ffmpeg_data *data, + const char *error) +{ + if (data->last_error) + bfree(data->last_error); + + data->last_error = bstrdup(error); +} + +void ffmpeg_log_error(int log_level, struct ffmpeg_data *data, + const char *format, ...) +{ + va_list args; + char out[4096]; + + va_start(args, format); + vsnprintf(out, sizeof(out), format, args); + va_end(args); + + ffmpeg_output_set_last_error(data, out); + + blog(log_level, "%s", out); +} + static bool new_stream(struct ffmpeg_data *data, AVStream **stream, AVCodec **codec, enum AVCodecID id, const char *name) { @@ -122,14 +148,14 @@ static bool new_stream(struct ffmpeg_data *data, AVStream **stream, avcodec_find_encoder(id); if (!*codec) { - blog(LOG_WARNING, "Couldn't find encoder '%s'", + ffmpeg_log_error(LOG_WARNING, data, "Couldn't find encoder '%s'", avcodec_get_name(id)); return false; } *stream = avformat_new_stream(data->output, *codec); if (!*stream) { - blog(LOG_WARNING, "Couldn't create stream for encoder '%s'", + ffmpeg_log_error(LOG_WARNING, data, "Couldn't create stream for encoder '%s'", avcodec_get_name(id)); return false; } @@ -186,14 +212,14 @@ static bool open_video_codec(struct ffmpeg_data *data) ret = avcodec_open2(context, data->vcodec, NULL); if (ret < 0) { - blog(LOG_WARNING, "Failed to open video codec: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to open video codec: %s", av_err2str(ret)); return false; } data->vframe = av_frame_alloc(); if (!data->vframe) { - blog(LOG_WARNING, "Failed to allocate video frame"); + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate video frame"); return false; } @@ -205,7 +231,7 @@ static bool open_video_codec(struct ffmpeg_data *data) ret = av_frame_get_buffer(data->vframe, base_get_alignment()); if (ret < 0) { - blog(LOG_WARNING, "Failed to allocate vframe: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate vframe: %s", av_err2str(ret)); return false; } @@ -223,7 +249,7 @@ static bool init_swscale(struct ffmpeg_data *data, AVCodecContext *context) SWS_BICUBIC, NULL, NULL, NULL); if (!data->swscale) { - blog(LOG_WARNING, "Could not initialize swscale"); + ffmpeg_log_error(LOG_WARNING, data, "Could not initialize swscale"); return false; } @@ -237,7 +263,7 @@ static bool create_video_stream(struct ffmpeg_data *data) struct obs_video_info ovi; if (!obs_get_video_info(&ovi)) { - blog(LOG_WARNING, "No active video"); + ffmpeg_log_error(LOG_WARNING, data, "No active video"); return false; } @@ -292,7 +318,7 @@ static bool open_audio_codec(struct ffmpeg_data *data, int idx) data->aframe[idx] = av_frame_alloc(); if (!data->aframe[idx]) { - blog(LOG_WARNING, "Failed to allocate audio frame"); + ffmpeg_log_error(LOG_WARNING, data, "Failed to allocate audio frame"); return false; } @@ -305,7 +331,7 @@ static bool open_audio_codec(struct ffmpeg_data *data, int idx) ret = avcodec_open2(context, data->acodec, NULL); if (ret < 0) { - blog(LOG_WARNING, "Failed to open audio codec: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to open audio codec: %s", av_err2str(ret)); return false; } @@ -315,7 +341,7 @@ static bool open_audio_codec(struct ffmpeg_data *data, int idx) ret = av_samples_alloc(data->samples[idx], NULL, context->channels, data->frame_size, context->sample_fmt, 0); if (ret < 0) { - blog(LOG_WARNING, "Failed to create audio buffer: %s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to create audio buffer: %s", av_err2str(ret)); return false; } @@ -330,7 +356,7 @@ static bool create_audio_stream(struct ffmpeg_data *data, int idx) struct obs_audio_info aoi; if (!obs_get_audio_info(&aoi)) { - blog(LOG_WARNING, "No active audio"); + ffmpeg_log_error(LOG_WARNING, data, "No active audio"); return false; } @@ -396,7 +422,7 @@ static inline bool open_output_file(struct ffmpeg_data *data) AVDictionary *dict = NULL; if ((ret = av_dict_parse_string(&dict, data->config.muxer_settings, "=", " ", 0))) { - blog(LOG_WARNING, "Failed to parse muxer settings: %s\n%s", + ffmpeg_log_error(LOG_WARNING, data, "Failed to parse muxer settings: %s\n%s", av_err2str(ret), data->config.muxer_settings); av_dict_free(&dict); @@ -419,8 +445,9 @@ static inline bool open_output_file(struct ffmpeg_data *data) ret = avio_open2(&data->output->pb, data->config.url, AVIO_FLAG_WRITE, NULL, &dict); if (ret < 0) { - blog(LOG_WARNING, "Couldn't open '%s', %s", - data->config.url, av_err2str(ret)); + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't open '%s', %s", data->config.url, + av_err2str(ret)); av_dict_free(&dict); return false; } @@ -432,7 +459,7 @@ static inline bool open_output_file(struct ffmpeg_data *data) ret = avformat_write_header(data->output, &dict); if (ret < 0) { - blog(LOG_WARNING, "Error opening '%s': %s", + ffmpeg_log_error(LOG_WARNING, data, "Error opening '%s': %s", data->config.url, av_err2str(ret)); return false; } @@ -503,6 +530,9 @@ static void ffmpeg_data_free(struct ffmpeg_data *data) avformat_free_context(data->output); } + if (data->last_error) + bfree(data->last_error); + memset(data, 0, sizeof(struct ffmpeg_data)); } @@ -565,13 +595,15 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, is_rtmp ? NULL : data->config.format_mime_type); if (output_format == NULL) { - blog(LOG_WARNING, "Couldn't find matching output format with " - " parameters: name=%s, url=%s, mime=%s", - safe_str(is_rtmp ? - "flv" : data->config.format_name), - safe_str(data->config.url), - safe_str(is_rtmp ? - NULL : data->config.format_mime_type)); + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't find matching output format with " + "parameters: name=%s, url=%s, mime=%s", + safe_str(is_rtmp ? + "flv" : data->config.format_name), + safe_str(data->config.url), + safe_str(is_rtmp ? + NULL : data->config.format_mime_type)); + goto fail; } @@ -579,7 +611,8 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, NULL, NULL); if (!data->output) { - blog(LOG_WARNING, "Couldn't create avformat context"); + ffmpeg_log_error(LOG_WARNING, data, + "Couldn't create avformat context"); goto fail; } @@ -603,7 +636,6 @@ static bool ffmpeg_data_init(struct ffmpeg_data *data, fail: blog(LOG_WARNING, "ffmpeg_data_init failed"); - ffmpeg_data_free(data); return false; } @@ -758,6 +790,7 @@ static void receive_video(void *param, struct video_data *frame) if (ret < 0) { blog(LOG_WARNING, "receive_video: Error encoding " "video: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } @@ -783,6 +816,7 @@ static void receive_video(void *param, struct video_data *frame) if (ret != 0) { blog(LOG_WARNING, "receive_video: Error writing video: %s", av_err2str(ret)); + //FIXME: stop the encode with an error } data->total_frames++; @@ -808,6 +842,7 @@ static void encode_audio(struct ffmpeg_output *output, int idx, if (ret < 0) { blog(LOG_WARNING, "encode_audio: avcodec_fill_audio_frame " "failed: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } @@ -829,6 +864,7 @@ static void encode_audio(struct ffmpeg_output *output, int idx, if (ret < 0) { blog(LOG_WARNING, "encode_audio: Error encoding audio: %s", av_err2str(ret)); + //FIXME: stop the encode with an error return; } @@ -989,8 +1025,9 @@ static int process_packet(struct ffmpeg_output *output) ret = av_interleaved_write_frame(output->ff_data.output, &packet); if (ret < 0) { av_free_packet(&packet); - blog(LOG_WARNING, "receive_audio: Error writing packet: %s", - av_err2str(ret)); + ffmpeg_log_error(LOG_WARNING, &output->ff_data, + "receive_audio: Error writing packet: %s", + av_err2str(ret)); return ret; } @@ -1108,8 +1145,14 @@ static bool try_connect(struct ffmpeg_output *output) success = ffmpeg_data_init(&output->ff_data, &config); obs_data_release(settings); - if (!success) + if (!success) { + if (output->ff_data.last_error) { + obs_output_set_last_error(output->output, + output->ff_data.last_error); + } + ffmpeg_data_free(&output->ff_data); return false; + } struct audio_convert_info aci = { .format = output->ff_data.audio_format @@ -1122,8 +1165,9 @@ static bool try_connect(struct ffmpeg_output *output) ret = pthread_create(&output->write_thread, NULL, write_thread, output); if (ret != 0) { - blog(LOG_WARNING, "ffmpeg_output_start: failed to create write " - "thread."); + ffmpeg_log_error(LOG_WARNING, &output->ff_data, + "ffmpeg_output_start: failed to create write " + "thread."); ffmpeg_output_full_stop(output); return false; }