2017-05-01 2 views
1

비 차단 모드에서 snd_pcm_writei()를 사용하면 모든 것이 잠시 동안 완벽하게 작동하지만 결국에는 오디오가 고르지 않게됩니다. 링 버퍼 포인터가 동기화되지 않는 것처럼 들립니다 (즉, 때로 오디오가 순서가 틀렸다는 것을 알 수 있습니다). 문제가 하드웨어에 의존하기 시작하는 데 얼마나 오랜 시간이 걸립니까. 실제 하드웨어의 Gentoo 상자에서는 거의 발생하지 않지만, QEMU에서 실행중인 buildroot 시스템에서는 약 5 분 후에 발생합니다. 두 경우 모두 pcm 스트림을 배출하면 문제가 해결됩니다. 나는 또한 샘플을 정확하게 파일에 기록하고 재생으로 샘플을 작성하고 있음을 확인했습니다.글리치가있는 오디오 출력, 언더런 없음

현재 avail_min을 기간 크기 (1024 프레임)로 설정하고 기간 크기의 청크를 작성하기 전에 snd_pcm_wait()를 호출합니다. 하지만 다른 변형 (다양한 청크 크기, 사용 가능한 체크, snd_pcm_wait() 대신 pthread_cond_timedwait() 사용)을 시도했다. 하지만 잘 작동하는 유일한 방법은 차단 모드를 사용하는 것입니다.하지만 그렇게 할 수는 없습니다.

여기에서 현재 소스 코드를 볼 수 있습니다 : https://bitbucket.org/frodzdev/mediabox/src/5a6471316c7ae481b329e7e0d4af1bb68a32e71d/src/audio.c?at=staging&fileviewer=file-view-default (모든 종류의 것을 시도하기 때문에 약간의 정리가 필요합니다). 실제 IO를 수행하는 코드 줄에서 시작 375

편집 : 은 내가 해결책을 찾은 것 같아하지만이 작동하는 것 같다 왜 이해가 안 돼요. 그것은 비 블로킹 모드를 사용하는 경우 문제가되지 않는 것, 문제는 내가 snd_pcm_wait(), pthread_cond_timedwait() 또는 usleep()를 통해 버퍼에 여유 공간이 있는지 확인할 때입니다.

작동하는 것으로 보이는 버전은 여기에 있습니다 : https://bitbucket.org/frodzdev/mediabox/src/c3eb290087d9bbe0d5f37653a33a1ba88ef0628b/src/audio.c?fileviewer=file-view-default. snd_pcm_writei()를 호출하기 전에 기다리는 동안 블로킹 모드로 전환했으며 차이를 만들지 않았습니다. 그런 다음 avbox_audiostream_gettime()에서 snd_pcm_status()를 호출하기 전에 snd_pcm_avail()에 호출을 추가했습니다. 이 함수는 다른 스레드에 의해 끊임없이 호출되어 스트림 클럭을 가져오고 snd_pcm_status()를 사용하여 타임 스탬프를 가져옵니다. 이제는 효과가있는 것 같습니다 (적어도 발생 가능성은 적습니다). 그러나 나는 그 이유를 정확히 이해하지 못합니다. 나는 snd_pcm_avail()이 커널과 포인터를 동기화한다는 것을 이해하지만 실제로 호출해야 할 때와 snd_pcm_state() 등등과 snd_pcm_status() 사이의 차이점을 이해하지 못한다. snd_pcm_status()도 아무것도 동기화합니까? snd_pcm_avail()이 -EPIPE를 반환 할 때 때때로 snd_pcm_status_get_state()가 RUNNING을 반환하기 때문입니다. ALSA 문서는 정말 모호합니다. 아마도 이걸 이해하면 내 문제를 이해하는 데 도움이 될 것입니까?

이제 실제 하드웨어에서 재생산 할 수 없다는 말을 들었습니다. 그것은 QEMU에서 여전히 덜 자주 발생합니다. 그러나 다음 커밋에서 기다리지 않고 차단 모드로 전환했음을 고려하면 (이전에는 사용했지만 실제 하드웨어에서는 전혀 문제가 없었습니다) 여전히 QEMU에서 발생하며 이것이 일반적인 문제입니다. QEMU 나는 내 문제를 해결했을지도 모른다고 생각하기 시작했으며 이제는 QEMU 문제 일 뿐이다. 문제가 에뮬레이터에서 트리거하기 쉬운 버그 또는 에뮬레이터 문제 일 뿐이라는 것을 판단 할 수있는 방법이 있습니까?

편집 : 기다리기 전에 버퍼를 채워야하지만이 시점에서 내 관심사는 언더런을 방지하는 것이 아니라 내 코드가 발생할 때이를 처리 할 수 ​​있는지 확인하는 것입니다. 버퍼가 몇 차례 반복되면서 채워집니다. 나는 각 패킷을 작성하기 전에 avail, buffer_size 등을 출력하여 이것을 확인했다. 그리고 얻은 숫자는 완벽하지 않다. 매 8 번째주기마다 1 또는 2 마침표의 오류를 보여준다. 또한 (그리고 이것은 주된 문제입니다.) 저는 언더런을 감지하지 못하고 오디오가 고르지 만 모든 기록이 성공합니다. 사실, 문제가 발생하기 시작하면 pcm을 재설정 할 때 CPU에 과부하로 인해 언더런이 발생합니다.

답변

1

In line 505 : 시간을 malloc의 인수로 사용하고 있습니다.

line 568 : 오디오를 재생하지 않았습니까? 이 경우 프레임을 작성한 후에 만 ​​기다려야합니다. 생각해 봅시다 ...

오디오 장치는 마침표를 처리하기 위해 종료되면 인터럽트를 생성합니다.

 
| period A | period B | 
      ^  ^
      irq   irq 

pcm을 시작하기 전에 오디오 장치가 인터럽트를 생성하지 않습니다. 기다리는 중이며 아직 pcm을 시작하지 않았다는 통지 인 here. snd_pcm_writei()에 전화 할 때만 시작됩니다.

오디오 데이터를 기다리면 현재 기간이 완전히 처리되었을 때만 깨어납니다. 첫 번째 대기 시간에는 기록되지 않았기 때문에 편안한 상황에서 전체 버퍼를 작성해야합니다 , 첫 번째 인터럽트를 기다린 다음 방금 처리 한 기간을 기록하고 계속해서 기록하십시오.

 
Initially, buffer is empty: 
    |   |   | 
write(): 
    |############|############| 
wait(): 
    .............. 
When we wake up: 
    |   |############| 
write(): 
    |############|############| 

나는 당신이 오디오를 재생하기 바로 전에 쓰고 있다는 것을 발견했다. 그런 다음 때로는 버퍼에 지연 될 수 있습니다.

+0

버퍼 크기를 잘 잡았지만 내 경우 (48000KHz)에 너무 많은 용량이 할당되어 임시 코드가되었습니다. 또한 snd_pcm_wait() avail> = avail_min 그래서 버퍼가 채워질 때까지 정말 기다리고있어 아니라면 즉시 반환해야 이해합니다. 오리지널 외에도 나는 EAGAIN을 얻었을 때만 기다리고 있었고 여전히 일어나고있었습니다. 이제 내가 그것에 대해 할 수있는 일로 최종 결론에 대해 옳을 수도 있습니다. 오디오를 네트워크 스트림에서 가져올 수 있으므로 버퍼를 먼저 채우는 것이 해결책이 아닙니다. – fernan

+0

나는 때때로 snd_pcm_writei()를 호출하기 전에 snd_pcm_delay()가 (buffer_size - avail) + 1024를 반환하지만 다음 쓰기는 여전히 성공한 것으로 나타났습니다. 그래서 나는 링 버퍼를 낮추지 않고 있지만 하드웨어 버퍼는 없다고 생각한다. 그렇다면 어떻게해야합니까? 나는 (delay - (buffer_size-avail))> 0이라는 링키퍼의 언더런을 허용하기 위해 내가 탐지 할 수있는 시간 동안 그냥 잠을 자야 하는가? – fernan

+0

또한 글리치는 단지 지연처럼 들리지 않습니다. 버퍼 포인터가 어떻게 든 손상된 것처럼 오디오가 순서대로 재생되는 것처럼 들립니다. 나는 심지어 내가 파일에 글을 쓰면서 올바른 순서로 쓰고 있음을 확인했다. – fernan