解决faplayer在android4.1上只有图像没有声音的问题

摘要:
任何研究过Twilove的fablayer代码的人都应该知道,fablayer的代码中使用了两个播放器程序,一个是Android作为默认播放器的播放器,另一个是使用vlc代码的播放器。)由于faplayer已经停止更新,我们只能自己解决这个问题。在fablayer的jni代码中查找此日志的输出位置:in fablayer/jni/vlc/modules/audio_output/android_in AudioTrack。c: 1p_library=初始化库;2if(!

  研究过twilove的faplayer代码的人应该都知道,faplayer代码中使用了两个播放器程序,一个是android自带的播放器作为默认的播放器,另外一个就是使用了vlc代码的播放器。之前写过一篇相关的文章:采用faplayer播放EPUB书中的mp4视频
  这次要讲的问题就是在使用faplayer中的vlc代码的时候,之前在2.3系统上正常,后来升级的4.04的时候发现只有声音没有图像,这个问题后来解决了,解决的过程下篇日志再说,这次要说的是系统升级的4.1之后,发现只有图像又没有声音了!!!(尼玛能靠点谱不?)由于faplayer早已停止更新了,所以这个问题只能自己想办法搞定。最终在我的“不懈努力下”,问题终于搞定了,在解决问题的过程中我觉得有些东西是比较有意思也值得记录下来的,因此写了这篇文章.

遇到问题第一件事情当然是查看日志,vlc的日志是非常详细的,查看后我发现这两条日志非常可疑:
01-15 14:31:50.960: E/faplayer(1622): [0x67389ef8]audiotrack_android audio output: Could not initialize libmedia.so!
01-15 14:31:50.960: D/faplayer(1622): [0x67389ef8]main audio output: using audio output module "dummy"
 
上面这条日志是报了个错说无法初始化libmedia.so这个库,下面的日志说的是使用虚拟的音频输出模块,是不是使用了虚拟的音频模块,才导致了没有声音呢?
在faplayer的jni代码中查找这条日志的输出位置:在faplayer/jni/vlc/modules/audio_output/android_AudioTrack.c中:
1     p_library = InitLibrary(p_this);
2     if (!p_library) {
3         msg_Err(VLC_OBJECT(p_this), "Could not initialize libmedia.so!");
4         return VLC_EGENERIC;
5     }

看来是InitLibrary函数出了问题,我们来看看这个函数的内容:

 1 void *InitLibrary() {
 2     void *p_library;
 3 
 4     p_library = dlopen("libmedia.so", RTLD_NOW);
 5     if (!p_library)
 6         return NULL;
 7     as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
 8     as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
 9     as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
10     at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
11     at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
12     at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
13     at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
14     at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
15     at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
16     at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
17     at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
18     at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
19     // need the first 3 or the last 1
20     if (!((as_getOutputFrameCount && as_getOutputLatency && as_getOutputSamplingRate) || at_getMinFrameCount)) {
21         dlclose(p_library);
22         return NULL;
23     }
24     // need all in the list
25     if (!((at_ctor || at_ctor_legacy) && at_dtor && at_initCheck && at_start && at_stop && at_write && at_flush)) {
26         dlclose(p_library);
27         return NULL;
28     }
29     return p_library;
30 }

嗯,看来这个函数的内容是从so库中查找相应的函数地址,然后把地址赋值给对应的函数指针,那么类似“_ZN7android11AudioSystem19getOutputFrameCountEPii”这样的字符串就是函数在so库中的签名了。我们看看这个字符串所代表的函数的签名:

1 // _ZN7android11AudioSystem19getOutputFrameCountEPii
2 typedef int (*AudioSystem_getOutputFrameCount)(int *, int);

为什么这样的函数在so中会有这样奇怪的代号呢?这方面的知识有一篇文章讲的很好:C++的函数重载

了解了这个后我就在猜想:应该是某个或是某几个函数的签名在4.1中发生了改变,导致找不到才出的错,于是我这样修改程序:

 1 void *InitLibrary() {
 2     void *p_library;
 3 
 4     p_library = dlopen("libmedia.so", RTLD_NOW);
 5     if (!p_library)
 6         return NULL;
 7     as_getOutputFrameCount = (AudioSystem_getOutputFrameCount)(dlsym(p_library, "_ZN7android11AudioSystem19getOutputFrameCountEPii"));
 8     as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
 9     as_getOutputSamplingRate = (AudioSystem_getOutputSamplingRate)(dlsym(p_library, "_ZN7android11AudioSystem21getOutputSamplingRateEPii"));
10     at_getMinFrameCount = (AudioTrack_getMinFrameCount)(dlsym(p_library, "_ZN7android10AudioTrack16getMinFrameCountEPiij"));
11     at_ctor = (AudioTrack_ctor)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_ii"));
12     at_ctor_legacy = (AudioTrack_ctor_legacy)(dlsym(p_library, "_ZN7android10AudioTrackC1EijiiijPFviPvS1_ES1_i"));
13     at_dtor = (AudioTrack_dtor)(dlsym(p_library, "_ZN7android10AudioTrackD1Ev"));
14     at_initCheck = (AudioTrack_initCheck)(dlsym(p_library, "_ZNK7android10AudioTrack9initCheckEv"));
15     at_start = (AudioTrack_start)(dlsym(p_library, "_ZN7android10AudioTrack5startEv"));
16     at_stop = (AudioTrack_stop)(dlsym(p_library, "_ZN7android10AudioTrack4stopEv"));
17     at_write = (AudioTrack_write)(dlsym(p_library, "_ZN7android10AudioTrack5writeEPKvj"));
18     at_flush = (AudioTrack_flush)(dlsym(p_library, "_ZN7android10AudioTrack5flushEv"));
19     // need the first 3 or the last 1
20     if (!((as_getOutputFrameCount && as_getOutputLatency && as_getOutputSamplingRate) || at_getMinFrameCount)) {
21         msg_Err(VLC_OBJECT(p_this), "interface error 1");
22         if (!as_getOutputFrameCount) {
23             msg_Err(VLC_OBJECT(p_this), "error1");
24         }
25         if (!as_getOutputLatency) {
26             msg_Err(VLC_OBJECT(p_this), "error2");
27         }
28         if (!as_getOutputSamplingRate) {
29             msg_Err(VLC_OBJECT(p_this), "error3");
30         }
31         if (!at_getMinFrameCount)
32         {
33             msg_Err(VLC_OBJECT(p_this), "error4");
34         }
35         dlclose(p_library);
36         return NULL;
37     }
38     // need all in the list
39     if (!((at_ctor || at_ctor_legacy) && at_dtor && at_initCheck && at_start && at_stop && at_write && at_flush)) {
40         msg_Err(VLC_OBJECT(p_this), "interface error 2");
41         dlclose(p_library);
42         return NULL;
43     }
44     return p_library;
45 }

查看日志后,发现是“error2” 也就是as_getOutputLatency为空,那说明“_ZN7android11AudioSystem16getOutputLatencyEPji”这个签名现在在libmedia.so中找不到了,那么现在这个函数的签名是什么呢?要想弄清楚这个,我们需要弄清楚怎么查看libmedia.so中函数的签名。

在网上查询方法后,在终端上使用readelf -s libmedia.so,结果是一长串符号列表,类似下面的内容:

 1 root@ubuntu:/mnt/hgfs/share/4.1.2# readelf -s libmedia.so
 2 
 3 Symbol table '.dynsym' contains 1918 entries:
 4    Num:    Value  Size Type    Bind   Vis      Ndx Name
 5      0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND 
 6      1: 00037069     4 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack16
 7      2: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr0
 8      3: 0003706d     2 FUNC    GLOBAL DEFAULT    7 _ZTv0_n16_N7android10Audi
 9      4: 0003706d     2 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack16
10      5: 0003706f    12 FUNC    GLOBAL DEFAULT    7 _ZTv0_n12_N7android10Audi
11      6: 0003707d    68 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack16
12      7: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_cond_destroy
13      8: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_destroy
14      9: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN7android6ThreadD2Ev
15     10: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN7android7RefBaseD2Ev
16     11: 000370c1    12 FUNC    GLOBAL DEFAULT    7 _ZTv0_n12_N7android10Audi
17     12: 000370cd    18 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack16
18     13: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZdlPv
19     14: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_lock
20     15: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_unwind_cpp_pr1
21     16: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_unlock
22     17: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_idiv
23     18: 00000000     0 FUNC    GLOBAL DEFAULT  UND __aeabi_uidiv
24     19: 000370ed    98 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack16
25     20: 0003f015    52 FUNC    GLOBAL DEFAULT    7 _ZN7android11AudioSystem2
26     21: 0003efdd    52 FUNC    GLOBAL DEFAULT    7 _ZN7android11AudioSystem1
27     22: 0003efa9    52 FUNC    GLOBAL DEFAULT    7 _ZN7android11AudioSystem1
28     23: 0003714f    62 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrackC2
29     24: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_mutex_init
30     25: 0003718d    80 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrackC1
31     26: 00000000     0 FUNC    GLOBAL DEFAULT  UND _ZN7android7RefBaseC2Ev
32     27: 000371dd     4 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack9
33     28: 000371e1     4 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack7
34     29: 000371e5     4 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack1
35     30: 000371e9     4 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack6
36     31: 000371ed     6 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack1
37     32: 000371f3     6 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack1
38     33: 000371f9    44 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack9
39     34: 00037225     4 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack12
40     35: 00037229    32 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack7
41     36: 00037249    48 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack7f
42     37: 00000000     0 FUNC    GLOBAL DEFAULT  UND pthread_cond_signal
43     38: 00037279    30 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack5f
44     39: 00037297    52 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack5p
45     40: 000372cb    22 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack4m
46     41: 000372e1    12 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack5
47     42: 000372ed   144 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack9s
48     43: 0003737d    14 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack9
49     44: 0003738d    96 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack21
50     45: 000373ed     8 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack2
51     46: 000373f5    74 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack13
52     47: 0003743f    40 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack1
53     48: 00037469   188 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack9s
54     49: 00000000     0 FUNC    GLOBAL DEFAULT  UND __android_log_print
55     50: 00037525    48 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack7s
56     51: 00037555    22 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack17
57     52: 0003756b    16 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack1
58     53: 0003757b    16 FUNC    GLOBAL DEFAULT    7 _ZNK7android10AudioTrack2
59     54: 0003758b    94 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack11
60     55: 00000000     0 FUNC    GLOBAL DEFAULT  UND android_atomic_or
61     56: 000375e9    50 FUNC    GLOBAL DEFAULT    7 _ZN7android10AudioTrack11
62 ...

看上去挺像那么回事的是吧?其实在仔细查找其中的内容后,没有发现任何跟“_ZN7android11AudioSystem16getOutputLatencyEPji”相关的代码,别说这个空函数了,连已经证明加载成功的函数的签名也没有找到,这是为什么呢?

这时候我就想到,android系统中的库文件都是在arm-linux环境下编译的,属于交叉编译,而我们使用的命令都是在x86架构下的指令,他解析的符号类型会不会也是x86的指令呢?为了弄清楚这个问题,我需要安装arm-linux的开发工具,在参考了这几篇文章后,我成功的安装了arm-linux工具链。

在 Linux 下安装 GNU ARM 工具链

Android 开发环境建立-ARM编译器安装

编译mini2440工具链

使用arm-2008q3-72-arm-none-linux-gnueabi-i686-pc-linux-gnu交叉编译成功在板子上运行

然后使用命令:arm-none-linux-gnueabi-objdump -d libmedia.so:

 1 root@ubuntu:/mnt/hgfs/share/4.1.2# arm-none-linux-gnueabi-objdump -d libmedia.so 
 2 
 3 libmedia.so:     file format elf32-littlearm
 4 
 5 Disassembly of section .plt:
 6 
 7 00036358 <.plt>:
 8    36358:    e52de004     push    {lr}        ; (str lr, [sp, #-4]!)
 9    3635c:    e59fe004     ldr    lr, [pc, #4]    ; 36368 <_ZN7android10AudioTrack16AudioTrackThread10readyToRunEv-0xd00>
10    36360:    e08fe00e     add    lr, pc, lr
11    36364:    e5bef008     ldr    pc, [lr, #8]!
12 ... ...
13    37058:    e5bcffa0     ldr    pc, [ip, #4000]!
14    3705c:    e28fc600     add    ip, pc, #0    ; 0x0
15    37060:    e28cca38     add    ip, ip, #229376    ; 0x38000
16    37064:    e5bcff98     ldr    pc, [ip, #3992]!
17 Disassembly of section .text:
18 
19 00037068 <_ZN7android10AudioTrack16AudioTrackThread10readyToRunEv>:
20    37068:    2000          movs    r0, #0
21    3706a:    4770          bx    lr
22 
23 0003706c <_ZN7android10AudioTrack16AudioTrackThread10onFirstRefEv>:
24    3706c:    4770          bx    lr
25 
26 0003706e <_ZTv0_n12_N7android10AudioTrack16AudioTrackThreadD1Ev>:
27    3706e:    6801          ldr    r1, [r0, #0]
28    37070:    f851 3c0c     ldr.w    r3, [r1, #-12]
29    37074:    18c0          adds    r0, r0, r3
30    37076:    f000 b801     b.w    3707c <_ZN7android10AudioTrack16AudioTrackThreadD1Ev>
31     ...

结果是上面这样的代码。把结果输出到文件中,进行查找:

1 root@ubuntu:/mnt/hgfs/share/4.1.2# arm-none-linux-gnueabi-objdump -d libmedia.so > libmedia.txt
2 root@ubuntu:/mnt/hgfs/share/4.1.2# ls
3 4.0.4  4.0.4.txt  4.1.2.txt  a.txt  lib  libmedia_jni.so  libmedia_native.so  libmediaplayerservice.so  libmedia.so  libmedia.txt
4 root@ubuntu:/mnt/hgfs/share/4.1.2# gedit libmedia.txt 
5 root@ubuntu:/mnt/hgfs/share/4.1.2# grep getOutputLatency libmedia.txt
6    3710c:    f007 ff4c     bl    3efa8 <_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t>
7 0003efa8 <_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t>:
8    3efc4:    b130          cbz    r0, 3efd4 <_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t+0x2c>

可知getOutputLatency函数的符号由“_ZN7android11AudioSystem16getOutputLatencyEPji”变为“_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t”,于是我们这样修改代码:

1 as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPji"));
2 
3     // edit by yueang
4     if (!as_getOutputLatency) {
5         as_getOutputLatency = (AudioSystem_getOutputLatency)(dlsym(p_library, "_ZN7android11AudioSystem16getOutputLatencyEPj19audio_stream_type_t"));
6     }
7     // edit by yueang end

然后编译运行,问题解决!

免责声明:文章转载自《解决faplayer在android4.1上只有图像没有声音的问题》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇计算同比和环比增长率微软Hololens学院教程Hologram 230空间场景建模(Spatial mapping )【微软教程已经更新,本文是老版本】下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

OpenCV-Python 傅里叶变换 | 三十

目标 在本节中,我们将学习 使用OpenCV查找图像的傅立叶变换 利用Numpy中可用的FFT函数 傅立叶变换的某些应用程序 我们将看到以下函数:cv.dft(),cv.idft()等 理论 傅立叶变换用于分析各种滤波器的频率特性。对于图像,使用2D离散傅里叶变换(DFT)查找频域。一种称为快速傅立叶变换(FFT)的快速算法用于DFT的计算。关于这...

C++ Opencv 傅里叶变换的代码实现及关键函数详解

一、前言 最近几天接触了图像的傅里叶变换,数学原理依旧不是很懂,因此不敢在这里妄言。下午用Opencv代码实现了这一变换,有一些经验心得,愿与大家分享。 二、关键函数解析 2.1copyMakeBorder() 扩展图片尺寸 傅里叶变换的计算对图像的尺寸有一定要求,尺寸不满足要求的,可用copyMakeBorder() 函数进行扩展。函数定义如下: voi...

vlc 播放器的点播和广播服务

vlc 是一个开源的,同时跨平台的播放器。在研究 rtsp 协议时发现,它同时还是一个强大的流媒体服务器 VLM VLM(VideoLAN Manager) 在 vlc 中是一个小型的媒体管理器,它能在只启用一个 vlc 的实例的情况下管理多个流。它只能在 telnet 接口和 http 接口下被控制 平时如果是 GUI 界面,那就是通过鼠标点击窗口的按钮...

LabVIEW部分视觉函数中文解说

IMAQ Learn Pattern 2 VI在匹配阶段创建您要搜索的图案匹配的模板图像的描述,此描述的数据被附加到输入模板图像中。在匹配阶段,从模板图像中提取模板描述符并且用于从检查图像中搜索模板。 Image:是一个您要搜索模板图像的参考检查图像。 Learn Pattern Setup Data(学习模式设置数据):是一个字符串,包含从本控件或从高级...

OpenCV 图像采样 插值 几何变换

InitLineIterator 初始化线段迭代器 int cvInitLineIterator( const CvArr* image, CvPoint pt1, CvPoint pt2, CvLineIterator* line_iterator, int connectivity=8 ); image 带采线段的输入图像. pt1 线段起始点 pt...

glViewport()函数和glOrtho()函数的理解

在OpenGL中有两个比较重要的投影变换函数,glViewport和glOrtho.glOrtho是创建一个正交平行的视景体。 一般用于物体不会因为离屏幕的远近而产生大小的变换的情况。比如,常用的工程中的制图等。需要比较精确的显示。 而作为它的对立情况, glFrustum则产生一个透视投影。这是一种模拟真是生活中,人们视野观测物体的真实情况。例如:观察两...