一. 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);
}