diff --git a/src/config.c b/src/config.c index cd5f7032..782c42b7 100644 --- a/src/config.c +++ b/src/config.c @@ -1041,6 +1041,46 @@ config_migrate_v13 ( void ) } } +static const char * +config_migrate_v14_codec(int i) +{ + switch (i) { + case 1: return "mpeg2video"; + case 2: return "mp2"; + case 3: return "libx264"; + case 4: return "ac3"; + case 8: return "aac"; + case 13: return "libvpx"; + case 14: return "libvorbis"; + default: return ""; + } +} + +static void +config_migrate_v14 ( void ) +{ + htsmsg_t *c, *e; + htsmsg_field_t *f; + int i; + + if ((c = hts_settings_load("profile")) != NULL) { + HTSMSG_FOREACH(f, c) { + if (!(e = htsmsg_field_get_map(f))) continue; + if (!htsmsg_get_s32(e, "vcodec", &i)) { + htsmsg_delete_field(e, "vcodec"); + htsmsg_set_str(e, "vcodec", config_migrate_v14_codec(i)); + } + if (!htsmsg_get_s32(e, "acodec", &i)) { + htsmsg_delete_field(e, "acodec"); + htsmsg_set_str(e, "acodec", config_migrate_v14_codec(i)); + } + if (!htsmsg_get_s32(e, "scodec", &i)) + htsmsg_delete_field(e, "scodec"); + hts_settings_save(e, "profile/%s", f->hmf_name); + } + } +} + /* * Perform backup */ @@ -1142,7 +1182,8 @@ static const config_migrate_t config_migrate_table[] = { config_migrate_v10, config_migrate_v11, config_migrate_v12, - config_migrate_v13 + config_migrate_v13, + config_migrate_v14 }; /* diff --git a/src/plumbing/transcoding.c b/src/plumbing/transcoding.c index 38b3a1ec..2becb1cd 100644 --- a/src/plumbing/transcoding.c +++ b/src/plumbing/transcoding.c @@ -184,28 +184,13 @@ transcoder_get_decoder(streaming_component_type_t ty) * */ static AVCodec * -transcoder_get_encoder(streaming_component_type_t ty) +transcoder_get_encoder(const char *codec_name) { - enum AVCodecID codec_id; AVCodec *codec; - codec_id = streaming_component_type2codec_id(ty); - if (codec_id == AV_CODEC_ID_NONE) { - tvhlog(LOG_ERR, "transcode", "Unable to find %s codec", - streaming_component_type2txt(ty)); - return NULL; - } - - if (!WORKING_ENCODER(codec_id)) { - tvhlog(LOG_WARNING, "transcode", "Unsupported output codec %s", - streaming_component_type2txt(ty)); - return NULL; - } - - codec = avcodec_find_encoder(codec_id); + codec = avcodec_find_encoder_by_name(codec_name); if (!codec) { - tvhlog(LOG_ERR, "transcode", "Unable to find %s encoder", - streaming_component_type2txt(ty)); + tvhlog(LOG_ERR, "transcode", "Unable to find %s encoder", codec_name); return NULL; } tvhlog(LOG_DEBUG, "transcode", "Using encoder %s", codec->name); @@ -1416,11 +1401,12 @@ transcoder_init_subtitle(transcoder_t *t, streaming_start_component_t *ssc) subtitle_stream_t *ss; AVCodec *icodec, *ocodec; transcoder_props_t *tp = &t->t_props; + int sct; - if (tp->tp_scodec == SCT_NONE) + if (tp->tp_scodec[0] == '\0') return 0; - else if (tp->tp_scodec == SCT_UNKNOWN) + else if (!strcmp(tp->tp_scodec, "copy")) return transcoder_init_stream(t, ssc); else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) @@ -1429,13 +1415,15 @@ transcoder_init_subtitle(transcoder_t *t, streaming_start_component_t *ssc) else if (!(ocodec = transcoder_get_encoder(tp->tp_scodec))) return transcoder_init_stream(t, ssc); - if (tp->tp_scodec == ssc->ssc_type) + sct = codec_id2streaming_component_type(ocodec->id); + + if (sct == ssc->ssc_type) return transcoder_init_stream(t, ssc); ss = calloc(1, sizeof(subtitle_stream_t)); ss->ts_index = ssc->ssc_index; - ss->ts_type = tp->tp_scodec; + ss->ts_type = sct; ss->ts_target = t->t_output; ss->ts_handle_pkt = transcoder_stream_subtitle; ss->ts_destroy = transcoder_destroy_subtitle; @@ -1453,7 +1441,7 @@ transcoder_init_subtitle(transcoder_t *t, streaming_start_component_t *ssc) streaming_component_type2txt(ssc->ssc_type), streaming_component_type2txt(ss->ts_type)); - ssc->ssc_type = tp->tp_scodec; + ssc->ssc_type = sct; ssc->ssc_gh = NULL; return 1; @@ -1506,11 +1494,12 @@ transcoder_init_audio(transcoder_t *t, streaming_start_component_t *ssc) transcoder_stream_t *ts; AVCodec *icodec, *ocodec; transcoder_props_t *tp = &t->t_props; + int sct; - if (tp->tp_acodec == SCT_NONE) + if (tp->tp_acodec[0] == '\0') return 0; - else if (tp->tp_acodec == SCT_UNKNOWN) + else if (!strcmp(tp->tp_acodec, "copy")) return transcoder_init_stream(t, ssc); else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) @@ -1523,13 +1512,15 @@ transcoder_init_audio(transcoder_t *t, streaming_start_component_t *ssc) if (SCT_ISAUDIO(ts->ts_type)) return 0; - if (tp->tp_acodec == ssc->ssc_type) + sct = codec_id2streaming_component_type(ocodec->id); + + if (sct == ssc->ssc_type) return transcoder_init_stream(t, ssc); as = calloc(1, sizeof(audio_stream_t)); as->ts_index = ssc->ssc_index; - as->ts_type = tp->tp_acodec; + as->ts_type = sct; as->ts_target = t->t_output; as->ts_handle_pkt = transcoder_stream_audio; as->ts_destroy = transcoder_destroy_audio; @@ -1559,7 +1550,7 @@ transcoder_init_audio(transcoder_t *t, streaming_start_component_t *ssc) streaming_component_type2txt(ssc->ssc_type), streaming_component_type2txt(as->ts_type)); - ssc->ssc_type = tp->tp_acodec; + ssc->ssc_type = sct; ssc->ssc_gh = NULL; // resampling not implemented yet @@ -1621,11 +1612,12 @@ transcoder_init_video(transcoder_t *t, streaming_start_component_t *ssc) AVCodec *icodec, *ocodec; double aspect; transcoder_props_t *tp = &t->t_props; + int sct; - if (tp->tp_vcodec == SCT_NONE) + if (tp->tp_vcodec[0] == '\0') return 0; - else if (tp->tp_vcodec == SCT_UNKNOWN) + else if (!strcmp(tp->tp_vcodec, "copy")) return transcoder_init_stream(t, ssc); else if (!(icodec = transcoder_get_decoder(ssc->ssc_type))) @@ -1634,10 +1626,12 @@ transcoder_init_video(transcoder_t *t, streaming_start_component_t *ssc) else if (!(ocodec = transcoder_get_encoder(tp->tp_vcodec))) return transcoder_init_stream(t, ssc); + sct = codec_id2streaming_component_type(ocodec->id); + vs = calloc(1, sizeof(video_stream_t)); vs->ts_index = ssc->ssc_index; - vs->ts_type = tp->tp_vcodec; + vs->ts_type = sct; vs->ts_target = t->t_output; vs->ts_handle_pkt = transcoder_stream_video; vs->ts_destroy = transcoder_destroy_video; @@ -1683,7 +1677,7 @@ transcoder_init_video(transcoder_t *t, streaming_start_component_t *ssc) vs->vid_width, vs->vid_height); - ssc->ssc_type = tp->tp_vcodec; + ssc->ssc_type = sct; ssc->ssc_width = vs->vid_width; ssc->ssc_height = vs->vid_height; ssc->ssc_gh = NULL; @@ -1712,7 +1706,7 @@ transcoder_calc_stream_count(transcoder_t *t, streaming_start_t *ss) { continue; if (SCT_ISVIDEO(ssc->ssc_type)) { - if (t->t_props.tp_vcodec == SCT_NONE) + if (t->t_props.tp_vcodec[0] == '\0') video = 0; else if (t->t_props.tp_vcodec == SCT_UNKNOWN) video++; @@ -1720,7 +1714,7 @@ transcoder_calc_stream_count(transcoder_t *t, streaming_start_t *ss) { video = 1; } else if (SCT_ISAUDIO(ssc->ssc_type)) { - if (t->t_props.tp_acodec == SCT_NONE) + if (t->t_props.tp_acodec[0] == '\0') audio = 0; else if (t->t_props.tp_acodec == SCT_UNKNOWN) audio++; @@ -1728,7 +1722,7 @@ transcoder_calc_stream_count(transcoder_t *t, streaming_start_t *ss) { audio = 1; } else if (SCT_ISSUBTITLE(ssc->ssc_type)) { - if (t->t_props.tp_scodec == SCT_NONE) + if (t->t_props.tp_scodec[0] == '\0') subtitle = 0; else if (t->t_props.tp_scodec == SCT_UNKNOWN) subtitle++; @@ -1898,9 +1892,9 @@ transcoder_set_properties(streaming_target_t *st, transcoder_t *t = (transcoder_t *)st; transcoder_props_t *tp = &t->t_props; - tp->tp_vcodec = props->tp_vcodec; - tp->tp_acodec = props->tp_acodec; - tp->tp_scodec = props->tp_scodec; + strncpy(tp->tp_vcodec, props->tp_vcodec, sizeof(tp->tp_vcodec)-1); + strncpy(tp->tp_acodec, props->tp_acodec, sizeof(tp->tp_acodec)-1); + strncpy(tp->tp_scodec, props->tp_scodec, sizeof(tp->tp_scodec)-1); tp->tp_channels = props->tp_channels; tp->tp_bandwidth = props->tp_bandwidth; tp->tp_resolution = props->tp_resolution; @@ -1930,7 +1924,7 @@ transcoder_get_capabilities(int experimental) { AVCodec *p = NULL; streaming_component_type_t sct; - htsmsg_t *array = htsmsg_create_list(); + htsmsg_t *array = htsmsg_create_list(), *m; while ((p = av_codec_next(p))) { @@ -1947,7 +1941,13 @@ transcoder_get_capabilities(int experimental) if (sct == SCT_NONE) continue; - htsmsg_add_s32(array, NULL, sct); + m = htsmsg_create_map(); + htsmsg_add_s32(m, "type", sct); + htsmsg_add_u32(m, "id", p->id); + htsmsg_add_str(m, "name", p->name); + if (p->long_name) + htsmsg_add_str(m, "long_name", p->long_name); + htsmsg_add_msg(array, NULL, m); } return array; } diff --git a/src/plumbing/transcoding.h b/src/plumbing/transcoding.h index 16e00332..d3137921 100644 --- a/src/plumbing/transcoding.h +++ b/src/plumbing/transcoding.h @@ -21,9 +21,9 @@ #include "htsmsg.h" typedef struct transcoder_prop { - streaming_component_type_t tp_vcodec; - streaming_component_type_t tp_acodec; - streaming_component_type_t tp_scodec; + char tp_vcodec[32]; + char tp_acodec[32]; + char tp_scodec[32]; int8_t tp_channels; int32_t tp_bandwidth; diff --git a/src/profile.c b/src/profile.c index 0d10e9f5..7c4abda6 100644 --- a/src/profile.c +++ b/src/profile.c @@ -539,9 +539,9 @@ typedef struct profile_transcode { uint32_t pro_channels; uint32_t pro_bandwidth; char *pro_language; - int pro_vcodec; - int pro_acodec; - int pro_scodec; + char *pro_vcodec; + char *pro_acodec; + char *pro_scodec; } profile_transcode_t; static htsmsg_t * @@ -611,90 +611,80 @@ profile_class_check_sct(htsmsg_t *c, int sct) } static htsmsg_t * -profile_class_vcodec_list(void *o) +profile_class_codec_list(int (*check)(int sct)) { - htsmsg_t *l = htsmsg_create_list(), *e, *c; - int i; + htsmsg_t *l = htsmsg_create_list(), *e, *c, *m; + htsmsg_field_t *f; + const char *s, *s2; + char buf[128]; + int sct; e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", -1); + htsmsg_add_str(e, "key", ""); htsmsg_add_str(e, "val", "Do not use"); htsmsg_add_msg(l, NULL, e); e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", 0); + htsmsg_add_str(e, "key", "copy"); htsmsg_add_str(e, "val", "Copy codec type"); htsmsg_add_msg(l, NULL, e); c = transcoder_get_capabilities(profile_transcode_experimental_codecs); - for (i = 0; i <= SCT_LAST; i++) { - if (!SCT_ISVIDEO(i)) + HTSMSG_FOREACH(f, c) { + if (!(m = htsmsg_field_get_map(f))) continue; - if (!profile_class_check_sct(c, i)) + if (htsmsg_get_s32(m, "type", &sct)) continue; + if (!check(sct)) + continue; + if (!(s = htsmsg_get_str(m, "name"))) + continue; + s2 = htsmsg_get_str(m, "long_name"); + if (s2) + snprintf(buf, sizeof(buf), "%s (%s)", s, s2); + else + snprintf(buf, sizeof(buf), "%s", s); e = htsmsg_create_map(); - htsmsg_add_u32(e, "key", i); - htsmsg_add_str(e, "val", streaming_component_type2txt(i)); + htsmsg_add_str(e, "key", s); + htsmsg_add_str(e, "val", buf); htsmsg_add_msg(l, NULL, e); } htsmsg_destroy(c); return l; } +static int +profile_class_vcodec_sct_check(int sct) +{ + return SCT_ISVIDEO(sct); +} + +static htsmsg_t * +profile_class_vcodec_list(void *o) +{ + return profile_class_codec_list(profile_class_vcodec_sct_check); +} + +static int +profile_class_acodec_sct_check(int sct) +{ + return SCT_ISAUDIO(sct); +} + static htsmsg_t * profile_class_acodec_list(void *o) { - htsmsg_t *l = htsmsg_create_list(), *e, *c; - int i; + return profile_class_codec_list(profile_class_acodec_sct_check); +} - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", -1); - htsmsg_add_str(e, "val", "Do not use"); - htsmsg_add_msg(l, NULL, e); - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", 0); - htsmsg_add_str(e, "val", "Copy codec type"); - htsmsg_add_msg(l, NULL, e); - c = transcoder_get_capabilities(profile_transcode_experimental_codecs); - for (i = 0; i <= SCT_LAST; i++) { - if (!SCT_ISAUDIO(i)) - continue; - if (!profile_class_check_sct(c, i)) - continue; - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", i); - htsmsg_add_str(e, "val", streaming_component_type2txt(i)); - htsmsg_add_msg(l, NULL, e); - } - htsmsg_destroy(c); - return l; +static int +profile_class_scodec_sct_check(int sct) +{ + return SCT_ISSUBTITLE(sct); } static htsmsg_t * profile_class_scodec_list(void *o) { - htsmsg_t *l = htsmsg_create_list(), *e, *c; - int i; - - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", -1); - htsmsg_add_str(e, "val", "Do not use"); - htsmsg_add_msg(l, NULL, e); - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", 0); - htsmsg_add_str(e, "val", "Copy codec type"); - htsmsg_add_msg(l, NULL, e); - c = transcoder_get_capabilities(profile_transcode_experimental_codecs); - for (i = 0; i <= SCT_LAST; i++) { - if (!SCT_ISSUBTITLE(i)) - continue; - if (!profile_class_check_sct(c, i)) - continue; - e = htsmsg_create_map(); - htsmsg_add_s32(e, "key", i); - htsmsg_add_str(e, "val", streaming_component_type2txt(i)); - htsmsg_add_msg(l, NULL, e); - } - htsmsg_destroy(c); - return l; + return profile_class_codec_list(profile_class_scodec_sct_check); } const idclass_t profile_transcode_class = @@ -734,27 +724,27 @@ const idclass_t profile_transcode_class = .list = profile_class_language_list, }, { - .type = PT_INT, + .type = PT_STR, .id = "vcodec", .name = "Video Codec", .off = offsetof(profile_transcode_t, pro_vcodec), - .def.i = SCT_H264, + .def.s = "libx264", .list = profile_class_vcodec_list, }, { - .type = PT_INT, + .type = PT_STR, .id = "acodec", .name = "Audio Codec", .off = offsetof(profile_transcode_t, pro_acodec), - .def.i = SCT_VORBIS, + .def.s = "libvorbis", .list = profile_class_acodec_list, }, { - .type = PT_INT, + .type = PT_STR, .id = "scodec", .name = "Subtitles Codec", .off = offsetof(profile_transcode_t, pro_scodec), - .def.i = SCT_NONE, + .def.s = "", .list = profile_class_scodec_list, }, { } @@ -777,9 +767,9 @@ profile_transcode_work(profile_t *_pro, streaming_target_t *src, streaming_target_t *st; memset(&props, 0, sizeof(props)); - props.tp_vcodec = pro->pro_vcodec; - props.tp_acodec = pro->pro_acodec; - props.tp_scodec = pro->pro_scodec; + strncpy(props.tp_vcodec, pro->pro_vcodec ?: "", sizeof(props.tp_vcodec)-1); + strncpy(props.tp_acodec, pro->pro_acodec ?: "", sizeof(props.tp_acodec)-1); + strncpy(props.tp_scodec, pro->pro_scodec ?: "", sizeof(props.tp_scodec)-1); props.tp_resolution = pro->pro_resolution >= 240 ? pro->pro_resolution : 240; props.tp_channels = pro->pro_channels; props.tp_bandwidth = pro->pro_bandwidth >= 64 ? pro->pro_bandwidth : 64; @@ -842,10 +832,20 @@ profile_transcode_get_mc(profile_t *_pro) return pro->pro_mc; } +static void +profile_transcode_free(profile_t *_pro) +{ + profile_transcode_t *pro = (profile_transcode_t *)_pro; + free(pro->pro_vcodec); + free(pro->pro_acodec); + free(pro->pro_scodec); +} + static profile_t * profile_transcode_builder(void) { profile_transcode_t *pro = calloc(1, sizeof(*pro)); + pro->pro_free = profile_transcode_free; pro->pro_work = profile_transcode_work; pro->pro_open = profile_transcode_open; pro->pro_get_mc = profile_transcode_get_mc; @@ -925,9 +925,8 @@ profile_init(void) htsmsg_add_s32 (conf, "container", MC_WEBM); htsmsg_add_u32 (conf, "resolution", 384); htsmsg_add_u32 (conf, "channels", 2); - htsmsg_add_s32 (conf, "vcodec", SCT_VP8); - htsmsg_add_s32 (conf, "acodec", SCT_VORBIS); - htsmsg_add_s32 (conf, "scodec", SCT_NONE); + htsmsg_add_str (conf, "vcodec", "libvpx"); + htsmsg_add_str (conf, "acodec", "libvorbis"); htsmsg_add_bool(conf, "shield", 1); (void)profile_create(NULL, conf, 1); htsmsg_destroy(conf); @@ -945,9 +944,8 @@ profile_init(void) htsmsg_add_s32 (conf, "container", MC_MPEGTS); htsmsg_add_u32 (conf, "resolution", 384); htsmsg_add_u32 (conf, "channels", 2); - htsmsg_add_s32 (conf, "vcodec", SCT_H264); - htsmsg_add_s32 (conf, "acodec", SCT_AAC); - htsmsg_add_s32 (conf, "scodec", SCT_NONE); + htsmsg_add_str (conf, "vcodec", "libx264"); + htsmsg_add_str (conf, "acodec", "aac"); htsmsg_add_bool(conf, "shield", 1); (void)profile_create(NULL, conf, 1); htsmsg_destroy(conf); @@ -965,9 +963,8 @@ profile_init(void) htsmsg_add_s32 (conf, "container", MC_MATROSKA); htsmsg_add_u32 (conf, "resolution", 384); htsmsg_add_u32 (conf, "channels", 2); - htsmsg_add_s32 (conf, "vcodec", SCT_H264); - htsmsg_add_s32 (conf, "acodec", SCT_AAC); - htsmsg_add_s32 (conf, "scodec", SCT_NONE); + htsmsg_add_str (conf, "vcodec", "libx264"); + htsmsg_add_str (conf, "acodec", "aac"); htsmsg_add_bool(conf, "shield", 1); (void)profile_create(NULL, conf, 1); htsmsg_destroy(conf);