FFMEPG 平台移植,接口简化和外部模块接入 (三)ffmpeg android移植(ffmpeg实现水印文字显示)




首先来看一副图,用来纪念对视频领域做出贡献的雷神:

当然在这个图片里面的decode不是必须的,Filter 模块本身是一个非常独立的模块,但因为相关的程序,给人造成了他必须要依赖于编码器或者解码器来工作。

OK 不多说,先看看内部实现代码:

#ifndef __CAREYE_PUBLIC_H__
#define __CAREYE_PUBLIC_H__

#define __STDC_CONSTANT_MACROS
#ifdef _WIN32
#define CE_API  __declspec(dllexport)
#define CE_APICALL  __stdcall
#else
#define CE_API
#define CE_APICALL 
#endif

#ifndef _WIN32
#define _ANDROID_
#endif

#ifdef _ANDROID_

#include <android/log.h>

#define CarEyeLog(...) __android_log_print(ANDROID_LOG_DEBUG, "Car-eye-ffmpeg", __VA_ARGS__)

#else

#define CarEyeLog	printf
#endif



typedef struct _CarEye_YUVFrame_
{
	// Y分量数据存储区
	unsigned char *Y;
	// Y分量数据字节数
	int YSize;
	// U分量数据存储区
	unsigned char *U;
	// U分量数据字节数
	int USize;
	// V分量数据存储区
	unsigned char *V;
	// V分量数据字节数
	int VSize;
}CarEye_YUVFrame;



//error number
#define NO_ERROR					0
#define PARAMTER_ERROR		1
#define NULL_MEMORY				2

#define MAX_FILTER_DESCR 512

#endif

#ifndef __CAREYE_FILTER_INTERFACE_H__
#define __CAREYE_FILTER_INTERFACE_H__


#define MAX_STRING_LENGTH 1024
#define MAX_FILE_NAME 64
// OSD水印结果定义
typedef struct _CarEye_OSDParam_
{
	int width;
	int height;
	int fps;
	// 起始X轴坐标
	int X;
	// 起始Y轴坐标
	int Y;
	// 字体大小
	int FontSize;
	// 16进制的RGB颜色值,如绿色:0x00FF00
	unsigned int FontColor;
	// 水印透明度 0~1
	float Transparency;
	// 水印内容
	char SubTitle[MAX_STRING_LENGTH];
	// 字体名称,字体文件放到库的同目录下,如“arial.ttf”
	char FontName[MAX_FILE_NAME];
}CarEye_OSDParam;


typedef struct
{
	AVFrame *VFrame;
	// 编码后的音频帧	
	CarEye_OSDParam para;
	void* handle;
}CarEyeFilter;


#ifdef __cplusplus
extern "C"
{
#endif
	/*
	* Comments: 打开水印资源
	* Param aEncoder: 编码器对象句柄
	* Param aParam: 水印参数
	* @Return int 是否成功,0成功,其他失败
	*/
	CE_API int CE_APICALL CarEye_OpenOsd(CarEyeFilter* pFilter, CarEye_OSDParam aParam);

	/*
	* Comments: 关闭水印资源
	* Param aDeocoder: 编码器对象
	* @Return int 关闭成功与否 0成功
	*/
	CE_API int CE_APICALL CarEye_CloseOsd(CarEyeFilter* pFilter);

	CE_API int CE_APICALL CarEye_add_osd(CarEyeFilter* pFilter, CarEye_YUVFrame *aYuv, CarEye_OSDParam aParam);
#ifdef __cplusplus
}
#endif
	
#endif


实现部分代码:

#include "FFVideoFilter.h"


/*
* Comments: 打开水印资源
* Param aEncoder: 编码器对象句柄
* Param aParam: 水印参数
* @Return int 是否成功,0成功,其他失败
*/

CE_API int CE_APICALL CarEye_OpenOsd( CarEyeFilter* pFliter, CarEye_OSDParam aParam)
{
	if(pFliter==NULL)
	{
		return -PARAMTER_ERROR;
	}
	avfilter_register_all();
	pFliter->VFrame = NULL;
	pFliter->VFrame = av_frame_alloc();
	if(pFliter->VFrame ==   NULL)
	{		
	    return -NULL_MEMORY;
	}		
	pFliter->VFrame->width = aParam.width;
	pFliter->VFrame->height = aParam.height;
	pFliter->VFrame->pts = 0;	
	if (av_image_alloc(pFliter->VFrame->data, pFliter->VFrame->linesize,
	pFliter->VFrame->width,  pFliter->VFrame->height,
	AV_PIX_FMT_YUV420P, 16) < 0)
	{
		CarEyeLog("Cannot av_image_alloc\n");
		av_frame_free(&pFliter->VFrame);
		return -NULL_MEMORY;
	}
	pFliter->VFrame->format = AV_PIX_FMT_YUV420P;
	FFVideoFilter *handle = new FFVideoFilter();
	pFliter->handle =(FFVideoFilter*)handle;	
	return handle->InitFilters( aParam);
}


CE_API int CE_APICALL CarEye_add_osd(CarEyeFilter* pFliter, CarEye_YUVFrame *aYuv,CarEye_OSDParam param)
{
    if(pFliter==NULL || pFliter->VFrame == NULL)
    {
    	  return -PARAMTER_ERROR;
    }   
    FFVideoFilter* handle = (FFVideoFilter*)pFliter->handle;
	  pFliter->VFrame->pts++;
	  memcpy(pFliter->VFrame->data[0], aYuv->Y, aYuv->YSize);
    memcpy(pFliter->VFrame->data[1], aYuv->U, aYuv->USize);
    memcpy(pFliter->VFrame->data[2], aYuv->V, aYuv->VSize);       
    if (handle->BlendFilters(pFliter->VFrame,param) < 0)
    {    	
        return -PARAMTER_ERROR;
    }
    memcpy(aYuv->Y, pFliter->VFrame->data[0], aYuv->YSize);
    memcpy(aYuv->U, pFliter->VFrame->data[1],  aYuv->USize);
    memcpy(aYuv->V, pFliter->VFrame->data[2],  aYuv->VSize);
    return NO_ERROR;
}
/*
* Comments: 关闭水印资源
* Param aDeocoder: 编码器对象
* @Return int 关闭成功与否 0 成功
*/
CE_API int CE_APICALL CarEye_CloseOsd(CarEyeFilter* pFliter)
{
	FFVideoFilter* handle = (FFVideoFilter*)pFliter->handle;
  if(pFliter->VFrame != NULL)
  {
      av_frame_free(&pFliter->VFrame);
  }
	delete handle;
	return NO_ERROR;


#include "FFVideoFilter.h"


/*
* Comments: 打开水印资源
* Param aEncoder: 编码器对象句柄
* Param aParam: 水印参数
* @Return int 是否成功,0成功,其他失败
*/

CE_API int CE_APICALL CarEye_OpenOsd( CarEyeFilter* pFliter, CarEye_OSDParam aParam)
{
	if(pFliter==NULL)
	{
		return -PARAMTER_ERROR;
	}
	avfilter_register_all();
	pFliter->VFrame = NULL;
	pFliter->VFrame = av_frame_alloc();
	if(pFliter->VFrame ==   NULL)
	{		
	    return -NULL_MEMORY;
	}		
	pFliter->VFrame->width = aParam.width;
	pFliter->VFrame->height = aParam.height;
	pFliter->VFrame->pts = 0;	
	if (av_image_alloc(pFliter->VFrame->data, pFliter->VFrame->linesize,
	pFliter->VFrame->width,  pFliter->VFrame->height,
	AV_PIX_FMT_YUV420P, 16) < 0)
	{
		CarEyeLog("Cannot av_image_alloc\n");
		av_frame_free(&pFliter->VFrame);
		return -NULL_MEMORY;
	}
	pFliter->VFrame->format = AV_PIX_FMT_YUV420P;
	FFVideoFilter *handle = new FFVideoFilter();
	pFliter->handle =(FFVideoFilter*)handle;	
	return handle->InitFilters( aParam);
}


CE_API int CE_APICALL CarEye_add_osd(CarEyeFilter* pFliter, CarEye_YUVFrame *aYuv,CarEye_OSDParam param)
{
    if(pFliter==NULL || pFliter->VFrame == NULL)
    {
    	  return -PARAMTER_ERROR;
    }   
    FFVideoFilter* handle = (FFVideoFilter*)pFliter->handle;
	  pFliter->VFrame->pts++;
	  memcpy(pFliter->VFrame->data[0], aYuv->Y, aYuv->YSize);
    memcpy(pFliter->VFrame->data[1], aYuv->U, aYuv->USize);
    memcpy(pFliter->VFrame->data[2], aYuv->V, aYuv->VSize);       
    if (handle->BlendFilters(pFliter->VFrame,param) < 0)
    {    	
        return -PARAMTER_ERROR;
    }
    memcpy(aYuv->Y, pFliter->VFrame->data[0], aYuv->YSize);
    memcpy(aYuv->U, pFliter->VFrame->data[1],  aYuv->USize);
    memcpy(aYuv->V, pFliter->VFrame->data[2],  aYuv->VSize);
    return NO_ERROR;
}
/*
* Comments: 关闭水印资源
* Param aDeocoder: 编码器对象
* @Return int 关闭成功与否 0 成功
*/
CE_API int CE_APICALL CarEye_CloseOsd(CarEyeFilter* pFliter)
{
	FFVideoFilter* handle = (FFVideoFilter*)pFliter->handle;
  if(pFliter->VFrame != NULL)
  {
      av_frame_free(&pFliter->VFrame);
  }
	delete handle;
	return NO_ERROR;
}

在写JNI之前先看下make文件,主要有两个,android.mk用来实现对ffmpeg 库和新增加的外部水印库的编译,application.mk 主要定义编译的全局变量,如参数和架构等,看下怎么编译ffmpeg的动态库的,这里我们没有用到FFMPEG的avdevice这个库。

#APP_ABI := armeabi armeabi-v7a x86
ifeq ($(APP_ABI), x86)
LIB_NAME_PLUS := x86
else
LIB_NAME_PLUS := armeabi
endif

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE:= avcodec-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)


include $(CLEAR_VARS)
LOCAL_MODULE:= avfilter-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavfilter-6.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE:= avformat-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES:= prebuilt/$(LIB_NAME_PLUS)/libavformat-57.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avutil-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libavutil-55.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swresample-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libswresample-2.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := swscale-prebuilt-$(LIB_NAME_PLUS)
LOCAL_SRC_FILES := prebuilt/$(LIB_NAME_PLUS)/libswscale-4.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
ifeq ($(APP_ABI), x86)
TARGET_ARCH:=x86
TARGET_ARCH_ABI:=x86
else
LOCAL_ARM_MODE := arm
endif

LOCAL_MODULE := libffmpegjni
LOCAL_SRC_FILES := com_li_sheldon_ffmpeg4android_FFmpegNative.c CarEyeEncoderAPI.cpp FFVideoFilter.cpp  CarEyeFilter_interface.cpp
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
LOCAL_SHARED_LIBRARIES:= avcodec-prebuilt-$(LIB_NAME_PLUS) \
                         avfilter-prebuilt-$(LIB_NAME_PLUS) \
                         avformat-prebuilt-$(LIB_NAME_PLUS) \
                         avutil-prebuilt-$(LIB_NAME_PLUS) \
                         swresample-prebuilt-$(LIB_NAME_PLUS) \
                         swscale-prebuilt-$(LIB_NAME_PLUS)
LOCAL_C_INCLUDES += -L$(SYSROOT)/usr/include
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include

ifeq ($(APP_ABI), x86)
LOCAL_CFLAGS := -DUSE_X86_CONFIG
else
LOCAL_CFLAGS := -DUSE_ARM_CONFIG
endif
include $(BUILD_SHARED_LIBRARY)

编译后生成libffmpegjni.so, 然后提供JNI的源码给上层调用:

(1)JNIEXPORT :在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。

(2)jint :这个学过编程的人都知道,当然是方法的返回值了。

(3)JNICALL :这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)。

(4)Java_com_test01_Test_firstTest:这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。

(5)JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)

(6)jobject obj:刚才在Test类的main方法中有这样一段代码: 
*/

/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_li_sheldon_ffmpeg4android_FFmpegNative.h"
/* Header for class com_hsb_ffmpeg_FFmpegNative */
#include "libavcodec/avcodec.h"
#include "libavcodec/avdct.h"
#include "libavcodec/avfft.h"
#include "libavcodec/dirac.h"
#include "libavcodec/dv_profile.h"
#include "libavcodec/vaapi.h"
#include "libavcodec/version.h"
#include "libavcodec/vorbis_parser.h"

#include "libavdevice/avdevice.h"
#include "libavdevice/version.h"

#include "libavfilter/avfilter.h"
#include "libavfilter/avfiltergraph.h"
#include "libavfilter/buffersink.h"
#include "libavfilter/buffersrc.h"
#include "libavfilter/version.h"

#include "libavformat/avformat.h"
#include "libavformat/avio.h"
#include "libavformat/version.h"

#include "libavutil/adler32.h"
#include "libavutil/aes_ctr.h"
#include "libavutil/aes.h"
#include "libavutil/attributes.h"
#include "libavutil/audio_fifo.h"
#include "libavutil/avassert.h"
#include "libavutil/avconfig.h"
#include "libavutil/avstring.h"
#include "libavutil/avutil.h"
#include "libavutil/base64.h"
#include "libavutil/blowfish.h"
#include "libavutil/bprint.h"
#include "libavutil/bswap.h"
#include "libavutil/buffer.h"
#include "libavutil/camellia.h"
#include "libavutil/cast5.h"
#include "libavutil/channel_layout.h"
#include "libavutil/common.h"
#include "libavutil/cpu.h"
#include "libavutil/crc.h"
#include "libavutil/des.h"
#include "libavutil/dict.h"
#include "libavutil/display.h"
#include "libavutil/downmix_info.h"
#include "libavutil/error.h"
#include "libavutil/eval.h"
#include "libavutil/ffversion.h"
#include "libavutil/fifo.h"
#include "libavutil/file.h"
#include "libavutil/frame.h"
#include "libavutil/hash.h"
#include "libavutil/hmac.h"
#include "libavutil/imgutils.h"
#include "libavutil/intfloat.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/lfg.h"
#include "libavutil/log.h"
#include "libavutil/lzo.h"
#include "libavutil/macros.h"
#include "libavutil/mastering_display_metadata.h"
#include "libavutil/mathematics.h"
#include "libavutil/md5.h"
#include "libavutil/mem.h"
#include "libavutil/motion_vector.h"
#include "libavutil/murmur3.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/pixdesc.h"
#include "libavutil/pixelutils.h"
#include "libavutil/pixfmt.h"
#include "libavutil/random_seed.h"
#include "libavutil/rational.h"
#include "libavutil/rc4.h"
#include "libavutil/replaygain.h"
#include "libavutil/ripemd.h"
#include "libavutil/samplefmt.h"
#include "libavutil/sha.h"
#include "libavutil/sha512.h"
#include "libavutil/stereo3d.h"
#include "libavutil/tea.h"
#include "libavutil/threadmessage.h"
#include "libavutil/time.h"
#include "libavutil/timecode.h"
#include "libavutil/timestamp.h"
#include "libavutil/tree.h"
#include "libavutil/twofish.h"
#include "libavutil/version.h"
#include "libavutil/xtea.h"
#include "libswresample/swresample.h"
#include "libswresample/version.h"
#include "libswscale/swscale.h"
#include "libswscale/version.h"
#include "CarEyeEncoderAPI.h"
#include "CarEyeFilter_interface.h"

#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "ffmpeg4android", __VA_ARGS__)

JNIEXPORT jint JNICALL Java_com_li_sheldon_ffmpeg4android_FFmpegNative_ffmpeg_1h264
  (JNIEnv* env, jobject obj, jint codecID)
 {
    AVCodec* codec = NULL;
    av_register_all();//该函数在所有基于ffmpeg的应用程序中几乎都是第一个被调用的。只有调用了该函数,才能使用复用器,编码器等
    codec = avcodec_find_decoder(codecID);//通过code ID查找一个已经注册的音视频编码器。H264的codecID是28,所以我们java那边传28下来如果检测到H264注册过了这边codec就不为空,返回0
    if(codec != NULL){
      return 0;
    }else{
      return -1;
    }
}
CarEyeFilter gFliter;

JNIEXPORT jint JNICALL Java_com_li_sheldon_ffmpeg4android_FFmpegNative_OpenOSD(JNIEnv* env, jobject obj, jint width, jint height, jint startX, jint startY, jint FontSize,  jint color, jstring  filename, jstring content  )
{
    char* Name;
    char* pContent;
    gFliter.para.X = startX;
    gFliter.para.Y = startY;
    gFliter.para.width = width;
    gFliter.para.height = height;
   	gFliter.para.FontSize = FontSize;
   	gFliter.para.FontColor = color;
   	gFliter.para.fps = 25;
    Name=(*env)->GetStringUTFChars(env,filename, JNI_FALSE);
    pContent=(*env)->GetStringUTFChars(env,content, JNI_FALSE);
   	strcpy(gFliter.para.FontName, Name);
   	strcpy(gFliter.para.SubTitle, pContent);	
   	gFliter.para.Transparency = 0.7;	 
   	(*env)->ReleaseStringUTFChars(env, filename, Name);
    (*env)->ReleaseStringUTFChars(env, content, pContent);   	      	
   	return CarEye_OpenOsd(&gFliter, gFliter.para);
}

JNIEXPORT jint JNICALL Java_com_li_sheldon_ffmpeg4android_FFmpegNative_CloseOSD(JNIEnv* env, jobject obj)
{		
    return CarEye_CloseOsd(&gFliter);
}

JNIEXPORT jint JNICALL Java_com_li_sheldon_ffmpeg4android_FFmpegNative_AddOSD(JNIEnv* env, jobject obj, jbyteArray frame, jstring txtoverlay)
{
    unsigned char * pBuffer;
    int ret;
    char *txt;
    CarEye_YUVFrame yuv_frame;
    pBuffer = (*env)->GetByteArrayElements(env,frame, 0 );
    int len = (*env)->GetArrayLength(env,frame);     
    yuv_frame.Y = pBuffer;
    yuv_frame.YSize = len*2/3;
    yuv_frame.U = &pBuffer[len*2/3];
    yuv_frame.USize = len/6;
    yuv_frame.V = &pBuffer[len*5/6];
    yuv_frame.VSize = len/6;
    txt = (*env)->GetStringUTFChars(env,txtoverlay, JNI_FALSE);
    strcpy(gFliter.para.SubTitle, txt);	   
    ret = CarEye_add_osd(&gFliter,&yuv_frame,gFliter.para);        
   (*env)->ReleaseStringUTFChars(env, txtoverlay, txt);     
   (*env)->ReleaseByteArrayElements(env,frame,pBuffer,0);
   return ret;
}

OK, 写一个简单例子测试下:

package com.li.sheldon.ffmpeg4android;

import android.util.Log;

/**
 * Created by sheldon on 17-1-4.
 */
public class FFmpegNative {

    static{
        System.loadLibrary("avcodec-57");
        System.loadLibrary("avfilter-6");
        System.loadLibrary("avformat-57");
        System.loadLibrary("avutil-55");
        System.loadLibrary("swresample-2");
        System.loadLibrary("swscale-4");
        System.loadLibrary("ffmpegjni");
    }
    private native int AddOSD(byte[] buffer, String txt);
    private native int CloseOSD();
    private native int OpenOSD(int width, int height, int startX, int startY, int fontsize, int color, String  filename, String  content);
    private native int ffmpeg_h264(int id);

    public int test_h246(int id){
        return ffmpeg_h264(id);
    }
    public int InitOSD(int width, int height, int startX, int startY, int fontsize, int color, String  filename, String  content)
    {
         return OpenOSD(width,height,startX,startY,fontsize,color, filename,content );
    }
    public int DelOSD()
    {
        return CloseOSD();
    }
    public int blendOSD(byte[] buffer,String txt)
    {
        return AddOSD(buffer,txt);
    }

}

package com.li.sheldon.ffmpeg4android;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        int codec_id = 28;
        final FFmpegNative ffmpeg = new FFmpegNative();
        int tmp = ffmpeg.test_h246(codec_id); //28 is the H264 Codec ID
        TextView tv = (TextView) this.findViewById(R.id.hello_ffmpeg);
        tv.setText(tmp == 0 ? "Support Codec ID:" + codec_id : "Not support Codec ID:" + codec_id);
        Log.d("ffmpeg4android", "OSD init start: ");
        new Thread(new Runnable() {
            @Override
            public void run() {
                int loop = 0;
                FileOutputStream out;
                FileInputStream in;
                FFmpegNative ffmpeg = new FFmpegNative();
                int ret = ffmpeg.InitOSD(1280, 720, 10, 10, 28, 0x00ff00, String.format("/mnt/sdcard/arial.ttf"), String.format("ddddd"));
                if (ret != 0) {
                    Log.d("ffmpeg4android", "OSD init fail: "+ret);
                }
                Log.d("ffmpeg4android", "OSD blend ");
                byte[] data=new byte[1280*720*3/2];
                try {
                    File f = new File("/mnt/sdcard/out.yuv");
                    if(f.exists()) f.delete();
                    f.createNewFile();
                    out = new FileOutputStream(f);
                    File input = new File("/mnt/sdcard/input.yuv");
                    in = new FileInputStream(input);
                    int len;
                    while(loop<1000)
                    {
                        if(in.read(data,0,1280*720*3/2)<0)
                        {
                            Log.d("ffmpeg4android", "read fail:");
                            break;
                        }else {
                            String txt = "car-eye-filter" + new SimpleDateFormat("yyyy-MM-dd").format(new Date())+loop;
                            int result =  ffmpeg.blendOSD(data, txt);
                            out.write(data,0,1280*720*3/2);
                            Log.d("ffmpeg4android", "write data sucessful:"+result+"data[0]"+data[0]);
                        }
                        loop++;
                    }
                    in.close();
                    out.close();
                    ffmpeg.DelOSD();

                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }
}



生成的YUV数据用播放器打开如下



car-eye开源官方网址:www.car-eye.cn   

car-eye 流媒体平台网址:www.liveoss.com    

car-eye 技术官方邮箱: support@car-eye.cn    
car-eye技术交流QQ群: 590411159     


CopyRight©  car-eye 开源团队 2018


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值