“双语播放器”已在app store上架,欢迎大家前去下载(主要用于看电影,学英语,程序员一定要学好英语!)
这里是链接:
https://itunes.apple.com/cn/app/shuang-yu-bo-fang-qi-kan-dian/id950279764?mt=8
1.获取音频格式支持的采样率
if (codec->supported_samplerates) {
for (int i = 0; codec->supported_samplerates[i] != 0; i++)
NSLog(@"supported_samplerates %d",codec->supported_samplerates[i]);
}
2.根据错误提示,搜索源码,直接定位错误
Specified sample_rate is not supported
搜索到utils.c文件,就能找到采样率对照表
3.一定要学会把不可控的网络流播放变成可控的本地播放
比如你要把网络流的h264视频保存成mp4格式,那么你先把数据保存到本地,可控性肯定更好,也能让你更容易找到规律,然后再对网络流进行操作就要好很多。
4.音视频同步
以前使用ffmpeg都是从网上找代码,然后直接使用,但是这样不是长久之计,所以想从源码提示开始学习。
大概知道pts,dts是用于音视频同步的,那么他们是怎么使音视频同步的?为什么需要两个参数?难道一个参数不可以吗?
ffmpeg描述dts: * Presentation timestamp in AVStream->time_base units; the time at which the decompressed packet will be presented to the user.
看完之后还是云里雾里,查维基百科,得到定义:that is used to achieve synchronization of programs' separate elementary streams (for example Video, Audio, Subtitles) when presented to the viewer. 大意是pts是被用于音频,视频,字幕流的同步。
那么有一个pts标注某个时间播某个流不就ok了,为什么还需要dts?
网上并没有找到dts资料,所以先用pts试试。
昨天找资料发现pts中文意思是显示时间戳,dts中文意思是解码时间戳,dts好像跟有b帧的视频有关,在我这个项目中可以先不用考虑
但是昨天尝试了几次pts,还是没有发现什么规律,那么该怎么使用了?
后来发现AVCodecContext还有一个时间基time_base的概念,它到底代表什么意思?和pts有什么联系了?
time_base是一个结构体,有两个成员den(分母英文单词的缩写),num(分子英文单词的缩写),根据这两个成员变量相除,你可以得到每秒播放多少帧或者每帧播放需要的时间,如果你有音频和视频的帧率,那么pts只要每次累加1,就能实现同步了。
根据时间基的同步并不准确,因为我是从网络上获取的视频流,本身自带了时间戳,有什么办法能让我自带的时间戳去显示图像了?
通过实验终于明白了,time_base的意思,比如你的den=10,num=1;那么表示你一帧是0.1秒,
如果你的pts是每帧pts加1,那么播放器就会每0.1秒播放一帧,你要播放的数据,如果你有自己的时间戳,那么你可以调整den,num来设定时间的单位,
比如den = 100,num = 1;表示以10ms为单位。
当我设置time_base.den = 100;time_base.num = 1;时,ffmpeg居然自己修改了这两个值
case AVMEDIA_TYPE_AUDIO:
den = (int64_t)st->time_base.num * st->codec->sample_rate;
break;
case AVMEDIA_TYPE_VIDEO:
den = (int64_t)st->time_base.num * st->codec->time_base.den;
break;
5.在把aac保存到mp4容器的时候报错
[mp4 @ 0x8980400] malformated aac bitstream, use -absf aac_adtstoasc
直接从ffmpeg源文件中搜索这句话
if (enc->codec_id == CODEC_ID_AAC && pkt->size > 2 &&
(AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
av_log(s, AV_LOG_ERROR, "malformated aac bitstream, use -absf aac_adtstoasc\n");
return -1;
}
看的解决这个问题的一个最简单的办法是把pkt->size设成小于等于2,但是不知道会不会对保存有影响呢?先试试再说吧!
6.ffmpeg音频转码
如果本来是采样频率是24000的,转码变成48000的,播放速度变快,我觉得原因是因为本来每秒采24000次,现在要变成48000次,有两种办法,一种是加入空的数据,另一种是把两个24000变成一个48000,ffmpge应该是采用了这种机制,那么我们的8000采样率的,变成16000的应该也只是加快播放速度吧。
7.ffmpeg把音视频流保存成mp4格式
视频是h264的保存完全没有问题,现在最大的问题是音频,原始音频是pcm的,采样率8000
首先查了一下mp4支持的音频,aac,ac3,mp3,mp2,大概是这四种比较常见了
下面就一个个尝试,ffmpeg内置支持mp2和ac3.
mp2编码出来声音应该听不太清楚,不知道是不是采样率的问题,因为它不支持8000采样率
ac3编码出来完全听不得,但是它是支持8000的采样率,不知道什么原因。
aac一些奇怪的问题,还没保存出来
mp3尝试中
比特率跟原来的视频一样居然没有声音
经过艰苦的试验,终于听到了声音,只是还有一个奇怪的杂音在干扰啊!!!
该如何把它消掉呢?
经过很多的尝试,可以说是无头苍蝇一样的尝试,还是没有找到原因,使用ffmpeg的经验告诉我,一样要学会从原理入手。
那么我们就从ffmpeg使用的libmp3lame开源的mp3项目开始,先看官方文档,发现ffmpeg和libmp3lame都有命令行,
其实命令行还是很有用的,那么就从学习命令行开始把。
经过几天的艰苦奋战,终于找到是字节转换的时候出现了问题,比如uint8_t*转uint16_t*,如果不明白原理,乱转的话很容易出现问题的。
uint8_t[640]转成uint16_t应该只有320个大小,因为它是一位占两个字节,所以以后遇到这些字节转换一定要小心,不要转错了!!!
1字节 uint8_t
2字节 uint16_t
4字节 uint32_t
8字节 uint64_t
如果uint8_t转uint16_t没有memset清零那么就有可能出现杂音,如果memset清零了,就能听出来声音变慢,因为中间插入了空的声音。
ffmpeg在编码的时候,最好指定好frame_size的大小
c->frame_size = 320;
out_size = avcodec_encode_audio(c, out_buf, 0, packet.data);
这样整个mp3就转出来了!
8.当我使用ffmpeg保存视频为mov时,我觉得时间戳明明是正确的,但是用其他播放器播放就是不对
这时可以用ffmpeg播放一下,对比一下参数的差别,就能很容易找到原因。
9.insufficient thread locking around avcodec_open error
具体代码文件是libavcodec/mpegvideo_enc.c,在函数estimate_best_b_count()中,将会调用avcodec_open(),如果一个线程调用了avcodec_open(),
但还没有调用avcodec_close(),此时再有一个线程来调用avcodec_open(),就会发生错误,提示"insufficient thread locking around avcodec_open/close()"。
所以使用完,就马上avcodec_close(),那么这样会不会导致不能播放多路视频?