SwsContext、sws_scale缩放失败,或者缩放后视频乱码

虽然 ffmpeg的缩放效率比较低,但随着电脑或者硬件设备发展,即使使用低效率的,也可以达到普通播放器的要求了。但在使用的过程中的坑还是很多的。

在android手机中使用SwsContext 和 sws_scale缩放后,视频总视出现乱码或者 干脆一个黑屏。

void  FFMVideoShow::show(AVFrame *avFrame)
{

    if(dataManager->swsContext == nullptr){
        dataManager -> outWidth = avFrame->width;
        dataManager -> outHeight = avFrame->height;

       dataManager->rgb = new char[dataManager -> outWidth * dataManager -> outHeight * 4];
        ANativeWindow_setBuffersGeometry(dataManager->aNativeWindow, dataManager->outWidth,
 dataManager->outHeight, WINDOW_FORMAT_RGBA_8888);

        // 这个开销也比较大,且经常失败,,这里不为空的时候,再去获取,发现视频解码速度快了很多。
        dataManager->swsContext = sws_getCachedContext(dataManager->swsContext,
                                                       avFrame->width,
                                                       avFrame->height,
                                                       (AVPixelFormat)avFrame->format,
                                                       dataManager->outWidth,
                                                       dataManager->outHeight,
                                                       AV_PIX_FMT_RGBA,
                                                       SWS_FAST_BILINEAR,
                                                       0,0,0 );

    }


    if(!dataManager->swsContext){
        LOGE("swsContext failed  %d!", dataManager->swsContext);
    }
    else {
        LOGE("show show 3" );
//        LOGE("swsContext success  %d!", dataManager->swsContext);
        // 指针数组AV_NUM_DATA_POINTERS=8 不一定全用
        uint8_t *data[AV_NUM_DATA_POINTERS]={0};
        if(dataManager->rgb != 0)
        data[0]= (uint8_t *)dataManager->rgb;

        int lines[AV_NUM_DATA_POINTERS]={0};
        lines[0]=dataManager->outWidth*4;
        int h = sws_scale(dataManager->swsContext,
                          (const uint8_t **)avFrame->data,
                          avFrame->linesize,0,
                          avFrame->height,
                          data,
                          lines
        );
        LOGE("show show 4   height =%d , width = %d" , dataManager->outHeight, dataManager->outWidth);
        LOGE("show show 4   h =%d" , h );

        av_frame_free(&avFrame);

        if(h >0)
        {
            LOGE("show show 5" );
            ANativeWindow_lock(dataManager->aNativeWindow, &(dataManager->wbuf), 0);
            uint8_t  *dst = (uint8_t*)dataManager->wbuf.bits;
            memcpy(dst, dataManager->rgb,dataManager->outHeight*dataManager->outWidth*4);
            ANativeWindow_unlockAndPost(dataManager->aNativeWindow);
        }
    }


}

问题:设置了natvieWindow的缓存区大小。而且 outwidth是 和 解码出来的frame的大小设置是一样的,像素格式rgba。但就是这么不缩放,也出现了问题,我的视频是抖音下随便下载的一个视频。宽高是368x640, 然而播放出来确实乱码。换了个标准的1920x1080的然而成了黑屏,sws_scale后h返回值为0。

在网上查资料,没找到类似的问题,作为一名从事图像的程序员,猜到可能是图像格式,特别是宽度在不够的时候,系统或者一些算法自己在后面补充一些空的像素,让图像宽度成为32、64的倍数。这样导致后面scale后,算法自己补充了一些像素,而native窗体已经设置了大小,从而不匹配,导致copy到buf的时候,一些像素丢失或者错位了。

于是,就做了以下实验:将窗体宽度从64开始,每次加2,发现,为64的倍数的时候都不会出现乱码。高度可以 随便scale都不会出现视频混乱清空。

从而得出一个结论:为了播放器的健壮性,窗体大小最好设置为64的倍数,为了保证视频的清晰度,可以尽可能靠近frame的宽度。

那么剩下一个问题,1920 是为64的倍数,为何我的视频scale 后 h为0,于是打印了log,查看了手机屏幕 和 surfaceview的大小,大小为:2012x1080 而我们的窗体大小为1920x1080

于是做了下面实验:

1、将高1080 修改为1079,就 是减去1个像素,视频就正常播放了。

2、将宽1920 - 64 后,1080不变也可以正常播放。

解决方案:

1、可以将宽度设置为linesize[0],这样就不会乱码了,但存在意外,视频1920*1080的时候太大,也无法显示。

2、缩放是根据宽度,android平台最好是64倍数,高度也是有相应的限制。为了播放器的 兼容性,最好使用opengl来渲染、或者利用 高效率libyuv 库来进行格式转换显示,libyuv高效转化git地址https://github.com/hurtnotbad/FFmpegDemo

具体的 解决方法请参考博客FFMpeg opengl显示解码avframe

sws_scale是FFmpeg库中的一个函数,用于图像的缩放和转换。它可以将输入图像的像素格式、大小和采样方式转换为输出图像的要求。 当使用sws_scale函数时,可能会出现崩溃的情况。这些崩溃可能是由以下原因引起的: 1. 内存错误:如果没有正确分配和释放内存,或者访问了无效的内存地址,就可能导致崩溃。确保在使用sws_scale函数之前,为输入和输出图像分配足够的内存,并在使用完毕后进行释放。 2. 参数错误:如果传递给sws_scale函数的参数不正确,比如图像大小不匹配、像素格式不支持等,就可能导致崩溃。请确保传递正确的参数,并根据需要进行必要的转换和调整。 3. 版本不匹配:如果使用的FFmpeg库版本与编译时使用的版本不匹配,就可能导致崩溃。请确保使用的FFmpeg库版本与编译时使用的版本一致,并按照正确的方式链接和调用库函数。 如果遇到sws_scale函数崩溃的问题,可以通过以下方式进行排查和解决: 1. 检查参数:确保传递给sws_scale函数的参数正确无误,包括输入和输出图像的大小、像素格式等。 2. 检查内存:确保为输入和输出图像分配了足够的内存,并在使用完毕后进行释放。 3. 检查版本:确保使用的FFmpeg库版本与编译时使用的版本一致,并按照正确的方式链接和调用库函数。 4. 调试代码:使用调试工具对代码进行调试,查看具体的崩溃原因和位置,以便进行修复。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值