android ANativeWindow旋转渲染角度

android ANativeWindow旋转渲染角度

MediaCodec 旋转角度借鉴

videoExtractor在打开有角度视频文件mediaFormat.getInteger(MediaFormat.KEY_ROTATION);获取角度

                MediaFormat mediaFormat = videoExtractor.getTrackFormat(j);
                String mime = mediaFormat.getString(MediaFormat.KEY_MIME);
                if (mime.startsWith(KEY_VIDEO)) {//匹配视频对应的轨道
                    videoExtractor.selectTrack(j);//选择视频对应的轨道

                    long duration = mediaFormat.getLong(MediaFormat.KEY_DURATION);
                    int width = mediaFormat.getInteger(MediaFormat.KEY_WIDTH);
                    int height = mediaFormat.getInteger(MediaFormat.KEY_HEIGHT);
                    int degrees = 0;
                    try {
                        if (mediaFormat.containsKey(MediaFormat.KEY_ROTATION))
                         degrees = mediaFormat.getInteger(MediaFormat.KEY_ROTATION);//有些视频没这个参数会空指针
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                } 

在MediaCodec在创建和configure时候传入角度,解码 mediaCodec.releaseOutputBuffer(outputBufferIndex, true);在渲染时候你发现你的视频正的,并没有旋转角度。

  mediaCodec = MediaCodec.createDecoderByType("video/avc");
  mediaCodec.configure(mediaFormat surface, null, 0);

上面根据android的api和framework你可以发现,底层在mediaCodec.releaseOutputBuffer(outputBufferIndex, true);在渲染时候进行旋转,解码并没有帮你旋转角度,验证只有当MediaCodec直接解码到Surface时,旋转角度才有效。

旋转角度在MediaCodec中的流转流程

1、MediaCodec::configure配置解码器,MediaFormat作为参数。
2、MediaCodec发送kWhatConfigure消息,在自有线程配置解码器。
3、调用ACodec->initiateConfigureComponent(MediaFormat为参数)配置ACodec。
4、ACodec发送kWhatConfigureComponent消息在自有线程配置ACodec。
5、接着是ACodec::LoadedState::onConfigureComponent方法。
6、然后是ACodec.configureCodec方法,负责通过MediaFormat配置ACodec,其中从format中取出了"rotation-degrees",保存在ACodec.mRotationDegrees变量中。
7、最后,ACodec.setupNativeWindowSizeFormatAndUsage中调用全局函数setNativeWindowSizeFormatAndUsage为Surface(ANativeWindow)设置transform flag。核心代码在setNativeWindowSizeFormatAndUsage函数中:

// 根据旋转角度获得transform flag
int transform = 0;
if ((rotation % 90) == 0) {
    switch ((rotation / 90) & 3) {
        case 1:  transform = HAL_TRANSFORM_ROT_90;  break;
        case 2:  transform = HAL_TRANSFORM_ROT_180; break;
        case 3:  transform = HAL_TRANSFORM_ROT_270; break;
        default: transform = 0;                     break;
    }
}
// 为Surface(ANativeWindow)设置transform
err = native_window_set_buffers_transform(nativeWindow, transform);

ANativeWindow在填充你解码数据能否旋转

从上面借鉴MediaCodec,正常是可以的实现的,通过ndk和android的api是可以实现的,ndk提供ANativeWindow_setBuffersTransform方法。
实现条件:
1、调用实现在GLSurfaceView配置出OpenGL环境获取surface在jni转换nativewindow
2、gradle 的android版本8.0也就是只兼容26以上 minSdkVersion 26
3、CMakeLists要关联nativewindow库
android ANativeWindow旋转渲染角度_第1张图片

实现代码:
关键头文件#include

void WlVideo::postDataFromNative(const AVFrame *avFrame,int angle) {

    int retval;
    char overlay_format[5] = {'Y', 'V', '1', '2', 0};
    int curr_w = ANativeWindow_getWidth(native_window);
    int curr_h = ANativeWindow_getHeight(native_window);
    int curr_format = ANativeWindow_getFormat(native_window);
    int buff_w = avFrame->width;
    int buff_h = avFrame->height;

    if (curr_format != HAL_PIXEL_FORMAT_YV12) {
        LOGE("ANativeWindow_setBuffersGeometry: w=%d, h=%d, f=%.4s(0x%x) => w=%d, h=%d, f=%.4s",
             curr_w, curr_h, (char *) &curr_format, curr_format,
             buff_w, buff_h, (char *) overlay_format);
        retval = ANativeWindow_setBuffersGeometry(native_window, buff_w, buff_h,
                                                  HAL_PIXEL_FORMAT_YV12);
        if (retval < 0) {
            LOGE("ANativeWindow_setBuffersGeometry: failed %d", retval);
            return;
        }
    }

    ANativeWindow_Buffer out_buffer;
    retval = ANativeWindow_lock(native_window, &out_buffer, NULL);
    if (retval < 0) {
        LOGE("ANativeWindow_lock: failed %d", retval);
        return;
    }
    LOGE("postDataFromNative1  native window buffer (%p)(out_buffer.width:%d, out_buffer.height:%d, out_buffer.format:'%.4s'0x%x), expecting (buff_w:%d, buff_h:%d, overlay_format:'%.4s')",
         native_window,
         out_buffer.width, out_buffer.height, (char *) &out_buffer.format, out_buffer.format,
         buff_w, buff_h, (char *) overlay_format);
    if (out_buffer.width != buff_w || out_buffer.height != buff_h) {
        LOGE("unexpected native window buffer (%p)(w:%d, h:%d, fmt:'%.4s'0x%x), expecting (w:%d, h:%d, fmt:'%.4s')",
             native_window,
             out_buffer.width, out_buffer.height, (char *) &out_buffer.format, out_buffer.format,
             buff_w, buff_h, (char *) overlay_format);
        // TODO: 8 set all black
        ANativeWindow_unlockAndPost(native_window);
        ANativeWindow_setBuffersGeometry(native_window, buff_w, buff_h, HAL_PIXEL_FORMAT_YV12);
        return;
    }

    int render_ret = android_render_yv12_on_yv12(&out_buffer, avFrame);
    if (render_ret < 0) {
        // TODO: 8 set all black
        // return after unlock image;
    }

    /**
     * minSdkVersion=26 gradle的sdk版本要大26
     * CMakeLists.txt需要 target_link_libraries 加入ndk的nativewindow不然会报错undefined reference to 'ANativeWindow_setBuffersTransform'
     * ANativeWindow_setBuffersTransform作用就参考mediacodec,解码渲染时候对有角度的进行旋转
     */
    switch (angle) {
        case 90:
            retval = ANativeWindow_setBuffersTransform(native_window, ANATIVEWINDOW_TRANSFORM_ROTATE_90);
            break;
        case 180:
            retval = ANativeWindow_setBuffersTransform(native_window, ANATIVEWINDOW_TRANSFORM_ROTATE_180);
            break;
        case 270:
            retval = ANativeWindow_setBuffersTransform(native_window, ANATIVEWINDOW_TRANSFORM_ROTATE_270);
            break;
        default:
            break;
    }
    if (retval < 0) {
        LOGE("ANativeWindow_setBuffersTransform: failed %d", retval);
    }


    LOGE("ANativeWindow_lock: ============= %d", render_ret);
    retval = ANativeWindow_unlockAndPost(native_window);
    if (retval < 0) {
        LOGE("ANativeWindow_unlockAndPost: failed %d", retval);
        return;
    }

    return;
}

上面方法对应的 int WlVideo::android_render_yv12_on_yv12(ANativeWindow_Buffer *out_buffer, const AVFrame *avFrame) 是yuv420p对ANativeWindow的数据填充,当数据填充完后SurfaceTexture.OnFrameAvailableListener对应的监听方法就会被调用可以onFrameAvailable刷新OpenGL渲染

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        Log.e("========","========onRender====");
        surfaceView.requestRender();
    }

你可能感兴趣的:(android,视频,android,ffmpeg,c语言,c++)