Chinaunix首页 | 论坛 | 博客
  • 博客访问: 64241
  • 博文数量: 8
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 144
  • 用 户 组: 普通用户
  • 注册时间: 2013-05-27 20:49
个人简介

里程

文章分类

全部博文(8)

文章存档

2013年(8)

我的朋友

分类: Android平台

2013-05-29 17:18:08

  前段时间花半个月左右实现Android平台上播放TCP传输的数据流,今天就做一个统一整理

需求:在android实现播放通过TCP传输过来的TS数据流,数据流不包含同步信息,只包含图像不需要处理声音信息(如需要处理同步,程序的复杂度至少翻倍)

实现:前前后后大概用了三种方式,下面逐一介绍

方案一:文件+JAVA MediaPlayer
    流程 :先将TCP传输的数据保存到文件中,然后通过Android上层MediaPlayer播放文件。这个方案技术实现较简单。不详述
     缺点: 需要频繁的切换播放文件,切换期间会黑屏,而且存在较严重延迟。

  方案二与方案三在接收数据上保持一致,使用C编写,在应用的目录创建命名管道文件,JAVA通过JNI进行调用。下面主要介绍解码流程
    
方案二:管道 + FFMPEG 
   流程:ffmpeg的编译参考 
     附上我在使用的编译脚本

点击(此处)折叠或打开

  1. #!/bin/bash
  2. ######################################################
  3. # Usage:
  4. # put this script in top of FFmpeg source tree
  5. # ./build_android
  6. # It generates binary for following architectures:
  7. # ARMv6
  8. # ARMv6+VFP
  9. # ARMv7+VFPv3-d16 (Tegra2)
  10. # ARMv7+Neon (Cortex-A8)
  11. # Customizing:
  12. # 1. Feel free to change ./configure parameters for more features
  13. # 2. To adapt other ARM variants
  14. # set $CPU and $OPTIMIZE_CFLAGS
  15. # call build_one
  16. ######################################################
  17. NDK=/tool/android-ndk-r8e
  18. PLATFORM=$NDK/platforms/android-8/arch-arm/
  19. PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86_64
  20. function build_one
  21. {
  22. ./configure --target-os=linux
  23. --prefix=$PREFIX
  24. --enable-cross-compile
  25. --extra-libs="-lgcc"
  26. --arch=arm
  27. --cc=$PREBUILT/bin/arm-linux-androideabi-gcc
  28. --cross-prefix=$PREBUILT/bin/arm-linux-androideabi-
  29. --nm=$PREBUILT/bin/arm-linux-androideabi-nm
  30. --sysroot=$PLATFORM
  31. --extra-cflags=" -O3 -fpic -DANDROID -DHAVE_SYS_UIO_H=1 -Dipv6mr_interface=ipv6mr_ifindex -fasm -Wno-psabi -fno-short-enums -fno-strict-aliasing -finline-limit=300 $OPTIMIZE_CFLAGS "
  32. --disable-shared
  33. --enable-static
  34. --extra-ldflags="-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl -llog"
  35. --disable-everything
  36. --enable-demuxer=mov
  37. --enable-demuxer=h264
  38. --enable-demuxer=mpegts
  39. --disable-ffplay
  40. --enable-protocol=file
  41. --enable-avformat
  42. --enable-avcodec
  43. --enable-decoder=rawvideo
  44. --enable-decoder=mjpeg
  45. --enable-decoder=h263
  46. --enable-decoder=mpeg4
  47. --enable-decoder=h264
  48. --enable-parser=h264
  49. --disable-network
  50. --enable-zlib
  51. --disable-avfilter
  52. --disable-ffprobe
  53. --disable-avdevice
  54. $ADDITIONAL_CONFIGURE_FLAG
  55. make clean
  56. make -j4 install
  57. $PREBUILT/bin/arm-linux-androideabi-ar d libavcodec/libavcodec.a inverse.o
  58. $PREBUILT/bin/arm-linux-androideabi-ld -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -soname libffmpeg.so -shared -nostdlib -z,noexecstack -Bsymbolic --whole-archive --no-undefined -o $PREFIX/libffmpeg.so libavcodec/libavcodec.a libavformat/libavformat.a libavutil/libavutil.a libswscale/libswscale.a -lc -lm -lz -ldl -llog --warn-once --dynamic-linker=/system/bin/linker $PREBUILT/lib/gcc/arm-linux-androideabi/4.4.3/libgcc.a
  59. }
  60. #arm v6
  61. #CPU=armv6
  62. #OPTIMIZE_CFLAGS="-marm -march=$CPU"
  63. #PREFIX=./android/$CPU
  64. #ADDITIONAL_CONFIGURE_FLAG=
  65. #build_one
  66. #arm v7vfpv3
  67. #CPU=armv7-a
  68. #OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -marm -march=$CPU "
  69. #PREFIX=./android/$CPU
  70. #ADDITIONAL_CONFIGURE_FLAG=
  71. #build_one
  72. #arm v7vfp
  73. #CPU=armv7-a
  74. #OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
  75. #PREFIX=./android/$CPU-vfp
  76. #ADDITIONAL_CONFIGURE_FLAG=
  77. #build_one
  78. #arm v7n
  79. CPU=armv7-a
  80. OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8"
  81. PREFIX=./android/$CPU
  82. ADDITIONAL_CONFIGURE_FLAG=--enable-neon
  83. build_one
  84. #arm v6+vfp
  85. #CPU=armv6
  86. #OPTIMIZE_CFLAGS="-DCMP_HAVE_VFP -mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
  87. #PREFIX=./android/${CPU}_vfp
  88. #ADDITIONAL_CONFIGURE_FLAG=
  89. #build_one
   之前使用相同脚本,在最后链接成ffmpeg.so时出现错误,反复折腾,最后发现问题出现在代码上,使用下载的代码包,无法正常链接出SO,使用GIT CLONE然后checkout tag,顺利编译出ffmpeg.so
   然后使用ffmpeg的API编写解码流程,这个网上资料较多不详述。主要问题出现在如何显示上,最开始的方案如下

点击(此处)折叠或打开

  1. static android::sp<android::Surface> native_surface;

  2. static android::Surface* getNativeSurface(JNIEnv* env, jobject jsurface, jint version)
  3. {
  4.     jclass clazz = env->FindClass("android/view/Surface");
  5.     jfieldID field_surface;
  6.     if(version <=8)
  7.     {
  8.         field_surface = env->GetFieldID(clazz, "mSurface", "I");
  9.     }
  10.     else
  11.         field_surface = env->GetFieldID(clazz, ANDROID_VIEW_SURFACE_JNI_ID, "I");

  12.     if (field_surface == NULL)
  13.     {
  14.         return NULL;
  15.     }
  16.     return (android::Surface *) env->GetIntField(jsurface, field_surface);
  17. }

  18. int setSurface(JNIEnv *env, jobject jsurface, jint version)
  19. {
  20.     native_surface = getNativeSurface(env, jsurface, version);

  21.     if(android::Surface::isValid(native_surface))
  22.     {
  23.         __android_log_print(ANDROID_LOG_INFO, "libjni", "native_surface is valid");
  24.         return 1;
  25.     }
  26.     else
  27.         __android_log_print(ANDROID_LOG_ERROR, "libjni", "native_surface is invalid");

  28.     return 0;
  29. }
    通过这个获得显示缓冲,解码转成RGB后直接写缓冲
  

点击(此处)折叠或打开

  1.  sws_scale(img_convert_ctx,
                     pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                     picture.data, picture.linesize)
  2. native_surface->lock(&info,&dirtyRegion,true);
  3. memcpy(info.bits,picture.data[0],(picture.linesize[0])*720);
  4. native_surface->unlockAndPost();
    这种方式获得的缓冲在高于4.0.3的系统上无法正常运行,且编译时需要引入android源代码库,操作繁琐且不具有通用性。
    
  后来换成NDK提供的方式

点击(此处)折叠或打开

  1. static ANativeWindow* theNativeWindow;
    ANativeWindow_Buffer wbuffer;

  2. JNIEXPORT void JNICALL Java_com_inesa_jni_DisVideo_setNativeSurface
  3. (JNIEnv *env, jobject obj, jobject jsurface){
  4.     //setSurface(env, jsurface, 16);
  5.           theNativeWindow = ANativeWindow_fromSurface(env, jsurface);
  6. }
  然后就可以在theNativeWindow上刷图片

  

点击(此处)折叠或打开

  1. if (ANativeWindow_lock(theNativeWindow, &wbuffer, NULL) < 0) {
  2.                     printf("---------Unable to lock window buffer");
  3.                     //return -1;
  4.                 }
  5.                 /*
  6.                  printf("linesize = %d linesize1 = %d linesize2 = %d linesize3 = %dn",picture.linesize[0],picture.linesize[1],picture.linesize[2],picture.linesize[3] );
  7.                  memcpy(wbuffer.bits, picture.data[0], picture.linesize[0] * wbuffer.height);
  8.                  memcpy(wbuffer.bits+picture.linesize[0] * wbuffer.height, picture.data[1], picture.linesize[1] * wbuffer.height/2);
  9.                  memcpy(wbuffer.bits+picture.linesize[0] * wbuffer.height +picture.linesize[1] * wbuffer.height/2, picture.data[2], picture.linesize[2] * wbuffer.height/2);
  10.                  */
  11.                 // printf("linesize = %d linesize1 = %d linesize2 = %d linesize3 = %d ph = %d pw = %dn",pFrame->linesize[0],pFrame->linesize[1],pFrame->linesize[2],pFrame->linesize[3],pFrame->height,pFrame->width);

  12.                 // resample_yv12((guchar*)wbuffer.bits,wbuffer.width,wbuffer.height, (guchar*)pdata, pFrame->width, pFrame->height, SCALE_TYEP_NEAREST);
  13.                 get_yuvdata(wbuffer.bits,pFrame);
  14.                 ANativeWindow_unlockAndPost(theNativeWindow);
      这种写缓冲方式编译简单,适用平台广。


 用FFMPEG的方案适用数据流的格式多,surface刷写方式。但是直接使用CPU解码,软解的性能依赖于CPU的性能,且功耗较高。
 为改善效率问题,试过好几种方式,如转换成YUV数据,不进行RGB转换,然后直接显示420SP(surfaceview.getholder.setformat).或者解码后通过BILINEAR算法缩放而不使用sws_scale函数,当然效果均不太理想,遂有了第三种方案

方案三:管道+OpenAL
    此方案是直接参考NDK实例代码native-media,就不贴代码了。
   优点是使用Android底层解码库,基本使用gpu解码,速度快,功耗小。不足就是仅支持TS流,且显示无法全部控制,以及依赖底层实现,部分设备无法流畅播放,且无法DEBUG。


其实最好的方式是在SERVER建立RTSP服务,当然还需在接收端使用LIVE555转码,不过这种方式不符合后面特殊的加密需求。



  



         
阅读(7078) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~