"""Sony FX3 yarım kalmış .RSV kaydını oynatılabilir .MOV yapar (yeniden kodlama yok, sadece paketleme).

RSV = MP4'ün 'mdat' içeriği; başlık (moov) yok. İçerik döngüler halinde:
  meta (48 × 29696 bayt) → 48 video karesi (4 bayt uzunluklu HEVC NAL'leri) → 0.96 sn PCM s16be stereo 48 kHz
Eksik olanlar aynı kameranın sağlam bir dosyasından alınır:
  - VPS/SPS/PPS (RSV'de yok)
  - kare sıralama deseni: dts = N-2, pts = dts + [5,2,0,1][N%4] (C2221'in tamamında sabit)
Sadece tam döngüler alınır; dosya sonundaki yarım döngü atılır.

Kullanım: python3 rsv_kurtar.py <RSV> <çıktı.MOV> [döngü sınırı]
"""
import os, re, struct, subprocess, sys, tempfile

ORNEK = "/home/stark/Desktop/Eğitim - 01/Eğitim/C2221.MP4"
META, SES, KARE = 48 * 29696, 184320, 48


def parametre_setleri():
    d = subprocess.run(["ffmpeg", "-v", "error", "-nostdin", "-i", ORNEK, "-map", "0:v", "-c:v", "copy",
                        "-bsf:v", "hevc_mp4toannexb", "-frames:v", "1", "-f", "hevc", "-"], capture_output=True).stdout
    bol = [m.start() for m in re.finditer(b"\0\0\1", d[:200000])]
    ps = b""; bulunan = set(); goruldu = set()
    for k, j in enumerate(bol):
        son = bol[k + 1] if k + 1 < len(bol) else len(d)
        t = (d[j + 3] >> 1) & 63
        nal = d[j + 3:son].rstrip(b"\0")
        if t in (32, 33, 34) and nal not in goruldu:      # birden fazla PPS olabilir, hepsi alınır
            ps += b"\0\0\0\1" + nal; bulunan.add(t); goruldu.add(nal)
    assert bulunan == {32, 33, 34}, f"VPS/SPS/PPS bulunamadı: {bulunan}"
    return ps


def dizin(yol, sinir):
    """Her tam döngü için video NAL'lerinin (konum, uzunluk) listesi ve ses konumu."""
    boyut = os.path.getsize(yol); f = open(yol, "rb"); p = 0; donguler = []
    while sinir is None or len(donguler) < sinir:
        p += META; naller = []
        try:
            for _ in range(KARE):
                vcl = 0; ilk = True
                while vcl < 8:
                    if p + 6 > boyut: raise EOFError
                    f.seek(p); n = struct.unpack(">I", f.read(4))[0]; t = (f.read(1)[0] >> 1) & 63
                    if ilk and t != 35: raise ValueError(f"kare başında AUD yok (bayt {p})")
                    if p + 4 + n > boyut: raise EOFError
                    naller.append((p + 4, n)); p += 4 + n; ilk = False
                    if t < 32: vcl += 1
            if p + SES > boyut: raise EOFError
        except (EOFError, ValueError) as e:
            print(f"  {len(donguler)} tam döngüden sonra durdu: {e or 'dosya sonu'}", flush=True); break
        donguler.append((naller, p)); p += SES
    return donguler


def main():
    rsv, cikti = sys.argv[1], sys.argv[2]
    sinir = int(sys.argv[3]) if len(sys.argv) > 3 else None
    ps = parametre_setleri()
    d = dizin(rsv, sinir)
    print(f"  {len(d)} döngü = {len(d) * KARE} kare = {len(d) * 0.96:.1f} sn", flush=True)
    f = open(rsv, "rb")
    with tempfile.NamedTemporaryFile(suffix=".pcm", dir=os.path.dirname(os.path.abspath(cikti))) as ses:
        for _, sp in d:
            f.seek(sp); ses.write(f.read(SES))
        ses.flush()
        off = r"eq(mod(N\,4)\,0)*5+eq(mod(N\,4)\,1)*2+eq(mod(N\,4)\,3)"   # virgüller kaçışlı
        ff = subprocess.Popen(["ffmpeg", "-v", "error", "-nostdin", "-y", "-framerate", "50", "-f", "hevc", "-i", "-",
                               "-f", "s16be", "-ar", "48000", "-ac", "2", "-i", ses.name,
                               "-map", "0:v", "-map", "1:a", "-c:v", "copy", "-c:a", "pcm_s16le", "-tag:v", "hvc1",
                               "-bsf:v", f"setts=time_base=1/50:dts=N-2:pts=N-2+{off}:duration=1",
                               "-movflags", "+faststart", cikti], stdin=subprocess.PIPE)
        for naller, _ in d:
            ff.stdin.write(ps)                       # her I-kare öncesi
            for p, n in naller:
                f.seek(p); ff.stdin.write(b"\0\0\0\1" + f.read(n))
        ff.stdin.close()
        if ff.wait() != 0:
            sys.exit("ffmpeg hata verdi")
    print("  yazıldı:", cikti, flush=True)


if __name__ == "__main__":
    main()
