FFmpeg-音樂播放器2
- 1.解決記憶體上漲的問題
- 2.解決雜音問題
- 3. 添加錯誤回呼到 Java
- 4.多執行緒解碼播放
- 5.ffmpeg的一些常用指令
具體代碼請看:NDKPractice專案的ffmpeg83
1.解決記憶體上漲的問題
將回圈中新建陣列操作提到回圈外面去
jbyteArray jPcmByteArray = env->NewByteArray(dataSize);
// native 創建 c 陣列
jbyte *jPcmData = env->GetByteArrayElements(jPcmByteArray, NULL);
pPacket = av_packet_alloc();
pFrame = av_frame_alloc();
// 回圈從背景關系中讀取幀到包中
while (av_read_frame(pFormatContext, pPacket) >= 0) {
if (pPacket->stream_index == audioStreamIndex) {
// Packet 包,壓縮的資料,解碼成 pcm 資料
int codecSendPacketRes = avcodec_send_packet(pCodecContext, pPacket);
if (codecSendPacketRes == 0) {
int codecReceiveFrameRes = avcodec_receive_frame(pCodecContext, pFrame);
if (codecReceiveFrameRes == 0) {
// AVPacket -> AVFrame
index++;
LOGE("解碼第 %d 幀", index);
// 呼叫重采樣的方法
swr_convert(swrContext, &resampleOutBuffer, pFrame->nb_samples,
(const uint8_t **) pFrame->data, pFrame->nb_samples);
// write 寫到緩沖區 pFrame.data -> javabyte
// size 是多大,裝 pcm 的資料
// 1s 44100 點,2通道, 2位元組 44100*2*2
// 1幀不是一秒,pFrame->nb_samples點
memcpy(jPcmData, resampleOutBuffer, dataSize);
// 1 把 c 的陣列的資料同步到 jbyteArray,然后不釋放native陣列
env->ReleaseByteArrayElements(jPcmByteArray, jPcmData, JNI_COMMIT);
pJniCall->callAudioTrackWrite(jPcmByteArray, 0, dataSize);
}
}
}
// 解參考
av_packet_unref(pPacket);
av_frame_unref(pFrame);
}
// 1.解參考資料 data, 2.銷魂 pPacket 結構體記憶體, 3.pPacket = NULL;
av_packet_free(&pPacket);
av_frame_free(&pFrame);
// 0 把 c 的陣列的資料同步到 jbyteArray,然后釋放native陣列
env->ReleaseByteArrayElements(jPcmByteArray, jPcmData, 0);
// 解除 jPcmDataArray 的持有,讓 javaGC 回收
env->DeleteLocalRef(jPcmByteArray);
2.解決雜音問題
分析
- 原因:是因為音頻的的采樣率和采用格式跟我們使用AudioTrack播放設定的會不一致,
- 解決:使用重采樣
// --------------- 重采樣 start --------------
//輸出的聲道布局(立體聲)
int64_t out_ch_layout = AV_CH_LAYOUT_STEREO;
//輸出采樣格式16bit PCM
enum AVSampleFormat out_sample_fmt = AVSampleFormat::AV_SAMPLE_FMT_S16;
//輸出采樣率
int out_sample_rate = AUDIO_SAMPLE_RATE;
//獲取輸入的聲道布局
//根據聲道個數獲取默認的聲道布局(2個聲道,默認立體聲stereo)
int64_t in_ch_layout = pCodecContext->channel_layout;
//輸入的采樣格式
enum AVSampleFormat in_sample_fmt = pCodecContext->sample_fmt;
//輸入采樣率
int in_sample_rate = pCodecContext->sample_rate;
swrContext = swr_alloc_set_opts(NULL, out_ch_layout, out_sample_fmt,
out_sample_rate, in_ch_layout, in_sample_fmt,
in_sample_rate, 0, NULL);
if (swrContext == NULL) {
LOGE("swr alloc set opts error");
callPlayerJniError(SWR_ALLOC_SET_OPTS_ERROR_CODE, "swr alloc set opts error");
// 提示錯誤
return;
}
int swrInitRes = swr_init(swrContext);
if (swrInitRes < 0) {
LOGE("swr context swr init error");
callPlayerJniError(SWR_CONTEXT_INIT_ERROR_CODE, "swr context swr init error");
return;
}
// size 是播放指定的大小,是最終輸出的大小
int outChannels = av_get_channel_layout_nb_channels(out_ch_layout);
int dataSize = av_samples_get_buffer_size(NULL, outChannels, pCodecParameters->frame_size,
out_sample_fmt, 0);
resampleOutBuffer = (uint8_t *) malloc(dataSize);
// --------------- 重采樣 end --------------
....
while (av_read_frame(pFormatContext, pPacket) >= 0) {
if (pPacket->stream_index == audioStreamIndex) {
// Packet 包,壓縮的資料,解碼成 pcm 資料
int codecSendPacketRes = avcodec_send_packet(pCodecContext, pPacket);
if (codecSendPacketRes == 0) {
int codecReceiveFrameRes = avcodec_receive_frame(pCodecContext, pFrame);
if (codecReceiveFrameRes == 0) {
// AVPacket -> AVFrame
index++;
LOGE("解碼第 %d 幀", index);
// 呼叫重采樣的方法
swr_convert(swrContext, &resampleOutBuffer, pFrame->nb_samples,
(const uint8_t **) pFrame->data, pFrame->nb_samples);
....
memcpy(jPcmData, resampleOutBuffer, dataSize);
// 1 把 c 的陣列的資料同步到 jbyteArray,然后不釋放native陣列
env->ReleaseByteArrayElements(jPcmByteArray, jPcmData, JNI_COMMIT);
pJniCall->callAudioTrackWrite(jPcmByteArray, 0, dataSize);
}
}
}
// 解參考
av_packet_unref(pPacket);
av_frame_unref(pFrame);
}
3. 添加錯誤回呼到 Java
jPlayerErrorMid = jniEnv->GetMethodID(jPlayerClass, "onError", "(ILjava/lang/String;)V");
void JNICall::callPlayerError(int code, char *msg) {
jstring jMsg = jniEnv->NewStringUTF(msg);
jniEnv->CallVoidMethod(jPlayerObj, jPlayerErrorMid, code, jMsg);
jniEnv->DeleteLocalRef(jMsg);
}
4.多執行緒解碼播放
void *threadPlay(void *arg) {
FFmpeg *pFFmpeg = (FFmpeg *) arg;
pFFmpeg->prepare();
return NULL;
}
void FFmpeg::play() {
// 創建一個執行緒去播放,多執行緒邊解碼邊播放
pthread_t tid;
pthread_create(&tid,NULL,threadPlay,this);
pthread_detach(tid); // 回收執行緒
}
5.ffmpeg的一些常用指令
ffmpeg 命令
轉格式
ffmpeg -i input.mp4 -vcodec copy -acodec copy out.flv
抽取視頻
ffmpeg -i input.mp4 -an -vcodec copy out.h264
抽取音頻
ffmpeg -i input.mp4 -vn -acodec copy out.aac
提取 YUV
ffmpeg -i input.mp4 -an -c:v rawvideo -pix_fmt yuv420p out.yuv
提取 PCM
ffmpeg -i input.mp4 -vn -ar 44100 -ac2 -f s16le out.pcm
視頻裁剪
ffmpeg -i input.mp4 -vf corp=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4
視頻裁剪
ffmpeg -i input.mp4 -ss 00:00:00 -t 10 out.mp4
視頻轉圖片
ffmpeg -i input.mp4 -r 1 -f image2 image-%3d.jpeg
圖片轉視頻
ffmpeg -i image-%3d.jpeg out.mp4
直播推流
ffmpeg -re -i output.mp4 -c copy -f flv rtmp://server/live/streamName
直播拉流
ffmpeg -i rtmp://server/live/streamName -c copy dump.flv
問題:子執行緒中回傳 java 錯誤會有問題
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/216358.html
標籤:其他
上一篇:按奇偶排序陣列 II
下一篇:論文閱讀:Content Aware Video Compression:-An Approach To VOS Algorithm
