将FFmpeg播放器移植到Android 2.1的问题求助
早期Android版本FFmpeg移植与播放问题
我查阅了大量早期Android版本构建与使用FFmpeg的资料,研究2011-2014年的播放器源码,已在NDKv5平台成功构建FFmpeg 4.0.4和3.1.4版本。核心要点如下:
- Android 2.2(API Level 8)之前不存在
<android/bitmap.h>和<android/native_window.h>头文件 - 音视频流需额外实现缓冲区管理,否则播放时会因溢出数秒后崩溃(附C++和Java代码示例)
- FFmpeg是支持Android 2.1及以上未官方收录编解码器的唯一方案
C++解码核心代码
void decodeVideoFromPacket(JNIEnv *env, jobject instance, jclass mplayer_class, AVPacket avpkt, int total_frames, int length) { AVFrame *pFrame = NULL AVFrame *pFrameRGB = NULL; pFrame = avcodec_alloc_frame(); pFrameRGB = avcodec_alloc_frame(); int frame_size = avpicture_get_size(PIX_FMT_RGB32, gVideoCodecCtx->width, gVideoCodecCtx->height); unsigned char* buffer = (unsigned char*)av_malloc((size_t)frame_size * 3); if (!buffer) { av_free(pFrame); av_free(pFrameRGB); return; } jbyteArray buffer2; jmethodID renderVideoFrames = env->GetMethodID(mplayer_class, "renderVideoFrames", "([BI)V"); int frameDecoded; avpicture_fill((AVPicture*) pFrame, buffer, gVideoCodecCtx->pix_fmt, gVideoCodecCtx->width, gVideoCodecCtx->height ); if (avpkt.stream_index == gVideoStreamIndex) { // If video stream found int size = avpkt.size; total_frames++; struct SwsContext *img_convert_ctx = NULL; avcodec_decode_video2(gVideoCodecCtx, pFrame, &frameDecoded, &avpkt); if (!frameDecoded || pFrame == NULL) { return; } try { PixelFormat pxf; // RGB565 by default for Android Canvas in pre-Gingerbread devices. if(android::get_android_api_version(env) >= ANDROID_API_CODENAME_GINGERBREAD) { pxf = PIX_FMT_BGR32; } else { pxf = PIX_FMT_RGB565; } int rgbBytes = avpicture_get_size(pxf, gVideoCodecCtx->width, gVideoCodecCtx->height); // Converting YUV to RGB frame & RGB frame to char* buffer buffer = convertYuv2Rgb(pxf, pFrame, rgbBytes); // result of av_image_copy_to_buffer() if(buffer == NULL) { return; } buffer2 = env->NewByteArray((jsize) rgbBytes); env->SetByteArrayRegion(buffer2, 0, (jsize) rgbBytes, (jbyte *) buffer); env->CallVoidMethod(instance, renderVideoFrames, buffer2, rgbBytes); env->DeleteLocalRef(buffer2); free(buffer); } catch (...) { if (debug_mode) { LOGE(10, "[ERROR] Render video frames failed"); return; } } } }
Java渲染核心代码
private void renderVideoFrames(final byte[] buffer, final int length) { new Thread(new Runnable() { @Override public void run() { Canvas c; VideoTrack track = null; for (int tracks_index = 0; tracks_index < tracks.size(); tracks_index++) { if (tracks.get(tracks_index) instanceof VideoTrack) { track = (VideoTrack) tracks.get(tracks_index); } } if (track != null) { int frame_width = track.frame_size[0]; int frame_height = track.frame_size[1]; if (frame_width > 0 && frame_height > 0) { try { // RGB_565 == 65K colours (16 bit) // RGB_8888 == 16.7M colours (24 bit w/ alpha ch.) int bpp = Build.VERSION.SDK_INT > 9 ? 16 : 24; Bitmap.Config bmp_config = bpp == 24 ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888; Paint paint = new Paint(); if(buffer != null && holder != null) { holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL); if((c = holder.lockCanvas()) == null) { Log.d(MPLAY_TAG, "Lock canvas failed"); return; } ByteBuffer bbuf = ByteBuffer.allocateDirect(minVideoBufferSize); bbuf.rewind(); for(int i = 0; i < buffer.length; i++) { bbuf.put(i, buffer[i]); } bbuf.rewind(); // The approximate location where the application crashed. Bitmap bmp = Bitmap.createBitmap(frame_width, frame_height, bmp_config); bmp.copyPixelsFromBuffer(bbuf); float aspect_ratio = (float) frame_width / (float) frame_height; int scaled_width = (int)(aspect_ratio * (c.getHeight())); c.drawBitmap(bmp, null, new RectF( ((c.getWidth() - scaled_width) / 2), 0, ((c.getWidth() - scaled_width) / 2) + scaled_width, c.getHeight()), null); holder.unlockCanvasAndPost(c); bmp.recycle(); bbuf.clear(); } else { Log.d(MPLAY_TAG, "Video frame buffer is null"); } } catch (Exception ex) { ex.printStackTrace(); } catch (OutOfMemoryError oom) { oom.printStackTrace(); stop(); } } } } }).start(); }
测试异常信息
E/dalvikvm-heap: Out of memory on a 1228812-byte allocation I/dalvikvm: "Thread-495" prio=5 tid=21 RUNNABLE ................................................ at android.graphics.Bitmap.nativeCreate(Native Method) at android.graphics.Bitmap.createBitmap(Bitmap.java:640) at android.graphics.Bitmap.createBitmap(Bitmap.java:620) at [app_package_name].MediaPlayer$5.run(MediaPlayer.java:406) at java.lang.Thread.run(Thread.java:856)
补充说明:我最初通过自研构建脚本在Ubuntu 12.04 LTS虚拟机编译FFmpeg 0.11.x,寻找Android 2.2以下的播放器示例(资料极少),但播放器打开文件显示几帧后因栈或缓冲区溢出崩溃,遂暂时搁置开发。
请问是否有现成的单C++文件方案,可兼顾向早期Android版本移植的所有细节?感谢解答。
内容的提问来源于stack exchange,提问作者tretdm
相关产品推荐
相关产品推荐

