DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

一. 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);
    }
 
posted on 2021-12-13 18:21  DoubleLi  阅读(125)  评论(0编辑  收藏  举报