【问题标题】:Stream video from a secured endpoint using Angular使用 Angular 从安全端点流式传输视频
【发布时间】:2020-11-21 19:38:44
【问题描述】:

我有安全的端点。我需要在来自 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);
}

角度代码: 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>

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);
                };
            });
        });
    }
}

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

我尝试在两端将“application/octet-stream”更改为“video/mp4”。但没有运气。

请注意,当我从后端删除安全代码并从 html 中删除 authVideo 管道时,它可以正常工作。请给我一些启示。谢谢!

【问题讨论】:

  • 你是如何解决这个问题的
  • 我最终将视频流端点移动到由安全服务保护的域后面。因此,无论谁在未登录的情况下打开视频端点,都会被重定向到登录页面。当用户登录时,视频可以通过普通的 HTML 视频标签进行流式传输。缺点是消费视频端点的人必须重复控制器中的代码。如果您必须创建独立的视频流服务,您可以考虑从安全的后端调用视频服务。这样您就不需要向客户端公开实际的服务端点。
  • “你可以考虑从安全的后端调用视频服务”你能告诉我更多关于这个的信息吗?这将非常有帮助。我的应用程序就像用户可以发布视频,只有登录用户才能看到视频。视频不应公开。
  • 你可以先尝试只获取端点吗?

标签: javascript c# angular jwt video-streaming


【解决方案1】:

虽然此解决方案适用于图像,因为所有数据都加载一次作为数据 URL,但您不应该对视频执行此操作,因为它会禁用浏览器的流式传输功能。实际上,这样做是在将整个视频加载到内存中,然后再将其转换为数据 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));
    });
}

working demo 给出以下结果:

【讨论】:

  • 感谢 Ans 我试过了,但由于某种原因它无法流式传输让我们从头开始再试一次
  • 没有调试细节我无法提供帮助,但我可以告诉它我已经测试过了。
  • 能否也添加 HTML 部分?
  • HTML 只是一个带有#videoElement 模板引用的video 标记(没有src),如果您需要,我可以分享整个项目。
  • 如果可能,请。我在这个表格上卡了这么久
【解决方案2】:

在我的例子中,webApi 的 URI 与主应用程序相同,由身份验证服务保护。流程基本上是这样工作的:

在请求到达 webApi 端点之前,authenticationService 将确定是否允许用户查看此端点。例如用户是否登录,用户是否有权限查看此端点等

如果请求已经到达 webApi 控制器,剩下的就很简单了。只需调用videoService中返回视频流的方法即可。

在前端,你只需要这个(当然你可以让videoId动态):

<video width="100%" height="320" controls #videoElement>
    <source
        [src]="'http://mydomain/api/GetVideo/1' | async"
    />
</video>

背面会是这样的:

[Route("api")]
public class WebApiController : Controller
{
    [HttpGet("GetVideo/{videoId}")]
    public async Task<IActionResult> GetVideoAsync(int videoId)
    {
        // Note: videoService can be on the same server, or a service
        // on a different server depends on how you implement it
        return this.videoService.GetVideoStream(videoId);
    }
}

【讨论】:

  • 对于这个请求 'mydomain/api/GetVideo/1' 的一件小事,您已经在 Angular 中设置了 jwt 令牌,您可以添加该代码。
  • 抱歉,我从 WebApi 控制器中删除了 [Authorize] 属性。就我而言,域由 IdentityServer 保护。一旦用户通过身份验证,客户端和服务器之间的其余请求就不需要 JWT 令牌。如果您需要保护 WeiApi 控制器,那么您应该尝试@Guerric P 的答案
猜你喜欢
  • 1970-01-01
  • 2013-11-21
  • 1970-01-01
  • 2012-11-17
  • 2021-11-10
  • 1970-01-01
  • 2013-09-08
  • 1970-01-01
  • 2017-05-29
相关资源
最近更新 更多