• ffmpeg函数使用


    一. av_guess_format()函数

    原型

    AVOutputFormat *av_guess_format(const char *short_name,
                                    const char *filename,
                                    const char *mime_type);
     

    av_guess_format中支持的short_name格式可以通过下面命令获取

    [root@node_94 cmake-build-debug-remote-host]# ffmpeg -formats|grep jpeg
    ffmpeg version 3.4.7 Copyright (c) 2000-2019 the FFmpeg developers
      built with gcc 4.8.5 (GCC) 20150623 (Red Hat 4.8.5-39)
      configuration: --enable-gpl --enable-libx264 --enable-vaapi --enable-avresample --enable-shared --enable-cuda --enable-cuvid --enable-nvenc --enable-nonfree --enable-libnpp --extra-cflags=-I/usr/local/cuda/include --extra-ldflags=-L/usr/local/cuda/lib64
      libavutil      55. 78.100 / 55. 78.100
      libavcodec     57.107.100 / 57.107.100
      libavformat    57. 83.100 / 57. 83.100
      libavdevice    57. 10.100 / 57. 10.100
      libavfilter     6.107.100 /  6.107.100
      libavresample   3.  7.  0 /  3.  7.  0
      libswscale      4.  8.100 /  4.  8.100
      libswresample   2.  9.100 /  2.  9.100
      libpostproc    54.  7.100 / 54.  7.100
     D  jpeg_pipe       piped jpeg sequence
     D  jpegls_pipe     piped jpegls sequence
     DE mjpeg           raw MJPEG video
     D  mjpeg_2000      raw MJPEG 2000 video
     DE mpjpeg          MIME multipart JPEG
      E singlejpeg      JPEG single image
     DE smjpeg          Loki SDL MJPEG
    [root@node_94 cmake-build-debug-remote-host]#

    可以看出jpeg编码支持的格式为

    mjpeg
    mpjpeg
    singlejpeg
    smjpeg

    二. av_log_set_callback

    在使用FFMPEG库的时候,如果有使用上的错误,FFMPEG 通过av_log 可以打印相应的消息到标准输出里。但有时候我们并没有标准输出,那么这个时候应该怎么处理呢?

    方法:使用 av_log_set_callback 获取 av_log的打印输出。

    示例如下:

      void Init()
        {
        	av_log_set_callback(&FFMPEG_Callback);
        }
         
        void FFMPEG_Callback(void* ptr, int level, const char* fmt, va_list vl)
        {
        	// 可以根据level的级别选择要打印显示的内容
        	if (level <= AV_LOG_INFO)
        	{
        		char buffer[1024];
        		vsprintf(buffer, fmt, vl);
         
        		LOG_A("msg : [%d] %s", level, buffer);
        	}
        }

    三. avformat_open_input阻塞操作中断的支持

    avformat_open_input默认是阻塞操作,如果不加控制,等待时间可能会达到30s以上,对于有些情况,等待30s的体验是无法接受的。

    ffmpeg支持interrupt_callback机制,可以对输入(或输出)的AVFormatContext的interrupt_callback成员设置,然后再回调函数中做控制。

    // 回调函数的参数,用了时间
    typedef struct {
        time_t lasttime;
    } Runner;
     
    // 回调函数
    static int interrupt_callback(void *p) {
        Runner *r = (Runner *)p;
        if (r->lasttime > 0) {
            if (time(NULL) - r->lasttime > 8) {
                // 等待超过8s则中断
                return 1;
            }
        }
         
        return 0;
    }
     
     
    // usage
    Runner input_runner = {0};
     
    AVFormatContext *ifmt_ctx = avformat_alloc_context();
    ifmt_ctx->interrupt_callback.callback = interrupt_callback;
    ifmt_ctx->interrupt_callback.opaque = &input_runner;
     
    input_runner.lasttime = time(NULL);
    // 调用之前初始化时间
    ret = avformat_open_input(&ifmt_ctx, url, NULL, NULL);
    if(ret < 0) {
        // error
    }

    特别提醒: rtsp 可以使用 timeout 配置参数, rtmp 使用timeout 配置参数会报错(ffmpeg bug), 所以只能使用 回调来结束 avformat_open_input的阻塞行为

    四. av_free_packet和av_packet_unref

    都使用了av_buffer_unref,该函数将缓存空间的引用计数-1,并将Packet中的其他字段设为初始值。如果引用计数为0,自动的释放缓存空间

    //与av_packet_free不同的是,传递参数不同,av_packet_free会销毁本身
    //较新版本ffmpeg可以使用av_packet_unref代替
    void av_free_packet(AVPacket *pkt)
    {
        if (pkt) {
            if (pkt->buf)
                av_buffer_unref(&pkt->buf);
            pkt->data            = NULL;
            pkt->size            = 0;
            av_packet_free_side_data(pkt);
        }
    }
    
    void av_packet_unref(AVPacket *pkt)
    {
        av_packet_free_side_data(pkt);
        av_buffer_unref(&pkt->buf);
        av_init_packet(pkt);
        pkt->data = NULL;
        pkt->size = 0;
    }

    五. av_image_fill_arrays

    说明FFmepg3.4版本

    需求

        创建一个BGR24的AVFrame帧,用于YUV420转换BGR24帧

    代码

      AVFrame *pBGRFrame = NULL;
      pBGRFrame = av_frame_alloc();
      uint8_t *pszBGRBuffer = NULL;
      int nBGRFrameSize;
      nBGRFrameSize = av_image_get_buffer_size(AV_PIX_FMT_BGR24, pVideoc->m_pAVCodecContext->width, pVideoc->m_pAVCodecContext->height, 1);
      pszBGRBuffer = (uint8_t*)av_malloc(nBGRFrameSize);
      av_image_fill_arrays(pBGRFrame->data, pBGRFrame->linesize, pszBGRBuffer, AV_PIX_FMT_BGR24, pFrame->width, pFrame->height, 1);
     

    旧版本函数

    int avpicture_fill(AVPicture *picture, uint8_t *ptr,
                       int pix_fmt, int width, int height);

    六. av_seek_frame()设置流偏移(起始位置)

    原型

    int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp,
                      int flags);

    参数解释:

    AVFormatContext *s,  解码的格式上下文
    int stream_index,  默认-1 指按照视频时间来移动
    int64_t timestamp,时间戳,计算是根据我们拖到的进度条占总视频长度比,来计算应该跳转到的时间。时间基数:AVStream.time_base
    int flags 移动的时间,可能不是B帧或者说 计算的时间在2帧之间,取向前还是向后的一帧。

    flag

    向前,向后指的是 相对于当前时间来定的,如下图

    在这里插入图片描述

    AVSEEK_FLAG_BACKWARD = 1 往后找

    AVSEEK_FLAG_BYTE = 2 按照字节来挑战移动位置

    AVSEEK_FLAG_ANY =4针对frame来说的,就跳转那一帧,不找关键帧

    AVSEEK_FLAG_FRAME = 8 表示往后找,找到关键帧

    flag是二进制表示的,可以同时取2个,用或来操作。‘|’,

    部分示例代码

      frame = avcodec_alloc_frame();
        if (!frame) {
            fprintf(stderr, "Could not allocate frame\n");
            ret = AVERROR(ENOMEM);
            goto end;
        }
    
        /* initialize packet, set data to NULL, let the demuxer fill it */
        av_init_packet(&pkt);
        pkt.data = NULL;
        pkt.size = 0;
    
        if (video_stream)
            printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename);
        if (audio_stream)
            printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename);
    
    / 孙悟空 说: 这里是最关键的/
        av_seek_frame(fmt_ctx, -1 , 20 * AV_TIME_BASE, AVSEEK_FLAG_ANY);
    
        /* read frames from the file [url]www.chinaffmpeg.com[/url] 孙悟空*/
        while (av_read_frame(fmt_ctx, &pkt) >= 0) {
            AVPacket orig_pkt = pkt;
            do {
                ret = decode_packet(&got_frame, 0);
                if (ret < 0)
                    break;
                pkt.data += ret;
                pkt.size -= ret;
            } while (pkt.size > 0);
            av_free_packet(&orig_pkt);
        }
     
  • 相关阅读:
    批量计算(batch computing)、流式计算(stream computing)、交互计算(interactive computing)、图计算(graph computing)
    ETL相关 ELT
    添加AD验证(域身份验证)到现有网站
    android开发导包升级到androidx踩坑记录【转载】
    Android Support v4v7v13和AndroidX理解【转载】
    架构师的成长之路初片~linux-基本防护措施
    架构师的成长之路初片~nginx优化篇
    架构师的成长之路初片~linux-监控脚本
    架构师的成长之路初片~Virtual-自定义容器
    架构师的成长之路初片~Virtual-容器和镜像常用的管理命令
  • 原文地址:https://www.cnblogs.com/lidabo/p/15684627.html
Copyright © 2020-2023  润新知