随笔 - 632  文章 - 17  评论 - 54  阅读 - 93万

Android使用FFMpeg生成pcm格式音频,并利用AudioTrack播放出来

一、场景

   1.可任意选择一个媒体文件(avi、mp4、ts、mp3),解析除媒体文件的音频,并保存为pcm,然后利用AudioTrack播放pcm。

   2.主要类介绍Java文件:

      a.PcmDecoder.java 用于和jni通讯的java类,里面定义了三个方法,初始化:init、解码:decode、销毁:destroy

      b.UserAudioTrackPlayPCMActivity.java类,用于选择媒体文件,初始化编码器以及播放音频pcm文件

      c.AudioTrackPlayer.java 用于播放pcm文件

  3.主要C++文件:

      a.pcm_decoder.cpp 其中的方法和PcmDecoder.java文件功能对应,用于java调用jni

      b.pcm_deocder_controller.cpp pcm解码器封装类

      c.pcm_real_decoder.cpp 具体的初始化以及解码类

  4.代码框架图:

    

    5.其实大体上就分两部分:第一部分是解码部分,将媒体文件解码为pcm。第二部分是播放,利用AudioTrack播放器播放解码出PCM文件。注意,解码出的pcm是裸数据由于没有给pcm裸数据增加头,所以一般的播放器是播放不出来的。所以要用AudioTrack进行验证。

二、代码演示

  1.UserAudioTrackPlayPCMActivity.java 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package com.yw.ffmpeg;
 
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
 
import androidx.annotation.Nullable;
 
import com.yw.ffmpeg.decoder.PcmDecoder;
import com.yw.ffmpeg.player.AudioTrackPlayer;
 
import java.io.File;
 
/**
 * @ProjectName: AndroidFFMpeg
 * @Package: com.yw.ffmpeg
 * @ClassName: 使用AudioTrack播放PCM文件
 * @Description: 使用AudioTrack播放PCM文件
 * @Author: wei.yang
 * @CreateDate: 2021/8/20 10:02
 * @UpdateUser: 更新者:wei.yang
 * @UpdateDate: 2021/8/20 10:02
 * @UpdateRemark: 更新说明:
 * @Version: 1.0
 */
public class UserAudioTrackPlayPCMActivity extends BaseActivity {
    //选择文件
    private Button btnChoiceVideo;
    //选择文件的路径
    private TextView tvVideoFilePath;
    //生成pcm文件
    private Button btnGeneratePCM;
    //生成的pcm路径
    private TextView tvPcmPath;
    //使用AudioTrack播放pcm裸数据
    private Button btnUseAudioTrackPaly;
    private PcmDecoder pcmDecoder;
    private String orgPath = null;
 
    /**
     * 测试步骤
     * 1.选择一个视频文件
     * 2.将视频文件和音频文件分离
     * 3.将分离后的音频文件解码并保存为PCM
     * 4.利用AudioTrack播放PCM
     *
     * @param savedInstanceState
     */
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_audio_track_play_pcm);
        initViews();
        pcmDecoder = new PcmDecoder();
    }
 
    private void initViews() {
        btnChoiceVideo = findViewById(R.id.btnChoiceVideo);
        btnChoiceVideo.setOnClickListener(v -> {
            choiceVideo();
        });
        tvVideoFilePath = findViewById(R.id.tvVideoFilePath);
        btnGeneratePCM = findViewById(R.id.btnGeneratePCM);
        btnGeneratePCM.setOnClickListener(v -> {
            if (pcmDecoder != null) {
                if (orgPath != null) {
//                    new Thread() {
//                        @Override
//                        public void run() {
                    try {
                        int ret = pcmDecoder.init(orgPath, "/mnt/sdcard/110119.pcm");
                        if (ret >= 0) {
                            pcmDecoder.decode();
                            pcmDecoder.destroy();
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
//                        }
//                    }.start();
 
 
                }
            }
        });
        tvPcmPath = findViewById(R.id.tvPcmPath);
        btnUseAudioTrackPaly = findViewById(R.id.btnUseAudioTrackPaly);
        btnUseAudioTrackPaly.setOnClickListener(v -> {
            AudioTrackPlayer.getAudioTrackPlayer().start(new File("/mnt/sdcard/110119.pcm"));
        });
 
    }
 
    @Override
    public void videoPathCallback(String videoPath) {
        orgPath = videoPath;
    }
 
    static {
        System.loadLibrary("native-lib");
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (pcmDecoder != null) {
            pcmDecoder.destroy();
        }
        AudioTrackPlayer.getAudioTrackPlayer().release();
    }
 
 
 
 
 
}

  

  2.AudioTrackPlayer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package com.yw.ffmpeg.player;
 
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.net.rtp.AudioStream;
 
import com.yw.ffmpeg.utils.LogUtil;
 
import java.io.File;
import java.io.FileInputStream;
 
/**
 * @ProjectName: AndroidFFMpeg
 * @Package: com.yw.ffmpeg.player
 * @ClassName: AudioTrackPlayer
 * @Description: 使用AudioTrack播放音频文件
 * @Author: wei.yang
 * @CreateDate: 2021/8/31 10:26
 * @UpdateUser: 更新者:wei.yang
 * @UpdateDate: 2021/8/31 10:26
 * @UpdateRemark: 更新说明:
 * @Version: 1.0
 * * ps:
 *  * 1.AudioTrack是Android SDK提供的最底层的音频播放API,因此只允许输入裸数据
 *  * <p>
 *  * AudioTrack的工作流程
 *  * 1.根据音频参数信息配置出一个AudioTrack实例
 *  * 2.调用play方法将AudioTrack切换到播放状态
 *  * 3.启动播放线程,循环向AudioTrack缓冲区中写入音频数据
 *  * 4.当数据写完或者停止播放的时候,停止播放线程并释放所有资源
 *  * <p>
 *  * AudioTrack的构造函数
 *  * AudioTrack(int streamType,int sampleRateInHz,int channelConfig,int audioFormat,int bufferSizeInBytes,int mode)
 *  * 1.streamType:音频管理策略。如:在AudioManager中的STREAM_VOICE_CALL:电话声音、STREAM_SYSTEM:系统声音、STREAM_RING:铃声、STREAM_MUSIC:音乐声、STREAM_ALARM:警告声,STREAM_NOTIFICATION:通知声
 *  * 2.sampleRateInHz:采样率,即播放音频每秒钟会有多少次采样。如:8000,16000,22050、24000、44100、48000
 *  * 3.channelConfig:声道。AudioFormat中的可选值为CHANNEL_IN_MONO(单声道)。CHANNEL_IN_STEREO(立体声)
 *  * 4.audioFormat:采样格式,ENCODING_PCM_16BIT(16bit)、ENCODING_PCM_8BIT(8bit),注意,前者是可以兼容所有Android手机的。
 *  * 5.bufferSizeInBytes:AudioTrack内部的音频缓冲区的大小,通过getMinBufferSize来确定这个缓冲区的大小
 *  * 6.mode:播放模式,MODE_STATIC:需要一次性将所有的数据都写入播放缓冲区中,简单高效,通常用于播放铃声、系统提醒的音频片段、MODE_STREAM:需要按照一定的时间间隔不间断地写入音频数据,理论上它可以应用于任何音频播放的场景
 */
public class AudioTrackPlayer {
    private static final String TAG = "AudioTrackPlayer:";
    private static final int HZ = 0xac44;
    private AudioTrack audioTrack;
 
    private AudioTrackPlayer() {
        int minBufferSize = AudioTrack.getMinBufferSize(HZ, AudioFormat.CHANNEL_CONFIGURATION_MONO, AudioFormat.ENCODING_PCM_16BIT);
        LogUtil.log(TAG + "minBufferSize:" + minBufferSize);
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, HZ, AudioFormat.CHANNEL_CONFIGURATION_STEREO,
                AudioFormat.ENCODING_PCM_16BIT, minBufferSize * 2, AudioTrack.MODE_STREAM);
        audioTrack.setStereoVolume(1.0f, 1.0f);// 设置当前音量大小
        audioTrack.play();//调用play方法将AudioTrack切换到播放状态
    }
 
    private static AudioTrackPlayer instance = null;
 
    public static synchronized AudioTrackPlayer getAudioTrackPlayer() {
        if (instance == null) {
            instance = new AudioTrackPlayer();
        }
        return instance;
    }
 
    /**
     * 开始播放pcm文件
     *
     * @param pcmFile
     */
    public void start(File pcmFile) {
        new Thread() {
            @Override
            public void run() {
                try {
                    FileInputStream fileInputStream = new FileInputStream(pcmFile);
                    fileInputStream.skip(0x2c);
                    byte buffer[] = new byte[16 * 10000];
                    while (fileInputStream.read(buffer) >= 0) {
                        System.out.println("write pcm data");
                        audioTrack.write(buffer, 0, buffer.length);
                    }
                    fileInputStream.close();
                    fileInputStream = null;
                    audioTrack.stop();
                    audioTrack.release();
                    audioTrack = null;
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }.start();
    }
 
    /**
     * 停止播放
     */
    public void stop() {
        if (audioTrack != null) {
            audioTrack.stop();
        }
    }
 
    /**
     * 销毁
     */
    public void release() {
        if (audioTrack != null) {
            audioTrack.stop();
            audioTrack.release();
            audioTrack = null;
        }
    }
}

  

  3.PcmDecoder.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class PcmDecoder {
    /**
     * 初始化解码器
     * @param mediaFilePath 原始媒体文件路径
     * @param pcmFilePath 目标pcm文件生成路径
     * @return
     */
    public native int init(String mediaFilePath,String pcmFilePath);
 
    /**
     * 开始解码
     */
    public native void decode();
 
    /**
     * 销毁解码器
     */
    public native void destroy();
}

  

  4.pcm_decoder.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
extern "C" {
/**
 * 初始化
 * @param env
 * @param obj
 * @param mediaFilePath 待解码的媒体文件路径
 * @param pcmFilePath  生成pcm文件的路径
 * @return
 */
PCMDecoderController *controller;
JNIEXPORT jint JNICALL
Java_com_yw_ffmpeg_decoder_PcmDecoder_init(JNIEnv *env, jobject obj, jstring mediaFilePath,
                                           jstring pcmFilePath) {
    //将jni字符串转换为c能识别的字符数组
    const char *pcmPath = env->GetStringUTFChars(pcmFilePath, NULL);
    const char *mediaPath = env->GetStringUTFChars(mediaFilePath, NULL);
    controller = new PCMDecoderController();
    controller->init(mediaPath, pcmPath);
    //销毁字符串
    env->ReleaseStringUTFChars(mediaFilePath, mediaPath);
    env->ReleaseStringUTFChars(pcmFilePath, pcmPath);
    return 0;
}
 
/**
 * 解码
 * @param env
 * @param obj
 */
JNIEXPORT void JNICALL Java_com_yw_ffmpeg_decoder_PcmDecoder_decode(JNIEnv *env, jobject obj) {
    if (controller) {
        controller->decode();
    }
}
/**
 * 销毁
 * @param env
 * @param obj
 */
JNIEXPORT void JNICALL Java_com_yw_ffmpeg_decoder_PcmDecoder_destroy(JNIEnv *env, jobject obj) {
    if (controller) {
        controller->destroy();
        delete controller;
        controller = NULL;
    }
}
}

  

  5.pcm_deocder_controller.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include "pcm_decoder_controller.h"
 
PCMDecoderController::PCMDecoderController() {
    realDecoder = NULL;
    pcmFile = NULL;
}
 
PCMDecoderController::~PCMDecoderController() {
 
}
 
void PCMDecoderController::init(const char *orgFilePath, const char *pcmFilePath) {
    //初始化decoder
    RealDecoder *tempDecoder = new RealDecoder();
    int metaData[2];
    tempDecoder->getMusicMeta(orgFilePath, metaData);
    delete tempDecoder;
    //初始化采样率
    sampleRate = metaData[0];
    int byteCountPreSec = sampleRate * 2 * 16 / 8;
    packetBufferSize = (int) ((byteCountPreSec / 2) * 0.2);
    realDecoder = new RealDecoder();
    realDecoder->init(orgFilePath, packetBufferSize);
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "init PCMDecoderController success");
    pcmFile = fopen(pcmFilePath, "wb+");
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "init pcmFile success");
 
}
 
void PCMDecoderController::decode() {
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "start decode audio data");
    while (true) {
        AudioPacket *packet = realDecoder->decodePacket();
        if (-1 == packet->size) {
            break;
        }
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "write buffer to file");
        fwrite(packet->buffer, sizeof(short), packet->size, pcmFile);
    }
}
 
void PCMDecoderController::destroy() {
    if (NULL != realDecoder) {
        realDecoder->destroy();
        delete realDecoder;
        realDecoder = NULL;
    }
    if (NULL != pcmFile) {
        fclose(pcmFile);
        pcmFile = NULL;
    }
}

  

  6.pcm_real_decoder.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include "pcm_real_decoder.h"
 
RealDecoder::RealDecoder() {
    this->seek_seconds = 0.0f;
    this->seek_req = false;
    this->seek_resp = false;
    mediaFilePath = NULL;
}
 
RealDecoder::~RealDecoder() {
    if (NULL != mediaFilePath) {
        delete[] mediaFilePath;
        mediaFilePath = NULL;
    }
 
}
 
int RealDecoder::getMusicMeta(const char *fileString, int *metaData) {
    init(fileString);
    int sampleRate = avCodecContext->sample_rate;
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "%d", "sampleRate:", sampleRate);
    int bitRate = avCodecContext->bit_rate;
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "%d", "bitRate:", bitRate);
    destroy();
    metaData[0] = sampleRate;
    metaData[1] = bitRate;
    return 0;
}
 
void RealDecoder::init(const char *fileString, int packetBufferSizeParam) {
    init(fileString);
    packetBufferSize = packetBufferSizeParam;
}
 
int RealDecoder::init(const char *audioFile) {
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "init:");
    audioBuffer = NULL;
    position = -1.0f;
    audioBufferCursor = 0;
    audioBufferSize = 0;
    swrContext = NULL;
    swrBuffer = NULL;
    swrBufferSize = 0;
    seek_success_read_frame_success = true;
    isNeedFirstFrameCorrectFlag = true;
    firstFrameCorrectionInSecs = 0.0f;
    av_register_all();
    avFormatContext = avformat_alloc_context();
    //打开输入文件
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "Open Input File");
    if (NULL == mediaFilePath) {
        int length = strlen(audioFile);
        mediaFilePath = new char[length + 1];
        //由于最后一个是'\0' 所以memset的长度要设置为length+1
        //作用是在一段内存块中填充某个给定的值,它对较大的结构体或数组进行清零操作的一种最快方法。
        memset(mediaFilePath, 0, length + 1);
        memcpy(mediaFilePath, audioFile, length + 1);
    }
 
    int result = avformat_open_input(&avFormatContext, audioFile, NULL, NULL);
    if (result != 0) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "Open Input File Fail");
        return -1;
    } else {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "Open Input File success");
    }
    avFormatContext->max_analyze_duration = 50000;
    //检查文件中的流信息
    result = avformat_find_stream_info(avFormatContext, NULL);
    if (result < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "find stream Fail");
        return -1;
    } else {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "find stream success");
    }
    stream_index = av_find_best_stream(avFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    if (stream_index == -1) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "find audio fail");
        return -1;
    }
    //获取音频流
    AVStream *audioStream = avFormatContext->streams[stream_index];
    if (audioStream->time_base.den && audioStream->time_base.num) {
        time_base = av_q2d(audioStream->time_base);
    } else if (audioStream->codec->time_base.den && audioStream->codec->time_base.num) {
        time_base = av_q2d(audioStream->codec->time_base);
    }
    //获取音频流解码器上下文
    avCodecContext = audioStream->codec;
    //根据加码器上下文找到解码器
    AVCodec *avCodec = avcodec_find_decoder(avCodecContext->codec_id);
    if (avCodec == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "find decode fail");
        return -1;
    } else {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "find decode success");
    }
    //打开解码器
    result = avcodec_open2(avCodecContext, avCodec, NULL);
    if (result < 0) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "open decode fail");
        return -1;
    } else {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "open decode success");
    }
    //判断是否需要resampler,重采样
    if (!audioCodecIsSupported()) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder",
                            "because of audio Codec Is Not Supported so we will init swresampler...");
        /**
          * 初始化resampler
          * @param s               Swr context, can be NULL
          * @param out_ch_layout   output channel layout (AV_CH_LAYOUT_*)
          * @param out_sample_fmt  output sample format (AV_SAMPLE_FMT_*).
          * @param out_sample_rate output sample rate (frequency in Hz)
          * @param in_ch_layout    input channel layout (AV_CH_LAYOUT_*)
          * @param in_sample_fmt   input sample format (AV_SAMPLE_FMT_*).
          * @param in_sample_rate  input sample rate (frequency in Hz)
          * @param log_offset      logging level offset
          * @param log_ctx         parent logging context, can be NULL
          */
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "start init swrContext");
        swrContext = swr_alloc_set_opts(NULL, av_get_default_channel_layout(OUT_PUT_CHANNELS),
                                        AV_SAMPLE_FMT_S16, avCodecContext->sample_rate,
                                        av_get_default_channel_layout(avCodecContext->channels),
                                        avCodecContext->sample_fmt, avCodecContext->sample_rate, 0,
                                        NULL);
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "end init swrContext");
        if (!swrContext || swr_init(swrContext)) {
            if (swrContext)
                swr_free(&swrContext);
            avcodec_close(avCodecContext);
            __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "init resampler failed...");
            return -1;
        }
 
 
    }
    //初始化AVFrame
    audioFrame = av_frame_alloc();
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "open create audioFrame success");
    return -1;
}
 
/**
 * 检测音频编码是否支持S16
 * @return
 */
bool RealDecoder::audioCodecIsSupported() {
    if (avCodecContext->sample_fmt == AV_SAMPLE_FMT_S16) {
        return true;
    }
    return false;
}
 
/**
 * 解码Packet
 * @return
 */
AudioPacket *RealDecoder::decodePacket() {
    short *samples = new short[packetBufferSize];
    int stereoSampleSize = readSamples(samples, packetBufferSize);
    AudioPacket *samplePacket = new AudioPacket();
    if (stereoSampleSize > 0) {
        //构造成一个packet
        samplePacket->buffer = samples;
        samplePacket->size = stereoSampleSize;
        /** 这里由于每一个packet的大小不一样有可能是200ms 但是这样子position就有可能不准确了 **/
        samplePacket->position = position;
    } else {
        samplePacket->size = -1;
    }
    return samplePacket;
}
 
int RealDecoder::readSamples(short *samples, int size) {
    if (seek_req) {
        audioBufferCursor = audioBufferSize;
    }
    int sampleSize = size;
    while (size > 0) {
        if (audioBufferCursor < audioBufferSize) {
            int audioBufferDataSize = audioBufferSize - audioBufferCursor;
            int copySize = MIN(size, audioBufferDataSize);
            memcpy(samples + (sampleSize - size), audioBuffer + audioBufferCursor, copySize * 2);
            size -= copySize;
            audioBufferCursor += copySize;
        } else {
            if (readFrame() < 0) {
                break;
            }
        }
//      LOGI("size is %d", size);
    }
    int fillSize = sampleSize - size;
    if (fillSize == 0) {
        return -1;
    }
    return fillSize;
}
 
void RealDecoder::seek_frame() {
    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder",
                        "\n seek frame firstFrameCorrectionInSecs is %.6f, seek_seconds=%f, position=%f \n",
                        firstFrameCorrectionInSecs, seek_seconds, position);
    float targetPosition = seek_seconds;
    float currentPosition = position;
    float frameDuration = duration;
    if (targetPosition < currentPosition) {
        this->destroy();
        this->init(mediaFilePath);
        //这里GT的测试样本会差距25ms 不会累加
        currentPosition = 0.0;
    }
    int readFrameCode = -1;
    while (true) {
        av_init_packet(&packet);
        readFrameCode = av_read_frame(avFormatContext, &packet);
        if (readFrameCode >= 0) {
            currentPosition += frameDuration;
            if (currentPosition >= targetPosition) {
                break;
            }
        }
//      LOGI("currentPosition is %.3f", currentPosition);
        av_free_packet(&packet);
    }
    seek_resp = true;
    seek_req = false;
    seek_success_read_frame_success = false;
}
 
/**
 * 读取视频中的每一帧
 * @return
 */
int RealDecoder::readFrame() {
    if (seek_req) {
        this->seek_frame();
    }
    int ret = 1;
    av_init_packet(&packet);
    int gotFrame = -1;
    int readFrameCode = -1;
    while (true) {
        readFrameCode = av_read_frame(avFormatContext, &packet);
        if (readFrameCode >= 0) {
            if (packet.stream_index == stream_index) {//说明是音频数据
                int len = avcodec_decode_audio4(avCodecContext, audioFrame, &gotFrame, &packet);
                if (len < 0) {
                    __android_log_print(ANDROID_LOG_ERROR, "RealDecoder",
                                        "decode audio error, skip packet");
                }
                if (gotFrame) {
                    int numChannels = OUT_PUT_CHANNELS;
                    int numFrames = 0;
                    void *audioData;
                    if (swrContext) {
                        const int ratio = 2;
                        const int bufSize = av_samples_get_buffer_size(NULL,
                                                                       numChannels,
                                                                       audioFrame->nb_samples *
                                                                       ratio,
                                                                       AV_SAMPLE_FMT_S16, 1);
                        if (!swrBuffer || swrBufferSize < bufSize) {
                            swrBufferSize = bufSize;
                            swrBuffer = realloc(swrBuffer, swrBufferSize);
                        }
                        byte *outbuf[2] = {(byte *) swrBuffer, NULL};
                        numFrames = swr_convert(swrContext, outbuf,
                                                audioFrame->nb_samples * ratio,
                                                (const uint8_t **) audioFrame->data,
                                                audioFrame->nb_samples);
                        if (numFrames < 0) {
                            __android_log_print(ANDROID_LOG_ERROR, "RealDecoder",
                                                "fail resample audio");
                            ret = -1;
                            break;
                        }
                        audioData = swrBuffer;
                    } else {
                        if (avCodecContext->sample_fmt != AV_SAMPLE_FMT_S16) {
                            __android_log_print(ANDROID_LOG_ERROR, "RealDecoder",
                                                "bucheck, audio format is invalid");
                            ret = -1;
                            break;
                        }
                        audioData = audioFrame->data[0];
                        numFrames = audioFrame->nb_samples;
                    }
                    if (isNeedFirstFrameCorrectFlag && position >= 0) {
                        float expectedPosition = position + duration;
                        float actualPosition =
                                av_frame_get_best_effort_timestamp(audioFrame) * time_base;
                        firstFrameCorrectionInSecs = actualPosition - expectedPosition;
                        isNeedFirstFrameCorrectFlag = false;
                    }
                    duration = av_frame_get_pkt_duration(audioFrame) * time_base;
                    position = av_frame_get_best_effort_timestamp(audioFrame) * time_base -
                               firstFrameCorrectionInSecs;
                    if (!seek_success_read_frame_success) {
                        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "position is %.6f",
                                            position);
                        actualSeekPosition = position;
                        seek_success_read_frame_success = true;
                    }
                    audioBufferSize = numFrames * numChannels;
//                  LOGI(" \n duration is %.6f position is %.6f audioBufferSize is %d\n", duration, position, audioBufferSize);
                    audioBuffer = (short *) audioData;
                    audioBufferCursor = 0;
                    break;
                }
            }
        } else {
            ret = -1;
            break;
        }
    }
    av_free_packet(&packet);
    return ret;
}
 
 
void RealDecoder::destroy() {
//  LOGI("start destroy!!!");
    if (NULL != swrBuffer) {
        free(swrBuffer);
        swrBuffer = NULL;
        swrBufferSize = 0;
    }
    if (NULL != swrContext) {
        swr_free(&swrContext);
        swrContext = NULL;
    }
    if (NULL != audioFrame) {
        av_free(audioFrame);
        audioFrame = NULL;
    }
    if (NULL != avCodecContext) {
        avcodec_close(avCodecContext);
        avCodecContext = NULL;
    }
    if (NULL != avFormatContext) {
        __android_log_print(ANDROID_LOG_ERROR, "RealDecoder", "leave LiveReceiver::destory");
        avformat_close_input(&avFormatContext);
        avFormatContext = NULL;
    }
//  LOGI("end destroy!!!");
}

  

三、源代码下载

  Gitee

posted on   飘杨......  阅读(1110)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示