瀏覽代碼

[*] 优化提交

yanxuyao 3 周之前
父節點
當前提交
d99db34c13

+ 42 - 0
CLAUDE.md

@@ -0,0 +1,42 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Commands
+
+**One-time setup:**
+```bash
+bash scripts/dev/setup_sdk_dev.sh
+```
+
+**Build demos:**
+```bash
+bash scripts/dev/build_demo.sh all    # both ObjC and Swift demos
+bash scripts/dev/build_demo.sh objc
+bash scripts/dev/build_demo.sh swift
+```
+
+**Tests:** Run via Xcode or `xcodebuild test` — no standalone test script exists.
+
+**IDE:** Open `QGVAPlayerDev.xcworkspace` to work on SDK + demos together.
+
+## Architecture
+
+QGVAPlayer is a high-performance iOS video animation player (iOS 12+) using H.264 hardware decoding and Metal GPU rendering. It plays VAP-format MP4s with transparent channel support, used for live-streaming special effects.
+
+**Data flow:** MP4 file → `LNMP4Parser` (reads VAP boxes) → `LNAnimatedImageDecodeThreadPool` (multi-threaded frame decode) → `LNHWDMetalCoreRenderer` (YUV→RGB, Metal shaders) → `LNVAPPlayerView` (UIView display)
+
+**Key source locations (all under `QGVAPlayer/QGVAPlayer/LNSwift/`):**
+
+| Directory | Purpose |
+|-----------|---------|
+| `Core/` | Playback engine (`LNControllers.swift`, `LNVAPFacade.swift`) |
+| `View/` | `LNVAPPlayerView` (primary API), `LNVAPWrapView` |
+| `Render/` | Metal renderer; OpenGL fallback via `renderByOpenGL` flag |
+| `Parser/` | `LNMP4Parser` — reads MP4 boxes including custom `vapc` metadata |
+| `Model/` | Playback types and data models |
+| `Bridges/` | ObjC compatibility shims (`LNLegacyMappings.swift`) |
+
+**Public API entry point:** `LNVAPPlayerView.lnPlay(filePath:repeatCount:)` — any UIView can host this. Delegates: `LNVAPPlaybackDelegate` (Swift) and `LNVAPLegacyPlaybackDelegate` (ObjC).
+
+**Rendering:** Metal (`LNHWDMetalCoreRenderer`) is default on iOS 13+; shaders live in `QGVAPlayer/QGVAPlayer/Shaders/QGHWDShaders.metal`. Alpha blend modes (left/right/top/bottom) are configured via `vapc` MP4 box metadata.

+ 2 - 2
QGVAPlayer/QGVAPlayer/LNSwift/Render/LNRenderers.swift

@@ -120,7 +120,7 @@ private final class LNHWDMetalCoreRenderer {
         self.pipelineState = pipelineState
 
         metalLayer.device = device
-        metalLayer.framebufferOnly = false
+        metalLayer.framebufferOnly = true
 
         let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
         guard cacheStatus == kCVReturnSuccess else { return nil }
@@ -281,7 +281,7 @@ private final class LNVAPMetalCoreRenderer {
         self.shaderLoader = LNVAPMetalShaderFunctionLoader(device: device)
 
         metalLayer.device = device
-        metalLayer.framebufferOnly = false
+        metalLayer.framebufferOnly = true
 
         let cacheStatus = CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, device, nil, &textureCache)
         guard cacheStatus == kCVReturnSuccess else { return nil }

+ 2 - 2
QGVAPlayer/QGVAPlayer/LNSwift/View/LNVAPPlayerView.swift

@@ -320,7 +320,7 @@ private final class LNPlayerCore: NSObject {
             return nil
         }
 
-        let mergeInfos = configManager.model.mergedConfig[NSNumber(value: currentFrame.frameIndex)] ?? []
+        let mergeInfos = (configManager.model.mergedConfig[NSNumber(value: currentFrame.frameIndex)] ?? [])
             .sorted { $0.renderIndex > $1.renderIndex }
 
         guard model.size.width > 0, model.size.height > 0 else {
@@ -367,7 +367,6 @@ private final class LNPlayerCore: NSObject {
 
         stopInternal(triggerDelegate: false)
 
-        // Keep parity with OC: each new play resets background behavior to do-nothing.
         enterBackgroundOperation = .doNothing
         isFinish = false
         onPause = false
@@ -701,6 +700,7 @@ extension LNPlayerCore: LNAnimatedImageDecoderDelegate {
     }
 
     func decoderDidFailDecode(_ decoder: LNBaseDecoder?, error: NSError) {
+        // Keep parity with OC UIView+VAP: stop first, then emit failure callback.
         stopInternal(triggerDelegate: true)
         performCallback { [weak self] in
             self?.owner?.notifyFail(error)

+ 1 - 1
QGVAPlayer/QGVAPlayer/LNSwift/View/LNVAPWrapView.swift

@@ -20,7 +20,7 @@ public final class LNVAPWrapView: UIView {
         set { contentModeOption = LNVAPWrapContentMode(rawValue: newValue) ?? .scaleToFill }
     }
 
-    public var autoDestroyAfterFinish: Bool = true
+    @nonobjc public var autoDestroyAfterFinish: Bool = true
 
     @objc(autoDestoryAfterFinish)
     public var autoDestoryAfterFinish: Bool {

+ 7 - 1
QGVAPlayerDemo/QGVAPlayerDemo/ViewController.m

@@ -67,11 +67,17 @@
     [self.wrapView removeFromSuperview];
     self.wrapView = [LNVAPFacade lnMakeWrapViewWithFrame:self.view.bounds];
     self.wrapView.contentModeOption = LNVAPWrapContentModeAspectFit;
-    self.wrapView.autoDestroyAfterFinish = YES;
+    self.wrapView.autoDestoryAfterFinish = YES;
     self.wrapView.delegate = self;
     [LNVAPFacade lnSetMuteWrap:self.wrapView mute:YES];
     [self.view addSubview:self.wrapView];
 
+    __weak typeof(self) weakSelf = self;
+    [self.wrapView lnAddVapTapGesture:^(UIGestureRecognizer *gesture, BOOL insideSource, LNVAPSourceDisplayItem *source) {
+        [LNVAPFacade lnStopWrap:weakSelf.wrapView];
+        [weakSelf.wrapView removeFromSuperview];
+    }];
+
     NSString *resPath = [NSString stringWithFormat:@"%@/Resource/vap.mp4", NSBundle.mainBundle.resourcePath];
     [LNVAPFacade lnPlayWrap:self.wrapView filePath:resPath repeatCount:-1 delegate:self];
 }

+ 3 - 3
QGVAPlayerDemoSwift/QGVAPlayerDemoSwift/ViewController.swift

@@ -63,11 +63,11 @@ final class ViewController: UIViewController, LNVAPPlaybackDelegate {
     }
 
     func lnPlayerDidPlay(_ playerView: LNVAPPlayerView, frame: LNMP4AnimatedImageFrame) {
-        print("[LN Demo Swift] player did play frame=\\(frame.frameIndex)")
+        print("[LN Demo Swift] player did play frame=\(frame.frameIndex)")
     }
 
     func lnPlayerDidFinish(_ playerView: LNVAPPlayerView, totalFrameCount: Int) {
-        print("[LN Demo Swift] player did finish total=\\(totalFrameCount)")
+        print("[LN Demo Swift] player did finish total=\(totalFrameCount)")
     }
 
     func lnPlayerDidStop(_ playerView: LNVAPPlayerView) {
@@ -78,6 +78,6 @@ final class ViewController: UIViewController, LNVAPPlaybackDelegate {
     }
 
     func lnPlayerDidFail(_ playerView: LNVAPPlayerView, error: NSError) {
-        print("[LN Demo Swift] player did fail error=\\(error)")
+        print("[LN Demo Swift] player did fail error=\(error)")
     }
 }