Firefly开源社区

标题: [原创 ]如何在Android平台实现H.264视频数据压制与传送 [打印本页]

作者: error结构体    时间: 2014-10-28 16:17
标题: [原创 ]如何在Android平台实现H.264视频数据压制与传送
本帖最后由 error结构体 于 2014-10-28 16:51 编辑

本帖子主要展示如何在Android平台实现对摄像头数据采集,再通过JNI调用ffmpeg库压制成H.264格式的视频数据,再通过简单UDP协议外发到WIN平台的简易例程,没有实现RTSP和考虑的效率,。实现步骤如下:

1、Android 摄像部分代码实现摄像数据采集。上传代码部分没有实现设定采集YUV数据的宽高。
  1.         @Override
  2.         public void onPreviewFrame(byte[] arg0, Camera arg1) {
  3.                 // TODO Auto-generated method stub
  4.                 if(arg0 != null)
  5.                 {
  6.                                int len = arg0.length;
  7.         Log.i("xinw","len:"+String.valueOf(len));
  8.         encodeprocess(arg0);
  9.                         
  10.                 }
  11.         }
  12.         
  13.         public boolean getIsPreviewCallback()
  14.         {
  15.                 return ispreviewcallback;
  16.         }
  17.         public void setIsPreviewCallback(boolean flag)
  18.         {
  19.                 ispreviewcallback = flag;
  20.         }
复制代码
android端界面效果如下:


2、定义JNI接口,实现对FFMPEG库的调用。JAVA部分:
  1.     static{
  2.         System.loadLibrary("postproc-53");
  3.         System.loadLibrary("avutil-54");
  4.         System.loadLibrary("avcodec-56");
  5.         System.loadLibrary("swresample-1");
  6.         System.loadLibrary("avformat-56");
  7.         System.loadLibrary("swscale-3");
  8.         System.loadLibrary("avfilter-5");
  9.         System.loadLibrary("avdevice-56");
  10.         System.loadLibrary("x264");
  11.         System.loadLibrary("ffmpeg_codec");
  12.    }
  13.    public native int encodeinit(String inputStr);
  14.    public native int encodeprocess(byte[] yuv420spArr);
复制代码

PS:另外如何编译FFMPEG库和X264看我上一篇发的帖子:http://developer.t-firefly.com/thread-201-1-1.html

JNI的C语言的实现:
  1. //static void video_encode_example(int i)
  2. JNIEXPORT jint JNICALL Java_com_tchip_remotepushvideo_CameraPreview_encodeprocess(JNIEnv *env, jobject obj,jbyteArray yuvspArr)
  3. {
  4.     jbyte *tmpHandle = (*env)->GetByteArrayElements(env,yuvspArr,NULL);
  5.         av_init_packet(&pkt);
  6.         pkt.data = NULL;    // packet data will be allocated by the encoder
  7.         pkt.size = 0;


  8.                 LOGD("ffmpeg cxx");
  9.         /* prepare a dummy image */
  10.         /* Y */
  11.         for (y = 0; y < c->height; y++) {
  12.             for (x = 0; x < c->width; x++) {
  13.                 frame->data[0][y * frame->linesize[0] + x] = tmpHandle[x + y*1024];
  14.             }
  15.         }

  16.         /* Cb and Cr */

  17.         for (y = 0; y < c->height/2; y++) {
  18.             for (x = 0; x < c->width/2; x++) {
  19.                 frame->data[1][y * frame->linesize[1] + x] = tmpHandle[1024*768 + 512*y + 2*x  + 1];
  20.                 frame->data[2][y * frame->linesize[2] + x] = tmpHandle[1024*768 + 512*y + 2*x];
  21.                
  22.                 }
  23.             }
  24. //以上代码作用是将YUV420SP转为YUV420P caixx
  25.         frame->pts = i++;

  26.         /* encode the image */
  27.         ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
  28.         if (ret < 0) {
  29.             //fprintf(stderr, "Error encoding frame\n");
  30.             //exit(1);
  31.         }

  32.         if (got_output) {
  33.             //printf("Write frame %3d (size=%5d)\n", i, pkt.size);
  34.                         //printf("count:%d\n",g_h264q.size());
  35.             //fwrite(pkt.data, 1, pkt.size, f);
  36.             sendto(g_socket,pkt.data, pkt.size, 0, (struct sockaddr *)&g_addr, sizeof(g_addr));
  37.             av_free_packet(&pkt);
  38.         }

  39. }  
复制代码

3、PC端实现对接收H.264数据的解码。效果如下:


其中代码部分主要是基于对ffmpeg中sample, decoding_encoding.c的改造。

PS:代码中的IP地址和数据数组宽高为了调试方便,都是绝对值,若要正常使用,请更改相关参数。

tcPlayersingle.rar (11.21 MB, 下载次数: 269)

x264.tar.gz (1.07 MB, 下载次数: 121)

ffmpeg.tar.gz (8.84 MB, 下载次数: 128)

ffmpeg_codec.tar.gz (201.91 KB, 下载次数: 96)

remotepushvideo.tar.gz (71.45 KB, 下载次数: 99)



作者: error结构体    时间: 2014-10-28 16:22
截取摄像头数据的YUV数组使用者要注意他们的大小,我用的一直罗技的免驱的摄像头,预览时显示是544*288的,但实际上YUV数组大小是1024*768的。
作者: ZZP    时间: 2014-10-28 17:56
可做视频监控,好文呀。{:2_31:}
不知道流畅度如何。
作者: error结构体    时间: 2014-10-29 11:39
有延时,没有去深究其中的原因。期待有网友的改善意见。
作者: isle    时间: 2014-10-29 14:18
建议:用硬件编码,参考MediaCodec类。

作者: error结构体    时间: 2014-10-29 16:03
用硬编就不用搞这么一大堆,是方便好多,效率也会好些。:lol
作者: error结构体    时间: 2014-10-29 16:15
找到个硬解相关的帖子,http://blog.csdn.net/liuhongxiangm/article/details/17584303
MARK一下。:P
作者: HJLAllen    时间: 2014-12-1 17:06
请问楼主怎么使用Android客户端啊
作者: error结构体    时间: 2014-12-1 17:35
就是这个包, remotepushvideo.tar.gz ,要自己编译的。。。
作者: wangrui    时间: 2015-12-22 12:07
楼主,我编译你的文件,跑步起来,报下列错误:
D:\Documents\xingyun\remotepushvideo\app\build\intermediates\ndk\debug\obj/local/arm64-v8a/objs/ffmpeg_codec/D_\Documents\xingyun\remotepushvideo\app\src\main\jni\ffmpeg_codec.o: In function `Java_com_tchip_codecvideo_CameraPreview_encodeinit':
D:\Documents\xingyun\remotepushvideo\app\build\intermediates\ndk\debug\obj/local/arm64-v8a/objs/ffmpeg_codec/D_\Documents\xingyun\remotepushvideo\app\src\main\jni\ffmpeg_codec.o: In function `Java_com_tchip_codecvideo_CameraPreview_encodeprocess':
D:\Documents\xingyun\remotepushvideo\app\src\main\jni\ffmpeg_codec.c
Error:(213) undefined reference to `av_register_all'
Error:(230) undefined reference to `avcodec_find_encoder'
Error:(236) undefined reference to `avcodec_alloc_context3'
Error:(262) undefined reference to `av_opt_set'
Error:(265) undefined reference to `avcodec_open2'
Error:(271) undefined reference to `av_frame_alloc'
Error:(282) undefined reference to `av_image_alloc'
Error:(294) undefined reference to `av_init_packet'
Error:(299) undefined reference to `__android_log_print'
Error:(325) undefined reference to `avcodec_encode_video2'
Error:(336) undefined reference to `av_free_packet'
Error:error: ld returned 1 exit status
make.exe: *** [D:\Documents\xingyun\remotepushvideo\app\build\intermediates\ndk\debug\obj/local/arm64-v8a/libffmpeg_codec.so] Error 1
Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'D:\Android\android-ndk-r10e\ndk-build.cmd'' finished with non-zero exit value 2

请问什么原因?
作者: jingjin221    时间: 2015-12-22 13:57
有机会发布一版FFMPEG+LIBSTAGEFRIGHT吧
作者: cangyue1993    时间: 2016-2-19 11:58
这硬解应该用MediaCodec还是用FFmpeg呢,楼主?
作者: liming2018    时间: 2016-5-23 21:19
学习学习

作者: 1种Feel\    时间: 2016-6-27 14:49
楼主在编译jni的时候出现这种问题??你以前有没出现过,谢谢啦!!!

PRODUCT_COPY_FILES device/rockchip/common/fstab.rk30board.bootmode.unknown:root/fstab.rk30board.bootmode.unknown ignored.
PRODUCT_COPY_FILES device/rockchip/common/fstab.rk30board.bootmode.emmc:root/fstab.rk30board.bootmode.emmc ignored.
make: Entering directory `/home/lemon/pro/firefly-rk3288'
make: *** No rule to make target `out/target/product/rk3288_box/obj/SHARED_LIBRARIES/avcodec-56-prebuilt_intermediates/export_includes', needed by `out/target/product/rk3288_box/obj/SHARED_LIBRARIES/ffmpeg_codec_intermediates/import_includes'.  Stop.
make: Leaving directory `/home/lemon/pro/firefly-rk3288'

#### make failed to build some targets (1 seconds) ####




作者: sankycui    时间: 2016-8-15 19:22
很有用啊,正在看着方面内容呢
作者: luminmin882001    时间: 2016-8-16 15:53
这个可是好东西啊。。。。。。。。。。。。。。
作者: qwm2005    时间: 2016-8-30 11:17
用板子上的硬编码应该效率更高些
作者: qwm2005    时间: 2016-8-30 11:21
能否改成用硬编码来处理呢?
作者: johnsfeng    时间: 2017-12-14 10:00
参考
作者: johnsfeng    时间: 2017-12-14 10:01
参考参考........
作者: johnsfeng    时间: 2017-12-14 10:01
参考参考........
作者: benjo    时间: 2017-12-19 18:01
支持h264格式的USB camera,直接通过操作dev/video节点,拿上来的h264数据,直接打包文件封装成Mp4就可以录像了。
作者: 吟风子燕    时间: 2018-2-1 10:14
好资料,大家好,才是真的好
作者: yancc    时间: 2018-4-4 15:57
好文章
作者: zhguo676    时间: 2018-6-27 09:29
谢谢共享




欢迎光临 Firefly开源社区 (https://dev.t-firefly.com/) Powered by Discuz! X3.1