summaryrefslogtreecommitdiff
path: root/subprojects/packagefiles/notcurses/duration_and_ffmpeg_fix.diff
blob: 71680f7beccfaa0714f2ee4bca5c4c0695b83301 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
diff --git a/include/notcurses/notcurses.h b/include/notcurses/notcurses.h
index 9e09cb3..f4da60f 100644
--- a/include/notcurses/notcurses.h
+++ b/include/notcurses/notcurses.h
@@ -3406,6 +3406,10 @@ API int ncvisual_geom(const struct notcurses* nc, const struct ncvisual* n,
                       const struct ncvisual_options* vopts, ncvgeom* geom)
   __attribute__ ((nonnull (4)));
 
+
+API uint64_t ncvisual_get_duration(const struct ncvisual* n)
+  __attribute__ ((nonnull (1)));
+
 // Destroy an ncvisual. Rendered elements will not be disrupted, but the visual
 // can be neither decoded nor rendered any further.
 API void ncvisual_destroy(struct ncvisual* ncv);
diff --git a/src/lib/visual-details.h b/src/lib/visual-details.h
index e9347e3..6852b76 100644
--- a/src/lib/visual-details.h
+++ b/src/lib/visual-details.h
@@ -25,6 +25,7 @@ typedef struct ncvisual {
   unsigned pixx, pixy; // pixel geometry, *not* cell geometry
   // lines are sometimes padded. this many true bytes per row in data.
   unsigned rowstride;
+  uint64_t duration;
   bool owndata; // we own data iff owndata == true
 } ncvisual;
 
diff --git a/src/lib/visual.c b/src/lib/visual.c
index 047b68e..25e8422 100644
--- a/src/lib/visual.c
+++ b/src/lib/visual.c
@@ -458,6 +458,10 @@ int ncvisual_geom(const notcurses* nc, const ncvisual* n,
                              &disppxy, &disppxx, &outy, &outx, &placey, &placex);
 }
 
+uint64_t ncvisual_get_duration(const ncvisual* n){
+    return n->duration;
+}
+
 void* rgb_loose_to_rgba(const void* data, int rows, int* rowstride, int cols, int alpha){
   if(*rowstride % 4){ // must be a multiple of 4 bytes
     return NULL;
diff --git a/src/media/ffmpeg.c b/src/media/ffmpeg.c
index 655f269..d2bf399 100644
--- a/src/media/ffmpeg.c
+++ b/src/media/ffmpeg.c
@@ -33,6 +33,7 @@ typedef struct ncvisual_details {
   struct SwsContext* swsctx;
   struct SwsContext* rgbactx;
   AVSubtitle subtitle;
+  AVRational timebase;
   int stream_index;        // match against this following av_read_frame()
   int sub_stream_index;    // subtitle stream index, can be < 0 if no subtitles
   bool packet_outstanding;
@@ -224,9 +225,7 @@ struct ncplane* ffmpeg_subtitle(ncplane* parent, const ncvisual* ncv){
 
 static int
 averr2ncerr(int averr){
-  if(averr == AVERROR_EOF){
-    return 1;
-  }
+  (void)averr;
   // FIXME need to map averror codes to ncerrors
 //fprintf(stderr, "AVERR: %d/%x %d/%x\n", averr, averr, -averr, -averr);
   return -1;
@@ -326,6 +325,11 @@ ffmpeg_decode(ncvisual* n){
           /*if(averr != AVERROR_EOF){
             fprintf(stderr, "Error reading frame info (%s)\n", av_err2str(averr));
           }*/
+          if(averr == AVERROR_EOF){
+              // Start flush.
+              unref = false;
+              break;
+          }
           return averr2ncerr(averr);
         }
         unref = true;
@@ -339,10 +343,10 @@ ffmpeg_decode(ncvisual* n){
         }
       }while(n->details->packet->stream_index != n->details->stream_index);
       n->details->packet_outstanding = true;
-      int averr = avcodec_send_packet(n->details->codecctx, n->details->packet);
+      int averr = avcodec_send_packet(n->details->codecctx, unref ? n->details->packet : NULL);
       if(averr < 0){
-        n->details->packet_outstanding = false;
         av_packet_unref(n->details->packet);
+        n->details->packet_outstanding = false;
   //fprintf(stderr, "Error processing AVPacket\n");
         return averr2ncerr(averr);
       }
@@ -350,10 +354,16 @@ ffmpeg_decode(ncvisual* n){
     int averr = avcodec_receive_frame(n->details->codecctx, n->details->frame);
     if(averr >= 0){
       have_frame = true;
+    }else if(averr == AVERROR_EOF){
+      n->details->packet_outstanding = false;
+      // Where EOF gets returned during normal operation.
+      return 1;
     }else if(averr < 0){
-      av_packet_unref(n->details->packet);
-      have_frame = false;
+      if(unref){
+        av_packet_unref(n->details->packet);
+      }
       n->details->packet_outstanding = false;
+      have_frame = false;
       if(averr != AVERROR(EAGAIN)){
         return averr2ncerr(averr);
       }
@@ -365,6 +375,8 @@ ffmpeg_decode(ncvisual* n){
   n->rowstride = f->linesize[0];
   n->pixx = n->details->frame->width;
   n->pixy = n->details->frame->height;
+  const uint64_t pktduration = ffmpeg_pkt_duration(f);
+  n->duration = av_rescale_q(pktduration, n->details->timebase, AV_TIME_BASE_Q);
 //fprintf(stderr, "good decode! %d/%d %d %p\n", n->details->frame->height, n->details->frame->width, n->rowstride, f->data);
   ncvisual_set_data(n, f->data[0], false);
   force_rgba(n);
@@ -457,6 +469,7 @@ ffmpeg_from_file(const char* filename){
     goto err;
   }
   AVStream* st = ncv->details->fmtctx->streams[ncv->details->stream_index];
+  ncv->details->timebase = st->time_base;
   if((ncv->details->codecctx = avcodec_alloc_context3(ncv->details->codec)) == NULL){
     //fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
     goto err;
@@ -501,14 +514,6 @@ ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale,
   memcpy(&activevopts, vopts, sizeof(*vopts));
   int ncerr;
   do{
-    // codecctx seems to be off by a factor of 2 regularly. instead, go with
-    // the time_base from the avformatctx. except ts isn't properly reset for
-    // all media when we loop =[. we seem to be accurate enough now with the
-    // tbase/ppd. see https://github.com/dankamongmen/notcurses/issues/1352.
-    double tbase = av_q2d(ncv->details->fmtctx->streams[ncv->details->stream_index]->time_base);
-    if(isnan(tbase)){
-      tbase = 0;
-    }
     if(activevopts.n){
       ncplane_erase(activevopts.n); // new frame could be partially transparent
     }
@@ -529,7 +534,7 @@ ffmpeg_stream(notcurses* nc, ncvisual* ncv, float timescale,
     }
     // display duration in units of time_base
     const uint64_t pktduration = ffmpeg_pkt_duration(ncv->details->frame);
-    uint64_t duration = pktduration * tbase * NANOSECS_IN_SEC;
+    uint64_t duration = av_rescale_q(pktduration, ncv->details->timebase, (AVRational){1, NANOSECS_IN_SEC});
     double schedns = nsbegin;
     sum_duration += (duration * timescale);
     schedns += sum_duration;
@@ -561,11 +566,13 @@ static int
 ffmpeg_decode_loop(ncvisual* ncv){
   int r = ffmpeg_decode(ncv);
   if(r == 1){
-    if(av_seek_frame(ncv->details->fmtctx, ncv->details->stream_index, 0, AVSEEK_FLAG_FRAME) < 0){
+    avformat_flush(ncv->details->fmtctx);
+    if(avformat_seek_file(ncv->details->fmtctx, ncv->details->stream_index, 0, 0, 0, AVSEEK_FLAG_FRAME) < 0){
       // FIXME log error
       return -1;
     }
-    if(ffmpeg_decode(ncv) < 0){
+    avcodec_flush_buffers(ncv->details->codecctx);
+    if(ffmpeg_decode(ncv) != 0){
       return -1;
     }
   }