最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - AWS elemental media convert service audio and subtitle track missing - Stack Overflow

programmeradmin3浏览0评论
async createVideoJob(
    movieId: string,
    mainVideoPath: string,  // HLS Video Input
    audioPaths: any[] = [],
    subtitlePaths: any[] = [],
    outputFileName: string,
    resolution: any
  ): Promise<string> {
    // Verify main video file exists.
    const videoExists = await this.checkFileExists(awsDetail.bucket, mainVideoPath);
    if (!videoExists) throw new Error(`Main video file does not exist: ${mainVideoPath}`);
  
    // Verify audio files.
    for (const audioPath of audioPaths) {
      const audioExists = await this.checkFileExists(awsDetail.bucket, audioPath.file);
      if (!audioExists) throw new Error(`Audio file does not exist: ${audioPath.file}`);
    }
  
    // Verify caption files.
    for (const subtitlePath of subtitlePaths) {
      const captionExists = await this.checkFileExists(awsDetail.bucket, subtitlePath.file);
      if (!captionExists) throw new Error(`Caption file does not exist: ${subtitlePath.file}`);
    }
  
    const outputBasePath = `MOVIEZU/PROCESSED/${outputFileName}`;
    const hlsDestination = `s3://${awsDetail.bucket}/${outputBasePath}/HLS/`;
  
    const { width, height } = resolution;
    const aspectRatio = width / height;
    const presets = [
      { height: 2160, name: "4K", bitrate: 12000000 },
      { height: 1440, name: "2K", bitrate: 8000000 },
      { height: 1080, name: "1080p", bitrate: 6000000 },
      { height: 720, name: "720p", bitrate: 4000000 },
      { height: 480, name: "480p", bitrate: 2500000 },
      { height: 360, name: "360p", bitrate: 1500000 },
      { height: 240, name: "240p", bitrate: 1000000 },
      { height: 144, name: "144p", bitrate: 500000 },
    ].filter(preset => preset.height <= height);
    const resolutions = presets.map(preset => {
      let computedWidth = Math.floor(preset.height * aspectRatio);
      if (computedWidth % 2 !== 0) computedWidth--;
      return { width: computedWidth, height: preset.height, name: preset.name, bitrate: preset.bitrate };
    });
  
    // ---- Audio Setup ----
    const audioSelectors: Record<string, any> = {
      "Audio Selector 1": { DefaultSelection: "DEFAULT" },
    };
    const hlsAudioDescriptions: any[] = [
      {
        AudioSourceName: "Audio Selector 1",
        CodecSettings: {
          Codec: "AAC",
          AacSettings: { Bitrate: 96000, CodingMode: "CODING_MODE_2_0", SampleRate: 48000 },
        },
        StreamName: "Original Audio",
        AudioGroupId: "program_audio",
        LanguageCode: "ENG",
      },
    ];
    let audioInputIndex = 2;
    for (const audioPath of audioPaths) {
      const selectorName = `Audio Selector ${audioInputIndex}`;
      audioSelectors[selectorName] = {
        ExternalAudioFileInput: `s3://${awsDetail.bucket}/${audioPath.file}`,
      };
      hlsAudioDescriptions.push({
        AudioSourceName: selectorName,
        CodecSettings: {
          Codec: "AAC",
          AacSettings: { Bitrate: 96000, CodingMode: "CODING_MODE_2_0", SampleRate: 48000 },
        },
        StreamName: audioPath.name || `Audio ${audioInputIndex}`,
        AudioGroupId: "program_audio",
        LanguageCode: audioPath.language || "ENG",
      });
      audioInputIndex++;
    }
  
    // ---- Video Outputs with Embedded Captions ----
    // Each output rendition includes a CaptionDescription referencing a CaptionSelector defined in Inputs.
    const videoOutputs = resolutions.map(res => ({
      VideoDescription: {
        Width: res.width,
        Height: res.height,
        CodecSettings: {
          Codec: "H_264",
          H264Settings: {
            RateControlMode: "QVBR",
            QualityTuningLevel: "SINGLE_PASS_HQ",
            QvbrSettings: { QvbrQualityLevel: 7 },
            MaxBitrate: res.bitrate,
          },
        },
      },
      AudioDescriptions: hlsAudioDescriptions,
      CaptionDescriptions: subtitlePaths.length
        ? subtitlePaths.map((subtitle, idx) => ({
            // Must match the CaptionSelector name defined in Inputs.
            CaptionSelectorName: `Caption Selector ${idx + 1}`,
            DestinationSettings: {
              // Use EMBEDDED so that captions appear as selectable tracks.
              DestinationType: "EMBEDDED"
            },
            LanguageCode: subtitle.language || "ENG",
            LanguageDescription: subtitle.name || "English",
          }))
        : undefined,
      ContainerSettings: { Container: "M3U8" },
      NameModifier: `/${res.name}`,
    }));
  
    // ---- Job Settings ----
    // Include CaptionSelectors in Inputs so that embedded captions can be processed.
    const jobSettings: JobSettings | any = {
      Inputs: [
        {
          FileInput: `s3://${awsDetail.bucket}/${mainVideoPath}`,
          AudioSelectors: audioSelectors,
          CaptionSelectors: subtitlePaths.length
            ? Object.fromEntries(
                subtitlePaths.map((subtitle, idx) => [
                  `Caption Selector ${idx + 1}`,
                  {
                    // For selectable (embedded) captions, use a supported SourceType (EIA608).
                    SourceSettings: {
                      SourceType: "EIA608",
                      FileSourceSettings: {
                        SourceFile: `s3://${awsDetail.bucket}/${subtitle.file}`,
                      },
                    },
                  },
                ])
              )
            : {},
        },
      ],
      OutputGroups: [
        {
          Name: "HLS Output Group",
          OutputGroupSettings: {
            Type: "HLS_GROUP_SETTINGS",
            HlsGroupSettings: {
              Destination: hlsDestination,
              SegmentLength: 6,
              MinSegmentLength: 6,
              OutputSelection: "MANIFESTS_AND_SEGMENTS",
              AudioGroupId: "program_audio",
              AudioRenditionSets: "program_audio",
              // Enable insertion of closed-caption manifest tags.
              CaptionLanguageSetting: "INSERT",
              // Provide caption language mappings for each caption selector.
              CaptionLanguageMappings: subtitlePaths.length
                ? subtitlePaths.map((subtitle, idx) => ({
                    CaptionSelectorName: `Caption Selector ${idx + 1}`,
                    CustomLanguageCode: subtitle.language || "ENG",
                    LanguageDescription: subtitle.name || "English"
                  }))
                : [],
            } as any,
          },
          Outputs: videoOutputs,
        } as any,
      ],
      TimecodeConfig: { Source: "ZEROBASED" },
    };
  
    const command = new CreateJobCommand({
      Role: awsDetail.mediaConvertRoleArn,
      Settings: jobSettings,
      AccelerationSettings: { Mode: "DISABLED" },
      StatusUpdateInterval: "SECONDS_60",
    });
  
    const movieName = mainVideoPath.split("/").pop()?.replace(/\.\w+$/, "") || "movie";
    const path = `${outputBasePath}/HLS/${movieName}.m3u8`;
  
    try {
      const data = await this.mediaConvertClient.send(command);
      await MovieZu.findOneAndUpdate(
        { _id: movieId },
        { jobId: data.Job.Id, processedVideoUrl: path },
        { new: true }
      );
      return `${outputBasePath}/`;
    } catch (error) {
      console.error("MediaConvertService error:", error);
      throw new Error("Failed to create video job");
    }
  }

audio and subtitle track are missing on master.m3u8, only HLS video quality track. Track missing

m3u8 response data

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-INDEPENDENT-SEGMENTS
#EXT-X-STREAM-INF:BANDWIDTH=6908373,AVERAGE-BANDWIDTH=2249342,CODECS="avc1.640028",RESOLUTION=1920x1080,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/1080p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=4868448,AVERAGE-BANDWIDTH=1105432,CODECS="avc1.64001f",RESOLUTION=1280x720,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/720p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3016272,AVERAGE-BANDWIDTH=665885,CODECS="avc1.64001e",RESOLUTION=852x480,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/480p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1825354,AVERAGE-BANDWIDTH=415826,CODECS="avc1.64001e",RESOLUTION=640x360,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/360p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1116720,AVERAGE-BANDWIDTH=254112,CODECS="avc1.640015",RESOLUTION=426x240,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/240p.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=506848,AVERAGE-BANDWIDTH=130283,CODECS="avc1.64000c",RESOLUTION=256x144,FRAME-RATE=24.000
Fateh.2025.1080p.JHS.WEB.DL.Hindi.DDPA5.1.ESub.x264-Vegamovies.is/144p.m3u8

When working with AWS Elemental MediaConvert to transcode media files using Node.js, a common issue developers face is that audio tracks or subtitle tracks are missing in the output files. This note aims to explain why this happens and how to properly configure your Node.js MediaConvert job to include audio and subtitle tracks.

发布评论

评论列表(0)

  1. 暂无评论