Browse code

Added option to write frames interleaved (yet disabled)

For this reason, a MuxerContext and write_trailer()-function was added,
to track the previous packet and flush the last packet at the end.

Originally committed as revision 24942 to svn://svn.ffmpeg.org/ffmpeg/trunk

Tobias Bindhammer authored on 2010/08/26 19:03:09
Showing 1 changed files
... ...
@@ -24,9 +24,16 @@
24 24
 #include "libavcodec/bytestream.h"
25 25
 #include "avformat.h"
26 26
 
27
+typedef struct A64MuxerContext {
28
+    int interleaved;
29
+    AVPacket prev_pkt;
30
+    int prev_frame_count;
31
+} A64MuxerContext;
32
+
27 33
 static int a64_write_header(struct AVFormatContext *s)
28 34
 {
29 35
     AVCodecContext *avctx=s->streams[0]->codec;
36
+    A64MuxerContext *c = s->priv_data;
30 37
     uint8_t header[5] = {
31 38
         0x00, //load
32 39
         0x40, //address
... ...
@@ -34,6 +41,7 @@ static int a64_write_header(struct AVFormatContext *s)
34 34
         0x00, //charset_lifetime (multi only)
35 35
         0x00  //fps in 50/fps;
36 36
     };
37
+    c->interleaved = 0;
37 38
     switch (avctx->codec->id) {
38 39
     case CODEC_ID_A64_MULTI:
39 40
         header[2] = 0x00;
... ...
@@ -50,16 +58,99 @@ static int a64_write_header(struct AVFormatContext *s)
50 50
         break;
51 51
     }
52 52
     put_buffer(s->pb, header, 2);
53
+    c->prev_pkt.size = 0;
54
+    c->prev_frame_count = 0;
53 55
     return 0;
54 56
 }
55 57
 
56 58
 static int a64_write_packet(struct AVFormatContext *s, AVPacket *pkt)
57 59
 {
58
-    put_buffer(s->pb, pkt->data, pkt->size);
60
+    AVCodecContext *avctx = s->streams[0]->codec;
61
+    A64MuxerContext *c = s->priv_data;
62
+    int i, j;
63
+    int ch_chunksize;
64
+    int lifetime;
65
+    int frame_count;
66
+    int charset_size;
67
+    int frame_size;
68
+    int num_frames;
69
+
70
+    /* fetch values from extradata */
71
+    switch (avctx->codec->id) {
72
+    case CODEC_ID_A64_MULTI:
73
+    case CODEC_ID_A64_MULTI5:
74
+        if(c->interleaved) {
75
+            /* Write interleaved, means we insert chunks of the future charset before each current frame.
76
+             * Reason: if we load 1 charset + corresponding frames in one block on c64, we need to store
77
+             * them first and then display frame by frame to keep in sync. Thus we would read and write
78
+             * the data for colram from/to ram first and waste too much time. If we interleave and send the
79
+             * charset beforehand, we assemble a new charset chunk by chunk, write current screen data to
80
+             * screen-ram to be displayed and decode the colram directly to colram-location $d800 during
81
+             * the overscan, while reading directly from source
82
+             * This is the only way so far, to achieve 25fps on c64 */
83
+            if(avctx->extradata) {
84
+                /* fetch values from extradata */
85
+                lifetime     = AV_RB32(avctx->extradata + 0);
86
+                frame_count  = AV_RB32(avctx->extradata + 4);
87
+                charset_size = AV_RB32(avctx->extradata + 8);
88
+                frame_size   = AV_RB32(avctx->extradata + 12);
89
+
90
+                /* TODO: sanity checks? */
91
+            }
92
+            else {
93
+                av_log(avctx, AV_LOG_ERROR, "extradata not set\n");
94
+                return AVERROR(EINVAL);
95
+            }
96
+            ch_chunksize=charset_size/lifetime;
97
+            /* TODO: check if charset/size is % lifetime, but maybe check in codec */
98
+            if(pkt->data) num_frames = lifetime;
99
+            else num_frames = c->prev_frame_count;
100
+            for(i = 0; i < num_frames; i++) {
101
+                if(pkt->data) {
102
+                    /* if available, put newest charset chunk into buffer */
103
+                    put_buffer(s->pb, pkt->data + ch_chunksize * i, ch_chunksize);
104
+                }
105
+                else {
106
+                       /* a bit ugly, but is there an alternative to put many zeros? */
107
+                        for(j = 0; j < ch_chunksize; j++) put_byte(s->pb, 0);
108
+                }
109
+                if(c->prev_pkt.data) {
110
+                    /* put frame (screen + colram) from last packet into buffer */
111
+                    put_buffer(s->pb, c->prev_pkt.data + charset_size + frame_size * i, frame_size);
112
+                }
113
+                else {
114
+                       /* a bit ugly, but is there an alternative to put many zeros? */
115
+                        for(j = 0; j < frame_size; j++) put_byte(s->pb, 0);
116
+                }
117
+            }
118
+            /* backup current packet for next turn */
119
+            if(pkt->data) {
120
+                av_new_packet(&c->prev_pkt, pkt->size);
121
+                memcpy(c->prev_pkt.data, pkt->data, pkt->size);
122
+            }
123
+            c->prev_frame_count = frame_count;
124
+            break;
125
+        }
126
+        default:
127
+            /* Write things as is. Nice for self-contained frames from non-multicolor modes or if played
128
+             * directly from ram and not from a streaming device (rrnet/mmc) */
129
+            if(pkt) put_buffer(s->pb, pkt->data, pkt->size);
130
+        break;
131
+    }
132
+
59 133
     put_flush_packet(s->pb);
60 134
     return 0;
61 135
 }
62 136
 
137
+static int a64_write_trailer(struct AVFormatContext *s)
138
+{
139
+    A64MuxerContext *c = s->priv_data;
140
+    AVPacket pkt;
141
+    /* need to flush last packet? */
142
+    if(c->interleaved) a64_write_packet(s, &pkt);
143
+    return 0;
144
+}
145
+
63 146
 AVOutputFormat a64_muxer = {
64 147
     .name = "a64",
65 148
     .long_name = NULL_IF_CONFIG_SMALL("a64 - video for Commodore 64"),
... ...
@@ -69,4 +160,5 @@ AVOutputFormat a64_muxer = {
69 69
     .video_codec = CODEC_ID_A64_MULTI,
70 70
     a64_write_header,
71 71
     a64_write_packet,
72
+    a64_write_trailer
72 73
 };