• 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);
        }
     
  • 相关阅读:
    Largest Rectangle in Histogram
    Valid Sudoku
    Set Matrix Zeroes
    Unique Paths
    Binary Tree Level Order Traversal II
    Binary Tree Level Order Traversal
    Path Sum II
    Path Sum
    Validate Binary Search Tree
    新手程序员 e
  • 原文地址:https://www.cnblogs.com/lidabo/p/15684627.html
Copyright © 2020-2023  润新知