使用 Angular 从安全端点流式传输视频

Lok*_*Lok 7 javascript c# video-streaming jwt angular

我有安全的端点。我需要在来自 Angular 的 Http Get 请求的头部传递 jwt 令牌以流式传输视频。

dotnet 核心控制器中的端点如下所示(简化):

[Route("api/GetVideo/{videoId}")]
public async Task<IActionResult> GetVideoAsync(int videoId)
{
    string videoFilePath = GiveMeTheVideoFilePath(videoId);  

    return this.PhysicalFile(videoFilePath, "application/octet-stream", enableRangeProcessing: true);
}
Run Code Online (Sandbox Code Playgroud)

角度代码: video.component.html

<video width="100%" height="320" crossorigin="anonymous" controls #videoElement>
        <source
            [src]="'http://mydomain/api/GetVideo/1' | authVideo | async"  type="application/octet-stream"
        />
</video>
Run Code Online (Sandbox Code Playgroud)

video.pipe.ts

@Pipe({
    name: 'authVideo',
})
export class AuthVideoPipe implements PipeTransform {
    constructor(private http: HttpClient, private auth: AuthTokenService) {}

    transform(url: string) {
        return new Observable<string>(observer => {
            const token = this.auth.getToken(); //this line gets the jwt token
            const headers = new HttpHeaders({ Authorization: `Bearer ${token}` });

            const { next, error } = observer;

            return this.http.get(url, { headers, responseType: 'blob' }).subscribe(response => {
                const reader = new FileReader();
                reader.readAsDataURL(response);
                reader.onloadend = function() {
                    observer.next(reader.result as string);
                };
            });
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

它确实使用上述代码向端点发出 get 请求。一些东西被返回到前端。但是视频没有播放。我从这个SO question 中找到了上述方法。它适用于图像,但显然不适用于视频。我的想法是我可能需要在前端逐字节读取流。如果是这样,我该怎么做?

我尝试在两端将“应用程序/八位字节流”更改为“视频/mp4”。但没有运气。

请注意,当我从后端删除安全代码,并从 html 中删除 authVideo 管道时,它运行良好。请给我一些启发。谢谢!

Twi*_*her 2

虽然此解决方案适用于图像,因为所有数据都作为数据 URL 加载一次,但您不应该对视频执行此操作,因为它会禁用浏览器的流传输功能。事实上,通过这样做,您可以将整个视频加载到内存中,这在性能和用户体验方面非常糟糕(需要在播放之前加载完整视频,并导致大量内存消耗)。

解决您的问题的明显方法是使用 cookie 进行身份验证:

  • 认证成功后设置cookie
  • 它会在后续请求中自动发回,包括视频请求

在下文中,我假设您由于某种原因无法这样做。

您可以使用MediaSource。它允许您控制发送的实际请求以检索视频(并添加Authorization标头)。请注意,即使所有浏览器都广泛支持这一点,这也是实验性的。

您的代码应如下所示:

assetURL = 'http://mydomain/api/GetVideo/1';

// Modify this with the actual mime type and codec
mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';

@ViewChild("videoElement") video: ElementRef;

ngAfterViewInit() {
  if (
    "MediaSource" in window &&
    MediaSource.isTypeSupported(this.mimeCodec)
  ) {
    const mediaSource = new MediaSource();
    (this.video.nativeElement as HTMLVideoElement).src = URL.createObjectURL(
      mediaSource
    );
    mediaSource.addEventListener("sourceopen", () =>
      this.sourceOpen(mediaSource)
    );
  } else {
    console.error("Unsupported MIME type or codec: ", this.mimeCodec);
  }
}

sourceOpen(mediaSource) {
  const sourceBuffer = mediaSource.addSourceBuffer(this.mimeCodec);
  const token = this.auth.getToken(); //this line gets the jwt token
  const headers = new HttpHeaders({ Authorization: `Bearer ${token}` });
  return this.http
    .get(this.assetURL, { headers, responseType: "blob" })
    .subscribe(blob => {
      sourceBuffer.addEventListener("updateend", () => {
        mediaSource.endOfStream();
        this.video.nativeElement.play();
      });
      blob.arrayBuffer().then(x => sourceBuffer.appendBuffer(x));
    });
}
Run Code Online (Sandbox Code Playgroud)

这个工作演示给出了以下结果:

请求标头