OSDN Git Service

Fix mkv A/V sync problem created by R1542.
[handbrake-jp/handbrake-jp-git.git] / libhb / muxcommon.c
1 /* $Id: muxcommon.c,v 1.23 2005/03/30 17:27:19 titer Exp $
2
3    This file is part of the HandBrake source code.
4    Homepage: <http://handbrake.fr/>.
5    It may be used under the terms of the GNU General Public License. */
6
7 #include "hb.h"
8
9 struct hb_mux_object_s
10 {
11     HB_MUX_COMMON;
12 };
13
14 typedef struct
15 {
16     hb_job_t * job;
17     uint64_t   pts;
18
19 } hb_mux_t;
20
21 typedef struct
22 {
23     hb_fifo_t     * fifo;
24     hb_mux_data_t * mux_data;
25     uint64_t        frames;
26     uint64_t        bytes;
27
28 } hb_track_t;
29
30 static hb_track_t * GetTrack( hb_list_t * list, hb_job_t *job )
31 {
32     hb_buffer_t * buf;
33     hb_track_t  * track = NULL, * track2;
34     int64_t       pts = 0;
35     int           i;
36
37     for( i = 0; i < hb_list_count( list ); i++ )
38     {
39         track2 = hb_list_item( list, i );
40         buf    = hb_fifo_see( track2->fifo );
41         if( !buf )
42         {
43             // XXX libmkv uses a very simple minded muxer that will fail if the
44             // audio & video are out of sync.  To keep them in sync we require
45             // that *all* fifos have a buffer then we take the oldest.
46             // Unfortunately this means we can hang in a deadlock with the
47             // reader process filling the fifos. With the current libmkv
48             // there's no way to avoid occasional deadlocks & we can only
49             // suggest that users evolve to using mp4s.
50             if ( job->mux == HB_MUX_MKV )
51             {
52                 return NULL;
53             }
54
55             // To make sure we don't camp on one fifo & prevent the others
56             // from making progress we take the earliest data of all the
57             // data that's currently available but we don't care if some
58             // fifos don't have data.
59             continue;
60         }
61         if( !track || buf->start < pts )
62         {
63             track = track2;
64             pts   = buf->start;
65         }
66     }
67     return track;
68 }
69
70 static void MuxerFunc( void * _mux )
71 {
72     hb_mux_t    * mux = _mux;
73     hb_job_t    * job = mux->job;
74     hb_title_t  * title = job->title;
75     hb_audio_t  * audio;
76     hb_list_t   * list;
77     hb_buffer_t * buf;
78     hb_track_t  * track;
79     int           i;
80
81     hb_mux_object_t * m = NULL;
82
83     /* Get a real muxer */
84     if( job->pass == 0 || job->pass == 2)
85     {
86         switch( job->mux )
87         {
88             case HB_MUX_MP4:
89             case HB_MUX_PSP:
90                         case HB_MUX_IPOD:
91                 m = hb_mux_mp4_init( job );
92                 break;
93             case HB_MUX_AVI:
94                 m = hb_mux_avi_init( job );
95                 break;
96             case HB_MUX_OGM:
97                 m = hb_mux_ogm_init( job );
98                 break;
99             case HB_MUX_MKV:
100                 m = hb_mux_mkv_init( job );
101         }
102     }
103
104     /* Wait for one buffer for each track */
105     while( !*job->die && !job->done )
106     {
107         int ready;
108
109         ready = 1;
110         if( !hb_fifo_size( job->fifo_mpeg4 ) )
111         {
112             ready = 0;
113         }
114         for( i = 0; i < hb_list_count( title->list_audio ); i++ )
115         {
116             audio = hb_list_item( title->list_audio, i );
117             if( !hb_fifo_size( audio->priv.fifo_out ) )
118             {
119                 ready = 0;
120                 break;
121             }
122         }
123
124         if( ready )
125         {
126             break;
127         }
128
129         hb_snooze( 50 );
130     }
131
132     /* Create file, write headers */
133     if( job->pass == 0 || job->pass == 2 )
134     {
135         m->init( m );
136     }
137
138     /* Build list of fifos we're interested in */
139     list = hb_list_init();
140
141     track           = calloc( sizeof( hb_track_t ), 1 );
142     track->fifo     = job->fifo_mpeg4;
143     track->mux_data = job->mux_data;
144     hb_list_add( list, track );
145
146     for( i = 0; i < hb_list_count( title->list_audio ); i++ )
147     {
148         audio           = hb_list_item( title->list_audio, i );
149         track           = calloc( sizeof( hb_track_t ), 1 );
150         track->fifo     = audio->priv.fifo_out;
151         track->mux_data = audio->priv.mux_data;
152         hb_list_add( list, track );
153     }
154
155         int thread_sleep_interval = 50;
156         while( !*job->die && !job->done )
157     {
158         if( !( track = GetTrack( list, job ) ) )
159         {
160             hb_snooze( thread_sleep_interval );
161             continue;
162         }
163
164         buf = hb_fifo_get( track->fifo );
165         if( job->pass == 0 || job->pass == 2 )
166         {
167             m->mux( m, track->mux_data, buf );
168             track->frames += 1;
169             track->bytes  += buf->size;
170             mux->pts = buf->stop;
171         }
172         hb_buffer_close( &buf );
173     }
174
175     if( job->pass == 0 || job->pass == 2 )
176     {
177         struct stat sb;
178         uint64_t bytes_total, frames_total;
179
180 #define p state.param.muxing
181         /* Update the UI */
182         hb_state_t state;
183         state.state   = HB_STATE_MUXING;
184                 p.progress = 0;
185         hb_set_state( job->h, &state );
186 #undef p
187         m->end( m );
188
189         if( !stat( job->file, &sb ) )
190         {
191             hb_log( "mux: file size, %lld bytes", (uint64_t) sb.st_size );
192
193             bytes_total  = 0;
194             frames_total = 0;
195             for( i = 0; i < hb_list_count( list ); i++ )
196             {
197                 track = hb_list_item( list, i );
198                 hb_log( "mux: track %d, %lld bytes, %.2f kbps",
199                         i, track->bytes,
200                         90000.0 * track->bytes / mux->pts / 125 );
201                 if( !i && ( job->vquality < 0.0 || job->vquality > 1.0 ) )
202                 {
203                     /* Video */
204                     hb_log( "mux: video bitrate error, %+lld bytes",
205                             track->bytes - mux->pts * job->vbitrate *
206                             125 / 90000 );
207                 }
208                 bytes_total  += track->bytes;
209                 frames_total += track->frames;
210             }
211
212             if( bytes_total && frames_total )
213             {
214                 hb_log( "mux: overhead, %.2f bytes per frame",
215                         (float) ( sb.st_size - bytes_total ) /
216                         frames_total );
217             }
218         }
219     }
220
221     free( m );
222
223     for( i = 0; i < hb_list_count( list ); i++ )
224     {
225         track = hb_list_item( list, i );
226         if( track->mux_data )
227         {
228             free( track->mux_data );
229         }
230         free( track );
231     }
232     hb_list_close( &list );
233
234     free( mux );
235 }
236
237 hb_thread_t * hb_muxer_init( hb_job_t * job )
238 {
239     hb_mux_t * mux = calloc( sizeof( hb_mux_t ), 1 );
240     mux->job = job;
241     return hb_thread_init( "muxer", MuxerFunc, mux,
242                            HB_NORMAL_PRIORITY );
243 }