bool MP4Encoder::Write264Metadata(MP4FileHandle hMp4File,LPMP4ENC_Metadata lpMetadata) { m_videoId = MP4AddH264VideoTrack (hMp4File, m_nTimeScale, m_nTimeScale / m_nFrameRate, m_nWidth, // width m_nHeight,// height lpMetadata->Sps[1], // sps[1] AVCProfileIndication lpMetadata->Sps[2], // sps[2] profile_compat lpMetadata->Sps[3], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if (m_videoId == MP4_INVALID_TRACK_ID) { printf("add video track failed.\n"); return false; } MP4SetVideoProfileLevel(hMp4File, 0x01); // Simple Profile @ Level 3 // write sps MP4AddH264SequenceParameterSet(hMp4File,m_videoId,lpMetadata->Sps,lpMetadata->nSpsLen); // write pps MP4AddH264PictureParameterSet(hMp4File,m_videoId,lpMetadata->Pps,lpMetadata->nPpsLen); return true; }
int Mp4Encoder::WriteH264Data(u_char * pData, int size) { int type = pData[4] & 0x1f; if (type == 0x07) { // sps // 添加h264 track if (m_videoId == MP4_INVALID_TRACK_ID) { m_videoId = MP4AddH264VideoTrack(m_hMp4File, m_nVTimeScale, m_nVTimeScale / m_nFrameRate, m_nWidth, // width m_nHeight, // height pData[5], // sps[1] AVCProfileIndication pData[6], // sps[2] profile_compat pData[7], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if (m_videoId == MP4_INVALID_TRACK_ID) { printf("add video track failed.\n"); return 0; } MP4SetVideoProfileLevel(m_hMp4File, 0x7F); // Simple Profile @ Level } MP4AddH264SequenceParameterSet(m_hMp4File, m_videoId, pData + 4, size - 4); } else if (type == 0x08) { // pps MP4AddH264PictureParameterSet(m_hMp4File, m_videoId, pData + 4, size - 4); } else { // MP4 Nalu前四个字节表示Nalu长度(no 00 00 00 01) pData[0] = (size - 4) >> 24; pData[1] = (size - 4) >> 16; pData[2] = (size - 4) >> 8; pData[3] = (size - 4) & 0xff; if (!MP4WriteSample (m_hMp4File, m_videoId, pData, size, MP4_INVALID_DURATION, 0, 1)) { return 0; } } return 0; }
static switch_status_t do_write_video(switch_file_handle_t *handle, switch_frame_t *frame) { uint32_t datalen = frame->datalen; switch_status_t status = SWITCH_STATUS_SUCCESS; int is_iframe = 0; uint32_t size; uint8_t *hdr = NULL; uint8_t fragment_type; uint8_t nal_type; uint8_t start_bit; mp4_file_context_t *context = handle->private_info; hdr = (uint8_t *)frame->data; fragment_type = hdr[0] & 0x1f; nal_type = hdr[1] & 0x1f; start_bit = hdr[1] & 0x80; is_iframe = (((fragment_type == 28 || fragment_type == 29) && nal_type == 5 && start_bit == 128) || fragment_type == 5 || fragment_type ==7 || fragment_type ==8) ? 1 : 0; // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%02x %02x %02x | len:%d m:%d st:%d i:%d\n", hdr[0], hdr[1], hdr[2], datalen, frame->m, start_bit, is_iframe); size = htonl(datalen); switch_buffer_write(context->buf, &size, 4); switch_buffer_write(context->buf, hdr, datalen); switch_mutex_lock(context->mutex); if (fragment_type == 7 && !context->sps_set) { //sps context->sps_set = 1; init_video_track(context->fd, &context->video, frame); if (context->video == MP4_INVALID_TRACK_ID) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error add video track!\n"); switch_goto_status(SWITCH_STATUS_FALSE, end); } } else if (fragment_type == 8 && context->sps_set && !context->pps_set) { //pps MP4AddH264PictureParameterSet(context->fd, context->video, hdr, datalen); context->pps_set = 1; } if (nal_type == 7 || nal_type == 8 || frame->m == 0) { } else if (context->sps_set && context->pps_set) { uint32_t used = switch_buffer_inuse(context->buf); const void *data; int duration = 0; if (!context->timer.interval) { switch_core_timer_init(&context->timer, "soft", 1, 1, context->pool); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "init timer\n"); } else { switch_core_timer_sync(&context->timer); } duration = context->timer.samplecount - context->last_pts; // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "samplecount: %u, duration: %u\n", context->timer.samplecount, duration); switch_buffer_peek_zerocopy(context->buf, &data); if (context->last_pts == 0) { // first img, write at the very beginning so we don't see blank screen duration /= 2; MP4WriteSample(context->fd, context->video, data, used, duration, 0, is_iframe); if (duration > context->offset) { duration -= context->offset; } else { duration = 0; } } context->last_pts = context->timer.samplecount; // switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "samplecount: %u, duration: %u\n", context->timer.samplecount, duration); if (duration) { MP4WriteSample(context->fd, context->video, data, used, duration, 0, is_iframe); } switch_buffer_zero(context->buf); } end: switch_mutex_unlock(context->mutex); return status; }
static void record_video_thread(switch_core_session_t *session, void *obj) { struct record_helper *eh = obj; switch_channel_t *channel = switch_core_session_get_channel(session); switch_status_t status; switch_frame_t *read_frame; uint bytes; MP4FileHandle mp4; MP4TrackId video; unsigned char buf[40960]; int len = 0; uint8_t iframe = 0; uint32_t *size = (uint32_t *)buf; uint8_t *hdr = NULL; uint8_t fragment_type; uint8_t nal_type; uint8_t start_bit; uint8_t *sps = NULL; // uint8_t *pps = NULL; int sps_set = 0; int pps_set = 0; eh->up = 1; mp4 = eh->fd; /* Tell the channel to request a fresh vid frame */ switch_core_session_request_video_refresh(session); len = 0; while (switch_channel_ready(channel) && eh->up) { status = switch_core_session_read_video_frame(session, &read_frame, SWITCH_IO_FLAG_NONE, 0); if (!SWITCH_READ_ACCEPTABLE(status)) { break; } if (switch_test_flag(read_frame, SFF_CNG)) { continue; } bytes = read_frame->datalen; if (bytes > 2000) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "xxxxxxxx buffer overflow\n"); continue; } hdr = read_frame->data; fragment_type = hdr[0] & 0x1f; nal_type = hdr[1] & 0x1f; start_bit = hdr[1] & 0x80; iframe = (((fragment_type == 28 || fragment_type == 29) && nal_type == 5 && start_bit == 128) || fragment_type == 5 || fragment_type ==7 || fragment_type ==8) ? 1 : 0; #if 0 switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "%02x %02x %02x | len:%d m:%d st:%d i:%d\n", hdr[0], hdr[1], hdr[2], bytes, read_frame->m, start_bit, iframe); #endif // echo back switch_core_session_write_video_frame(session, read_frame, SWITCH_IO_FLAG_NONE, 0); if (fragment_type == 7 && !sps_set) { //sps sps = malloc(bytes); memcpy(sps, read_frame->data, bytes); sps_set = 1; switch_mutex_lock(eh->mutex); init_video_track(mp4, &video, read_frame); if (video == MP4_INVALID_TRACK_ID) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error add video track!\n"); switch_mutex_unlock(eh->mutex); goto end; } switch_mutex_unlock(eh->mutex); continue; } else if (fragment_type == 8 && !pps_set) { //pps switch_mutex_lock(eh->mutex); MP4AddH264PictureParameterSet(mp4, video, read_frame->data, bytes); switch_mutex_unlock(eh->mutex); pps_set = 1; // continue; } if ((!sps_set) && (!pps_set)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Waiting for SPS/PPS\n"); // continue; } len += 4 + read_frame->datalen; if (len > 40960) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "buffer overflow!!!! %d\n", len); len = 0; size = (uint32_t *)buf; continue; } *size = htonl(read_frame->datalen); memcpy(size + 1, read_frame->data, read_frame->datalen); size = (uint32_t *)((uint8_t *)size + 4 + read_frame->datalen); if (read_frame->m) { int duration = 0; switch_mutex_lock(eh->mutex); if (!eh->timer.interval) { switch_core_timer_init(&eh->timer, "soft", 1, 1, switch_core_session_get_pool(session)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "init timer\n"); } else { switch_core_timer_sync(&eh->timer); } switch_mutex_unlock(eh->mutex); if (eh->last_pts) { duration = eh->timer.samplecount - eh->last_pts; } eh->last_pts = eh->timer.samplecount; switch_mutex_lock(eh->mutex); MP4WriteSample(mp4, video, buf, len, duration, 0, iframe); switch_mutex_unlock(eh->mutex); len = 0; size = (uint32_t *)buf; } } end: eh->up = 0; return; }
int MP4Encoder::WriteH264Data(MP4FileHandle hMp4File,const unsigned char* pData,int size) { if(hMp4File == NULL) { return -1; } if(pData == NULL) { return -1; } MP4ENC_NaluUnit nalu; int pos = 0, len = 0; while (len = ReadOneNaluFromBuf(pData,size,pos,nalu)) { if(nalu.type == 0x07) // sps { // 添加h264 track m_videoId = MP4AddH264VideoTrack (hMp4File, m_nTimeScale, m_nTimeScale / m_nFrameRate, m_nWidth, // width m_nHeight, // height nalu.data[1], // sps[1] AVCProfileIndication nalu.data[2], // sps[2] profile_compat nalu.data[3], // sps[3] AVCLevelIndication 3); // 4 bytes length before each NAL unit if (m_videoId == MP4_INVALID_TRACK_ID) { printf("add video track failed.\n"); return 0; } MP4SetVideoProfileLevel(hMp4File, 1); // Simple Profile @ Level 3 MP4AddH264SequenceParameterSet(hMp4File,m_videoId,nalu.data,nalu.size); } else if(nalu.type == 0x08) // pps { MP4AddH264PictureParameterSet(hMp4File,m_videoId,nalu.data,nalu.size); } else { int datalen = nalu.size+4; unsigned char *data = new unsigned char[datalen]; // MP4 Nalu前四个字节表示Nalu长度 data[0] = nalu.size>>24; data[1] = nalu.size>>16; data[2] = nalu.size>>8; data[3] = nalu.size&0xff; memcpy(data+4,nalu.data,nalu.size); if(!MP4WriteSample(hMp4File, m_videoId, data, datalen,MP4_INVALID_DURATION, 0, 1)) { return 0; } delete[] data; } pos += len; } return pos; }