2011-12-15 27 views
3

注:最初はライブカメラの画像をffmpegライブラリに読み込む方法について学んだことがあるので、これを更新しました。Androidでjavacvのffmpegを使用してビデオをエンコードするとネイティブコードがクラッシュする

私はアプリケーションのエンコード/デコードのためにAndroid用にコンパイルされたjavacvからffmpegを使用しています。私はに実行した問題は、私は現在、Bitmap(同じように、各フレームを取得していますということです。

オリジナル問題を(もともと、私はffmpeg-javaを使用しようとしていたが、それはいくつかの非互換のライブラリを持っていることに注意してください)プレーンandroid.graphics.Bitmap)と私はエンコーダにそれを詰める方法を把握することはできません。 javacvffmpeg

ソリューション:使用avpicture_fill()、アンドロイドからのフォーマットは(下)私のエンコーダの問題が修正されるまで、私はこれを確認することはできませんが、おそらくYUV420Pです。今

avcodec.avpicture_fill((AVPicture)mFrame, picPointer, avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT) 

問題:実際にデータをエンコードすることになっている行は、スレッドをクラッシュします。私は理解できない大きなネイティブコードスタックトレースを取得します。誰か提案がありますか?ここで

は、私はすべてのffmpegライブラリをインスタンス化するために使用していたコードです:

avcodec.avcodec_register_all(); 
    avcodec.avcodec_init(); 
    avformat.av_register_all(); 

    mCodec = avcodec.avcodec_find_encoder(avcodec.CODEC_ID_H263); 
    if (mCodec == null) 
    { 
     Logging.Log("Unable to find encoder."); 
     return; 
    } 
    Logging.Log("Found encoder."); 

    mCodecCtx = avcodec.avcodec_alloc_context(); 
    mCodecCtx.bit_rate(300000); 
    mCodecCtx.codec(mCodec); 
    mCodecCtx.width(VIDEO_WIDTH); 
    mCodecCtx.height(VIDEO_HEIGHT); 
    mCodecCtx.pix_fmt(avutil.PIX_FMT_YUV420P); 
    mCodecCtx.codec_id(avcodec.CODEC_ID_H263); 
    mCodecCtx.codec_type(avutil.AVMEDIA_TYPE_VIDEO); 
    AVRational ratio = new AVRational(); 
    ratio.num(1); 
    ratio.den(30); 
    mCodecCtx.time_base(ratio); 
    mCodecCtx.coder_type(1); 
    mCodecCtx.flags(mCodecCtx.flags() | avcodec.CODEC_FLAG_LOOP_FILTER); 
    mCodecCtx.me_cmp(avcodec.FF_LOSS_CHROMA); 
    mCodecCtx.me_method(avcodec.ME_HEX); 
    mCodecCtx.me_subpel_quality(6); 
    mCodecCtx.me_range(16); 
    mCodecCtx.gop_size(30); 
    mCodecCtx.keyint_min(10); 
    mCodecCtx.scenechange_threshold(40); 
    mCodecCtx.i_quant_factor((float) 0.71); 
    mCodecCtx.b_frame_strategy(1); 
    mCodecCtx.qcompress((float) 0.6); 
    mCodecCtx.qmin(10); 
    mCodecCtx.qmax(51); 
    mCodecCtx.max_qdiff(4); 
    mCodecCtx.max_b_frames(1); 
    mCodecCtx.refs(2); 
    mCodecCtx.directpred(3); 
    mCodecCtx.trellis(1); 
    mCodecCtx.flags2(mCodecCtx.flags2() | avcodec.CODEC_FLAG2_BPYRAMID | avcodec.CODEC_FLAG2_WPRED | avcodec.CODEC_FLAG2_8X8DCT | avcodec.CODEC_FLAG2_FASTPSKIP); 

    if (avcodec.avcodec_open(mCodecCtx, mCodec) == 0) 
    { 
     Logging.Log("Unable to open encoder."); 
     return; 
    } 
    Logging.Log("Encoder opened."); 

    mFrameSize = avcodec.avpicture_get_size(avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT); 
    Logging.Log("Frame size - '" + mFrameSize + "'."); 
    //mPic = new AVPicture(mPicSize); 
    mFrame = avcodec.avcodec_alloc_frame(); 
    if (mFrame == null) 
    { 
     Logging.Log("Unable to alloc frame."); 
    } 

これは私が次に実行できるようにしたいものです。

BytePointer picPointer = new BytePointer(data); 
    int bBuffSize = mFrameSize; 

    BytePointer bBuffer = new BytePointer(bBuffSize); 

    int picSize = 0; 
    if ((picSize = avcodec.avpicture_fill((AVPicture)mFrame, picPointer, avutil.PIX_FMT_YUV420P, VIDEO_WIDTH, VIDEO_HEIGHT)) <= 0) 
    { 
     Logging.Log("Couldn't convert preview to AVPicture (" + picSize + ")"); 
     return; 
    } 
    Logging.Log("Converted preview to AVPicture (" + picSize + ")"); 

    VCAP_Package vPackage = new VCAP_Package(); 

    if (mCodecCtx.isNull()) 
    { 
     Logging.Log("Codec Context is null!"); 
    } 

    //encode the image 
    int size = avcodec.avcodec_encode_video(mCodecCtx, bBuffer, bBuffSize, mFrame); 

    int totalSize = 0; 
    while (size >= 0) 
    { 
     totalSize += size; 
     Logging.Log("Encoded '" + size + "' bytes."); 
     //Get any delayed frames 
     size = avcodec.avcodec_encode_video(mCodecCtx, bBuffer, bBuffSize, null); 
    } 
    Logging.Log("Finished encoding. (" + totalSize + ")"); 

しかし、今のようビットマップを適切なものにする方法や、正しく設定したかどうかわかりません。

コードに関するいくつかの注意事項: - VIDEO_WIDTH = 352 - VIDEO_HEIGHT = 288 - VIDEO_FPS = 30。

+0

ビデオを取得できれば、ミニプロジェクトへのリンクを教えてください。 – Lunatikul

答えて

3

をffmpegのについては何も知らない、私はあなたがかなり厳しいと厄介な方法でポインタをロードする必要があることを考え出しました。これは、私が働いてすべて得た方法である:

コーデックを設定:

avcodec.avcodec_register_all(); 
    avcodec.avcodec_init(); 
    avformat.av_register_all(); 

    /* find the H263 video encoder */ 
    mCodec = avcodec.avcodec_find_encoder(avcodec.CODEC_ID_H263); 
    if (mCodec == null) { 
     Log.d("TEST_VIDEO", "avcodec_find_encoder() run fail."); 
    } 

    mCodecCtx = avcodec.avcodec_alloc_context(); 
    picture = avcodec.avcodec_alloc_frame(); 

    /* put sample parameters */ 
    mCodecCtx.bit_rate(400000); 
    /* resolution must be a multiple of two */ 
    mCodecCtx.width(VIDEO_WIDTH); 
    mCodecCtx.height(VIDEO_HEIGHT); 
    /* frames per second */ 
    AVRational avFPS = new AVRational(); 
    avFPS.num(1); 
    avFPS.den(VIDEO_FPS); 
    mCodecCtx.time_base(avFPS); 
    mCodecCtx.pix_fmt(avutil.PIX_FMT_YUV420P); 
    mCodecCtx.codec_id(avcodec.CODEC_ID_H263); 
    mCodecCtx.codec_type(avutil.AVMEDIA_TYPE_VIDEO); 

    /* open it */ 
    if (avcodec.avcodec_open(mCodecCtx, mCodec) < 0) { 
     Log.d("TEST_VIDEO", "avcodec_open() run fail."); 
    } 

    /* alloc image and output buffer */ 
    output_buffer_size = 100000; 
    output_buffer = avutil.av_malloc(output_buffer_size); 

    size = mCodecCtx.width() * mCodecCtx.height(); 
    picture_buffer = avutil.av_malloc((size * 3)/2); /* size for YUV 420 */ 

    picture.data(0, new BytePointer(picture_buffer)); 
    picture.data(1, picture.data(0).position(size)); 
    picture.data(2, picture.data(1).position(size/4)); 
    picture.linesize(0, mCodecCtx.width()); 
    picture.linesize(1, mCodecCtx.width()/2); 
    picture.linesize(2, mCodecCtx.width()/2); 

は、プレビューデータの取り扱い:私はまだ、データをパケット化するために働いています

//(1)Convert byte[] first 
    byte[] data420 = new byte[data.length]; 
    convert_yuv422_to_yuv420(data, data420, VIDEO_WIDTH, VIDEO_HEIGHT); 

    //(2) Fill picture buffer 
    int data1_offset = VIDEO_HEIGHT * VIDEO_WIDTH; 
    int data2_offset = data1_offset * 5/4; 
    int pic_linesize_0 = picture.linesize(0); 
    int pic_linesize_1 = picture.linesize(1); 
    int pic_linesize_2 = picture.linesize(2); 

    //Y 
    for(y = 0; y < VIDEO_HEIGHT; y++) 
    { 
     for(x = 0; x < VIDEO_WIDTH; x++) 
     { 
      picture.data(0).put((y * pic_linesize_0 + x), data420[y * VIDEO_WIDTH + x]); 
     } 
    } 

    //Cb and Cr 
    for(y = 0; y < VIDEO_HEIGHT/2; y++) { 
     for(x = 0; x < VIDEO_WIDTH/2; x++) { 
      picture.data(1).put((y * pic_linesize_1 + x), data420[data1_offset + y * VIDEO_WIDTH/2 + x]); 
      picture.data(2).put((y * pic_linesize_2 + x), data420[data2_offset + y * VIDEO_WIDTH/2 + x]); 
     } 
    } 

    //(2)Encode 
    //Encode the image into output_buffer 
    out_size = avcodec.avcodec_encode_video(mCodecCtx, new BytePointer(output_buffer), output_buffer_size, picture); 
    Log.d("TEST_VIDEO", "Encoded '" + out_size + "' bytes"); 

    //Delayed frames 
    for(; out_size > 0; i++) { 
     out_size = avcodec.avcodec_encode_video(mCodecCtx, new BytePointer(output_buffer), output_buffer_size, null); 
     Log.d("TEST_VIDEO", "Encoded '" + out_size + "' bytes"); 
     //fwrite(output_buffer, 1, out_size, file); 
    } 

を、しかし、現在進行中のテストプロジェクトをすることができここに見つかりました@http://code.google.com/p/test-video-encode/

0

はアンドロイドグラフィックスライブラリのサポートにYUVフォーマットを行います。あなたはARGBやRGB32に設定することができるかどう

codecCtx.pix_fmt = AVCodecLibrary.PIX_FMT_YUV420P; 

を参照してください。私はアンドロイドのグラフィックスライブラリがこのピクセルフォーマットをサポートしていることを知っています。

PS:私は検索をたくさんした後

+0

私のプロジェクトでは、YUV420Pを出力するにはエンコードされたビデオが必要ですが、必要に応じてその変換を行うことができます。私はちょうど私がそれを処理するためにffmpegのためにそのデータをロードするはずですが理解していません。 – gtcompscientist

+0

私は、ffmpegサンプル(それらがどのような言語であろうと)を見て、javaで同じ動作シーケンスを複製しようとお勧めします。これはjavaのffmpegの標準ポートですか? –

+0

私が投稿しているプロジェクトでわかるように、AndroidはYUV422Pを返し、エンコードする前にYUV420Pに変換します。 – gtcompscientist

関連する問題