ijkplayer 开源项目分析(一)编译
ijkplayer 开源项目分析(二)播放流程概要
ijkplayer 开源项目分析(三)msg_queue消息机制剖析
ijkplayer 开源项目分析(四)解决video size识别问题
ijkplayer 开源项目分析(五)ffmpeg升级到4.X版本
ijkplayer 开源项目分析(六)视频解析核心流程
ijkplayer 开源项目分析(七)ijkplayer自动旋转功能
ijkplayer 开源项目分析(八)avformat_open_input剖析
ijkplayer 开源项目分析(九)核心option解析
ijkplayer 开源项目分析(十)提升直播画面质量
ijkplayer 开源项目分析(十一)filter过滤器介绍
ijkplayer 开源项目分析(十二)filter改变声音音量

正常播放的情况是这样子的:

很显然,使用ijkplayer播放的时候,视频旋转角度没有调整好。
这个视频的基本信息如下:

可以看出来视频的旋转角度是90度,是逆时针90度。
两个问题:
- 1.如何识别视频的旋转角度?
- 2.如何自动旋转视频?
如何识别视频的旋转角度
视频的旋转角度是视频的基本信息,就和宽、高、时长等信息一样,都是基本的metadata信息。
IjkMediaPlayer有一个onInfo的回调——MEDIA_INFO_VIDEO_ROTATION_CHANGED
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;
这儿显然是回调告知视频的旋转角度的,根据对代码的分析,我们知道这个回调的起点有两处。
一个是ff_ffplay.c中的ffplay_video_thread函数,调用的地方如下:
#if CONFIG_AVFILTER
AVFilterGraph *graph = avfilter_graph_alloc();
AVFilterContext *filt_out = NULL, *filt_in = NULL;
int last_w = 0;
int last_h = 0;
enum AVPixelFormat last_format = -2;
int last_serial = -1;
int last_vfilter_idx = 0;
if (!graph) {
av_frame_free(&frame);
return AVERROR(ENOMEM);
}
#else
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, ffp_get_video_rotate_degrees(ffp));
#endif
从代码中看,CONFIG_AVFILTER打开的时候,就不会回调FFP_MSG_VIDEO_ROTATION_CHANGED了,为什么这样做?
另一个调用的地方是ffpipenode_android_mediacodec_vdec.c中的recreate_format_l函数, 如下:
rotate_degrees = ffp_get_video_rotate_degrees(ffp);
if (ffp->mediacodec_auto_rotate &&
rotate_degrees != 0 &&
SDL_Android_GetApiLevel() >= IJK_API_21_LOLLIPOP) {
ALOGI("amc: rotate in decoder: %d\n", rotate_degrees);
opaque->frame_rotate_degrees = rotate_degrees;
SDL_AMediaFormat_setInt32(opaque->input_aformat, "rotation-degrees", rotate_degrees);
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, 0);
} else {
ALOGI("amc: rotate notify: %d\n", rotate_degrees);
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, rotate_degrees);
}
fp->mediacodec_auto_rotate &&
rotate_degrees != 0 &&
SDL_Android_GetApiLevel() >= IJK_API_21_LOLLIPOP,这么一大堆条件成立的情况下,回调的旋转角度就是0了, 为什么?
发现获取旋转角度的函数是ffp_get_video_rotate_degrees,这个函数的源码如下:
int ffp_get_video_rotate_degrees(FFPlayer *ffp)
{
VideoState *is = ffp->is;
if (!is)
return 0;
int theta = abs((int)((int64_t)round(fabs(get_rotation(is->video_st))) % 360));
switch (theta) {
case 0:
case 90:
case 180:
case 270:
break;
case 360:
theta = 0;
break;
default:
ALOGW("Unknown rotate degress: %d\n", theta);
theta = 0;
break;
}
return theta;
}
从is->video_st中得到,is-video_st是AVStream。
在分析代码的时候,上面提出了两个问题?
- CONFIG_AVFILTER宏和视频旋转有什么关系?
- ffp->mediacodec_auto_rotate和视频旋转有什么关系?
这两个问题很有意思,因为解释了上面这两个问题,我们就能做到根据视频的选选角度自动旋转视频了。
如何自动旋转视频
1.CONFIG_AVFILTER配置1
CONFIG_AVFILTER打开的时候,就不会回调旋转角度的函数,不回调,本身说明此时视频的旋转角度为0,说明视频已经被自动旋转过了。大胆猜测一下,简答验证一下。
首先修改./config/module.sh中的配置,注释掉:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-filters"
然后打开./ijkmedia/ijkplayer/config.h
#ifndef FFPLAY__CONFIG_H
#define FFPLAY__CONFIG_H
#include "libffmpeg/config.h"
// FIXME: merge filter related code and enable it
// remove these lines to enable avfilter
#ifdef CONFIG_AVFILTER
#undef CONFIG_AVFILTER
#endif
#define CONFIG_AVFILTER 0
#ifdef FFP_MERGE
#undef FFP_MERGE
#endif
#ifdef FFP_SUB
#undef FFP_SUB
#endif
#ifndef FFMPEG_LOG_TAG
#define FFMPEG_LOG_TAG "IJKFFMPEG"
#endif
#endif//FFPLAY__CONFIG_H
将CONFIG_AVFILTER改为1,然后重新编译一下,这时候再尝试一下,可以自动旋转视频了。这时候再去源码中找答案。发现ff_ffplay.c中configure_video_filters函数中有下面关键的几行代码:
#define INSERT_FILT(name, arg) do { \
AVFilterContext *filt_ctx; \
\
ret = avfilter_graph_create_filter(&filt_ctx, \
avfilter_get_by_name(name), \
"ffplay_" name, arg, NULL, graph); \
if (ret < 0) \
goto fail; \
\
ret = avfilter_link(filt_ctx, 0, last_filter, 0); \
if (ret < 0) \
goto fail; \
\
last_filter = filt_ctx; \
} while (0)
if (ffp->autorotate) {
double theta = get_rotation(is->video_st);
if (fabs(theta - 90) < 1.0) {
INSERT_FILT("transpose", "clock");
} else if (fabs(theta - 180) < 1.0) {
INSERT_FILT("hflip", NULL);
INSERT_FILT("vflip", NULL);
} else if (fabs(theta - 270) < 1.0) {
INSERT_FILT("transpose", "cclock");
} else if (fabs(theta) > 1.0) {
char rotate_buf[64];
snprintf(rotate_buf, sizeof(rotate_buf), "%f*PI/180", theta);
INSERT_FILT("rotate", rotate_buf);
}
}
这个ffp->autorotate初始值就是1,这儿看一下transpose/hflip/vflip/rotate的含义。
- 如果视频逆时针90旋转,直接transpose=clock就行
- 如果视频逆时针180旋转,先hflip水平翻转,再vflip竖直翻转
- 如果视频逆时针270旋转,直接transpose=cclock就行
- 如果是其他的角度,则直接使用rotate方法直接旋转。
transpose=clock表示顺时针90度旋转,transpose=cclock表示逆时针90度旋转。
打开ffp->mediacodec_auto_rotate
这个mediacodec_auto_rotate是ijkplayer 的option的一种。我们在ijkplayer中IjkMediaPlayer初始化的时候,可以设置一些option参数,打开或者关闭一些开关,设置播放器的基本参数。option的参数很多种,可以单独写一篇文章介绍了,这儿不作展开。
mediacodec_auto_rotate参数默认情况下是关掉的,如果你要打开,可以在初始化设置option参数:
mIjkMediaPlayerImpl.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
mIjkMediaPlayerImpl.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec_auto_rotate", 1);
mediacodec_auto_rotate使用硬解码的自动旋转功能,所以肯定要打开mediacodec开关的。
rotate_degrees = ffp_get_video_rotate_degrees(ffp);
if (ffp->mediacodec_auto_rotate &&
rotate_degrees != 0 &&
SDL_Android_GetApiLevel() >= IJK_API_21_LOLLIPOP) {
ALOGI("amc: rotate in decoder: %d\n", rotate_degrees);
opaque->frame_rotate_degrees = rotate_degrees;
SDL_AMediaFormat_setInt32(opaque->input_aformat, "rotation-degrees", rotate_degrees);
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, 0);
} else {
ALOGI("amc: rotate notify: %d\n", rotate_degrees);
ffp_notify_msg2(ffp, FFP_MSG_VIDEO_ROTATION_CHANGED, rotate_degrees);
}
代码中条件有讲得很清楚:
- (1) 打开mediacodec_auto_rotate开关
- (2) 旋转角度不为0
- (3) 当前的api-level不能小于21
最终调用的SDL_AMediaFormat_setInt32(opaque->input_aformat, "rotation-degrees", rotate_degrees); 设置到SDL底层了,主要的工作还是MediaCodec在解码视频帧的时候,需要将角度旋转以下,这样帧图片就摆正了。
这也就解释了为什么MediaPlayer会自动帮忙旋转视频,因为MediaPlayer默认使用的是MediaCodec解码。
小结
- 1.如何识别一个视频的旋转角度:
表面上可以通过onInfo回调中的MEDIA_INFO_VIDEO_ROTATION_CHANGED选项来获取旋转角度,但是ffmpeg内部是通过获取视频metadata的所有信息的,视频旋转角度知识metadata中的其中一个元素。- 2.ijkplayer如何完成视频自动旋转的:
有两种办法,一种是通过加上avfilter通过filter来实现对视频帧的转换;另一种是通过MediaCodec来自动旋转。
网友评论