精华内容
下载资源
问答
  • info
    千次阅读
    2021-02-18 10:23:19

    1.输出调试信息
    ROS自带了大量的能够输出调试信息的函数和宏,包括错误,警告等.它提供了如信息级别,条件触发消息和STL的流接口等方式.
    直接上例子,新建catkin工程,工程名为rosdebugtest

    #include <ros/ros.h>
    #include <ros/console.h>
    
    int main( int argc, char **argv )
    {
      ros::init( argc, argv, "rosdebug" );
      ros::NodeHandle n;
      ros::Rate rate( 1 );
    
      while( ros::ok() ) {
        ROS_INFO_STREAM_ONCE("loop start");
        ROS_DEBUG_STREAM( "DEBUG message." );
        ROS_INFO_STREAM ( "INFO message."  );
        ROS_WARN_STREAM ( "WARN message."  );
        ROS_ERROR_STREAM( "ERROR message." );
        ROS_FATAL_STREAM( "FATAL message." );
        ROS_INFO_STREAM_NAMED( "named_msg", "INFO named message." );
        ROS_INFO_STREAM_THROTTLE( 2, "INFO throttle message." );
        ros::spinOnce();
        rate.sleep();
      }
      return 1;
    }
    

    (1)调试级别c和c++用法
    其中 ROS_INFO(“INFO message %d”,k),相当于c中的printf;
    ROS_INFO_STREAM ( "INFO message." <<k);相当于c++中的cout;
    而调试的显示级别有5种
    DEBUG
    INFO
    WARN
    ERROR
    FATAL
    (2)为调试信息命名
    ROS_INFO_STREAM_NAMED( “named_msg”, “INFO named message.” );
    表示为这段信息命名,为了更容易知道这段信息来自那段代码.

    (3)设置显示频率

    ROS_INFO_STREAM_THROTTLE( 2, "INFO throttle message." );
    

    THROTTLE表示节流的意思,以上代码运行两次输出一次INFO throttle message.
    (4)单次显示
    ros中可以在循环中让信息只输出一次
    使用ROS_[_STREAM]_ONCE[_NAMED]

    ROS_INFO_STREAM_ONCE("loop start")
    

    2.使用rosconsole和设置显示级别
    ROS的调试信息可以用rosconsole查看和设置,用法如下
    (1)查看节点
    rosnode list
    (2)查看节点的log(节点名rosdebugtest)
    rosconsole list rosdebugtest
    输出如下
    ros
    ros.roscpp
    ros.roscpp.roscpp_internal
    ros.roscpp.superdebug
    ros.rosdebugtest
    ros.rosdebugtest.named_msg
    (3)查看log显示级别
    Usage: rosconsole get
    rosconsole get rosdebugtest ros.rosdebugtest.named_msg

    (4)动态设置log的显示级别

    Usage:rosconsole set <node> <logger> <level>
    

    rosconsole set rosdebugtest ros.rosdebugtest debug

    或者用图形化界面设置
    启动rqt_logger_level

    rosrun rqt_logger_level rqt_logger_level
    

    (5)在编译时设置源代码的调试级别
    由于编译时默认显示的是INFO级别,因此调试时为了看到Debug信息,需要在编译是设置调试级别。
    在 ros::init( argc, argv, “rosdebugtest” );后加入这一句就行了

    ros::console::set_logger_level(ROSCONSOLE_DEFAULT_NAME,ros::console::levels::Debug);
    

    (6)设置ros默认输出级别
    更改文件
    $ROS_ROOT/config/rosconsole.config
    文件内容如下

    # Set the default ros output to warning and higher
    log4j.logger.ros=WARN
    # Override my package to output everything
    log4j.logger.ros.my_package_name=DEBUG
    

    http://wiki.ros.org/rosconsole

    更多相关内容
  • iOS Info.plist知多少

    千次阅读 2020-08-01 21:38:27
    文章目录一、什么是Info.plist二、Info.plist长什么样1 Info.plist的打开方式2 每个key的含义三、Info.plist中的常见的keys类别1 Core Foundation Keys2 Lanch Services Keys3 Cocoa Keys4 App Extension Keys四、...

    一、什么是Info.plist

    iOS中很多功能需要配置Info.plist才能实现,如设置后台运行、支持打开的文件类型等。了解Info.plist中各字段及其含义,可以访问苹果开发网站相关文档,https://developer.apple.com/library/prerelease/ios/documentation/General/Reference/InfoPlistKeyReference/Introduction/Introduction.html#//apple_ref/doc/uid/TP40009248-SW1

    苹果官方对Info.plist的定义:
    To provide a better experience for users, iOS and macOS rely on the presence of special metadata in each app or bundle. This metadata is used in many different ways. Some of it is displayed to the user, some of it is used internally by the system to identify your app and the document types it supports, and some of it is used by the system frameworks to facilitate the launch of apps. The way an app provides its metadata to the system is through the use of a special file called an information property list file, or Info.plistfor short.
    A property list is a way to structure arbitrary data that the system can access at runtime. An information property list is a specialized type of property list that contains configuration data for a bundle. The keys and values in the file describe the various behaviors and configuration options you want applied to your bundle. An Xcode project template typically specifies an information property list file with an initial set of keys and appropriate default values. You can edit the file to change or add keys and values, as appropriate for your project.
    翻译:
    为了提供更好的用户体验,iOS 和 macOS 的每个app或bundle都依赖于特殊的元数据。元数据有多种用途,包括:直接向用户展示信息;系统内部用来标识你的app或其支持的文档类型;系统框架用来辅助app的加载等。这个提供给系统的元数据是通过一个名字叫 information property list file(属性列表文件)的特殊文件来实现的,这个特殊的文件简称 Info.plist
    Info.plist可用来构建任意数据,这些数据在运行时是可访问的。Info.plist是每个bundle的专属配置,Info.plist文件中的keys和values描述了许多要应用于该bundle的行为以及配置选项。Xcode工程通常会自动创建一个Info.plist,并且提供许多合适的keys以及其对应的默认的values。我们可以修改或增加keys和values。

    二、Info.plist长什么样

    1 Info.plist的打开方式

    XCode工程中打开
    在这里插入图片描述
    代码打开方式
    在这里插入图片描述

    2 每个key的含义

    属性名称类型描述
    CFBundleDevelopmentRegionLocalization native development regionString本地化相关数据,如果用户没有响应的语言资源,则默认使用这个key的value
    CFBundleExecutableExecutable fileString程序安装包的名称
    CFBundleIdentifierBundle indentifierString唯一标识字符串
    CFBundleInfoDictionaryVersionInfoDictionary versionStringInfo.plist格式的版本信息,XCode会自动填写,不要修改这个值
    CFBundleNameBundle nameString程序安装后在界面上显示的名称
    CFBundlePackageTypeBundle OS Type codeString例:BNDL、APPL、FMWK。详见官方说明
    CFBundleShortVersionStringBundle versions string, shortString版本号,三位,例:1.0.0。详见官方说明
    CFBundleTypeOSTypesBundle creator OS Type codeArray of strings该字段包含了一组映射到这个类型的四字母长的类型代码。为了打开所有类型的文档,可以把它设为 “****” 。
    CFBundleVersionBundle versionString应用程序版本号,每次部署应用程序的一个新版本时,应该增加这个编号,app store审核需要用
    LSRequiresIPhoneOSApplication require iPhone environmentBoolean用于指示程序包是否只能运行在iPhone OS 系统上。默认是YES
    UILaunchStoryboardNameLaunch screen interface file base nameString程序启动时的所加载的启动画面,主要成xib文件中加载,这里的值为LaunchScreen,说明从LaunchScreen.storyboard中加载
    UIMainStoryboardFileMian storyboard file base nameString程序的启动时的主画面,此文件中的视图将作为程序启动后的主画面
    UISupportedInterfaceOrientationsSupported interface orientationsArray of strings横竖屏设置,UIInterfaceOrientationPortrait,UIInterfaceOrientationPortraitUpsideDown,UIInterfaceOrientationLandscapeLeft,UIInterfaceOrientationLandscapeRight
    UIRequiredDeviceCapabilitiesRequired device capabilitiesArray of strings应用程序运行所需的设备限制,详见官方说明

    三、Info.plist中的常见的keys类别

    根据功能的分类,Info.plist文件的keys大致划分为以下几类

    1 Core Foundation Keys

    该类的keys的特点是以CF为前缀,用以代表Core Foundation,描述了一些常用的行为项
    详见官方文档
    https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html#//apple_ref/doc/uid/TP40009249-SW1

    2 Lanch Services Keys

    该类的keys的特点是以LS为前缀。加载服务项,提供了App加载所依赖的配置,描述了App启动的方式选择。
    详见官方文档
    https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW1

    常用的属性
    LSBackgroundOnly
    如果该字段存在并且被设为“ 1 ”,启动服务将只会运行在后台。您可以使用该字段来创建无用户界面的后台应用程序。如果您的应用程序使用了连接到窗口服务器 的高级框架,但并不需要显示出来,您也应该使用该字段。后台应用程序必须被编译成 Mach-O可执行文件。该选项不适用于CFM应用程序。
    您也可以指定该字段的类型为 BooleanNumber。然而,只有 Mac OS X 10.2或以上的版本才支持这些类型的值。

    LSPrefersCarbon
    如果该字段被设为“ 1 ”Finder将会在显示简介面板中显示“ 在 Classic 环境中打开 ”控制选项,缺省情况下该控件未被选中。如果需要,用户可以修改这个控制选项来在 Classic环境中启动应用程序。

    您也可以指定该字段的类型为 BooleanNumber。然而,只有 Mac OS X 10.2或以上的版本才支持这些类型的值。如果您在您的属性列表中加入了该字段,那么就不要同时加入 LSPrefersClassic, LSRequiresCarbon, 或LSRequiresClassic字段 。

    LSPrefersClassic
    如果该字段被设为“ 1 ”Finder将会在显示简介面板中显示“ 在 Classic 环境中打开 ”控制选项,缺省情况下该控件被选中。如果需要,用户可以修改这个控制选项来在 Carbon环境中启动应用程序。

    您也可以指定该字段的类型为 BooleanNumber。然而,只有Mac OS X 10.2或以上的版本才支持这些类型的值。如果您在您的属性列表中加入了该字段,那么就不要同时加入LSPrefersCarbon, LSRequiresCarbon, 或 LSRequiresClassic字段。

    LSRequiresCarbon
    如果该字段被设为 “ 1 ”,启动服务将只在 Carbon环境中运行应用程序。如果您的应用程序不应该运行在 Classic环境中的话,可以使用该字段。

    您也可以指定该字段的类型为BooleanNumber。然而,只有 Mac OS X 10.2或以上的版本才支持这些类型的值。如果您在您的属性列表中加入了该字段,那么就不要同时加入 LSPrefersCarbon, LSPrefersClassic, 或 LSRequiresClassic字段。

    LSRequiresClassic
    如果该字段被设为 “ 1 ”,启动服务将只在Classic环境中运行应用程序。如果您的应用程序不应该运行在Carbon兼容环境中的话,可以使用该字段。

    您也可以指定该字段的类型为 BooleanNumber。然而,只有 Mac OS X 10.2或以上的版本才支持这些类型的值。如果您在您的属性列表中加入了该字段,那么就不要同时加入 LSPrefersCarbon, LSPrefersClassic, 或 LSRequiresCarbon字段。

    LSUIElement
    如果该字段被设为“ 1 ”,启动服务会将该应用程序作为一个用户界面组件来运行。用户界面组件不会出现在 Dock或强制退出窗口中。虽然它们通常作为后台应 用程序运行,但是如果希望的话,它们也可以在前台显示一个用户界面。点击属于用户界面组件的窗口,应用程序将会处理产生的事件。
    Dock和登录窗口是两个用户界面组件应用程序。

    3 Cocoa Keys

    该类的keys的特点是以NS为前缀。Cocoa框架或Cocoa Touch框架依赖这些keys来标识更高级别的配置项目,如Appmain nib文件,主要类。这些key描述影响着CocoaCocoa Touch框架初始化和运行App的运行方式。
    详见官方文档
    https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW1
    常见的权限配置

    <!-- 相册 -->
    <key>NSPhotoLibraryUsageDescription</key>
    <string>需要您的同意,APP才能访问相册</string>
    
    <!-- 相机 -->
    <key>NSCameraUsageDescription</key>
    <string>需要您的同意,APP才能访问相机</string>
    
    <!-- 麦克风 -->
    <key>NSMicrophoneUsageDescription</key>
    <string>需要您的同意,APP才能访问麦克风</string>
    
    <!-- 位置 -->
    <key>NSLocationUsageDescription</key>
    <string>需要您的同意, APP才能访问位置</string>
    
    <!-- 在使用期间访问位置 -->
    <key>NSLocationWhenInUseUsageDescription</key>
    <string>App需要您的同意, APP才能在使用期间访问位置</string>
    
    <!-- 始终访问位置 -->
    <key>NSLocationAlwaysUsageDescription</key>
    <string>App需要您的同意, APP才能始终访问位置</string>
    
    <!-- 日历 -->
    <key>NSCalendarsUsageDescription</key>
    <string>App需要您的同意, APP才能访问日历</string>
    
    <!-- 提醒事项 -->
    <key>NSRemindersUsageDescription</key>
    <string>需要您的同意, APP才能访问提醒事项</string>
    
    <!-- 运动与健身 -->
    <key>NSMotionUsageDescription</key>
    <string>需要您的同意, APP才能访问运动与健身</string>
    
    <!-- 健康更新 -->
    <key>NSHealthUpdateUsageDescription</key>
    <string>需要您的同意, APP才能访问健康更新 </string>
    
    <!-- 健康分享 -->
    <key>NSHealthShareUsageDescription</key>
    <string>需要您的同意, APP才能访问健康分享</string>
    
    <!-- 蓝牙 -->
    <key>NSBluetoothPeripheralUsageDescription</key>
    <string>需要您的同意, APP才能访问蓝牙</string>
    
    <!-- 媒体资料库 -->
    <key>NSAppleMusicUsageDescription</key>
    <string>需要您的同意, APP才能访问媒体资料库</string>
    

    4 App Extension Keys

    App可能需要扩展默认的plist来描述更多的信息,如定制App启动后的默认旋转方向,标识App是否支持文件共享等等。
    详见官方文档
    https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/LaunchServicesKeys.html#//apple_ref/doc/uid/TP40009250-SW1

    四、读取Info.plist

    App运行的时候,需要读取Info.plist中的信息,用以下代码可获取整个Info.plist的信息

    [[NSBundle mainBundle] infoDictionary]
    

    例,根据 key值去取 URLTypes里面对应的值

    + (NSString *)URLSchemesForkey:(NSString *)key {
        
        NSDictionary *dict = [[NSBundle mainBundle] infoDictionary];
        NSArray *urlTypes = dict[@"CFBundleURLTypes"];
        NSString *urlSchemes = nil;
        for (NSDictionary *scheme in urlTypes) {
            NSString *schemeKey = scheme[@"CFBundleURLName"];
            if ([schemeKey isEqualToString:key]) {
                urlSchemes = scheme[@"CFBundleURLSchemes"][0];
                break;
            }
        }
        return urlSchemes;
    }
    
    展开全文
  • FFmpeg在调用avformat_open_input()之后,可能码流信息不够完整,可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。

    FFmpeg在调用avformat_open_input()之后,可能码流信息不够完整,可以使用avformat_find_stream_info()获取更多的码流信息。比如获取视频帧率、视频宽高,重新计算最大分析时长,打开解码器解码获取codec数据。具体流程如下图所示:

    avformat_find_stream_info方法位于libavformat/utils.c,具体代码如下(部分删减):

    int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
    {
        // 针对flv/mpeg/mpegts格式,max_analyze_duration另外赋值
        if (!max_analyze_duration) {
            max_stream_analyze_duration =
            max_analyze_duration        = 5*AV_TIME_BASE;
            max_subtitle_analyze_duration = 30*AV_TIME_BASE;
            if (!strcmp(ic->iformat->name, "flv"))
                max_stream_analyze_duration = 90*AV_TIME_BASE;
            if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
                max_stream_analyze_duration = 7*AV_TIME_BASE;
        }
    
        for (i = 0; i < ic->nb_streams; i++) {
            // 查找解码器
            codec = find_probe_decoder(ic, st, st->codecpar->codec_id);
            // 确保字幕头信息已正确设置
            if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE
                && codec && !avctx->codec) {
                if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0)
                    av_log(ic, AV_LOG_WARNING, "Failed to open codec in %s\n",__FUNCTION__);
            }
            // 尝试打开解码器
            if (!has_codec_parameters(st, NULL) && st->internal->request_probe <= 0) {
                if (codec && !avctx->codec)
                    if (avcodec_open2(avctx, codec, options ? &options[i] : &thread_opt) < 0)
                        av_log(ic, AV_LOG_WARNING, "Failed to open codec in %s\n",__FUNCTION__);
            }
        }
        ......
        for (;;) {
            for (i = 0; i < ic->nb_streams; i++) {
                int fps_analyze_framecount = 20;
                int count;
    
                st = ic->streams[i];
                if (!has_codec_parameters(st, NULL))
                    break;
                // 如果时间基是粗糙的,比如mkv采用ms作为单位
                // 需要分析更多帧数据来获取帧率
                if (av_q2d(st->time_base) > 0.0005)
                    fps_analyze_framecount *= 2;
                if (!tb_unreliable(st->internal->avctx))
                    fps_analyze_framecount = 0;
                if (ic->fps_probe_size >= 0)
                    fps_analyze_framecount = ic->fps_probe_size;
                if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
                    fps_analyze_framecount = 0;
                count = (ic->iformat->flags & AVFMT_NOTIMESTAMPS) ?
                           st->internal->info->codec_info_duration_fields/2 :
                           st->internal->info->duration_count;
                if (!(st->r_frame_rate.num && st->avg_frame_rate.num) &&
                    st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                    if (count < fps_analyze_framecount)
                        break;
                }
                // 如果存在帧延时,查看前3帧
                if (st->internal->info->frame_delay_evidence && count < 2 && st->internal->avctx->has_b_frames == 0)
                    break;
                if (!st->internal->avctx->extradata &&
                    (!st->internal->extract_extradata.inited ||
                     st->internal->extract_extradata.bsf) &&
                    extract_extradata_check(st))
                    break;
                if (st->first_dts == AV_NOPTS_VALUE &&
                    !(ic->iformat->flags & AVFMT_NOTIMESTAMPS) &&
                    st->codec_info_nb_frames < ((st->disposition & AV_DISPOSITION_ATTACHED_PIC) ? 1 : ic->max_ts_probe) &&
                    (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO ||
                     st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO))
                    break;
            }
    
            // 读取超过指定最大数据量,但还没获取到足够的解码器信息
            if (read_size >= probesize) {
                ret = count;
                av_log(ic, AV_LOG_DEBUG,
                       "Probe buffer size limit of %"PRId64" bytes reached\n", probesize);
                for (i = 0; i < ic->nb_streams; i++)
                    if (!ic->streams[i]->r_frame_rate.num &&
                        ic->streams[i]->internal->info->duration_count <= 1 &&
                        ic->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
                        strcmp(ic->iformat->name, "image2"))
                        av_log(ic, AV_LOG_WARNING,
                               "Stream #%d: not enough frames to estimate rate; "
                               "consider increasing probesize\n", i);
                break;
            }
            // 读取一帧数据
            ret = read_frame_internal(ic, pkt1);
            ......
            if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
                // 检测非单调递增的dts
                if (st->internal->info->fps_last_dts != AV_NOPTS_VALUE &&
                    st->internal->info->fps_last_dts >= pkt->dts) {
                    av_log(ic, AV_LOG_DEBUG,
                           "Non-increasing DTS in stream %d: packet %d with DTS "
                           "%"PRId64", packet %d with DTS %"PRId64"\n",
                           st->index, st->internal->info->fps_last_dts_idx,
                           st->internal->info->fps_last_dts, st->codec_info_nb_frames,
                           pkt->dts);
                    st->internal->info->fps_first_dts =
                    st->internal->info->fps_last_dts  = AV_NOPTS_VALUE;
                }
    			// 检测不连续的dts:序列的平均帧时长是否超过1000帧存在差异
                if (st->internal->info->fps_last_dts != AV_NOPTS_VALUE &&
                    st->internal->info->fps_last_dts_idx > st->internal->info->fps_first_dts_idx &&
                    (pkt->dts - (uint64_t)st->internal->info->fps_last_dts) / 1000 >
                    (st->internal->info->fps_last_dts     - (uint64_t)st->internal->info->fps_first_dts) /
                    (st->internal->info->fps_last_dts_idx - st->internal->info->fps_first_dts_idx)) {
                    st->internal->info->fps_first_dts =
                    st->internal->info->fps_last_dts  = AV_NOPTS_VALUE;
                }
                // 更新dts
                if (st->internal->info->fps_first_dts == AV_NOPTS_VALUE) {
                    st->internal->info->fps_first_dts     = pkt->dts;
                    st->internal->info->fps_first_dts_idx = st->codec_info_nb_frames;
                }
                st->internal->info->fps_last_dts     = pkt->dts;
                st->internal->info->fps_last_dts_idx = st->codec_info_nb_frames;
            }
            // 提取额外数据数组
            if (!st->internal->avctx->extradata) {
                ret = extract_extradata(st, pkt);
                if (ret < 0)
                    goto unref_then_goto_end;
            }
            // 如果仍然没有信息,则打开解码器尝试解码一帧
            // 针对MPEG-4, 需要为QuickTime进行解码
            try_decode_frame(ic, st, pkt,
                             (options && i < orig_nb_streams) ? &options[i] : NULL);
    
            if (ic->flags & AVFMT_FLAG_NOBUFFER)
                av_packet_unref(pkt1);
    
            st->codec_info_nb_frames++;
            count++;
        }
        if (flush_codecs) {
            for (i = 0; i < ic->nb_streams; i++) {
                st = ic->streams[i];
                // 刷新解码器,尝试解码一帧数据
                if (st->internal->info->found_decoder == 1) {
                    do {
                        err = try_decode_frame(ic, st, empty_pkt,
                                                (options && i < orig_nb_streams)
                                                ? &options[i] : NULL);
                    } while (err > 0 && !has_codec_parameters(st, NULL));
                }
            }
        }
        ......
        for (i = 0; i < ic->nb_streams; i++) {
            st = ic->streams[i];
            avctx = st->internal->avctx;
            if (avctx->codec_type == AVMEDIA_TYPE_VIDEO) {
                if (avctx->codec_id == AV_CODEC_ID_RAWVIDEO && !avctx->codec_tag && !avctx->bits_per_coded_sample) {
                    uint32_t tag= avcodec_pix_fmt_to_codec_tag(avctx->pix_fmt);
                    if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == avctx->pix_fmt)
                        avctx->codec_tag= tag;
                }
    
                // 如果demuxer没有设置帧率,则估算平均帧率
                if (st->internal->info->codec_info_duration_fields &&
                    !st->avg_frame_rate.num &&
                    st->internal->info->codec_info_duration) {
                    int best_fps      = 0;
                    double best_error = 0.01;
                    AVRational codec_frame_rate = avctx->framerate;
    
                    if (st->internal->info->codec_info_duration        >= INT64_MAX / st->time_base.num / 2||
                        st->internal->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
                        st->internal->info->codec_info_duration        < 0)
                        continue;
                    av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                              st->internal->info->codec_info_duration_fields * (int64_t) st->time_base.den,
                              st->internal->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
    
                    // 推测标准帧率
                    for (j = 0; j < MAX_STD_TIMEBASES; j++) {
                        AVRational std_fps = { get_std_framerate(j), 12 * 1001 };
                        double error       = fabs(av_q2d(st->avg_frame_rate) /
                                                  av_q2d(std_fps) - 1);
    
                        if (error < best_error) {
                            best_error = error;
                            best_fps   = std_fps.num;
                        }
                        if (ic->internal->prefer_codec_framerate && codec_frame_rate.num > 0 && codec_frame_rate.den > 0) {
                            error       = fabs(av_q2d(codec_frame_rate) /
                                               av_q2d(std_fps) - 1);
                            if (error < best_error) {
                                best_error = error;
                                best_fps   = std_fps.num;
                            }
                        }
                    }
                    if (best_fps)
                        av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
                                  best_fps, 12 * 1001, INT_MAX);
                }
                // 计算帧率的分子与分母
                if (!st->r_frame_rate.num) {
                    if (    avctx->time_base.den * (int64_t) st->time_base.num
                        <= avctx->time_base.num * (uint64_t)avctx->ticks_per_frame * st->time_base.den) {
                        av_reduce(&st->r_frame_rate.num, &st->r_frame_rate.den,
                                  avctx->time_base.den, (int64_t)avctx->time_base.num * avctx->ticks_per_frame, INT_MAX);
                    } else {
                        st->r_frame_rate.num = st->time_base.den;
                        st->r_frame_rate.den = st->time_base.num;
                    }
                }
    			// 计算宽高比
                if (st->internal->display_aspect_ratio.num && st->internal->display_aspect_ratio.den) {
                    AVRational hw_ratio = { avctx->height, avctx->width };
                    st->sample_aspect_ratio = av_mul_q(st->internal->display_aspect_ratio,
                                                       hw_ratio);
                }
            }
        }
        ......
    }

    1、最大分析时长

    max_analyze_duration用于设定探测格式阶段的分析时长,如果最大分析时长没有被赋值,默认值如下:

    max_stream_analyze_duration = 5*AV_TIME_BASE;
    max_subtitle_analyze_duration = 30*AV_TIME_BASE;
    if (!strcmp(ic->iformat->name, "flv"))
        max_stream_analyze_duration = 90*AV_TIME_BASE;
    if (!strcmp(ic->iformat->name, "mpeg") || !strcmp(ic->iformat->name, "mpegts"))
        max_stream_analyze_duration = 7*AV_TIME_BASE;

    2、查找解码器

    使用find_probe_decoder()来查找解码器,具体代码如下:

    static const AVCodec *find_probe_decoder(AVFormatContext *s, const AVStream *st, enum AVCodecID codec_id)
    {
        const AVCodec *codec;
    
    #if CONFIG_H264_DECODER
        if (codec_id == AV_CODEC_ID_H264)
            return avcodec_find_decoder_by_name("h264");
    #endif
    
        codec = find_decoder(s, st, codec_id);
        if (!codec)
            return NULL;
    
        if (codec->capabilities & AV_CODEC_CAP_AVOID_PROBING) {
            const AVCodec *probe_codec = NULL;
            void *iter = NULL;
            while ((probe_codec = av_codec_iterate(&iter))) {
                if (probe_codec->id == codec->id &&
                        av_codec_is_decoder(probe_codec) &&
                        !(probe_codec->capabilities & (AV_CODEC_CAP_AVOID_PROBING | AV_CODEC_CAP_EXPERIMENTAL))) {
                    return probe_codec;
                }
            }
        }
    
        return codec;
    }

    3、打开解码器

    使用avcodec_open2()打开解码器,传递三个参数:AVCodecContext、AVCodec、AVOption。分两种情况:字幕流和缺少codec参数。

    3.1 字幕流

    如果是字幕流,打开解码器,确保字幕头信息已经正确设置。

    3.2 没有codec参数

    如果缺少codec参数,也会打开解码器,来获取codec相关参数。判断是否有codec参数方法如下:

    static int has_codec_parameters(AVStream *st, const char **errmsg_ptr)
    {
        AVCodecContext *avctx = st->internal->avctx;
    
    #define FAIL(errmsg) do {                                         \
            if (errmsg_ptr)                                           \
                *errmsg_ptr = errmsg;                                 \
            return 0;                                                 \
        } while (0)
    
        if (   avctx->codec_id == AV_CODEC_ID_NONE
            && avctx->codec_type != AVMEDIA_TYPE_DATA)
            FAIL("unknown codec");
        switch (avctx->codec_type) {
        case AVMEDIA_TYPE_AUDIO:
            if (!avctx->frame_size && determinable_frame_size(avctx))
                FAIL("unspecified frame size");
            if (st->internal->info->found_decoder >= 0 &&
                avctx->sample_fmt == AV_SAMPLE_FMT_NONE)
                FAIL("unspecified sample format");
            if (!avctx->sample_rate)
                FAIL("unspecified sample rate");
            if (!avctx->channels)
                FAIL("unspecified number of channels");
            if (st->internal->info->found_decoder >= 0 && !st->internal->nb_decoded_frames && avctx->codec_id == AV_CODEC_ID_DTS)
                FAIL("no decodable DTS frames");
            break;
        case AVMEDIA_TYPE_VIDEO:
            if (!avctx->width)
                FAIL("unspecified size");
            if (st->internal->info->found_decoder >= 0 && avctx->pix_fmt == AV_PIX_FMT_NONE)
                FAIL("unspecified pixel format");
            if (st->codecpar->codec_id == AV_CODEC_ID_RV30 || st->codecpar->codec_id == AV_CODEC_ID_RV40)
                if (!st->sample_aspect_ratio.num && !st->codecpar->sample_aspect_ratio.num && !st->codec_info_nb_frames)
                    FAIL("no frame in rv30/40 and no sar");
            break;
        case AVMEDIA_TYPE_SUBTITLE:
            if (avctx->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && !avctx->width)
                FAIL("unspecified size");
            break;
        case AVMEDIA_TYPE_DATA:
            if (avctx->codec_id == AV_CODEC_ID_NONE) return 1;
        }
    
        return 1;
    }

    检测音频:sample_rate 、sample_format、channels、frame_size;

    检测视频:width、pixel_format、sample_aspect_ratio;

    检测字幕:如果是PGS图像字幕流,检测width;

    4、读取一帧数据

    使用read_frame_internal()来读取一帧数据,内部调用ff_read_packet()和parse_packet(),主要过程是读取packet进行组包,最终读取到一个完整帧。

    5、检测dts

    检测非单调递增的dts,如果dts不是单调递增,那么判定码流有问题。检测不连续的dts,如果序列的平均帧时长超过1000帧存在差异,那么判定dts不连续。最终更新dts。

    6、解码一帧

    调用try_decode_frame()进行解码,具体代码如下:

    static int try_decode_frame(AVFormatContext *s, AVStream *st,
                                const AVPacket *avpkt, AVDictionary **options)
    {
        ......
        if (!avcodec_is_open(avctx) &&
            st->internal->info->found_decoder <= 0 &&
            (st->codecpar->codec_id != -st->internal->info->found_decoder || !st->codecpar->codec_id)) {
            AVDictionary *thread_opt = NULL;
    
            codec = find_probe_decoder(s, st, st->codecpar->codec_id);
    
            if (!codec) {
                st->internal->info->found_decoder = -st->codecpar->codec_id;
                ret                     = -1;
                goto fail;
            }
    
            av_dict_set(options ? options : &thread_opt, "threads", "1", 0);
            av_dict_set(options ? options : &thread_opt, "lowres", "0", 0);
            if (s->codec_whitelist)
                av_dict_set(options ? options : &thread_opt, "codec_whitelist", s->codec_whitelist, 0);
            ret = avcodec_open2(avctx, codec, options ? options : &thread_opt);
            if (!options)
                av_dict_free(&thread_opt);
            if (ret < 0) {
                st->internal->info->found_decoder = -avctx->codec_id;
                goto fail;
            }
            st->internal->info->found_decoder = 1;
        } else if (!st->internal->info->found_decoder)
            st->internal->info->found_decoder = 1;
        ......
        while ((pkt.size > 0 || (!pkt.data && got_picture)) &&
               ret >= 0 &&
               (!has_codec_parameters(st, NULL) || !has_decode_delay_been_guessed(st) ||
                (!st->codec_info_nb_frames &&
                 (avctx->codec->capabilities & AV_CODEC_CAP_CHANNEL_CONF)))) {
            got_picture = 0;
            if (avctx->codec_type == AVMEDIA_TYPE_VIDEO ||
                avctx->codec_type == AVMEDIA_TYPE_AUDIO) {
                ret = avcodec_send_packet(avctx, &pkt);
                if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
                    break;
                if (ret >= 0)
                    pkt.size = 0;
                ret = avcodec_receive_frame(avctx, frame);
                if (ret >= 0)
                    got_picture = 1;
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                    ret = 0;
            } else if (avctx->codec_type == AVMEDIA_TYPE_SUBTITLE) {
                ret = avcodec_decode_subtitle2(avctx, &subtitle,
                                               &got_picture, &pkt);
                if (got_picture)
                    avsubtitle_free(&subtitle);
                if (ret >= 0)
                    pkt.size = 0;
            }
            if (ret >= 0) {
                if (got_picture)
                    st->internal->nb_decoded_frames++;
                ret       = got_picture;
            }
        }
        ......
        return ret;
    }

    7、计算帧率

    针对mkv格式,时间基单位是ms,需要读取更多帧来计算帧率。针对有帧延时情况,读取前3帧。然后计算帧率的分子与分母。

    展开全文
  • PageInfo介绍及使用

    千次阅读 2021-12-12 20:30:05
    "男":"女"} ${epm.email} ${epm.department.depName} 分页文字信息 --%> 当前第${pageInfo.pageNum}页,共有${pageInfo.pages}页,总计${pageInfo.total}条记录 ← Previous ${num} Next → 全整合完报错了 ...

    1. MyBatis分页插件-PageHelper的配置

    导入依赖

            <!--引入PageHelper分页插件 → PageHelper-->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper</artifactId>
                <version>5.0.0</version>
            </dependency>

     mybatis-config.xml全局配置文件中配置拦截器插件

    注意

     plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
        properties?, settings?, 
        typeAliases?, typeHandlers?, 
        objectFactory?,objectWrapperFactory?, 
        plugins?, 
        environments?, databaseIdProvider?, mappers?

    注意顺序

    
    <plugins>
        <!-- com.github.pagehelper为PageHelper类所在包名 -->
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
    	</plugin>
    </plugins>
    

     2. 分页的使用

    Controller

       //查询全部的书籍,并且返回到一个书籍展示界面
        @RequestMapping("/allBook")
        public String list(@RequestParam(value = "pn", required = true, defaultValue = "1") Integer pn, Model model){
            //在查询之前调用,传入pn默认值是1,pageSize是5,意思是从第1页开始,每页显示5条记录。
            PageHelper.startPage(pn, 5);
            //startPage后面紧跟查询就是一个分页查询。
            List<Books> list = bookService.queryAllBook();
            //使用PageInfo包装查询后的结果,只需要将PageInfo交给页面就行。
            //封装了详细的分页信息,包括我们查询出来的数据userList,传入连续显示的页数5。
            PageInfo<Books> page = new PageInfo<Books>(list, 5);
            model.addAttribute("pageInfo",page);//返回给前端展示
            return "allBook";
        }

    service

    //select all book
        List<Books> queryAllBook();

     3. 页面使用

    1). 头部引用
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    2). 页面使用

        <div>
            <c:forEach items="${pageInfo.list}" var="books">
                <tr>
                    <th> ${epm.empId} </th>
                    <th> ${epm.empName} </th>
                    <th> ${epm.gender == "M"?"男":"女"}</th>
                    <th> ${epm.email}</th>
                    <th> ${epm.department.depName}</th>
                </tr>
            </c:forEach>
        </div>
       <%-- 分页文字信息 --%>
            <div class="col-md-6">
                当前第<span class="badge">${pageInfo.pageNum}</span>页,共有<span class="badge">${pageInfo.pages}</span>页,总计<span
                    class="badge">${pageInfo.total}</span>条记录
            </div>
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    <li class="prev"><a href="${pageContext.request.contextPath}/book/allBook?pn=${pageInfo.pageNum-1}">← Previous</a></li>
                    <li class="${num == pageInfo.pageNum?'active':''}">
                        <c:forEach items="${pageInfo.navigatepageNums}" var="num">
                            <a href="${pageContext.request.contextPath}/book/allBook?pn=${num}">${num}</a>
                        </c:forEach>
                    </li>
                    <li class="next"><a href="${pageContext.request.contextPath}/book/allBook?pn=${pageInfo.pageNum+1}">Next → </a></li>
                </ul>
            </nav>

    全整合完报错了 ClassNotFoundException: com.github.pagehelper.PageInterceptor

    转载请注明出处:Enterprising boy亲笔。

    解决方法看下一篇博客 、

    ClassNotFoundException: com.github.pagehelper.PageInterceptor_wangZY的博客-CSDN博客idea解决maven导入pageHelper插件,启动tomcat报ClassNotFoundException: com.github.pagehelper.PageInterceptor问题_C_bianchengxiaobai的博客-CSDN博客转载自idea解决maven导入pageHelper插件,启动tomcat报ClassNotFoundException: com.github.pagehelper.PageInterceptor问题_C_bianchengxiaobai的博客-CSDhttps://blog.csdn.net/NMdemon/article/details/121893293

    展开全文
  • PageInfo实现快速分页查询

    万次阅读 2020-08-17 16:33:25
    4.service中的接口getDemandList 条件getDemandList 页数pageNum 每页条数显示pageSize public PageInfo getDemandList(DemandQueryCriteria getDemandList,Integer pageNum,Integer pageSize); 5.在service的实现...
  • 前言 ... Logback:同时按照日期和大小分割日志(最新日志可以不带日期或数字) logback配置 ...xml version="1.0" ...-- 从高到地低 OFF 、 FATAL 、 ERROR 、 WARN 、 INFO 、 DEBUG 、 TRACE 、 ALL --> &l...
  • Python是进行数据分析的一...Pandas dataframe.info()函数用于获取 DataFrame 的简要摘要。在对数据进行探索性分析时,它非常方便。为了快速浏览数据集,我们使用dataframe.info()功能。用法: DataFrame.info(verb...
  • 使用Python中的data.info()显示所有信息

    万次阅读 2021-03-17 16:06:33
    我会显示我的数据框的所有信息,其中包含来自熊猫的.info()超过100列,但它不会:data_train.info()RangeIndex: 85529 entries, 0 to 85528Columns: 110 entries, ID to TARGETdtypes: float64(40), int64(19), ...
  • 使用PageInfo把list手动进行分页处理

    千次阅读 2020-04-21 15:12:22
    在项目中经常会遇到对list列表进行分页处理,这时就不能使用PageInfo在查询数据库时分页,需要查询全部之后,进行手动分页。 从PageInfo的类中可以看出,PageInfo里面包含各个参数,各个参数的含义如下: //当前...
  • 如何使用log.info打印日志

    万次阅读 2021-10-18 10:43:57
    // (2) public void sub() { log.info("Hello Logging World"); // (3) } public static void main(String[] args) { HelloLogWorld logWorld = new HelloLogWorld(); logWorld.sub(); } } Logger logger=Logger....
  • Java学习系列:package-info.java的作用

    万次阅读 多人点赞 2020-05-06 13:59:39
    学习开源源码的时候,发现每个目录都有一个package-info.java文件,查找其用法,并整理总结。
  • Linux命令之info命令

    千次阅读 2020-06-12 16:23:42
    声明:原文链接一步一步学Linux——info命令(17) info命令 命令概述 info命令是Linux下info格式的帮助指令。阅读 info 格式的文档。 就内容来说,info页面比man page编写得要更好、更容易理解,也更友好,但man ...
  • PageHelper分页插件及PageInfo介绍及使用

    万次阅读 多人点赞 2020-02-21 17:19:26
    PageInfo介绍及使用 1.MyBatis分页插件-PageHelper的配置与应用 2.参考封装PageInfo类 3.PageInfo属性表 下载PageInfo文档 1.MyBatis分页插件-PageHelper的配置与应用 pom.xml 引入依赖: <!-- ...
  • definition.info["class"]): self.definitions[definition.info["name"]] = definition logging.info("Loaded %s" % name) 我试着理解这段代码在做什么,我已经成功到了一定程度。但是,我完全无法理解代码的后面...
  • WEB-INFO和META-INFO作用说明

    千次阅读 2020-03-13 10:02:04
    /WEB-INF/web.xml Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。 /WEB-INF/classes/ 包含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中。...
  • 在使用Mybatis查询数据库展示到前端的过程中不可避免的要考虑到分页问题,这时就引入了Mybatis的PageHelper插件,这个插件对分页功能进行了强有力的...public PageInfo<Po> pageList(Integer pageNum, Intege...
  • 手动PageInfo分页

    万次阅读 2020-04-15 15:45:09
    项目中有时候遇到list多种来源,不能使用PageInfo在查询数据库时分页,需要查询全部之后,手动分页。 //手动分页的分割起始下标 Integer fromIndex = 0; //手动分页的分割结尾下标 Integer toIndex = 0; ...
  • PageInfo对处理过的list进行分页

    千次阅读 2020-05-01 22:24:21
    } PageInfo<T> pageInfo = new PageInfo(pageResult); //获取PageInfo其他参数 pageInfo.setTotal(arrayList.size()); int endRow = pageInfo.getEndRow() == 0 ? 0 : (pageNum - 1) * pageSize + pageInfo....
  • Pandas中的info()函数与describe()函数

    万次阅读 多人点赞 2020-03-14 17:42:32
    对于这两个函数,我首先抛出官网的解释:info()函数和describe()函数 1.   info()函数\color{red}{1.\,\,\,info()函数}1.info()函数 DataFrame.info(self, verbose=None, buf=None, max_cols=None, memory_usage=...
  • logging.basicConfig(filename=train_log, format='%(asctime)s - %(name)s - %(levelname)s -%(module)s: %(message)s', ... level=logging.INFO) logger = logging.getLogger() KZT = logging.Str.
  • labelme:缺少生成"info.yaml"文件

    千次阅读 多人点赞 2020-02-21 13:05:58
    .json"操作后,缺少生成info.yaml文件(已解决) 最近起步图像识别,打算构建需要的图形数据集,pip安装labelme==4.2.9版本,利用anaconda+Pycharm编写代码,生成Python==3.6的环境,具体安装方法请参考(不得不说这...
  • 安装软件时出现Command "python setup.py egg_info" failed with error code 1 in /tmp的解决方案
  • 解决Logger没有info输出的问题

    万次阅读 2020-03-05 20:51:53
    解决Logger没有info输出的问题 背景 最近在写爬虫的时候,打算使用logging来输出中间的一些过程,查看过程是否出现错误。写好一个logging类,用于记录。开始测试的好好地,后来进行了一些修改,导致没办法输出info...
  • info级别日志与debug

    千次阅读 2020-10-21 23:45:32
    日志默认info级别debug日志不会打印,但是会执行日志填充的数据 例如:logger.debug("日志输出",2*10); 1. 2*10会先执行出结果,然后继续往下走 2. 在ch.qos.logback.classic.Logger#filterAndLog_1方法中判断...
  • 分页插件中关于PageInfo

    万次阅读 2018-07-19 09:55:19
    //使用分页插件 //传入查询的页码,以及显示的条数 PageHelper.startPage(pn,5); List&amp;amp;lt;Employee&amp;... emps = employeeService.get... //使用pageInfo包装查询后的结果,封装了详细查询数据,...
  • tcp_info说明

    千次阅读 2019-07-14 09:11:03
    以下摘抄 Linux内核源码剖析-...struct tcp_info { __u8 tcpi_state; //tcp state: TCP_SYN_SENT,TCP_SYN_RECV,TCP_FIN_WAIT1,TCP_CLOSE etc __u8 tcpi_ca_state; //congestion state: __u8 tcpi_retransmits...
  • sharding异常之no table route info

    万次阅读 2019-10-17 20:54:07
    ### Cause: java.lang.IllegalStateException: no table route info at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30) at org.apache.ibatis.ses...
  • ROS INFO()的用法

    万次阅读 2020-09-07 16:18:59
    1.ROS_INFO_STREAM(“Hello ROS”),输出字符串 2.ROS_INFO(“s%”, msg.data.c_str()),输出一个字符串变量 3.ROS_INFO(“I heard: [s%]”, msg.data.c_str()),输出一个字符串变量,这里的中括号不是必须的,输出时会...
  • list实现PageInfo方式手动分页

    千次阅读 2019-07-09 16:18:52
    项目中有时候遇到list多种来源,不能使用PageInfo在查询数据库时分页,需要查询全部之后,手动分页。 //list自定义排序 ComparatorUser comparator=new ComparatorUser(); Collections.sort(list, comparator); ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,731,431
精华内容 1,092,572
关键字:

info

友情链接: 突破.zip