You've already forked FFmpeg
							
							
				mirror of
				https://github.com/FFmpeg/FFmpeg.git
				synced 2025-10-30 23:18:11 +02:00 
			
		
		
		
	lavc/videotoolboxenc: implement a53cc
Signed-off-by: Rick Kern <kernrj@gmail.com>
This commit is contained in:
		| @@ -32,6 +32,8 @@ | |||||||
| #include "libavutil/pixdesc.h" | #include "libavutil/pixdesc.h" | ||||||
| #include "internal.h" | #include "internal.h" | ||||||
| #include <pthread.h> | #include <pthread.h> | ||||||
|  | #include "h264.h" | ||||||
|  | #include "h264_sei.h" | ||||||
|  |  | ||||||
| #if !CONFIG_VT_BT2020 | #if !CONFIG_VT_BT2020 | ||||||
| # define kCVImageBufferColorPrimaries_ITU_R_2020   CFSTR("ITU_R_2020") | # define kCVImageBufferColorPrimaries_ITU_R_2020   CFSTR("ITU_R_2020") | ||||||
| @@ -55,8 +57,14 @@ typedef enum VTH264Entropy{ | |||||||
|  |  | ||||||
| static const uint8_t start_code[] = { 0, 0, 0, 1 }; | static const uint8_t start_code[] = { 0, 0, 0, 1 }; | ||||||
|  |  | ||||||
|  | typedef struct ExtraSEI { | ||||||
|  |   void *data; | ||||||
|  |   size_t size; | ||||||
|  | } ExtraSEI; | ||||||
|  |  | ||||||
| typedef struct BufNode { | typedef struct BufNode { | ||||||
|     CMSampleBufferRef cm_buffer; |     CMSampleBufferRef cm_buffer; | ||||||
|  |     ExtraSEI *sei; | ||||||
|     struct BufNode* next; |     struct BufNode* next; | ||||||
|     int error; |     int error; | ||||||
| } BufNode; | } BufNode; | ||||||
| @@ -94,6 +102,7 @@ typedef struct VTEncContext { | |||||||
|     bool flushing; |     bool flushing; | ||||||
|     bool has_b_frames; |     bool has_b_frames; | ||||||
|     bool warned_color_range; |     bool warned_color_range; | ||||||
|  |     bool a53_cc; | ||||||
| } VTEncContext; | } VTEncContext; | ||||||
|  |  | ||||||
| static int vtenc_populate_extradata(AVCodecContext   *avctx, | static int vtenc_populate_extradata(AVCodecContext   *avctx, | ||||||
| @@ -136,7 +145,7 @@ static void set_async_error(VTEncContext *vtctx, int err) | |||||||
|     pthread_mutex_unlock(&vtctx->lock); |     pthread_mutex_unlock(&vtctx->lock); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf) | static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf, ExtraSEI **sei) | ||||||
| { | { | ||||||
|     BufNode *info; |     BufNode *info; | ||||||
|  |  | ||||||
| @@ -173,6 +182,12 @@ static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf) | |||||||
|     pthread_mutex_unlock(&vtctx->lock); |     pthread_mutex_unlock(&vtctx->lock); | ||||||
|  |  | ||||||
|     *buf = info->cm_buffer; |     *buf = info->cm_buffer; | ||||||
|  |     if (sei && *buf) { | ||||||
|  |         *sei = info->sei; | ||||||
|  |     } else if (info->sei) { | ||||||
|  |         if (info->sei->data) av_free(info->sei->data); | ||||||
|  |         av_free(info->sei); | ||||||
|  |     } | ||||||
|     av_free(info); |     av_free(info); | ||||||
|  |  | ||||||
|     vtctx->frame_ct_out++; |     vtctx->frame_ct_out++; | ||||||
| @@ -180,7 +195,7 @@ static int vtenc_q_pop(VTEncContext *vtctx, bool wait, CMSampleBufferRef *buf) | |||||||
|     return 0; |     return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void vtenc_q_push(VTEncContext *vtctx, CMSampleBufferRef buffer) | static void vtenc_q_push(VTEncContext *vtctx, CMSampleBufferRef buffer, ExtraSEI *sei) | ||||||
| { | { | ||||||
|     BufNode *info = av_malloc(sizeof(BufNode)); |     BufNode *info = av_malloc(sizeof(BufNode)); | ||||||
|     if (!info) { |     if (!info) { | ||||||
| @@ -190,6 +205,7 @@ static void vtenc_q_push(VTEncContext *vtctx, CMSampleBufferRef buffer) | |||||||
|  |  | ||||||
|     CFRetain(buffer); |     CFRetain(buffer); | ||||||
|     info->cm_buffer = buffer; |     info->cm_buffer = buffer; | ||||||
|  |     info->sei = sei; | ||||||
|     info->next = NULL; |     info->next = NULL; | ||||||
|  |  | ||||||
|     pthread_mutex_lock(&vtctx->lock); |     pthread_mutex_lock(&vtctx->lock); | ||||||
| @@ -420,6 +436,7 @@ static void vtenc_output_callback( | |||||||
| { | { | ||||||
|     AVCodecContext *avctx = ctx; |     AVCodecContext *avctx = ctx; | ||||||
|     VTEncContext   *vtctx = avctx->priv_data; |     VTEncContext   *vtctx = avctx->priv_data; | ||||||
|  |     ExtraSEI *sei = sourceFrameCtx; | ||||||
|  |  | ||||||
|     if (vtctx->async_error) { |     if (vtctx->async_error) { | ||||||
|         if(sample_buffer) CFRelease(sample_buffer); |         if(sample_buffer) CFRelease(sample_buffer); | ||||||
| @@ -440,7 +457,7 @@ static void vtenc_output_callback( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     vtenc_q_push(vtctx, sample_buffer); |     vtenc_q_push(vtctx, sample_buffer, sei); | ||||||
| } | } | ||||||
|  |  | ||||||
| static int get_length_code_size( | static int get_length_code_size( | ||||||
| @@ -1258,7 +1275,8 @@ static int copy_replace_length_codes( | |||||||
| static int vtenc_cm_to_avpacket( | static int vtenc_cm_to_avpacket( | ||||||
|     AVCodecContext    *avctx, |     AVCodecContext    *avctx, | ||||||
|     CMSampleBufferRef sample_buffer, |     CMSampleBufferRef sample_buffer, | ||||||
|     AVPacket          *pkt) |     AVPacket          *pkt, | ||||||
|  |     ExtraSEI          *sei) | ||||||
| { | { | ||||||
|     VTEncContext *vtctx = avctx->priv_data; |     VTEncContext *vtctx = avctx->priv_data; | ||||||
|  |  | ||||||
| @@ -1269,6 +1287,7 @@ static int vtenc_cm_to_avpacket( | |||||||
|     size_t  header_size = 0; |     size_t  header_size = 0; | ||||||
|     size_t  in_buf_size; |     size_t  in_buf_size; | ||||||
|     size_t  out_buf_size; |     size_t  out_buf_size; | ||||||
|  |     size_t  sei_nalu_size = 0; | ||||||
|     int64_t dts_delta; |     int64_t dts_delta; | ||||||
|     int64_t time_base_num; |     int64_t time_base_num; | ||||||
|     int nalu_count; |     int nalu_count; | ||||||
| @@ -1298,9 +1317,14 @@ static int vtenc_cm_to_avpacket( | |||||||
|     if(status) |     if(status) | ||||||
|         return status; |         return status; | ||||||
|  |  | ||||||
|  |     if (sei) { | ||||||
|  |         sei_nalu_size = sizeof(start_code) + 3 + sei->size + 1; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     in_buf_size = CMSampleBufferGetTotalSampleSize(sample_buffer); |     in_buf_size = CMSampleBufferGetTotalSampleSize(sample_buffer); | ||||||
|     out_buf_size = header_size + |     out_buf_size = header_size + | ||||||
|                    in_buf_size + |                    in_buf_size + | ||||||
|  |                    sei_nalu_size + | ||||||
|                    nalu_count * ((int)sizeof(start_code) - (int)length_code_size); |                    nalu_count * ((int)sizeof(start_code) - (int)length_code_size); | ||||||
|  |  | ||||||
|     status = ff_alloc_packet2(avctx, pkt, out_buf_size, out_buf_size); |     status = ff_alloc_packet2(avctx, pkt, out_buf_size, out_buf_size); | ||||||
| @@ -1317,7 +1341,7 @@ static int vtenc_cm_to_avpacket( | |||||||
|         length_code_size, |         length_code_size, | ||||||
|         sample_buffer, |         sample_buffer, | ||||||
|         pkt->data + header_size, |         pkt->data + header_size, | ||||||
|         pkt->size - header_size |         pkt->size - header_size - sei_nalu_size | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
|     if (status) { |     if (status) { | ||||||
| @@ -1325,6 +1349,19 @@ static int vtenc_cm_to_avpacket( | |||||||
|         return status; |         return status; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (sei_nalu_size > 0) { | ||||||
|  |         uint8_t *sei_nalu = pkt->data + pkt->size - sei_nalu_size; | ||||||
|  |         memcpy(sei_nalu, start_code, sizeof(start_code)); | ||||||
|  |         sei_nalu += sizeof(start_code); | ||||||
|  |         sei_nalu[0] = H264_NAL_SEI; | ||||||
|  |         sei_nalu[1] = SEI_TYPE_USER_DATA_REGISTERED; | ||||||
|  |         sei_nalu[2] = sei->size; | ||||||
|  |         sei_nalu += 3; | ||||||
|  |         memcpy(sei_nalu, sei->data, sei->size); | ||||||
|  |         sei_nalu += sei->size; | ||||||
|  |         sei_nalu[0] = 1; // RBSP | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (is_key_frame) { |     if (is_key_frame) { | ||||||
|         pkt->flags |= AV_PKT_FLAG_KEY; |         pkt->flags |= AV_PKT_FLAG_KEY; | ||||||
|     } |     } | ||||||
| @@ -1707,6 +1744,7 @@ static int vtenc_send_frame(AVCodecContext *avctx, | |||||||
|     CMTime time; |     CMTime time; | ||||||
|     CFDictionaryRef frame_dict; |     CFDictionaryRef frame_dict; | ||||||
|     CVPixelBufferRef cv_img = NULL; |     CVPixelBufferRef cv_img = NULL; | ||||||
|  |     ExtraSEI *sei = NULL; | ||||||
|     int status = create_cv_pixel_buffer(avctx, frame, &cv_img); |     int status = create_cv_pixel_buffer(avctx, frame, &cv_img); | ||||||
|  |  | ||||||
|     if (status) return status; |     if (status) return status; | ||||||
| @@ -1717,6 +1755,20 @@ static int vtenc_send_frame(AVCodecContext *avctx, | |||||||
|         return status; |         return status; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     if (vtctx->a53_cc) { | ||||||
|  |         sei = av_mallocz(sizeof(*sei)); | ||||||
|  |         if (!sei) { | ||||||
|  |             av_log(avctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); | ||||||
|  |         } else { | ||||||
|  |             int ret = ff_alloc_a53_sei(frame, 0, &sei->data, &sei->size); | ||||||
|  |             if (ret < 0) { | ||||||
|  |                 av_log(avctx, AV_LOG_ERROR, "Not enough memory for closed captions, skipping\n"); | ||||||
|  |                 av_free(sei); | ||||||
|  |                 sei = NULL; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     time = CMTimeMake(frame->pts * avctx->time_base.num, avctx->time_base.den); |     time = CMTimeMake(frame->pts * avctx->time_base.num, avctx->time_base.den); | ||||||
|     status = VTCompressionSessionEncodeFrame( |     status = VTCompressionSessionEncodeFrame( | ||||||
|         vtctx->session, |         vtctx->session, | ||||||
| @@ -1724,7 +1776,7 @@ static int vtenc_send_frame(AVCodecContext *avctx, | |||||||
|         time, |         time, | ||||||
|         kCMTimeInvalid, |         kCMTimeInvalid, | ||||||
|         frame_dict, |         frame_dict, | ||||||
|         NULL, |         sei, | ||||||
|         NULL |         NULL | ||||||
|     ); |     ); | ||||||
|  |  | ||||||
| @@ -1749,6 +1801,7 @@ static av_cold int vtenc_frame( | |||||||
|     bool get_frame; |     bool get_frame; | ||||||
|     int status; |     int status; | ||||||
|     CMSampleBufferRef buf = NULL; |     CMSampleBufferRef buf = NULL; | ||||||
|  |     ExtraSEI *sei = NULL; | ||||||
|  |  | ||||||
|     if (frame) { |     if (frame) { | ||||||
|         status = vtenc_send_frame(avctx, vtctx, frame); |         status = vtenc_send_frame(avctx, vtctx, frame); | ||||||
| @@ -1785,11 +1838,15 @@ static av_cold int vtenc_frame( | |||||||
|         goto end_nopkt; |         goto end_nopkt; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     status = vtenc_q_pop(vtctx, !frame, &buf); |     status = vtenc_q_pop(vtctx, !frame, &buf, &sei); | ||||||
|     if (status) goto end_nopkt; |     if (status) goto end_nopkt; | ||||||
|     if (!buf)   goto end_nopkt; |     if (!buf)   goto end_nopkt; | ||||||
|  |  | ||||||
|     status = vtenc_cm_to_avpacket(avctx, buf, pkt); |     status = vtenc_cm_to_avpacket(avctx, buf, pkt, sei); | ||||||
|  |     if (sei) { | ||||||
|  |         if (sei->data) av_free(sei->data); | ||||||
|  |         av_free(sei); | ||||||
|  |     } | ||||||
|     CFRelease(buf); |     CFRelease(buf); | ||||||
|     if (status) goto end_nopkt; |     if (status) goto end_nopkt; | ||||||
|  |  | ||||||
| @@ -1878,7 +1935,7 @@ static int vtenc_populate_extradata(AVCodecContext   *avctx, | |||||||
|     if (status) |     if (status) | ||||||
|         goto pe_cleanup; |         goto pe_cleanup; | ||||||
|  |  | ||||||
|     status = vtenc_q_pop(vtctx, 0, &buf); |     status = vtenc_q_pop(vtctx, 0, &buf, NULL); | ||||||
|     if (status) { |     if (status) { | ||||||
|         av_log(avctx, AV_LOG_ERROR, "popping: %d\n", status); |         av_log(avctx, AV_LOG_ERROR, "popping: %d\n", status); | ||||||
|         goto pe_cleanup; |         goto pe_cleanup; | ||||||
| @@ -1976,6 +2033,8 @@ static const AVOption options[] = { | |||||||
|     { "frames_after", "Other frames will come after the frames in this session. This helps smooth concatenation issues.", |     { "frames_after", "Other frames will come after the frames in this session. This helps smooth concatenation issues.", | ||||||
|         OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, |         OFFSET(frames_after), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, VE }, | ||||||
|  |  | ||||||
|  |     { "a53cc", "Use A53 Closed Captions (if available)", OFFSET(a53_cc), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, VE }, | ||||||
|  |  | ||||||
|     { NULL }, |     { NULL }, | ||||||
| }; | }; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user