제가 ffmpeg을 이용해서 오디오를 출력하려고 하는데요
로그캣을 보니 버퍼는 다 읽는거 같은데 파일 경로를 찾지못하는건지 자꾸 NO SUCH A FILE OR DIRECTORY(2) 이게 뜨면서
오류가 나고 오디오 소리가 들리지 않는거 같아서요..
Thread mAudioThread = new Thread(new Runnable(){
public void run(){
String file = "/mnt/sdcard/Wildlife.mp3";
loadFile(file,bytes);
}
});
mAudioThread.setPriority(Thread.MAX_PRIORITY);
mAudioThread.start();
여기서 file이란 변수에 파일 있는 path를 지정해줬구요
void playSound(byte[] buf,int size){
track.play();
track.write(buf, 0, size);
}
여기서 오디오트랙 실행을 하게끔 했어요
void Java_com_hancom_FFmpegBasic_FFmpegBasic_loadFile(JNIEnv* env, jobject obj, jstring file, jbyteArray array)
{
/*비디오 스트리밍을 통해 코덱정보를 얻고 코덱을 여는 방법과 동일하다 */
AVFormatContext *gFormatCtx = NULL;
AVCodecContext *gAudioCodecCtx = NULL;
AVCodec *gAudioCodec = NULL;
int gAudioStreamIdx = -1;
char *gAudioBuffer = NULL;
int i, outsize = 0;
AVPacket packet;
const char *str; //파일 경로를 받아올 char형 변수
str = (*env)->GetStringUTFChars(env, file, NULL); //함수 jstring 변수인 file의 데이터 값을 받아온다
jclass cls = (*env)->GetObjectClass(env, obj); //객체불러오기
jmethodID play = (*env)->GetMethodID(env, cls, "playSound", "([BI)V"); //playSound 함수 불러오기 //byte, int를 받고 void형 리턴
/* 프로시저 비디오 스트리밍과 동일 */
if (gFormatCtx != NULL)
return -1;
if (av_open_input_file(&gFormatCtx,str,NULL,0,NULL)!=0)
return -2;
if (av_find_stream_info(gFormatCtx) < 0)
return -3;
for(i=0; i<gFormatCtx->nb_streams; i++)
{
if(gFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
gAudioStreamIdx = i;
break;
}
}
if (gAudioStreamIdx == -1)
return -4;
gAudioCodecCtx = gFormatCtx->streams[gAudioStreamIdx]->codec;
gAudioCodec = avcodec_find_decoder(gAudioCodecCtx->codec_id);
if (gAudioCodec == NULL)
return -5;
if (avcodec_open(gAudioCodecCtx, gAudioCodec)<0)
return -6;
/*******************************************************************/
gAudioBuffer = (char *)av_malloc(AVCODEC_MAX_AUDIO_FRAME_SIZE *2); //오디오 버퍼를 위해 동적할당
int decode = 0;
while (av_read_frame(gFormatCtx, &packet) >= 0)
{
if (gFormatCtx-> streams[packet.stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 8; //SDL 라이브러리 사용시에 callback 함수 안의 변수
gAudioBuffer = (char *)av_malloc(data_size);
int size=packet.size;
while(size > 0)
{
int len = avcodec_decode_audio3(gAudioCodecCtx,
(short *) gAudioBuffer, &data_size, &packet);
if (data_size > 0)
{
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL); //자바단에서 array변수로 받은 값을 bytes로 초기화한다
memcpy(bytes + decode, (int16_t *)gAudioBuffer, size);
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
(*env)->CallVoidMethod(env, obj, play, array, data_size); //play함수 호출하는데 아규먼트는 (byte[] , int)
decode += size;
size -= len;
}
}
}
av_free_packet(&packet);
}
av_close_input_file(gFormatCtx);
return 0;
}
이건 C단의 jni로 바꾼 코드인데요
디코딩하는 코드나 버퍼를 읽거나 하는 부분은 제가 생각하기에는 잘못된게 없는거같은데
file path를 잘 못읽어서 그런건지 아니면 코드 문제인지 좀 알려주실수있나요
error opening trace file: No such file or directory (2)
FATAL EXCEPTION: Thread-78
java.lang.UnsatisfiedLinkError: Native method not found: com.hancom.ffmpegbasic.FFmpegBasic.loadFile:(Ljava/lang/String;[BI)I
at com.hancom.ffmpegbasic.FFmpegBasic.loadFile(Native Method)
at com.hancom.ffmpegbasic.FFmpegBasic.access$0(FFmpegBasic.java:82)
at com.hancom.ffmpegbasic.FFmpegBasic$1.run(FFmpegBasic.java:43)
at java.lang.Thread.run(Thread.java:856)
장확히 이런 오류가 나오는데 잘 고쳐지지가 않네요 ㅜ.ㅜ




loadFile의 반환값이 0인지 확인해보세요. 음수라면 파일 핸들을 못 열어서일겁니다. /sdcard 퍼미션 확인해보세요.
그리고 memcpy(bytes + decode, (int16_t *)gAudioBuffer, size); 에서 size 대신 디코딩 된 데이터 사이즈인 data_size가 들어가야 합니다.
또한 데이터를 매번 올리니 decode만큼 오프셋을 더해주는 것도 잘 못 되었으며,
while (size > 0) 부분도 잘 못 된듯 합니다. Packet.size만큼만 디코딩하고 빠져 나가다보니 금방 종료 될 겁니다.
자바에서 bytearry를 입력 받다보니, 디코딩 된 데이터가 bytearry 사이즈보다 클 경우 대응은 안되긴 합니다만
bytearry크기를 AVCODEC_MAX_AUDIO_FRAME_SIZE * 8 보다 크게 주셨다면, 아래와 같은 코드면 될 겁니다.
if (gFormatCtx-> streams[packet.stream_index]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
int data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE * 8; //SDL 라이브러리 사용시에 callback 함수 안의 변수
gAudioBuffer = (char *)av_malloc(data_size);
int size=packet.size;
int len = avcodec_decode_audio3(gAudioCodecCtx,
(short *) gAudioBuffer, &data_size, &packet);
if (data_size > 0)
{
jbyte *bytes = (*env)->GetByteArrayElements(env, array, NULL); //자바단에서 array변수로 받은 값을 bytes로 초기화한다
memcpy(bytes, (int16_t *)gAudioBuffer, data_size);
(*env)->CallVoidMethod(env, obj, play, array, data_size); //play함수 호출하는데 아규먼트는 (byte[] , int)
(*env)->ReleaseByteArrayElements(env, array, bytes, 0);
}
}
마지막으로 동작과는 큰 상관없지만 나중을 위해 gAudioBuffer가 프리가 안 되는 것과
track.play();가 매번 불리게 되어 있고 중간에 오류가 있을 경우 바로 반환해서 FFMPEG 핸들이 프리가 안되는 것들도 잡아 주셔야 할 듯 합니다.