android ffmpeg软,硬解码实现(ffmpeg 3.3.4)

前提:编译出ffmpeg.so库文件,或者从某处得到可用so,可依照上一篇配置文件进行配置,裁剪编译。

1 软解码实现:

JNIEXPORT int JNICALL Java_h264_Native_PlayLocalVideo(JNIEnv *env, jobject obj,jstring inputFilePath_,jobject surface) {
    const char *path = env->GetStringUTFChars(inputFilePath_, 0);

    av_log_set_callback(ffmpeg_android_log_callback);
    av_register_all();
    int ret;
    AVFormatContext *fmt_ctx = avformat_alloc_context();
    if (avformat_open_input(&fmt_ctx, path, NULL, NULL) < 0) {
        LOGD("can not open file");
        return -1;
    }

    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env,surface);
    if (nativeWindow == NULL) {
        LOGD("ANativeWindow_fromSurface error");
        return -3;
    }
    //绘制时候的缓冲区
    ANativeWindow_Buffer outBuffer;
    //获取视频流解码器

    AVCodecContext *codec_ctx = avcodec_alloc_context3(NULL);
    codec_ctx->width = 1280;
    codec_ctx->height = 720;
    codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;

    AVCodec *avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
    LOGD("mcodec is %d \n ",avCodec);
    //打开解码器
    if ((ret = avcodec_open2(codec_ctx, avCodec, NULL)) < 0) {
        ret = -3;
        return -4;
    }
    //循环从文件读取一帧压缩数据
    //开始读取视频
    int y_size = codec_ctx->width * codec_ctx->height;
    AVPacket *pkt = (AVPacket *) malloc(sizeof(AVPacket));//分配一个packet
    av_new_packet(pkt, y_size);//分配packet的数据
    AVFrame *yuvFrame = av_frame_alloc();
    AVFrame *rgbFrame = av_frame_alloc();
    // 颜色转换器
    struct SwsContext *m_swsCtx = sws_getContext(codec_ctx->width, codec_ctx->height,
                                                 codec_ctx->pix_fmt, codec_ctx->width,
                                                 codec_ctx->height, AV_PIX_FMT_RGBA, SWS_BICUBIC,
                                                 NULL, NULL, NULL);
    int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGBA, codec_ctx->width, codec_ctx->height, 1);
    uint8_t *out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
    LOGD("开始解码");
    int index = 0;
    while (1) {
        if (av_read_frame(fmt_ctx, pkt) < 0) {
            //这里就认为视频读完了
            break;
        }
        FILE * pFile;
        pFile = fopen("/sdcard/h264.txt", "wb");
        if(pFile != NULL){
            fwrite (pkt->data ,1,pkt->size, pFile);
        }
        LOGD("avcodec_send_packet index is = %d size=%d",index,pkt->size);
        ret = avcodec_send_packet(codec_ctx, pkt);
        if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
            LOGD("avcodec_send_packet ret=%d", ret);
            av_packet_unref(pkt);
            continue;
        }
        //从解码器返回解码输出数据
        ret = avcodec_receive_frame(codec_ctx, yuvFrame);
        if (ret < 0 && ret != AVERROR_EOF) {
            LOGD("avcodec_receive_frame ret=%d", ret);
            av_packet_unref(pkt);
            continue;
        }

        LOGD("frame pkt dts is %lld", yuvFrame->pkt_dts);
        LOGD("frame pkt pts is %lld", yuvFrame->pkt_pts);
        LOGD("frame pkt is %lld", yuvFrame->pts);

        sws_scale(m_swsCtx, (const uint8_t *const *) yuvFrame->data, yuvFrame->linesize, 0,codec_ctx->height, rgbFrame->data, rgbFrame->linesize);
        //设置缓冲区的属性
        ANativeWindow_setBuffersGeometry(nativeWindow, codec_ctx->width, codec_ctx->height, WINDOW_FORMAT_RGBA_8888);
        ret = ANativeWindow_lock(nativeWindow, &outBuffer, NULL);
        if (ret != 0) {
            LOGD("ANativeWindow_lock error");
            return -5;
        }
        av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize,
                             (const uint8_t *) outBuffer.bits, AV_PIX_FMT_RGBA,
                             codec_ctx->width, codec_ctx->height, 1);
        //fill_ANativeWindow(&outBuffer,outBuffer.bits,rgbFrame);
        // 将缓冲区数据显示到surfaceView
        ret = ANativeWindow_unlockAndPost(nativeWindow);
        if (ret != 0) {
            LOGD("ANativeWindow_unlockAndPost error");
            return -6;
        }
        LOGD("成功显示到缓冲区%d次", ++index);
        av_packet_unref(pkt);
        usleep(150000);//stop 1/6 second
    }
    av_free(out_buffer);
    av_frame_free(&rgbFrame);
    avcodec_close(codec_ctx);
    sws_freeContext(m_swsCtx);
    avformat_close_input(&fmt_ctx);
    env->ReleaseStringUTFChars(inputFilePath_, path);
    LOGD("解析完成");
    return 1;
}

2 硬解码实现
1:修改ffmpeg配置文件,开启硬解码选项,重新编译

–enable-jni \
–enable-mediacodec \
–enable-decoder=h264_mediacodec \
–enable-hwaccel=h264_mediacodec \

2:修改解码器获取方式
codec = avcodec_find_decoder_by_name(“h264_mediacodec”);

总结:
ffmpeg简单使用总是这样,其中编译流程可谓艰难困苦,前后编译尝试上百次。开启neno,开启硬解码。都不是容易的事情。自己也是琢磨了三个月的空余时间,才走完软硬解码流程。其中为完成单帧硬解码需求涉及源码的修改也是有,不过很少。在出现调用问题的情况下,出现问题的解决办法,查看源码是最快的。我的配置应该还是可行的,不过ubuntu版本问题,ndk版本问题,ffmpeg版本问题,所以觉知此时要躬行,我能给的帮助是,android ffmpeg硬解码可行,可多路,多线程同时解码,如果只是单路视频的播放,大可不必费这个心思,直接采用android硬解码也是可行的,ffmpeg实现其实也是反射java的mediacodec实现。在实际的商业项目中使用,开启neno加速,开启多线程解码,能使解码效率大大提高。

猜你喜欢

转载自blog.csdn.net/qq_27688259/article/details/78963431