在android上利用ffmpeg解码后,怎么回调解码的数据,回调NV21格式的图像

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/teleger/article/details/81982659

1.在android上用ffmpeg.  肯定要先 交叉编译好,ffmpeg的so库.在这,假设你已经编译好库

2. 在用AS (android studio) 写c/c++代码时,也就是所谓的解码 视频代码.在这里不做特殊说明.

    if (filename != nullptr){
        filenameStr = filename;
        LOG(ERROR) << TAG << "file path:" << filename;
    }else{
        LOG(ERROR) << TAG << "file empty";
        return -1;
    }
    pFormatCtx = avformat_alloc_context();
    int result = 0;
    result = avformat_open_input(&pFormatCtx, filename, NULL, NULL);
    if (result != 0)
    {
        LOG(ERROR) << TAG << " open file fail";
        return -2;
    }

    result = avformat_find_stream_info(pFormatCtx, NULL);
    if (result != 0)
    {
        LOG(ERROR) << TAG << " find stream fail";
        return -3;
    }

    //find videostream data
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
    {
        LOG(ERROR)<< TAG  << " Couldn't find a video stream";//
        return -4;
    }

    //pCodecCtx = pFormatCtx->streams[videoStream]->codec;//deprecated
    pCodecCtx = avcodec_alloc_context3(NULL);

    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar);

    //声明位于libavcodec/avcodec.h
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);//用于查找FFmpeg的解码器。
    //函数的参数是一个解码器的ID,返回查找到的解码器(没有找到就返回NULL)

    parser = av_parser_init(pCodec->id);

    if (!pCodecCtx)
    {
        LOG(ERROR)<< TAG << "Could not allocate video codec context";
        exit(1);
    }

    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        LOG(ERROR) << TAG << " Could not open codec";
        exit(1);
    }

以上代码片段,主要是打开了视频。一切运行正常后,就可以进行下面的工作了。

接下来,格式 转化函数

convert_NV21

来啦.

    nv21_pSwsCtx = sws_getContext(pCodecCtx->width,
                             pCodecCtx->height,
                             pCodecCtx->pix_fmt,
                             pCodecCtx->width,
                             pCodecCtx->height,
                             AV_PIX_FMT_NV21,
                             SWS_BICUBIC, NULL, NULL, NULL);
    sws_scale(nv21_pSwsCtx,
              pFrame->data,
              pFrame->linesize,
              0,pCodecCtx->height,
              pFrameNV21->data,
              pFrameNV21->linesize);//swscale主要用于在2个AVFrame之间进行转换

下面是循环解码流程:

    while(av_read_frame(pFormatCtx,pkt) >= 0)
    {
        /* use the parser to split the data into frames */
        if (pkt->stream_index == videoStream)
        {
            int ret = avcodec_send_packet(pCodecCtx, pkt);

            if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF)
                return -1;

            while(avcodec_receive_frame(pCodecCtx, pFrame) == 0)//解码出来的frame?
            {
                convert(pFrame,pFrameRGBA);
                convert_NV21(pFrame,pFrameYuv_Nv21);
                if(ANativeWindow_lock(m_nativeWindow,&windowBuffer,NULL) >= 0)
                {
                    uint8_t * dst = (uint8_t*)windowBuffer.bits;
                    for (int h = 0; h < pCodecCtx->height; ++h)
                    {
                        memcpy(dst+h*windowBuffer.stride*4,
                               out_buffer+h*pFrameRGBA->linesize[0],
                               pFrameRGBA->linesize[0]
                        );
                    }
                    ANativeWindow_unlockAndPost(m_nativeWindow);
                }
                m_callbackfuntion(pFrameYuv_Nv21);//此处将得到的图像回调出去....
            }
            if (ret < 0 && ret != AVERROR_EOF)
                return -1;

        }
        av_packet_unref(pkt);
    }

最后,是在jni 层   的  回调函数:

unsigned char* pYuvBuf= nullptr;

static int revice(AVFrame *pFrameNV21){//AVFrame *pFrameYuv
    if(mjlxff != nullptr){
        const int framelength = getFrameSize();//得到图像的长×宽
        const int yuvNv21Framesize = framelength*3/2;//图像所需内存大小
        if(pYuvBuf == nullptr)//避免重复分配内存
        {
            pYuvBuf = new unsigned char[yuvNv21Framesize];
        }
        size_t Ystep = framelength* sizeof(uint8_t);

        memcpy(pYuvBuf,pFrameNV21->data[0],Ystep);//拷贝Y分量

        memcpy(pYuvBuf + Ystep ,pFrameNV21->data[1], Ystep /2);//调试到这里,data[2]没有值

    }
    LOG(ERROR) << "This is callback ... ";
    return 0;
}

---->

写在最后的话,鸣谢:

1..jni 使用规范:http://www.cnblogs.com/nuliniaoboke/archive/2012/10/31/2747715.html

2.anroid上使用ffmpeg https://www.jianshu.com/p/fb19cfb93548

3.android ffmpeg 使用ffmpeg 解码h264 https://blog.csdn.net/junzia/article/details/68952183

4. android 上 ,在文件中保存 byte[] 为 图像.  https://ask.csdn.net/questions/10824

5.校验解码出来的yuv 数据 ,用ffmpeg 查看YUV图片/视频.

校验解码出来的yuv 数据(nv21)   https://blog.csdn.net/matrix_laboratory/article/details/49470689

ffplay -video_size  852x480  -i ./save.yuv -pixel_format  nv21

            //大小                            //文件名                    /  /文件格式

6.【视频处理】 YUV 格式说明: http://www.cnblogs.com/dwdxdy/p/3713968.html

展开阅读全文

没有更多推荐了,返回首页