一、问题描述

接到反馈直播录制的mp4文件用android手机h5播放/自研播放器播放没有声音或杂音,用快手播放声音正常。我拿到录制的视频文件试着带着耳机能正常播放,用扬声器就有问题。测试的机型为华为nova9和vivo Y3

二、解决步骤

1.因为iOS没有问题,开始怀疑是Android系统自带的AudioTrack有问题,改用OpenSL ES输出测试,还是有问题,感觉还得分析音频原始数据格式(PCM)。

2.用ffplay播放此mp4文件,输出音频信息为,采样率 48000Hz,双声道,采样位数16bit。

因为此视频时长2个多小时,用ffmpeg截取一部分视频,比如只截取30秒

ffmpeg -ss 00:00:00 -t 00:00:30 -i output.mp4 -vcodec copy -acodec copy output1.mp4

在ffplay中加录制PCM的代码并保存到本地

//为了方便,用全局变量

FILE *filename = NULL;

AVFormatContext *ifmt_ctx = NULL;

int g_AudioStreamIndex = -1;

int size ;

//保存文件的路径

char tmpName[100];

sprintf(tmpName, "stereo_%d_%dchannel.pcm",ic->streams[g_AudioStreamIndex]->codec->sample_rate, ifmt_ctx->streams[g_AudioStreamIndex]->codec->channels);

filename = fopen(tmpName, "w+b");

size = av_get_bytes_per_sample(ifmt_ctx->streams[g_AudioStreamIndex]->codec->sample_fmt);

audio_thread中got_frame之后保存文件

//planner方式存储PCM

if (frame->data[0] && frame->data[1])

{

    int frame_size = ifmt_ctx->streams[g_AudioStreamIndex]->codec->frame_size;


    for (int i = 0; i < frame_size; i++)

    {
        //左右声道间隔存储

        fwrite(frame->data[0] + i * size, 1, size, filename);

        fwrite(frame->data[1] + i * size, 1, size, filename);

    }

}

//packet方式存储PCM

else if(frame->data[0])

{

    fwrite(frame->data[0], 1, frame->linesize[0], filename);

}

知识点:

PCM存储分为planner和packet两种方式

packet方式如下图:

android录视频对方打电话我这边怎么听不到声音呢 安卓手机录视频没声音_android手机

planer方式

android录视频对方打电话我这边怎么听不到声音呢 安卓手机录视频没声音_保存文件_02

3.保存下来的PCM用cooledit播放着也没有发现问题,后来从网上找到一篇重要的文章( 解决部分Android手机播放MP4视频外音是杂音的问题,耳机播放正常_TLuffy的博客-CSDN博客),与我的情况一样,按着文章的的方法,将mp4第二个声道的数据拷贝到第一个声道。

ffmpeg -i output1.mp4 -vcodec copy -af pan="stereo|c0=c1|c1=c1" sample.mp4

(ffmpeg 命令不清楚的可以自行查阅官方说明文档),remapping之后用播放器播放,android手机的扬声器就可以正常播放了。

4.按照文章中的思路,在播放器代码中将第一声道的数据拷贝到第二个声道,上面的代码稍微改一下

int frame_size = is->ic->streams[is->last_audio_stream]->codec->frame_size;
int size = av_get_bytes_per_sample(is->ic->streams[is->last_audio_stream]->codec->sample_fmt);
for (int i = 0; i < frame_size; i++)
{
    memcpy(frame->data[1]+ i * size, frame->data[0]+ i * size, size);
}

改完代码后验证播放器能够正常播放了,感觉还是我们播放器的兼容性问题,应该在检测到扬声器的时候做一次转换,觉得把代码放到swr_convert后比较合适,改动如下:


if (AV_SAMPLE_FMT_S16 == is->audio_tgt.fmt) { int sample_size = av_get_bytes_per_sample(AV_SAMPLE_FMT_S16); for (int i=0; i<resampled_data_size; ) {         //packet模式,第二声道拷贝到第一个声道,i每次需要累加4字节,即sample_size*2 memcpy(&is->audio_buf[i], &is->audio_buf[i+sample_size], sample_size); i+= sample_size*2; } }


5.为什么用耳机能够正常播放,用扬声器就不能播放?播放器输出声音也选择不了耳机还是扬声器的输出方式。从bilibili下载了一个测试立体声的媒体文件,用播放器测试耳机和扬声器都能正常播放,这就奇怪了,为什么都是立体声的媒体文件,直播录制的mp4(output1.mp4)扬声器输出有问题呢?可能android手机的扬声器与output1.mp4文件在兼容性上出了问题。后来无意中发现小米10的扬声器能够正常播放output1.mp4文件。看来是华为和vivo的兼容性不如小米做的好啊,网上查了一下小米10的扬声器是支持立体声的,华为nova9不支持立体声。那就明白了,小米10的扬声器是双声道的,就能正常output1.mp4播放。

6.下载安装了爱奇艺和腾讯视频,播放有问题的output1.mp4都不能正常播放,主流播放器不能播放,应该就是mp4文件的问题了。

7.下载了audacity pcm播放器,看看是否能够发现写端倪。在audacity里面找到有立体声混音功能,bilibili下载的立体声测试文件混音后正常播放,output1.mp4的立体声混音之后就是有很小的杂音。放大波形图,发现output1.mp4hun两个声道的波峰与波谷重合,混音就几乎没有声音。如图所示:

android录视频对方打电话我这边怎么听不到声音呢 安卓手机录视频没声音_android手机_03

 混音前波形图如下:

android录视频对方打电话我这边怎么听不到声音呢 安卓手机录视频没声音_保存文件_04

混音后没有声音或杂音的波形图如下:

android录视频对方打电话我这边怎么听不到声音呢 安卓手机录视频没声音_ci_05

8.因此基本定位,直播源录制的mp4音频左右声道不同步,只能支持立体声设备输出,不支持单声道的设备。