+// send cc_buf to the CC decoder(s)
+static void cc_send_to_decoder( hb_libmpeg2_t *m, hb_buffer_t *cc_buf )
+{
+ hb_subtitle_t *subtitle;
+
+ // if there's more than one decoder for the captions send a copy
+ // of the buffer to all but the last. Then send the buffer to
+ // the last one (usually there's just one decoder so the 'while' is skipped).
+ int i = 0, n = hb_list_count( m->list_subtitle );
+ while ( --n > 0 )
+ {
+ // make a copy of the buf then forward it to the decoder
+ hb_buffer_t *cpy = hb_buffer_init( cc_buf->size );
+ hb_buffer_copy_settings( cpy, cc_buf );
+ memcpy( cpy->data, cc_buf->data, cc_buf->size );
+
+ subtitle = hb_list_item( m->list_subtitle, i++ );
+ hb_fifo_push( subtitle->fifo_in, cpy );
+ }
+ subtitle = hb_list_item( m->list_subtitle, i );
+ hb_fifo_push( subtitle->fifo_in, cc_buf );
+}
+
+static void hb_mpeg2_cc( hb_libmpeg2_t *m, const uint8_t *cc_block )
+{
+ uint8_t cc_hdr = *cc_block;
+
+ if ( ( cc_hdr & 0x4 ) == 0 )
+ // not valid - ignore
+ return;
+
+ switch (cc_hdr & 3)
+ {
+ case 0:
+ // CC1 stream
+ if ( ( cc_block[1] & 0x7f ) == 0 && ( cc_block[2] & 0x7f ) == 0 )
+ // just padding - ignore
+ return;
+
+ if ( m->last_cc1_buf )
+ {
+ // new data from the same time as last call - add to buffer
+ int len = m->last_cc1_buf->size;
+ hb_buffer_realloc( m->last_cc1_buf, len + 2 );
+ memcpy( m->last_cc1_buf->data + len, cc_block+1, 2 );
+ m->last_cc1_buf->size = len + 2;
+ return;
+ }
+
+ // allocate a new buffer and copy the caption data into it.
+ // (we don't send it yet because we don't know what timestamp to use).
+ hb_buffer_t *cc_buf = hb_buffer_init( 2 );
+ if( !cc_buf )
+ return;
+
+ memcpy( cc_buf->data, cc_block+1, 2 );
+ m->last_cc1_buf = cc_buf;
+ break;
+#ifdef notyet
+ case 1:
+ // CC2 stream
+ //process608( cc_block+1, 2, &m->cc608 );
+ break;
+ case 2: //EIA-708
+ // DTVCC packet data
+ // Fall through
+ case 3: //EIA-708
+ {
+ uint8_t temp[4];
+ temp[0]=cc_valid;
+ temp[1]=cc_type;
+ temp[2]=cc_block[1];
+ temp[3]=cc_block[2];
+ do_708 ((const unsigned char *) temp, 4);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+static inline int have_captions( const uint8_t *user_data, uint32_t len )
+{
+ return len >= 6 &&
+ ( ( user_data[0] == 0x43 && user_data[1] == 0x43 ) ||
+ ( user_data[0] == 0x47 && user_data[1] == 0x41 &&
+ user_data[2] == 0x39 && user_data[3] == 0x34 &&
+ user_data[4] == 3 && (user_data[5] & 0x40) ) );
+}
+
+static void do_one_dvd_cc( hb_libmpeg2_t *m, const uint8_t *header, int field1 )
+{
+ uint8_t data[3];
+
+ data[0] = ( header[0] == 0xff && 0 == field1 )? 0x04 : 0x05;
+ data[1] = header[1];
+ data[2] = header[2];
+ hb_mpeg2_cc( m, data );
+
+ data[0] = ( header[3] == 0xff && 1 == field1 )? 0x04 : 0x05;
+ data[1] = header[4];
+ data[2] = header[5];
+ hb_mpeg2_cc( m, data );
+}
+
+// extract all the captions in the current frame and send them downstream
+// to the decoder.
+//
+// (this routine should only be called if there are captions in the current
+// frame. I.e., only if a call to 'have_captions' returns true.)
+static void extract_mpeg2_captions( hb_libmpeg2_t *m )
+{
+ const uint8_t *user_data = m->info->user_data;
+ int dvd_captions = user_data[0] == 0x43;
+ int capcount, field1packet = 0;
+ const uint8_t *header = &user_data[4];
+ if ( !dvd_captions )
+ {
+ // ATSC encapsulated captions - header starts one byte later
+ // and has an extra unused byte following it.
+ capcount = header[1] & 0x1f;
+ header += 3;
+ }
+ else
+ {
+ // DVD captions
+ if ( ( header[0] & 0x80 ) == 0x00 )
+ field1packet=1; /* expect Field 1 second */
+ capcount=(header[0] & 0x1e) / 2;
+ header++;
+ }
+
+ int i;
+ for( i=0; i<capcount; i++ )
+ {
+ if ( !dvd_captions )
+ {
+ hb_mpeg2_cc( m, header );
+ header += 3;
+ }
+ else
+ {
+ do_one_dvd_cc( m, header, field1packet );
+ header += 6;
+ }
+ }
+ if ( dvd_captions )
+ {
+ // Deal with extra closed captions some DVDs have.
+ while( header[0]==0xfe || header[0]==0xff )
+ {
+ do_one_dvd_cc( m, header, field1packet );
+ header += 6;
+ }
+ }
+}
+
+static void next_tag( hb_libmpeg2_t *m, hb_buffer_t *buf_es )
+{
+ m->cur_tag = ( m->cur_tag + 1 ) & (NTAGS-1);
+ if ( m->tags[m->cur_tag].start >= 0 || m->tags[m->cur_tag].cc_buf )
+ {
+ if ( m->tags[m->cur_tag].start < 0 ||
+ ( m->got_iframe && m->tags[m->cur_tag].start >= m->first_pts ) )
+ hb_log("mpeg2 tag botch: pts %lld, tag pts %lld buf 0x%p",
+ buf_es->start, m->tags[m->cur_tag].start, m->tags[m->cur_tag].cc_buf);
+ if ( m->tags[m->cur_tag].cc_buf )
+ hb_buffer_close( &m->tags[m->cur_tag].cc_buf );
+ }
+ m->tags[m->cur_tag].start = buf_es->start;
+ mpeg2_tag_picture( m->libmpeg2, m->cur_tag, 0 );
+}
+