/*
 * TS segment analyzer — run on your server against actual output segments
 * Usage: ./analyze_ts /hls/jayatv/index0.ts
 */
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>

#define TS_SZ 188

static uint16_t ts_pid(const uint8_t *p){ return ((p[1]&0x1F)<<8)|p[2]; }
static int ts_pusi(const uint8_t *p){ return (p[1]>>6)&1; }
static int ts_afc(const uint8_t *p){ return (p[3]>>4)&3; }
static int ts_cc(const uint8_t *p){ return p[3]&0x0F; }
static int ts_poff(const uint8_t *p){
    int a=ts_afc(p);
    if(a==1) return 4;
    if(a==3) return 5+(int)p[4];
    return -1;
}

static int64_t read_pts(const uint8_t *p){
    return (int64_t)(((uint64_t)(p[0]&0x0E)<<29)|((uint64_t)p[1]<<22)|
                     ((uint64_t)(p[2]&0xFE)<<14)|((uint64_t)p[3]<<7)|
                     ((uint64_t)(p[4]&0xFE)>>1));
}

int main(int argc, char *argv[]){
    const char *fname = argc>1 ? argv[1] : "index0.ts";
    FILE *f = fopen(fname,"rb");
    if(!f){ fprintf(stderr,"Cannot open %s\n",fname); return 1; }

    printf("=== Analyzing %s ===\n\n", fname);

    uint8_t pkt[TS_SZ];
    int pkt_num = 0;
    int pat_found=0, pmt_found=0;
    uint16_t pmt_pid=0, vid_pid=0, aud_pid=0, pcr_pid=0;
    int64_t first_vid_pts=-1, first_aud_pts=-1;
    int64_t last_vid_pts=-1,  last_aud_pts=-1;
    int vid_pkt_count=0, aud_pkt_count=0;
    int vid_idr_count=0;
    int cc_errors=0;
    uint8_t cc_last[8192]; memset(cc_last,0xFF,sizeof cc_last);
    int pcr_found=0; int64_t first_pcr=-1;

    /* NAL type names */
    const char *nal_names[] = {
        "?","non-IDR","?","?","?","IDR","SEI","SPS","PPS","AUD","?",
        "?","?","?","?","?","?","?","?","?","MVC-IDR"
    };

    while(fread(pkt,1,TS_SZ,f)==TS_SZ){
        if(pkt[0]!=0x47){ printf("pkt%d: NO SYNC BYTE!\n",pkt_num); pkt_num++; continue; }
        uint16_t pid = ts_pid(pkt);
        int afc = ts_afc(pkt);
        int cc  = ts_cc(pkt);

        /* CC check */
        if((afc==1||afc==3) && pid!=0x1FFF){
            uint8_t prev = cc_last[pid&0x1FFF];
            if(prev!=0xFF && cc!=prev && cc!=(uint8_t)((prev+1)&0xF)){
                if(pkt_num<20) printf("pkt%d: CC ERROR pid=0x%04X exp=%d got=%d\n",
                       pkt_num, pid, (prev+1)&0xF, cc);
                cc_errors++;
            }
            cc_last[pid&0x1FFF]=cc;
        }

        /* PCR */
        if(afc==2||afc==3){
            if(pkt[5]&0x10){ /* PCR_flag */
                int64_t pcr=(int64_t)(((uint64_t)pkt[6]<<25)|((uint64_t)pkt[7]<<17)|
                             ((uint64_t)pkt[8]<<9)|((uint64_t)pkt[9]<<1)|
                             ((pkt[10]>>7)&1));
                if(!pcr_found){ first_pcr=pcr; pcr_found=1; pcr_pid=pid;
                    printf("PCR: pid=0x%04X first_pcr=%lld (%.3fs)\n",
                           pid,(long long)pcr,(double)pcr/90000.0); }
            }
        }

        /* PAT */
        if(pid==0x0000 && ts_pusi(pkt)){
            int o=ts_poff(pkt); if(o>=0&&o+1<TS_SZ){
                int b=o+1+pkt[o];
                if(b+8<TS_SZ && pkt[b]==0x00){
                    int e=b+3+(((pkt[b+1]&0xF)<<8)|pkt[b+2])-4;
                    for(int q=b+8;q+3<=e&&q+3<TS_SZ;q+=4){
                        uint16_t pn=(pkt[q]<<8)|pkt[q+1];
                        uint16_t pp=((pkt[q+2]&0x1F)<<8)|pkt[q+3];
                        if(pn){ pmt_pid=pp; pat_found=1; }
                    }
                }
            }
            if(!pat_found&&pkt_num==0)
                printf("PAT: MISSING or malformed in first packet!\n");
            else if(pat_found&&pkt_num<5)
                printf("PAT: found  pmt_pid=0x%04X\n",pmt_pid);
        }

        /* PMT */
        if(pat_found && pid==pmt_pid && ts_pusi(pkt) && !pmt_found){
            int o=ts_poff(pkt); if(o>=0&&o+1<TS_SZ){
                int b=o+1+pkt[o];
                if(b+12<TS_SZ && pkt[b]==0x02){
                    pcr_pid=((pkt[b+8]&0x1F)<<8)|pkt[b+9];
                    int pil=((pkt[b+10]&0xF)<<8)|pkt[b+11];
                    int es=b+12+pil;
                    int sec=((pkt[b+1]&0xF)<<8)|pkt[b+2];
                    int ee=b+3+sec-4;
                    printf("PMT: pcr_pid=0x%04X\n",pcr_pid);
                    while(es+4<TS_SZ&&es+4<=ee){
                        uint8_t st=pkt[es];
                        uint16_t sp=((pkt[es+1]&0x1F)<<8)|pkt[es+2];
                        printf("  stream_type=0x%02X pid=0x%04X  (%s)\n",
                               st,sp,
                               st==0x1B?"H264 ✓":
                               st==0x02?"MPEG2 ✗ (wrong for H264 output!)":
                               st==0x0F?"AAC ✓":
                               st==0x03?"MP2 ✗ (not transcoded!)":
                               st==0x11?"AAC LATM ✓":"other");
                        if(st==0x1B||st==0x02) vid_pid=sp;
                        if(st==0x0F||st==0x03||st==0x11) aud_pid=sp;
                        es+=5+((pkt[es+3]&0xF)<<8)|pkt[es+4];
                        es+=5;
                    }
                    pmt_found=1;
                }
            }
        }

        /* Video PES */
        if(vid_pid && pid==vid_pid && ts_pusi(pkt)){
            vid_pkt_count++;
            int o=ts_poff(pkt);
            if(o>=0&&o+13<TS_SZ){
                const uint8_t *pes=pkt+o;
                if(pes[0]==0&&pes[1]==0&&pes[2]==1){
                    /* PTS */
                    uint8_t pf=(pes[7]>>6)&3;
                    if(pf&&o+13<TS_SZ){
                        int64_t pts=read_pts(pes+9);
                        if(first_vid_pts<0) first_vid_pts=pts;
                        last_vid_pts=pts;
                    }
                    /* Scan NALs (skip PES header) */
                    int skip=9+pes[8]; 
                    const uint8_t *h=pes+skip; int hl=(TS_SZ-o)-skip;
                    for(int j=0;j+3<hl;j++){
                        int nt=-1;
                        if(h[j]==0&&h[j+1]==0&&h[j+2]==0&&j+4<hl&&h[j+3]==1)
                            {nt=h[j+4]&0x1F;j+=4;}
                        else if(h[j]==0&&h[j+1]==0&&h[j+2]==1&&j+3<hl)
                            {nt=h[j+3]&0x1F;j+=3;}
                        if(nt>=0&&nt<=20){
                            if(vid_pkt_count<=3||nt==5||nt==7)
                                printf("vid pkt%d: pts=%lld NAL=%d(%s)%s\n",
                                       vid_pkt_count,(long long)(first_vid_pts<0?-1:
                                       read_pts(pes+9)),nt,
                                       nt<21?nal_names[nt]:"?",
                                       nt==5?" ← IDR":"");
                            if(nt==5||nt==20) vid_idr_count++;
                        }
                    }
                }
            }
        }

        /* Audio PES */
        if(aud_pid && pid==aud_pid && ts_pusi(pkt)){
            aud_pkt_count++;
            int o=ts_poff(pkt);
            if(o>=0&&o+13<TS_SZ){
                const uint8_t *pes=pkt+o;
                if(pes[0]==0&&pes[1]==0&&pes[2]==1){
                    uint8_t pf=(pes[7]>>6)&3;
                    if(pf&&o+13<TS_SZ){
                        int64_t pts=read_pts(pes+9);
                        if(first_aud_pts<0) first_aud_pts=pts;
                        last_aud_pts=pts;
                    }
                    /* Check ADTS sync byte */
                    int skip=9+pes[8];
                    if(skip+2<TS_SZ-o){
                        uint8_t s0=pes[skip],s1=pes[skip+1];
                        if(aud_pkt_count<=2)
                            printf("aud pkt%d: pts=%lld  first2bytes=%02X%02X %s\n",
                                   aud_pkt_count,(long long)read_pts(pes+9),
                                   s0,s1,
                                   (s0==0xFF&&(s1&0xF0)==0xF0)?"ADTS ✓":"NOT ADTS ✗");
                    }
                }
            }
        }
        pkt_num++;
    }
    fclose(f);

    printf("\n=== Summary ===\n");
    printf("Total TS packets: %d\n", pkt_num);
    printf("CC errors: %d\n", cc_errors);
    printf("Video packets: %d  IDR count: %d\n", vid_pkt_count, vid_idr_count);
    printf("Audio packets: %d\n", aud_pkt_count);
    if(first_vid_pts>=0 && first_aud_pts>=0){
        double vt=(double)first_vid_pts/90000.0;
        double at=(double)first_aud_pts/90000.0;
        printf("First video PTS: %lld (%.3fs)\n",(long long)first_vid_pts,vt);
        printf("First audio PTS: %lld (%.3fs)\n",(long long)first_aud_pts,at);
        double diff=vt-at; if(diff<0)diff=-diff;
        printf("A/V PTS diff at start: %.3fs %s\n", diff,
               diff>0.5?"← TOO LARGE (VLC/ExoPlayer will fail!)":"✓ OK");
    }
    if(vid_idr_count==0)
        printf("*** NO IDR FOUND — segment not independently decodable! ***\n");
    if(!pcr_found)
        printf("*** NO PCR FOUND — VLC/ExoPlayer cannot sync! ***\n");

    printf("\n=== Diagnosis ===\n");
    if(!pat_found) printf("✗ PAT missing\n");
    else printf("✓ PAT present\n");
    if(!pmt_found) printf("✗ PMT missing\n");
    else printf("✓ PMT present\n");
    if(pcr_found) printf("✓ PCR present (pid=0x%04X)\n",pcr_pid);
    else printf("✗ PCR MISSING — add PCR packets\n");
    if(vid_idr_count>0) printf("✓ IDR keyframe present\n");
    else printf("✗ NO IDR — segment cannot be decoded independently\n");
    if(cc_errors==0) printf("✓ No CC errors\n");
    else printf("✗ %d CC errors\n",cc_errors);
    return 0;
}
