2016-06-14 5 views
5

는 배경 내가 웹캠에서 레코드 비디오 클립을 작성한 코드는, 메모리 스트림에 기록UWP - 소켓을 통해 MediaElement로 웹캠 스트리밍 - 깨진 그림?

는, 다음은 비디오로 재 조립에 재생있어 소켓 연결을 통해 데이터를 전송 미디어 요소.

궁극적 인 목표는 서버/카메라가 Windows IOT Raspberry Pi에서 실행되는 아기 모니터 시스템을 만들고 여자 친구와 내가 휴대 전화 또는 노트북에서 볼 수있는 UWP 앱을 만드는 것입니다. 집의 다른 부분에서 카메라를 보는 것 외에도 우리 중 한 명이 집을 비울 때도 로그인 할 수 있으며 PIR 모션 센서와 경고 시스템을 연결하기도하지만 우선 먼저.

시스템 전체가 정상적으로 작동하므로 나에게 받아 들일 수있는 비디오에 5 초의 지연이 있으며 MediaPlaybackList를 사용하면 비디오가 일정한 속도로 끊김없이 원활하게 스트리밍됩니다 (지금 당장 얻을 수 있습니다.) 비디오 간의 전환. MediaPlaybackList는 재생 된 항목을 제거하여 메모리 풋 프린트를 상대적 상수로 유지합니다.

일 문제

비디오가 클라이언트 측에 재생, 그것은 깨진 사진을 자주하지만, 임의 섹션을 가져옵니다. 어쨌든 찾을 수있는 패턴이 아니며 설명 할 수있는 유일한 방법은 그림의 일부가 수평으로 반으로 나뉘며 두 반쪽이 서로 바뀌고 그림의 오른쪽이 표시되는 것입니다. 왼쪽, 그리고 그 반대입니다. 깜박이처럼, 깨진 비트가 몇 초 동안 만 표시됩니다. 다른 하나가 그림의 다른 곳에서 나중에 표시되기 때문입니다. 여기

은 예입니다 : 이제

Here you can see part of the frame is in the wrong position , 여기서 흥미로운 점 몇입니다 ..

1) 내가, 내가 추출하는 방법을 사용하고 데이터 스트림을 큐에 MediaPlaybackList를 사용하기 시작하기 전에 들어오는 소켓 스트림의 각 비디오를 StorageFile로 로컬 디스크에 저장 한 다음이 StorageFiles를 대기열에 넣고 순서대로 재생 한 다음 나중에 삭제합니다 (소스 코드에이 코드의 버전이 있습니다. 그러나 나는 끔찍하게 비효율적 인 것처럼 StorageFiles를 만들고 파괴한다는 생각을 좋아하지 않습니다.) 그러나이 방법을 사용하여 지금 보았던 깨진 사진을 얻지는 못했습니다 ... 이로 인해 비디오 자체가 훌륭하다고 믿을 수 있습니다. 아마도 다시 함께 놓여지고 스트리밍되는 방식과 관련된 문제 일 것입니다. 미디어 요소?

2) 내 여자 친구의 고양이가 웹캠 (Microsoft Lifecam HD-3000)을 깨닫지 않고 옆으로 쳐다 보았고, 서버를 실행하고 사진이 90도 각도에 있음을 알게되었습니다. 이것에 대한 흥미롭고 당혹스러운 점은 위에서 설명한 것처럼 클라이언트에게 전달 된 그림이 깨지지 않았기 때문입니다. 여기서 볼 수있는 유일한 차이점은 표준 640 x 480이 아닌 480x640 (카메라가 측면에 장착 된 카메라)에서 나온 것입니다. 이것이 의미하는 바는 확실하지 않습니다 ...문제 비디오의 크기/치수와는

  • 뭔가에

    생각 (그것의 측면에서 잘 연주, 그래서 그와 함께 할 수있는 뭔가입니다)?

  • 비트 전송률과 관련이 있습니까?
  • 클라이언트에서 바이트가 다시 어셈블되는 방식과 관련이 있습니까?
  • 스트림의 인코딩과 관련이 있습니까?

소스

이 여기에 아마 관련이 생각하는 코드 몇 조각을의 전체 솔루션 소스가 여기, GitHub의에서 찾을 수 있습니다 : Video Socket Server .

서버

while (true) 
{ 
    try 
    { 
     //record a 5 second video to stream 
     Debug.WriteLine($"Recording started"); 
     var memoryStream = new InMemoryRandomAccessStream(); 
     await _mediaCap.StartRecordToStreamAsync(MediaEncodingProfile.CreateMp4(VideoEncodingQuality.Vga), memoryStream); 
     await Task.Delay(TimeSpan.FromSeconds(5)); 
     await _mediaCap.StopRecordAsync(); 
     Debug.WriteLine($"Recording finished, {memoryStream.Size} bytes"); 

     //create a CurrentVideo object to hold stream data and give it a unique id 
     //which the client app can use to ensure they only request each video once 
     memoryStream.Seek(0); 
     CurrentVideo.Id = Guid.NewGuid(); 
     CurrentVideo.Data = new byte[memoryStream.Size]; 

     //read the stream data into the CurrentVideo 
     await memoryStream.ReadAsync(CurrentVideo.Data.AsBuffer(), (uint)memoryStream.Size, InputStreamOptions.None); 
     Debug.WriteLine($"Bytes written to stream"); 

     //signal to waiting connections that there's a new video 
     _signal.Set(); 
     _signal.Reset(); 
    } 
    catch (Exception ex) 
    { 
     Debug.WriteLine($"StartRecording -> {ex.Message}"); 
     break; 
    } 
} 

연결

//use the guid to either get the current video, or wait for the 
//next new one that's added by the server 
Guid guid = Guid.Empty; 
Guid.TryParse(command, out guid); 
byte[] data = _server.GetCurrentVideoDataAsync(guid); 
if (data != null) 
    await _socket.OutputStream.WriteAsync(data.AsBuffer()); 

클라이언트 응용 프로그램

byte[] inbuffer = new byte[10000000]; 

//block on the input stream until we've received the full packet, 
//but use the Partial option so that we don't have to fill the entire buffer before we continue. 
//this is important, because the idea is to set the buffer big enough to handle any packet we'll receive, 
//meaning we'll never fill the entire buffer... and we don't want to block here indefinitely 
result = await socket.InputStream.ReadAsync(inbuffer.AsBuffer(), inbuffer.AsBuffer().Capacity, InputStreamOptions.Partial); 

//strip off the Guid, leaving just the video data 
byte[] guid = result.ToArray().Take(16).ToArray(); 
byte[] data = result.ToArray().Skip(16).ToArray(); 
_guid = new Guid(guid); 

//wrap the data in a stream, create a MediaSource from it, 
//then use that to create a MediaPlackbackItem which gets added 
//to the back of the playlist... 
var stream = new MemoryStream(data); 
var source = MediaSource.CreateFromStream(stream.AsRandomAccessStream(), "video/mp4"); 
var item = new MediaPlaybackItem(source); 
_playlist.Items.Add(item); 
+0

로지텍 캠으로 rPI3에이 서버 측을 추가하여 비디오 결함없이 코드를 실행 시켰습니다. 스트리밍 구현에는 좀 더 많은 생각이 필요하다고 생각하지만 저에게는 서버에 문제가있었습니다. 는'_mediaCap.VideoDeviceController.SetMediaStreamPropertiesAsync ( { 프레임 속도 = {분모 = 1, 분자는 = 15}, 높이 480, 폭 = 640, 하위 유형 = "YUY2" } MediaStreamType.VideoRecord, 새로운 VideoEncodingProperties()를) 기다리고 있습니다 ; ' –

답변

2

비슷한 것을 (라스베리 파이의 UWP 앱에서 스트림 비디오/오디오)하고 싶습니다. 그러나 Windows 10 SDK의 간단한 통신 샘플을 사용하고 있습니다. 안정적으로 작동 할 수있었습니다 (샘플 코드와 스레드 동기화 문제가 있음). 그러나 SDK 샘플은 미디어 확장을 사용하는 독점 프로토콜을 사용하며 인터넷을 통해 스트림을 내 유스 케이스로 리디렉션하는 것은 쉽지 않으므로 코드를보고 (동일한 버그로) 작동하도록했습니다. Simple Real Time communication

당신의 접근 방식에 대한 의견의 몇 :

1)는 하드웨어 비디오 인코더를 사용할 수 없습니다 같이 RPI 아주 잘 Win10에 비디오를 처리 할 수 ​​없습니다 때문에 소프트웨어의 모든 작업을 수행합니다. 이로 인해 글리치가 발생하고 CPU 사용률이 50 % 이상으로 크게 늘어남에 따라 CPU 코어 중 적어도 하나가 최대 (가능하면 MP4로 비디오 압축을 처리하는 코어)에 가깝게 작동한다는 것을 의미합니다. 그러나 SDK 샘플을 실행하고 결함이 없으므로 약 70 %의 CPU가 문제를 일으킬 가능성이 있습니다.

2) 지연 시간은 5 초입니다. 실시간 샘플로 100mSec 미만의 레이턴시를 얻었지만 스트리밍 타이머를 1 초로 조정하면 해체가 심각하고 실행 불가능했습니다. 캡처 중 스트림이 발생하도록 디자인을 변경하는 방법에 대해 생각해 봤지만 InMemoryRandomAccessStream이 그렇게 할 수 있는지 확신 할 수 없습니다. 또 다른 대안은 Simple Communication 샘플처럼 미리보기 스트림을 캡처하고 버퍼에 사용자 지정 미디어 싱크를 쓰는 것입니다 (관리 코드가 아니거나 쉽게 압축 할 수 없을 수도 있음).

3) MP4는 압축 형식이 아닌 컨테이너이며, moov 메타 데이터 레코드가 파일의 시작 부분에 놓여 있지 않으면 시작하기 전에 전체 파일을 다운로드해야하므로 스트리밍 용으로 빌드되지 않습니다. UWP가 이것을 처리하는 방법을 잘 모르면 전송하기 전에 스트림을 닫는 접근 방식이 필요하므로 상대방이 제대로 재생할 수 있는지 확인해야합니다.

완전한 대답은 아니지만 위의 내용이 도움이됩니다.

+0

나는 유사한 것을하려고 노력하고있다. 비록 내가 복제 할 수 있고 샘플에서 작동하는 Simple Real Time 통신을 얻을지라도, 나는 내 자신의 앱에서 똑같은 코드를 사용하여 작동하도록 할 수 없다. ... – ChadT

+0

안녕하세요! 원래 게시물이나 Simple Communication Sample을 기반으로하는 무언가 사이에 효과적인 해결책을 찾았습니까? 나는 약간의 도움으로 칩을 쓰고 싶다. 그리고 내가 당신이 듣고 싶어하는이 포스트 이후로 어떤 진보를 만든다면. –

+0

아니요, 최근에 보지 못했지만 마지막으로 브라우저 재생을 위해 새로운 ORTC API를 통해 구현할 예정이었습니다. – deandob