obs-ffmpeg: Add logging of last error for passing to UI
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user