// ***********************************************************/ // ffmpeg_init.cpp // // Copy Right @ Steven Huang. All rights reserved. // // FFmpeg utils. // ***********************************************************/ #include "ffmpeg_init.h" #if !NDEBUG #define OPEN_FFMPEG_LOG 1 #else #define OPEN_FFMPEG_LOG 0 #endif #define PRINT_LIB_INFO(libname, LIBNAME) \ if (true) \ { \ const char* indent = " "; \ unsigned int version = libname##_version(); \ qInfo("%slib%-11s %2d.%3d.%3d / %2d.%3d.%3d", indent, #libname, \ LIB##LIBNAME##_VERSION_MAJOR, LIB##LIBNAME##_VERSION_MINOR, \ LIB##LIBNAME##_VERSION_MICRO, AV_VERSION_MAJOR(version), \ AV_VERSION_MINOR(version), AV_VERSION_MICRO(version)); \ } #define BUFF_MAXLEN 256 static void log_callback(void* ptr, int level, const char* fmt, va_list vl) { if (level > av_log_get_level()) return; va_list vl2; char line[1024]; static int print_prefix = 1; va_copy(vl2, vl); av_log_format_line(ptr, level, fmt, vl2, line, sizeof(line), &print_prefix); va_end(vl2); #if OPEN_FFMPEG_LOG qInfo("FFMPEG:%s", line); #endif } int ffmpeg_init() { av_log_set_flags(AV_LOG_SKIP_REPEATED); av_log_set_level(AV_LOG_INFO); av_log_set_callback(log_callback); // print_ffmpeg_info(AV_LOG_INFO); check_error(-22); return 0; } void check_error(int error) { char errorStr[256] = {0}; qDebug("ENOMEM: %d", AVERROR(ENOMEM)); qDebug("ENOMEM: %d", AVERROR(EINVAL)); qDebug("ENOMEM: %d", AVERROR_OPTION_NOT_FOUND); av_strerror(error, errorStr, sizeof(errorStr)); } void print_ffmpeg_info() { // ffmpeg-snapshot_0401 qInfo("%s version %s", "ffmpeg", FFMPEG_VERSION); PRINT_LIB_INFO(avutil, AVUTIL); PRINT_LIB_INFO(avcodec, AVCODEC); PRINT_LIB_INFO(avformat, AVFORMAT); // PRINT_LIB_INFO(avdevice, AVDEVICE); // PRINT_LIB_INFO(avfilter, AVFILTER); PRINT_LIB_INFO(swscale, SWSCALE); PRINT_LIB_INFO(swresample, SWRESAMPLE); qInfo(""); } QString dump_format(AVFormatContext* ic, int index, const char* url, int is_output) { QString str; char tmp[BUFF_MAXLEN]; const char* indent = " "; uint8_t* printed = nullptr; if (!ic) { qErrnoWarning("invalid parameter!"); goto fail; } if (!url || !url[0]) { qErrnoWarning("url is invalid!"); goto fail; } printed = ic->nb_streams ? (uint8_t*)av_mallocz(ic->nb_streams) : nullptr; if (ic->nb_streams && !printed) goto fail; snprintf(tmp, sizeof(tmp), "%s #%d, %s, %s '%s':", is_output ? "Output" : "Input", index, is_output ? ic->oformat->name : ic->iformat->name, is_output ? "to" : "from", url); str += tmp; str += "\n"; str += dump_metadata(ic->metadata, indent); if (!is_output) { snprintf(tmp, sizeof(tmp), "%sDuration: ", indent); str += tmp; if (ic->duration != AV_NOPTS_VALUE) { int64_t hours, mins, secs, us; int64_t duration = ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0); secs = duration / AV_TIME_BASE; us = duration % AV_TIME_BASE; mins = secs / 60; secs %= 60; hours = mins / 60; mins %= 60; snprintf(tmp, sizeof(tmp), "%02lld:%02lld:%02lld.%02lld", hours, mins, secs, (100 * us) / AV_TIME_BASE); str += tmp; } else { str += "N/A"; } if (ic->start_time != AV_NOPTS_VALUE) { int secs, us; secs = llabs(ic->start_time / AV_TIME_BASE); us = llabs(ic->start_time % AV_TIME_BASE); // av_log(nullptr, AV_LOG_INFO, ", start: %s%d.%06d", // ic->start_time >= 0 ? "" : "-", secs, (int)av_rescale(us, 1000000, // AV_TIME_BASE)); snprintf(tmp, sizeof(tmp), ", start: %s%d.%06d", ic->start_time >= 0 ? "" : "-", secs, (int)av_rescale(us, 1000000, AV_TIME_BASE)); str += tmp; } str += ", bitrate: "; if (ic->bit_rate) { snprintf(tmp, sizeof(tmp), "%lld kb/s", ic->bit_rate / 1000); str += tmp; } else str += "N/A"; str += "\n"; } if (ic->nb_chapters) { snprintf(tmp, sizeof(tmp), "%sChapters:\n", indent); str += tmp; } for (unsigned int i = 0; i < ic->nb_chapters; i++) { const AVChapter* ch = ic->chapters[i]; snprintf(tmp, sizeof(tmp), " Chapter #%d:%d: start %f, end %f\n", index, i, ch->start * av_q2d(ch->time_base), ch->end * av_q2d(ch->time_base)); str += tmp; str += dump_metadata(ch->metadata, " "); } if (ic->nb_programs) { unsigned int j, k, total = 0; for (j = 0; j < ic->nb_programs; j++) { const AVProgram* program = ic->programs[j]; const AVDictionaryEntry* name = av_dict_get(program->metadata, "name", nullptr, 0); snprintf(tmp, sizeof(tmp), " Program %d %s\n", program->id, name ? name->value : ""); str += tmp; str += dump_metadata(program->metadata, " "); for (k = 0; k < program->nb_stream_indexes; k++) { str += dump_stream_format(ic, program->stream_index[k], index, is_output); printed[program->stream_index[k]] = 1; } total += program->nb_stream_indexes; } if (total < ic->nb_streams) { str += " No Program\n"; } } str += "\n"; for (unsigned int i = 0; i < ic->nb_streams; i++) { if (!printed[i]) { str += dump_stream_format(ic, i, index, is_output); str += "\n"; } } return str; fail: return QString(""); } QString dump_metadata(const AVDictionary* m, const char* indent) { QString str; char tmp[BUFF_MAXLEN]; if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", nullptr, 0))) { const AVDictionaryEntry* tag = nullptr; snprintf(tmp, sizeof(tmp), "%sMetadata:\n", indent); str += tmp; while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX))) { if (strcmp("language", tag->key)) { const char* p = tag->value; // av_log(ctx, AV_LOG_INFO,"%s %-16s: ", indent, tag->key); snprintf(tmp, sizeof(tmp), "%s %-16s: ", indent, tag->key); str += tmp; while (*p) { char tmp_str[256]; size_t len = strcspn(p, "\x8\xa\xb\xc\xd"); av_strlcpy(tmp_str, p, FFMIN(sizeof(tmp_str), len + 1)); // str += "%s", tmp); snprintf(tmp, sizeof(tmp), "%s", tmp_str); str += tmp; p += len; if (*p == 0xd) str += " "; if (*p == 0xa) str += "\n%s %-16s: "; if (*p) p++; } str += "\n"; } } } return str; } QString dump_stream_format(const AVFormatContext* ic, int i, int index, int is_output) { QString str; char tmp[BUFF_MAXLEN]; char buf[256]; int flags = (is_output ? ic->oformat->flags : ic->iformat->flags); const AVStream* st = ic->streams[i]; // const FFStream* const sti = cffstream(st); const AVDictionaryEntry* lang = av_dict_get(st->metadata, "language", nullptr, 0); const char* separator = (const char*)ic->dump_separator; AVCodecContext* avctx; // AVCodecContext* st_avctx = (AVCodecContext*)st->priv_data; int ret; avctx = avcodec_alloc_context3(nullptr); if (!avctx) goto fail; ret = avcodec_parameters_to_context(avctx, st->codecpar); if (ret < 0) { avcodec_free_context(&avctx); goto fail; } // Fields which are missing from AVCodecParameters need to be taken from the // AVCodecContext /* avctx->properties = st_avctx->properties; avctx->codec = st_avctx->codec; avctx->qmin = st_avctx->qmin; avctx->qmax = st_avctx->qmax; avctx->coded_width = st_avctx->coded_width; avctx->coded_height = st_avctx->coded_height; if (separator) av_opt_set(avctx, "dump_separator", separator, 0);*/ avcodec_string(buf, sizeof(buf), avctx, is_output); avcodec_free_context(&avctx); snprintf(tmp, sizeof(tmp), " Stream #%d:%d", index, i); str += tmp; if (flags & AVFMT_SHOW_IDS) { snprintf(tmp, sizeof(tmp), "[0x%x]", st->id); str += tmp; } if (lang) { snprintf(tmp, sizeof(tmp), "(%s)", lang->value); str += tmp; } snprintf(tmp, sizeof(tmp), ": %s", buf); str += tmp; if (st->sample_aspect_ratio.num && av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio)) { AVRational display_aspect_ratio; av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den, st->codecpar->width * (int64_t)st->sample_aspect_ratio.num, st->codecpar->height * (int64_t)st->sample_aspect_ratio.den, 1024 * 1024); snprintf(tmp, sizeof(tmp), ", SAR %d:%d DAR %d:%d", st->sample_aspect_ratio.num, st->sample_aspect_ratio.den, display_aspect_ratio.num, display_aspect_ratio.den); str += tmp; } if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { int fps = st->avg_frame_rate.den && st->avg_frame_rate.num; int tbr = st->r_frame_rate.den && st->r_frame_rate.num; int tbn = st->time_base.den && st->time_base.num; if (fps || tbr || tbn) { snprintf(tmp, sizeof(tmp), "%s", separator); str += tmp; } if (fps) str += print_fps(av_q2d(st->avg_frame_rate), tbr || tbn ? "fps, " : "fps"); if (tbr) str += print_fps(av_q2d(st->r_frame_rate), tbn ? "tbr, " : "tbr"); if (tbn) str += print_fps(1 / av_q2d(st->time_base), "tbn"); } if (st->disposition & AV_DISPOSITION_DEFAULT) str += " (default)"; if (st->disposition & AV_DISPOSITION_DUB) str += " (dub)"; if (st->disposition & AV_DISPOSITION_ORIGINAL) str += " (original)"; if (st->disposition & AV_DISPOSITION_COMMENT) str += " (comment)"; if (st->disposition & AV_DISPOSITION_LYRICS) str += " (lyrics)"; if (st->disposition & AV_DISPOSITION_KARAOKE) str += " (karaoke)"; if (st->disposition & AV_DISPOSITION_FORCED) str += " (forced)"; if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED) str += " (hearing impaired)"; if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED) str += " (visual impaired)"; if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS) str += " (clean effects)"; if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) str += " (attached pic)"; if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS) str += " (timed thumbnails)"; if (st->disposition & AV_DISPOSITION_CAPTIONS) str += " (captions)"; if (st->disposition & AV_DISPOSITION_DESCRIPTIONS) str += " (descriptions)"; if (st->disposition & AV_DISPOSITION_METADATA) str += " (metadata)"; if (st->disposition & AV_DISPOSITION_DEPENDENT) str += " (dependent)"; if (st->disposition & AV_DISPOSITION_STILL_IMAGE) str += " (still image)"; str += "\n"; str += dump_metadata(st->metadata, " "); str += dump_sidedata(st, " "); return str; fail: return QString(""); } QString print_fps(double d, const char* postfix) { char tmp[BUFF_MAXLEN]; uint64_t v = lrintf(d * 100); if (!v) { snprintf(tmp, sizeof(tmp), "%1.4f %s", d, postfix); } else if (v % 100) { snprintf(tmp, sizeof(tmp), "%3.2f %s", d, postfix); } else if (v % (100 * 1000)) { snprintf(tmp, sizeof(tmp), "%1.0f %s", d, postfix); } else { snprintf(tmp, sizeof(tmp), "%1.0fk %s", d / 1000, postfix); } return QString(tmp); } QString dump_sidedata(const AVStream* st, const char* indent) { QString str; char tmp[BUFF_MAXLEN]; if (!st->codecpar) return str; if (st->codecpar->nb_coded_side_data) { snprintf(tmp, sizeof(tmp), "%sSide data:\n", indent); str += tmp; } for (int i = 0; i < st->codecpar->nb_coded_side_data; i++) { const AVPacketSideData* sd = &st->codecpar->coded_side_data[i]; snprintf(tmp, sizeof(tmp), "%s ", indent); str += tmp; switch (sd->type) { case AV_PKT_DATA_PALETTE: str += "palette"; break; case AV_PKT_DATA_NEW_EXTRADATA: str += "new extradata"; break; case AV_PKT_DATA_PARAM_CHANGE: str += "paramchange: "; // dump_paramchange(ctx, sd); break; case AV_PKT_DATA_H263_MB_INFO: str += "H.263 macroblock info"; break; case AV_PKT_DATA_REPLAYGAIN: str += "replaygain: "; // dump_replaygain(ctx, sd); break; case AV_PKT_DATA_DISPLAYMATRIX: { snprintf(tmp, sizeof(tmp), "displaymatrix: rotation of %.2f degrees", av_display_rotation_get((const int32_t*)sd->data)); str += tmp; } break; case AV_PKT_DATA_STEREO3D: str += "stereo3d: "; // dump_stereo3d(ctx, sd); break; case AV_PKT_DATA_AUDIO_SERVICE_TYPE: str += "audio service type: "; // dump_audioservicetype(ctx, sd); break; case AV_PKT_DATA_QUALITY_STATS: { snprintf(tmp, sizeof(tmp), "quality factor: %p, pict_type: %c", sd->data, av_get_picture_type_char(AVPictureType(sd->data[4]))); str += tmp; } break; case AV_PKT_DATA_CPB_PROPERTIES: str += "cpb: "; // dump_cpb(ctx, sd); break; case AV_PKT_DATA_MASTERING_DISPLAY_METADATA: // dump_mastering_display_metadata(ctx, sd); break; case AV_PKT_DATA_SPHERICAL: str += "spherical: "; // dump_spherical(ctx, st->codecpar, sd); break; case AV_PKT_DATA_CONTENT_LIGHT_LEVEL: // dump_content_light_metadata(ctx, sd); break; case AV_PKT_DATA_ICC_PROFILE: str += "ICC Profile"; break; case AV_PKT_DATA_DOVI_CONF: str += "DOVI configuration record: "; // dump_dovi_conf(ctx, sd); break; case AV_PKT_DATA_S12M_TIMECODE: str += "SMPTE ST 12-1:2014: "; // dump_s12m_timecode(ctx, st, sd); break; default: { snprintf(tmp, sizeof(tmp), "unknown side data type %d, size:%llu bytes", sd->type, sd->size); str += tmp; } break; } str += "\n"; } return str; }