Android直接播放yuv数据

#include <jni.h>
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <android/log.h>
#include <android/native_window_jni.h>
#include <unistd.h>

#define TAG "recordvideo-lib" // 这个是自定义的LOG的标识

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型


JNIEXPORT jint JNICALL
Java_com_uestc_smileteeth_view_recordvideo_RecordVideoLib_videoPlay(JNIEnv *env, jclass type,
                                                                    jbyteArray data_, jint width,
                                                                    jint height, jint pts,
                                                                    jobject surface) {
    jint *data = (*env)->GetByteArrayElements(env, data_, NULL);


    uint8_t *picture_buf;
    int y_size = width * height;
    int picture_size;
    //原视频格式;
    enum AVPixelFormat srcFormat = AV_PIX_FMT_YUV420P;
    //目标视频格式;
    enum AVPixelFormat dstFormat = AV_PIX_FMT_RGBA;
    int src_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(srcFormat));
    //LOGI("YUV420P 格式每个像素占的bit:%d",src_bpp);
    int dst_bpp = av_get_bits_per_pixel(av_pix_fmt_desc_get(dstFormat));


    picture_size = avpicture_get_size(srcFormat, width, height);
    picture_buf = (uint8_t *) av_malloc(picture_size);



//分配解码后的每一数据信息的结构体(指针)
    AVFrame *frame = av_frame_alloc();

    frame->format = srcFormat;
    frame->width = width;
    frame->height = height;

    int ret = av_image_alloc(frame->data, frame->linesize, width, height,
                             srcFormat, 2);
    if (ret < 0) {
        LOGI("Could not allocate raw picture buffer!");
    }

//分配最终显示出来的目标帧信息的结构体(指针)
    AVFrame *outFrame = av_frame_alloc();
    uint8_t *out_buffer = (uint8_t *) av_malloc(
            (size_t) av_image_get_buffer_size(dstFormat, width, width,
                                              1));
//更具指定的数据初始化/填充缓冲区
    av_image_fill_arrays(outFrame->data, outFrame->linesize, out_buffer, dstFormat,
                         width, height, 1);

//初始化SwsContext
    struct SwsContext *swsContext = sws_getContext(
            width   //原图片的宽
            , height  //源图高
            , srcFormat  //源图片format
            , width  //目标图的宽
            , height  //目标图的高
            , dstFormat, SWS_BILINEAR, NULL, NULL, NULL
    );
    if (swsContext == NULL) {
        LOGI("swsContext==NULL");
        return -1;
    }
//Android 原生绘制工具
    ANativeWindow *nativeWindow = ANativeWindow_fromSurface(env, surface);
//定义绘图缓冲区
    ANativeWindow_Buffer outBuffer;
//通过设置宽高限制缓冲区中的像素数量,而非屏幕的物流显示尺寸。
//如果缓冲区与物理屏幕的显示尺寸不相符,则实际显示可能会是拉伸,或者被压缩的图像
    ANativeWindow_setBuffersGeometry(nativeWindow, width, height,
                                     WINDOW_FORMAT_RGBA_8888);

    //数据;
    picture_buf = data;

    frame->data[0] = picture_buf;              // Y
    frame->data[1] = picture_buf + y_size;      // U
    frame->data[2] = picture_buf + y_size * 5 / 4;  // V


    //锁定窗口绘图界面
    ANativeWindow_lock(nativeWindow, &outBuffer, NULL);

    //对输出图像进行色彩,分辨率缩放,滤波处理(转换一帧图像)
    sws_scale(swsContext, (const uint8_t *const *) frame->data,
              frame->linesize, 0,
              frame->height, outFrame->data, outFrame->linesize);
    uint8_t *dst = (uint8_t *) outBuffer.bits;
    //解码后的像素数据首地址
    //这里由于使用的是RGBA格式,所以解码图像数据只保存在data[0]中。但如果是YUV就会有data[0]
    //data[1],data[2]
    uint8_t *src = outFrame->data[0];
    //获取一行字节数
    int oneLineByte = outBuffer.stride * 4;
    //复制一行内存的实际数量
    int srcStride = outFrame->linesize[0];
    for (int i = 0; i < height; i++) {
        memcpy(dst + i * oneLineByte, src + i * srcStride, srcStride);
    }
    //解锁
    ANativeWindow_unlockAndPost(nativeWindow);
    //进行短暂休眠。如果休眠时间太长会导致播放的每帧画面有延迟感,如果短会有加速播放的感觉。
    //一般一每秒30帧
    usleep(pts);

//内存释放
    ANativeWindow_release(nativeWindow);
    av_frame_free(&outFrame);
    av_frame_free(&frame);

    (*env)->
            ReleaseByteArrayElements(env, data_, data,
                                     0);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在安卓应用中实现将字幕添加到YUV数据中,可以使用Android NDK结合OpenCV库来实现。以下是一些基本的步骤: 1. 引入OpenCV库 在Android Studio中引入OpenCV库,可以在build.gradle中添加以下代码: ``` dependencies { implementation 'org.opencv:opencv-android:3.4.3' } ``` 2. 创建OpenCV的Mat对象 使用OpenCV中的YuvImage类可以将YUV数据转换为OpenCV的Mat对象。例如: ``` byte[] yuvData = ... // 读取YUV数据 int width = ... // 设置视频宽度 int height = ... // 设置视频高度 YuvImage yuvImage = new YuvImage(yuvData, ImageFormat.NV21, width, height, null); ByteArrayOutputStream out = new ByteArrayOutputStream(); yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, out); byte[] jpegData = out.toByteArray(); Bitmap bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); Mat mat = new Mat(); Utils.bitmapToMat(bitmap, mat); ``` 3. 添加字幕 使用OpenCV的putText方法可以在Mat对象上添加字幕。例如: ``` Scalar color = new Scalar(255, 255, 255); String text = "Hello, world!"; Point org = new Point(100, 100); int fontFace = Core.FONT_HERSHEY_SIMPLEX; double fontScale = 1; int thickness = 2; Imgproc.putText(mat, text, org, fontFace, fontScale, color, thickness); ``` 4. 将Mat对象转换为YUV数据 使用OpenCV中的Imgproc类可以将Mat对象转换为YUV数据。例如: ``` int frameSize = width * height * 3 / 2; // 计算YUV数据大小 byte[] yuvData = new byte[frameSize]; Mat yuvMat = new Mat(height + height / 2, width, CvType.CV_8UC1); Imgproc.cvtColor(mat, yuvMat, Imgproc.COLOR_RGBA2YUV_I420); yuvMat.get(0, 0, yuvData); ``` 5. 写入处理后的YUV数据 将处理后的YUV数据写入视频文件或者直接渲染到SurfaceView中。例如: ``` FileOutputStream out = new FileOutputStream("output.yuv"); out.write(yuvData); out.close(); ``` 以上是一个基本的实现过程,具体的实现还需要根据实际需求进行调整。注意,在安卓应用中使用OpenCV库需要考虑到性能和内存的问题,需要根据实际情况进行优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值