在Android上使用javacv中的ffmpeg编码视频会导致本机代码崩溃
注意:自从最初提出这个问题以来,我已经对其进行了更新,以反映我在将实时摄像机图像加载到ffmpeg库中所学到的一些知识 我正在使用为Android编译的在Android上使用javacv中的ffmpeg编码视频会导致本机代码崩溃,java,android,opencv,ffmpeg,video-encoding,Java,Android,Opencv,Ffmpeg,Video Encoding,注意:自从最初提出这个问题以来,我已经对其进行了更新,以反映我在将实时摄像机图像加载到ffmpeg库中所学到的一些知识 我正在使用为Android编译的javacv中的ffmpeg为我的应用程序编码/解码视频。(注意,最初我试图使用ffmpeg-java,但它有一些不兼容的库) 原始问题:我遇到的问题是,我目前将每个帧作为位图(只是一个普通的android.graphics.Bitmap)获取,我不知道如何将其填充到编码器中 javacv的ffmpeg中的解决方案:使用avpicture\u f
javacv
中的ffmpeg
为我的应用程序编码/解码视频。(注意,最初我试图使用ffmpeg-java
,但它有一些不兼容的库)
原始问题:我遇到的问题是,我目前将每个帧作为位图(只是一个普通的android.graphics.Bitmap
)获取,我不知道如何将其填充到编码器中
javacv
的ffmpeg
中的解决方案:使用avpicture\u fill(),来自Android的格式应该是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 + ")");
BytePointer picPointer=新的BytePointer(数据);
int bBuffSize=mFrameSize;
BytePointer bBuffer=新的BytePointer(bBuffSize);
int picSize=0;
如果((picSize=avcodec.avpicture\u fill((avpicture)mFrame,picPointer,avutil.PIX\u FMT\u YUV420P,VIDEO\u WIDTH,VIDEO\u HEIGHT))=0)
{
总尺寸+=尺寸;
Logging.Log(“编码的“+”大小“+”字节”);
//有延迟帧吗
size=avcodec.avcodec_encode_视频(mCodecCtx、bBuffer、bBuffSize、null);
}
Logging.Log(“已完成编码(“+totalSize+”));
但是,到目前为止,我不知道如何将位图放入正确的块中,或者我的设置是否正确
关于代码的一些注释:
-视频宽度
=352
-视频高度
=288
-VIDEO\u FPS
=30 android图形库是否支持YUV格式:
codecCtx.pix_fmt = AVCodecLibrary.PIX_FMT_YUV420P;
看看是否可以将其设置为ARGB或RGB32。我知道android图形库支持这种像素格式
PS:我对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);
avcodec.avcodec_register_all();
avcodec.avcodec_init();
avformat.av_register_all();
/*查找H263视频编码器*/
mCodec=avcodec.avcodec\u find\u编码器(avcodec.CODEC\u ID\u H263);
if(mCodec==null){
Log.d(“TEST_VIDEO”、“avcodec_find_encoder()run fail”);
}
mCodecCtx=avcodec.avcodec_alloc_context();
picture=avcodec.avcodec_alloc_frame();
/*输入样本参数*/
mCodecCtx.比特率(400000);
/*分辨率必须是2的倍数*/
mCodecCtx.宽度(视频宽度);
MCODECTX.高度(视频高度);
/*每秒帧数*/
AVRational avFPS=新的AVRational();
avFPS.num(1);
avFPS.den(视频FPS);
mCodecCtx.时基(avFPS);
mCodecCtx.pix_fmt(avutil.pix_fmt_YUV420P);
mCodecCtx.codec_id(avcodec.codec_id_H263);
mCodecCtx.codec_类型(avutil.AVMEDIA_类型_视频);
/*打开它*/
如果(avcodec.avcodec_打开(MCODECTX,mCodec)<0){
Log.d(“TEST_VIDEO”,“avcodec_open()run fail”);
}
/*alloc映像和输出缓冲区*/
输出缓冲区大小=100000;
输出缓冲区=avutil.av\u malloc(输出缓冲区大小);
size=mCodecCtx.width()*mCodecCtx.height();
picture_buffer=avutil.av_malloc((大小*3)/2);/*YUV 420的尺寸*/
picture.data(0,新的字节指针(picture_缓冲区));
图片.数据(1,图片.数据(0).位置(大小));
图片.数据(2,图片.数据(1).位置(大小/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);
}
/(1)首先转换字节[]
字节[]数据420=新字节[data.length];
将_yuv422_转换为_yuv420(数据、数据420、视频宽度、视频高度);
//(2) 填充图片缓冲区
int data1_offset=视频高度*视频宽度;
int data2_offset=data1_offset*5/4;
int pic_linesize_0=picture.linesize(0);
int pic_linesize_1=图片。linesize(1);
int pic_linesize_2=图片。linesize(2);
//Y
对于(y=0;y
我仍在对数据进行打包,但是正在进行的测试项目可以在这里找到@对于我的项目,我需要编码的视频来输出YUV420P,但如果需要,我可以进行转换。我只是不明白我应该在哪里加载数据以供ffmpeg处理。我建议您查看ffmpeg示例(无论它们使用何种语言)