{
  "scope": "Static source call paths. Arrows include task/IPC/callback and conditional edges; not a runtime stack.",
  "branch": "tizen (local snapshot)",
  "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
  "chromiumVersion": "MAJOR=130\nMINOR=0\nBUILD=6723\nPATCH=116",
  "chains": [
    {
      "id": "renderer",
      "title": "共用初始化：WebMediaPlayer → browser 平台 renderer",
      "description": "URL 与 MSE 共用控制桥接；资源分别以 URL 参数或 DemuxerStream endpoints 交接。",
      "nodes": [
        {
          "label": "WebMediaPlayerImpl::Load() → DoLoad()",
          "path": "third_party/blink/renderer/platform/media/web_media_player_impl.cc",
          "line": 739,
          "anchor": "WebMediaPlayer::LoadTiming WebMediaPlayerImpl::Load(",
          "role": "加载入口；后续创建 demuxer 和启动 pipeline。",
          "boundary": "入口",
          "snippet": "737: }\n738: \n739: WebMediaPlayer::LoadTiming WebMediaPlayerImpl::Load(\n740:     LoadType load_type,\n741:     const WebMediaPlayerSource& source,\n742:     CorsMode cors_mode,\n743:     bool is_cache_disabled) {\n744:   // Only URL or MSE blob URL is supported.\n745:   DCHECK(source.IsURL());\n746:   WebURL url = source.GetAsURL();\n747:   DVLOG(1) << __func__ << \"(\" << load_type << \", \" << GURL(url) << \", \""
        },
        {
          "label": "WebMediaPlayerImpl::StartPipeline()",
          "path": "third_party/blink/renderer/platform/media/web_media_player_impl.cc",
          "line": 3823,
          "anchor": "void WebMediaPlayerImpl::StartPipeline()",
          "role": "调用 DemuxerManager::CreateDemuxer()；PipelineController 管理状态。",
          "boundary": "异步生命周期",
          "snippet": "3821: }\n3822: \n3823: void WebMediaPlayerImpl::StartPipeline() {\n3824:   DCHECK(main_task_runner_->BelongsToCurrentThread());\n3825: \n3826:   vfc_task_runner_->PostTask(\n3827:       FROM_HERE,\n3828:       base::BindOnce(&VideoFrameCompositor::SetOnNewProcessedFrameCallback,\n3829:                      base::Unretained(compositor_.get()),\n3830:                      base::BindPostTaskToCurrentDefault(base::BindOnce(\n3831:                          &WebMediaPlayerImpl::OnFirstFrame, weak_this_))));"
        },
        {
          "label": "MediaPlayerRendererClientFactory::CreateRenderer()",
          "path": "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.cc",
          "line": 23,
          "anchor": "MediaPlayerRendererClientFactory::CreateRenderer(",
          "role": "建立 Mojo renderer 及双向扩展接口；由 MediaFactory 选择此工厂。",
          "boundary": "工厂选择",
          "snippet": "21: \n22: std::unique_ptr<media::Renderer>\n23: MediaPlayerRendererClientFactory::CreateRenderer(\n24:     const scoped_refptr<base::SequencedTaskRunner>& media_task_runner,\n25:     const scoped_refptr<base::TaskRunner>& worker_task_runner,\n26:     media::AudioRendererSink* audio_renderer_sink,\n27:     media::VideoRendererSink* video_renderer_sink,\n28:     media::RequestOverlayInfoCB request_overlay_info_cb,\n29:     const gfx::ColorSpace& target_color_space) {\n30:   // Used to send messages from the MPRC (Renderer process), to the MPR (Browser\n31:   // process). The |renderer_extension_request| will be bound in"
        },
        {
          "label": "MediaInterfaceProxy::CreateMediaPlayerRenderer()",
          "path": "content/browser/media/media_interface_proxy.cc",
          "line": 435,
          "anchor": "std::make_unique<content::TizenRendererImpl>(",
          "role": "browser 创建 TizenRendererImpl，交给 MojoRendererService 托管。",
          "boundary": "Mojo IPC / 创建",
          "snippet": "433:   media::MojoRendererService::Create(\n434:       nullptr,\n435:       std::make_unique<content::TizenRendererImpl>(\n436:           render_frame_host().GetProcess()->GetID(),\n437:           render_frame_host().GetRoutingID(),\n438:           WebContents::FromRenderFrameHost(&render_frame_host()),\n439:           std::move(renderer_extension_receiver),\n440:           std::move(client_extension_remote)),\n441:       std::move(receiver));\n442: }\n443: #endif"
        },
        {
          "label": "MediaPlayerRendererClient::Initialize() → MojoRendererWrapper::Initialize()",
          "path": "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.cc",
          "line": 46,
          "anchor": "void MediaPlayerRendererClient::Initialize(",
          "role": "renderer 侧包装层将初始化转给 MojoRenderer。",
          "boundary": "初始化阶段",
          "snippet": "44: }\n45: \n46: void MediaPlayerRendererClient::Initialize(\n47:     media::MediaResource* media_resource,\n48:     media::RendererClient* client,\n49:     media::PipelineStatusCallback init_cb) {\n50:   DCHECK(media_task_runner_->RunsTasksInCurrentSequence());\n51:   DCHECK(!init_cb_);\n52: \n53:   LOG(INFO) << \"MediaPlayerRendererClient Initialize:\" << (void*)this;\n54: "
        },
        {
          "label": "MojoRenderer::InitializeRendererFromUrl() / InitializeRendererFromStreams()",
          "path": "media/mojo/clients/mojo_renderer.cc",
          "line": 63,
          "anchor": "switch (media_resource_->GetType())",
          "role": "URL 发送 MediaUrlParams；stream 绑定 MojoDemuxerStreamImpl 并发送 endpoints。",
          "boundary": "资源类型分支",
          "snippet": "61:   init_cb_ = std::move(init_cb);\n62: \n63:   switch (media_resource_->GetType()) {\n64:     case MediaResource::Type::kStream:\n65:       InitializeRendererFromStreams(client);\n66:       break;\n67:     case MediaResource::Type::KUrl:\n68:       InitializeRendererFromUrl(client);\n69:       break;\n70:   }\n71: }"
        },
        {
          "label": "MojoRendererService::Initialize() / OnAllStreamsReady()",
          "path": "media/mojo/services/mojo_renderer_service.cc",
          "line": 56,
          "anchor": "void MojoRendererService::Initialize(",
          "role": "URL 重建 MediaUrlDemuxer；stream 建立 MediaResourceShim，流就绪后调用 renderer。",
          "boundary": "Mojo IPC / 异步就绪",
          "snippet": "54: MojoRendererService::~MojoRendererService() = default;\n55: \n56: void MojoRendererService::Initialize(\n57:     mojo::PendingAssociatedRemote<mojom::RendererClient> client,\n58:     std::optional<std::vector<mojo::PendingRemote<mojom::DemuxerStream>>>\n59:         streams,\n60:     mojom::MediaUrlParamsPtr media_url_params,\n61:     InitializeCallback callback) {\n62:   LOG(INFO) << \"MojoRendererService Initialize:\" << (void*)this;\n63:   DCHECK_EQ(state_, STATE_UNINITIALIZED);\n64: "
        },
        {
          "label": "TizenRendererImpl::Initialize() → MediaPlayerRegistry::CreateMediaPlayer()",
          "path": "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc",
          "line": 202,
          "anchor": "media::MediaPlayerRegistry::GetInstance()->CreateMediaPlayer(",
          "role": "按资源类型选择平台工厂，创建并初始化原生播放器。",
          "boundary": "browser 平台桥接",
          "snippet": "200: \n201:   // Create a media player and get player id for the created media player.\n202:   media_player_ = media::MediaPlayerRegistry::GetInstance()->CreateMediaPlayer(\n203:       media_resource_, user_agent, client_, volume_, player_id_);\n204: \n205: #if BUILDFLAG(IS_TIZEN_TV)\n206:   client_extension_->UpdateVideoId(player_id_);\n207:   media_player_->SetContentMimeType(mime_type_);\n208: #endif\n209: \n210:   if (!media_player_->CreatePlayer(player_id_)) {"
        }
      ]
    },
    {
      "id": "url",
      "title": "src / URL：把 URL 交给原生播放器",
      "description": "展示 Tizen TV 的普通平台 URL 路径；音频、upstream 模式和 HbbTV 等条件分支另列。",
      "nodes": [
        {
          "label": "ContentRendererClient::OverrideDemuxerForUrl()",
          "path": "content/public/renderer/content_renderer_client.cc",
          "line": 188,
          "anchor": "ContentRendererClient::OverrideDemuxerForUrl(",
          "role": "TV / 非 upstream 模式下可用 MediaUrlDemuxer；MSE 或音频条件直接返回 nullptr。",
          "boundary": "URL 路径选择",
          "snippet": "186: #endif\n187: \n188: std::unique_ptr<media::Demuxer> ContentRendererClient::OverrideDemuxerForUrl(\n189:     RenderFrame* render_frame,\n190:     const GURL& url,\n191: #if BUILDFLAG(IS_TIZEN_TV)\n192:     bool is_media_source,\n193: #endif\n194:     scoped_refptr<base::SequencedTaskRunner> task_runner) {\n195: #if defined(TIZEN_MULTIMEDIA) && BUILDFLAG(IS_TIZEN_TV)\n196:   if (media::IsUpstreamArchitectureEnabled()) {"
        },
        {
          "label": "DemuxerManager::CreateDemuxer()",
          "path": "media/filters/demuxer_manager.cc",
          "line": 393,
          "anchor": "PipelineStatus DemuxerManager::CreateDemuxer(",
          "role": "管理 URL demuxer / override；这里的 MediaUrlDemuxer 是 URL 资源桥，不负责拆出平台媒体包。",
          "boundary": "资源创建",
          "snippet": "391: }\n392: \n393: PipelineStatus DemuxerManager::CreateDemuxer(\n394:     bool load_media_source,\n395: #if defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)\n396:     bool load_elementary_media_stream_source,\n397: #endif  // defined(SAMSUNG_ELEMENTARY_MEDIA_STREAM_SOURCE)\n398:     DataSource::Preload preload,\n399:     bool needs_first_frame,\n400:     DemuxerManager::DemuxerCreatedCB on_demuxer_created,\n401:     base::flat_map<std::string, std::string> headers) {"
        },
        {
          "label": "MojoRenderer::InitializeRendererFromUrl()",
          "path": "media/mojo/clients/mojo_renderer.cc",
          "line": 109,
          "anchor": "void MojoRenderer::InitializeRendererFromUrl(",
          "role": "将媒体 URL 与相关请求参数发送到 browser；共用初始化链见上一节。",
          "boundary": "Mojo IPC / URL 参数",
          "snippet": "107: }\n108: \n109: void MojoRenderer::InitializeRendererFromUrl(media::RendererClient* client) {\n110:   DVLOG(2) << __func__;\n111:   DCHECK(task_runner_->RunsTasksInCurrentSequence());\n112: \n113:   BindRemoteRendererIfNeeded();\n114: \n115:   const MediaUrlParams& url_params = media_resource_->GetMediaUrlParams();\n116: \n117:   // Using base::Unretained(this) is safe because |this| owns"
        },
        {
          "label": "MediaPlayerRegistry::CreateMediaPlayer()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_registry.cc",
          "line": 63,
          "anchor": "std::unique_ptr<MediaPlayerTizen> MediaPlayerRegistry::CreateMediaPlayer(",
          "role": "URL 默认选择 BridgeCapi 工厂；启用 PlusPlayer 的 HbbTV 构建存在独立分支。",
          "boundary": "browser 工厂分支",
          "snippet": "61: }\n62: \n63: std::unique_ptr<MediaPlayerTizen> MediaPlayerRegistry::CreateMediaPlayer(\n64:     MediaResource* media_resource,\n65:     const std::string& user_agent,\n66:     media::RendererClient* client,\n67:     double volume,\n68:     int& player_id) {\n69:   LOG(INFO) << __func__;\n70:   std::unique_ptr<MediaPlayerFactory> factory;\n71:   if (media_resource->GetType() == MediaResource::Type::kStream) {"
        },
        {
          "label": "MediaPlayerBridgeCapiFactory::Create()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi_factory.cc",
          "line": 28,
          "anchor": "return std::make_unique<MediaPlayerBridgeCapiTV>",
          "role": "IS_TIZEN_TV 创建 MediaPlayerBridgeCapiTV，其他构建使用 common 类。",
          "boundary": "TV 编译分支",
          "snippet": "26:             << media_resource->GetMediaUrlParams().media_url.spec()\n27:             << \", clean url: \" << clean_url.spec();\n28:   return std::make_unique<MediaPlayerBridgeCapiTV>(clean_url, user_agent,\n29:                                                    volume);\n30: #else\n31:   return std::make_unique<MediaPlayerBridgeCapi>(\n32:       media_resource->GetMediaUrlParams().media_url, user_agent, volume);\n33: #endif\n34: }\n35: \n36: }  // namespace media"
        },
        {
          "label": "MediaPlayerBridgeCapi::Prepare() → player_set_uri()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc",
          "line": 242,
          "anchor": "player_set_uri(player_, url_str.c_str())",
          "role": "普通 TV URL 在 Prepare 中交给平台；HbbTV 可使用 TV 子类的 URL 配置分支。",
          "boundary": "平台 API / 配置",
          "snippet": "240:       LOG(INFO) << \"Blob URL detected, using temp file: \" << url_str;\n241:     }\n242:     player_set_uri(player_, url_str.c_str());\n243:   }\n244: #else\n245:   player_set_uri(player_, url_.spec().c_str());\n246: #endif\n247: \n248: #if !defined(EWK_BRINGUP)\n249:   player_set_sound_type(player_, SOUND_TYPE_MEDIA);\n250: #endif"
        },
        {
          "label": "Prepare() → player_prepare_async()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc",
          "line": 348,
          "anchor": "player_prepare_async(player_,",
          "role": "准备过程异步完成；不是配置 URI 后立即同步播放。",
          "boundary": "平台异步准备",
          "snippet": "346:   LOG_ID(INFO, player_id_) << \"Call player_prepare_async\";\n347: \n348:   int ret = player_prepare_async(player_, PlayerPreparedCb, this);\n349:   if (ret != PLAYER_ERROR_NONE) {\n350:     is_preparing_ = false;\n351:     OnHandlePlayerError(ret, FROM_HERE);\n352:     return false;\n353:   }\n354: \n355:   is_preparing_ = true;\n356:   return true;"
        },
        {
          "label": "MediaPlayerBridgeCapi::Play() → player_start()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc",
          "line": 488,
          "anchor": "bool MediaPlayerBridgeCapi::Play()",
          "role": "在准备、状态与播放请求满足条件时启动平台播放。",
          "boundary": "平台 API / 播放",
          "snippet": "486: }\n487: \n488: bool MediaPlayerBridgeCapi::Play() {\n489:   LOG_ID(INFO, player_id_) << \"(\" << static_cast<void*>(this) << \") \"\n490:                            << __func__;\n491: \n492:   if (IsPlayerSuspended()) {\n493:     LOG(INFO) << \"(\" << static_cast<void*>(this) << \") \" << __func__\n494:               << \" Player is suspended...\";\n495:     is_play_pending_ = true;\n496:     delayed_player_state_ = PLAYER_STATE_DELAYED_PLAY;"
        }
      ]
    },
    {
      "id": "append",
      "title": "MSE / MP4：从 appendBuffer 到 HEVC 样本解析",
      "description": "此链在 renderer 处理分片并形成压缩样本。HEVC 是 codec 条件分支，不代表所有 MSE 内容都经过 HEVC。",
      "nodes": [
        {
          "label": "SourceBuffer.appendBuffer() → AppendBufferInternal()",
          "path": "third_party/blink/renderer/modules/mediasource/source_buffer.cc",
          "line": 602,
          "anchor": "void SourceBuffer::appendBuffer(DOMArrayBuffer* data,",
          "role": "检查追加状态并接收 JS 提供的媒体字节。",
          "boundary": "JS / Blink",
          "snippet": "600: }\n601: \n602: void SourceBuffer::appendBuffer(DOMArrayBuffer* data,\n603:                                 ExceptionState& exception_state) {\n604:   DVLOG(2) << __func__ << \" this=\" << this << \" size=\" << data->ByteLength();\n605:   // Section 3.2 appendBuffer()\n606:   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#widl-SourceBuffer-appendBuffer-void-ArrayBufferView-data\n607:   AppendBufferInternal(data->ByteSpan(), exception_state);\n608: }\n609: \n610: void SourceBuffer::appendBuffer(NotShared<DOMArrayBufferView> data,"
        },
        {
          "label": "WebSourceBufferImpl::AppendToParseBuffer()",
          "path": "third_party/blink/renderer/platform/media/web_source_buffer_impl.cc",
          "line": 137,
          "anchor": "bool WebSourceBufferImpl::AppendToParseBuffer(",
          "role": "先把追加字节交给 ChunkDemuxer 的解析缓冲。",
          "boundary": "字节缓存",
          "snippet": "135: }\n136: \n137: bool WebSourceBufferImpl::AppendToParseBuffer(\n138:     base::span<const unsigned char> data) {\n139:   return demuxer_->AppendToParseBuffer(id_, data);\n140: }\n141: \n142: media::StreamParser::ParseStatus WebSourceBufferImpl::RunSegmentParserLoop(\n143:     double* timestamp_offset) {\n144:   base::TimeDelta old_offset = timestamp_offset_;\n145:   media::StreamParser::ParseStatus parse_result ="
        },
        {
          "label": "SourceBuffer::AppendBufferAsyncPart_Locked()",
          "path": "third_party/blink/renderer/modules/mediasource/source_buffer.cc",
          "line": 2130,
          "anchor": "void SourceBuffer::AppendBufferAsyncPart_Locked(",
          "role": "排队任务运行 segment parser loop；可能分多轮处理。",
          "boundary": "任务边界 / 异步",
          "snippet": "2128: }\n2129: \n2130: void SourceBuffer::AppendBufferAsyncPart_Locked(\n2131:     MediaSourceAttachmentSupplement::ExclusiveKey pass_key) {\n2132:   DCHECK(source_);\n2133:   source_->AssertAttachmentsMutexHeldIfCrossThreadForDebugging();\n2134:   DCHECK(updating_);\n2135: \n2136:   // Section 3.5.4 Buffer Append Algorithm\n2137:   // https://dvcs.w3.org/hg/html-media/raw-file/default/media-source/media-source.html#sourcebuffer-buffer-append\n2138: "
        },
        {
          "label": "WebSourceBufferImpl::RunSegmentParserLoop() → ChunkDemuxer::RunSegmentParserLoop()",
          "path": "third_party/blink/renderer/platform/media/web_source_buffer_impl.cc",
          "line": 142,
          "anchor": "media::StreamParser::ParseStatus WebSourceBufferImpl::RunSegmentParserLoop(",
          "role": "把 append window 与 timestamp offset 交给 demuxer。",
          "boundary": "分片解析",
          "snippet": "140: }\n141: \n142: media::StreamParser::ParseStatus WebSourceBufferImpl::RunSegmentParserLoop(\n143:     double* timestamp_offset) {\n144:   base::TimeDelta old_offset = timestamp_offset_;\n145:   media::StreamParser::ParseStatus parse_result =\n146:       demuxer_->RunSegmentParserLoop(id_, append_window_start_,\n147:                                      append_window_end_, &timestamp_offset_);\n148: \n149:   // Coded frame processing may update the timestamp offset. If the caller\n150:   // provides a non-nullptr |timestamp_offset| and frame processing changes the"
        },
        {
          "label": "SourceBufferState::RunSegmentParserLoop() → MP4StreamParser::Parse()",
          "path": "media/filters/source_buffer_state.cc",
          "line": 234,
          "anchor": "stream_parser_->Parse(StreamParser::kMaxPendingBytesPerParse)",
          "role": "MP4 parser 分析 box、track run 与媒体样本。",
          "boundary": "容器解析 / MP4 条件",
          "snippet": "232:   // and timestamp offset pointer. See http://crbug.com/351454.\n233:   StreamParser::ParseStatus result =\n234:       stream_parser_->Parse(StreamParser::kMaxPendingBytesPerParse);\n235: \n236:   if (result == StreamParser::ParseStatus::kFailed) {\n237:     MEDIA_LOG(ERROR, media_log_)\n238:         << __func__ << \": stream parsing failed. append_window_start=\"\n239:         << append_window_start.InSecondsF()\n240:         << \" append_window_end=\" << append_window_end.InSecondsF();\n241:   }\n242: "
        },
        {
          "label": "MP4StreamParser::EnqueueSample() → TrackRunIterator::GetDecryptConfig()",
          "path": "media/formats/mp4/mp4_stream_parser.cc",
          "line": 1248,
          "anchor": "decrypt_config = runs_->GetDecryptConfig()",
          "role": "加密样本取得 KID / IV / subsamples；样本输出阶段附加时间戳与配置。",
          "boundary": "样本准备",
          "snippet": "1246:   std::vector<SubsampleEntry> subsamples;\n1247:   if (runs_->is_encrypted()) {\n1248:     decrypt_config = runs_->GetDecryptConfig();\n1249:     if (!decrypt_config)\n1250:       return ParseResult::kError;\n1251:     subsamples = decrypt_config->subsamples();\n1252:   }\n1253: \n1254:   // This may change if analysis results indicate runs_->is_keyframe() is\n1255:   // opposite of what the coded frame contains.\n1256:   bool is_keyframe = runs_->is_keyframe();"
        },
        {
          "label": "HEVCBitstreamConverter::ConvertAndAnalyzeFrame()",
          "path": "media/formats/mp4/hevc.cc",
          "line": 649,
          "anchor": "bool HEVCBitstreamConverter::ConvertAndAnalyzeFrame(",
          "role": "长度前缀转 Annex B，分析关键帧并按需插入参数集；同步维护 subsamples。",
          "boundary": "HEVC 条件分支",
          "snippet": "647: }\n648: \n649: bool HEVCBitstreamConverter::ConvertAndAnalyzeFrame(\n650:     std::vector<uint8_t>* frame_buf,\n651:     bool is_keyframe,\n652:     std::vector<SubsampleEntry>* subsamples,\n653:     AnalysisResult* analysis_result) const {\n654:   RCHECK(AVC::ConvertFrameToAnnexB(hevc_config_->lengthSizeMinusOne + 1,\n655:                                    frame_buf, subsamples));\n656:   // |is_keyframe| may be incorrect. Analyze the frame to see if it is a\n657:   // keyframe. |is_keyframe| will be used if the analysis is inconclusive."
        },
        {
          "label": "HEVC::AnalyzeAnnexB() / InsertParamSetsAnnexB() → H265NaluParser",
          "path": "media/formats/mp4/hevc.cc",
          "line": 461,
          "anchor": "H265NaluParser::Result h265_result = parser.AdvanceToNextNALU",
          "role": "分析与参数集插入路径调用 NAL parser，属于解密前的码流结构处理。",
          "boundary": "NAL 解析",
          "snippet": "459:   // units\n460:   while (true) {\n461:     H265NaluParser::Result h265_result = parser.AdvanceToNextNALU(&nalu);\n462:     if (h265_result == H265NaluParser::kEOStream) {\n463:       break;\n464:     }\n465: \n466:     if (h265_result != H265NaluParser::kOk) {\n467:       DCHECK_NE(h265_result, H265NaluParser::kUnsupportedStream)\n468:           << \"AdvanceToNextNALU() returned kUnsupportedStream!\";\n469:       return result;"
        },
        {
          "label": "H265NaluParser::AdvanceToNextNALU()",
          "path": "media/parsers/h265_nalu_parser.cc",
          "line": 128,
          "anchor": "H265NaluParser::Result H265NaluParser::AdvanceToNextNALU(",
          "role": "读取 NAL 头；此快照中的具体检查以代码片段为准，不套用 M94 或历史故障提交。",
          "boundary": "codec parser",
          "snippet": "126: }\n127: \n128: H265NaluParser::Result H265NaluParser::AdvanceToNextNALU(H265NALU* nalu) {\n129:   off_t start_code_size;\n130:   off_t nalu_size_with_start_code;\n131:   if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) {\n132:     DVLOG(4) << \"Could not find next NALU, bytes left in stream: \"\n133:              << bytes_left_;\n134:     return kEOStream;\n135:   }\n136: "
        },
        {
          "label": "MP4StreamParser → StreamParserBuffer / buffers",
          "path": "media/formats/mp4/mp4_stream_parser.cc",
          "line": 1410,
          "anchor": "stream_buf->set_decrypt_config",
          "role": "形成带压缩样本和元数据的输出，交给后续缓冲及异步读取；append 不直接调用平台 submit。",
          "boundary": "缓冲交接",
          "snippet": "1408: \n1409:   if (decrypt_config)\n1410:     stream_buf->set_decrypt_config(std::move(decrypt_config));\n1411: \n1412:   if (runs_->duration() != kNoTimestamp) {\n1413:     stream_buf->set_duration(runs_->duration());\n1414:   } else {\n1415:     MEDIA_LOG(ERROR, media_log_) << \"Frame duration exceeds representable \"\n1416:                                  << \"limit\";\n1417:     return ParseResult::kError;\n1418:   }"
        }
      ]
    },
    {
      "id": "read",
      "title": "MSE / ES：平台请求样本 → Mojo 返回 → 喂入播放器",
      "description": "读取方向为 browser → renderer，数据回传方向相反；异步回调后才进入平台样本队列。",
      "nodes": [
        {
          "label": "MojoRenderer::InitializeRendererFromStreams()",
          "path": "media/mojo/clients/mojo_renderer.cc",
          "line": 85,
          "anchor": "auto mojo_stream = std::make_unique<MojoDemuxerStreamImpl>",
          "role": "renderer 将本地 DemuxerStream 暴露为 Mojo 接口。",
          "boundary": "预先绑定 streams",
          "snippet": "83:   for (media::DemuxerStream* stream : streams) {\n84:     mojo::PendingRemote<mojom::DemuxerStream> stream_proxy;\n85:     auto mojo_stream = std::make_unique<MojoDemuxerStreamImpl>(\n86:         stream, stream_proxy.InitWithNewPipeAndPassReceiver());\n87: \n88:     // Using base::Unretained(this) is safe because |this| owns |mojo_stream|,\n89:     // and the error handler can't be invoked once |mojo_stream| is destroyed.\n90:     mojo_stream->set_disconnect_handler(\n91:         base::BindOnce(&MojoRenderer::OnDemuxerStreamConnectionError,\n92:                        base::Unretained(this), mojo_stream.get()));\n93: "
        },
        {
          "label": "MediaResourceShim → MojoDemuxerStreamAdapter",
          "path": "media/mojo/services/media_resource_shim.cc",
          "line": 23,
          "anchor": "std::make_unique<MojoDemuxerStreamAdapter>",
          "role": "browser 把远程 streams 包装为本地可读的 DemuxerStream。",
          "boundary": "browser 适配",
          "snippet": "21: \n22:   for (auto& s : streams) {\n23:     streams_.emplace_back(std::make_unique<MojoDemuxerStreamAdapter>(\n24:         std::move(s), base::BindOnce(&MediaResourceShim::OnStreamReady,\n25:                                      weak_factory_.GetWeakPtr())));\n26:   }\n27: }\n28: \n29: MediaResourceShim::~MediaResourceShim() = default;\n30: \n31: std::vector<DemuxerStream*> MediaResourceShim::GetAllStreams() {"
        },
        {
          "label": "MediaPlayerRegistry → MediaPlayerESPlusPlayerFactory::Create()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_factory.cc",
          "line": 21,
          "anchor": "return std::make_unique<MediaPlayerESPlusPlayerTV>",
          "role": "kStream 资源选择 ESPlusPlayer，TV 构建创建 TV 子类。",
          "boundary": "平台流播放器",
          "snippet": "19:     double volume) {\n20: #if BUILDFLAG(IS_TIZEN_TV)\n21:   return std::make_unique<MediaPlayerESPlusPlayerTV>();\n22: #else\n23:   return std::make_unique<MediaPlayerESPlusPlayerCommon>();\n24: #endif\n25: }\n26: \n27: }  // namespace media"
        },
        {
          "label": "MediaPlayerESPlusPlayer::ReadBuffer()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc",
          "line": 998,
          "anchor": "void MediaPlayerESPlusPlayer::ReadBuffer(",
          "role": "先消耗已有队列；检查背压、暂停、EOS 和 pending read，再请求一批样本。",
          "boundary": "browser 拉取",
          "snippet": "996: }\n997: \n998: void MediaPlayerESPlusPlayer::ReadBuffer(DemuxerStream::Type type) {\n999:   if (!ReadFromBufferQueue(type))\n1000:     return;\n1001: \n1002:   // Avoid unnecessary or redundant read requests.\n1003:   if (!ShouldFeed(type) || ReadRequested(type) || IsEos(type) ||\n1004:       (is_prepared_ && is_paused_ && !is_seeking_)) {\n1005:     return;\n1006:   }"
        },
        {
          "label": "MojoDemuxerStreamAdapter::Read() ⇢ MojoDemuxerStreamImpl::Read()",
          "path": "media/mojo/services/mojo_demuxer_stream_adapter.cc",
          "line": 43,
          "anchor": "demuxer_stream_->Read(count,",
          "role": "读取请求通过 Mojo 发往 renderer 的 stream 实现。",
          "boundary": "Mojo IPC / 请求",
          "snippet": "41: \n42:   read_cb_ = std::move(read_cb);\n43:   demuxer_stream_->Read(count,\n44:                         base::BindOnce(&MojoDemuxerStreamAdapter::OnBufferReady,\n45:                                        weak_factory_.GetWeakPtr()));\n46: }\n47: \n48: AudioDecoderConfig MojoDemuxerStreamAdapter::audio_decoder_config() {\n49:   DCHECK_EQ(type_, AUDIO);\n50:   return audio_config_;\n51: }"
        },
        {
          "label": "MojoDemuxerStreamImpl::Read() → stream_->Read()",
          "path": "media/mojo/clients/mojo_demuxer_stream_impl.cc",
          "line": 55,
          "anchor": "void MojoDemuxerStreamImpl::Read(",
          "role": "异步读取本地压缩样本，并在 OnBufferReady 中序列化返回。",
          "boundary": "renderer / 异步回调",
          "snippet": "53: }\n54: \n55: void MojoDemuxerStreamImpl::Read(uint32_t count, ReadCallback callback) {\n56:   DVLOG(3) << __func__ << \": count=\" << count;\n57:   stream_->Read(\n58:       count, base::BindOnce(&MojoDemuxerStreamImpl::OnBufferReady,\n59:                             weak_factory_.GetWeakPtr(), std::move(callback)));\n60: }\n61: \n62: void MojoDemuxerStreamImpl::EnableBitstreamConverter() {\n63:   stream_->EnableBitstreamConverter();"
        },
        {
          "label": "MojoDemuxerStreamAdapter::OnBufferReady() → OnBufferRead()",
          "path": "media/mojo/services/mojo_demuxer_stream_adapter.cc",
          "line": 134,
          "anchor": "mojo_decoder_buffer_reader_->ReadDecoderBuffer(",
          "role": "通过 buffer reader 还原数据；回调完成后向平台读取者交付 buffer。",
          "boundary": "Mojo 数据返回",
          "snippet": "132: \n133:   batch_buffers_ = std::move(batch_buffers);\n134:   mojo_decoder_buffer_reader_->ReadDecoderBuffer(\n135:       std::move(batch_buffers_[0]),\n136:       base::BindOnce(&MojoDemuxerStreamAdapter::OnBufferRead,\n137:                      weak_factory_.GetWeakPtr()));\n138: }\n139: \n140: void MojoDemuxerStreamAdapter::OnBufferRead(\n141:     scoped_refptr<DecoderBuffer> buffer) {\n142:   if (!buffer) {"
        },
        {
          "label": "MediaPlayerESPlusPlayer::OnBufferReady() → ReadBuffer()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc",
          "line": 1046,
          "anchor": "void MediaPlayerESPlusPlayer::OnBufferReady(",
          "role": "检查状态与 flush generation，将有效样本放入队列后继续喂入。",
          "boundary": "browser 回调",
          "snippet": "1044: }\n1045: \n1046: void MediaPlayerESPlusPlayer::OnBufferReady(\n1047:     uint64_t posted_flush_id,\n1048:     DemuxerStream::Type type,\n1049:     DemuxerStream::Status status,\n1050:     DemuxerStream::DecoderBufferVector buffers) {\n1051:   // Defensive check: drop buffers if player has been released.\n1052:   // After Release(), esplayer_ handle is closed and player state is cleaned up.\n1053:   // OnBufferReady may still be called by pending DemuxerStream::Read() callbacks.\n1054:   if (IsReleased()) {"
        },
        {
          "label": "MediaPlayerESPlusPlayer::ReadFromBufferQueue() → SubmitEsPacket()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc",
          "line": 1118,
          "anchor": "status = SubmitEsPacket(type, buffer)",
          "role": "提交队首样本；FULL / NOT_PREPARED 等状态保留队列并停止继续喂入。",
          "boundary": "背压 / 平台提交",
          "snippet": "1116:     status = SubmitEosPacket(type);\n1117:   else\n1118:     status = SubmitEsPacket(type, buffer);\n1119: \n1120:   if (status == ESPLUSPLAYER_SUBMIT_STATUS_FULL) {\n1121:     return false;\n1122:   }\n1123: \n1124:   if ((status == ESPLUSPLAYER_SUBMIT_STATUS_NOT_PREPARED) ||\n1125:       (status == ESPLUSPLAYER_SUBMIT_STATUS_OUT_OF_MEMORY))\n1126:     return false;"
        },
        {
          "label": "MediaPlayerESPlusPlayerTV::SubmitEsPacket()",
          "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc",
          "line": 644,
          "anchor": "esplusplayer_submit_status MediaPlayerESPlusPlayerTV::SubmitEsPacket(",
          "role": "按 DRM / handle 分支提交 encrypted packet、trust-zone packet，或调用普通父类提交。",
          "boundary": "平台 API 边界",
          "snippet": "642: }\n643: \n644: esplusplayer_submit_status MediaPlayerESPlusPlayerTV::SubmitEsPacket(\n645:     DemuxerStream::Type type,\n646:     scoped_refptr<DecoderBuffer> buffer) {\n647:   // Defensive check: prevent submitting packets after Release() has been called.\n648:   // This guards against race conditions where OnBufferReady may still execute\n649:   // after the player has been released and esplayer_ handle has been closed.\n650:   if (IsReleased()) {\n651:     LOG_ID(WARNING, player_id_)\n652:         << \"Player is released, skip submit \""
        }
      ]
    }
  ],
  "extraEvidence": [
    {
      "path": "content/renderer/media/media_factory.cc",
      "anchor": "!media::IsUpstreamArchitectureEnabled()",
      "lines": [
        667
      ]
    },
    {
      "path": "content/renderer/media/media_factory.cc",
      "anchor": "RendererType::kMediaPlayer",
      "lines": [
        674,
        694
      ]
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_registry.cc",
      "anchor": "MediaResource::Type::kStream",
      "lines": [
        71
      ]
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_registry.cc",
      "anchor": "MediaPlayerPlusPlayerFactory",
      "lines": [
        79
      ]
    },
    {
      "path": "media/filters/chunk_demuxer.cc",
      "anchor": "ChunkDemuxer::RunSegmentParserLoop",
      "lines": [
        1145,
        1713
      ]
    },
    {
      "path": "media/mojo/clients/mojo_renderer_wrapper.cc",
      "anchor": "mojo_renderer_->Initialize",
      "lines": [
        23
      ]
    },
    {
      "path": "media/formats/mp4/mp4_stream_parser.cc",
      "anchor": "frame_bitstream_converter->ConvertAndAnalyzeFrame",
      "lines": [
        1272
      ]
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc",
      "anchor": "esplusplayer_submit_encrypted_packet(",
      "lines": [
        694,
        705
      ]
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc",
      "anchor": "esplusplayer_submit_trust_zone_packet(",
      "lines": [
        710
      ]
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc",
      "anchor": "player_start(player_)",
      "lines": [
        544,
        1024
      ]
    },
    {
      "path": "media/mojo/services/mojo_renderer_service.cc",
      "anchor": "std::make_unique<MediaResourceShim>",
      "lines": [
        70
      ]
    },
    {
      "path": "media/mojo/services/mojo_renderer_service.cc",
      "anchor": "renderer_->Initialize(",
      "lines": [
        84,
        544
      ]
    }
  ],
  "files": [
    {
      "path": "chrome/VERSION",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "7b5058c4b6c9727afb690ee5f147c63a11490c78fcbfba0de5eb5e79d4e2678e"
    },
    {
      "path": "content/browser/media/media_interface_proxy.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "c69110f4ed7e8f3979b022478d6564229b1435580b69685a886f2a546ab6edf1"
    },
    {
      "path": "content/public/renderer/content_renderer_client.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "5cfc26997d3c33565e50e105e536c1dac28b62e57f5fd3a465da0d2838be9baf"
    },
    {
      "path": "content/renderer/media/media_factory.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "126f531073c18de9fc062e553d1831bbe89b3189ddd5c20581c07da0228b9503"
    },
    {
      "path": "media/filters/chunk_demuxer.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "4d07c40d64d539c880493bc1435d3a7a4f9b6bb837614be0eeb3ebad147fa1e0"
    },
    {
      "path": "media/filters/demuxer_manager.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "29f459167f4b8626d98b8b752b8f1b4cd71ede6c9a43a9a0f2052d0fd9a634bf"
    },
    {
      "path": "media/filters/source_buffer_state.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "69e931888ff713d75b065d38ed593db219e0e9b8616d7082cfb9a9ec0968e1ee"
    },
    {
      "path": "media/formats/mp4/hevc.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "53a16d67754d7e1a17641e387d754503ff3d8338fdeded0e557ec9275874a773"
    },
    {
      "path": "media/formats/mp4/mp4_stream_parser.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "2c567cdd16fc5b86b47dba0b7a1a74d361f4106e89fa96d12e561b51c0840906"
    },
    {
      "path": "media/mojo/clients/mojo_demuxer_stream_impl.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "ec1027b8116bf04271e3287c137e835d9b68b35943cd493de01f599a10049fa2"
    },
    {
      "path": "media/mojo/clients/mojo_renderer.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "a186ef2f1e76c0c3fd7b7005ed25fce72061d2f20a8b936e10a0f54bef1439b2"
    },
    {
      "path": "media/mojo/clients/mojo_renderer_wrapper.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "a020bf49795e8602fa262f9e7538ea7d1b63912cbedc115ac3282d12025dd15e"
    },
    {
      "path": "media/mojo/services/media_resource_shim.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "ffef76654a98f15e3fe478c86fdcb480cee4ff7c8150185ba72ffe863423a5ac"
    },
    {
      "path": "media/mojo/services/mojo_demuxer_stream_adapter.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "747b12f8760cb26dc4191daa89898e88f823993e4c48d475894a4c402bbbf0b5"
    },
    {
      "path": "media/mojo/services/mojo_renderer_service.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "260af251877ff1d5b894060f485edbf508abe32ea80d268091d246b5d41dbce8"
    },
    {
      "path": "media/parsers/h265_nalu_parser.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "2b8dccbcf65e94697e770c7e26d3460bbcd42c622e2559cb3076d190bf6235a9"
    },
    {
      "path": "third_party/blink/renderer/modules/mediasource/source_buffer.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "4249cdc197ea40d62a6baa7da45eb9da43b21307fd6ebeb1620bc29cc0bdd253"
    },
    {
      "path": "third_party/blink/renderer/platform/media/web_media_player_impl.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "8cba18b989dbbe884261d1ab70efc04fda09089955e2e3361773afed6ea82ce9"
    },
    {
      "path": "third_party/blink/renderer/platform/media/web_source_buffer_impl.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "7f092d7a78edc894d9cf85ec14ef448f19c633ff082ccb1690a4bad6e94d54b4"
    },
    {
      "path": "tizen_src/chromium_impl/content/browser/media/tizen_renderer_impl.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "8b39721a62a783750c02dcd9d768ae0cb0e65a72f31ff60f2ba16901dac653b9"
    },
    {
      "path": "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "28f4d781f97d61140efbc5132c9094500644625d26f5da50218cc96d02764a09"
    },
    {
      "path": "tizen_src/chromium_impl/content/renderer/media/tizen/media_player_renderer_client_factory.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "147855e3a5a3de15eb5351864e3dfd57475b4d7183a72d725551203b03489a99"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "401f5cf38138c61360301a07346f877102ae4f04d4e7251b1c21b18d7899cb54"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_bridge_capi_factory.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "0dbb779d56351f9aedc9239a8fc2c2efaf8e2c00222a9789694dff944296f8f6"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "8322d2ac2b4cea7a26538d15df469171e8a8e3f80b4669853d829e3bb02e074c"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_factory.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "6b148f8453943872cb8927a3586bc6581023845e3a2c68df1989a593069f7fbf"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_esplusplayer_tv.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "213456a4c39ad3afbf4da6012b8936b253a7b8a42498caa1afd353d7256003f1"
    },
    {
      "path": "tizen_src/chromium_impl/media/filters/media_player_registry.cc",
      "revision": "f5439772622daf5a2f513e4c305f690b9d49ba9d",
      "sha256": "e246ac71c99a9e5e4cb8522e1844f848f9c194ac796d005613de4613b0686268"
    }
  ]
}
