ffmpeg深入研究

.获取音频格式支持的采样率

    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.

扫描二维码关注公众号,回复: 4874989 查看本文章

看完之后还是云里雾里,查维基百科,得到定义: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_tuint16_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(),那么这样会不会导致不能播放多路视频?

猜你喜欢

转载自blog.csdn.net/liuxuejin/article/details/12617277