1. 程式人生 > >android ffmpeg軟,硬解碼實現(ffmpeg 3.3.4)

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加速,開啟多執行緒解碼,能使解碼效率大大提高。