|
- #include "flv-muxer.h"
- #include "flv-proto.h"
- #include "flv-header.h"
- #include "amf0.h"
- #include <stdio.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <assert.h>
- #include <stdint.h>
- #include <string.h>
- #include "aom-av1.h"
- #include "mpeg4-aac.h"
- #include "mpeg4-avc.h"
- #include "mpeg4-hevc.h"
- #include "mp3-header.h"
- #include "opus-head.h"
-
- #define FLV_MUXER "ireader/media-server"
-
- struct flv_muxer_t
- {
- flv_muxer_handler handler;
- void* param;
-
- uint8_t audio_sequence_header;
- uint8_t video_sequence_header;
-
- union
- {
- struct mpeg4_aac_t aac;
- struct opus_head_t opus;
- } a;
-
- union
- {
- struct aom_av1_t av1;
- struct mpeg4_avc_t avc;
- struct mpeg4_hevc_t hevc;
- } v;
- int vcl; // 0-non vcl, 1-idr, 2-p/b
- int update; // avc/hevc sequence header update
-
- uint8_t* ptr;
- size_t bytes;
- size_t capacity;
- };
-
- struct flv_muxer_t* flv_muxer_create(flv_muxer_handler handler, void* param)
- {
- struct flv_muxer_t* flv;
- flv = (struct flv_muxer_t*)calloc(1, sizeof(struct flv_muxer_t));
- if (NULL == flv)
- return NULL;
-
- flv_muxer_reset(flv);
- flv->handler = handler;
- flv->param = param;
- return flv;
- }
-
- void flv_muxer_destroy(struct flv_muxer_t* flv)
- {
- if (flv->ptr)
- {
- assert(flv->capacity > 0);
- free(flv->ptr);
- flv->ptr = NULL;
- }
-
- free(flv);
- }
-
- int flv_muxer_reset(struct flv_muxer_t* flv)
- {
- memset(&flv->v, 0, sizeof(flv->v));
- flv->audio_sequence_header = 0;
- flv->video_sequence_header = 0;
- return 0;
- }
-
- static int flv_muxer_alloc(struct flv_muxer_t* flv, size_t bytes)
- {
- void* p;
- p = realloc(flv->ptr, bytes);
- if (!p)
- return -ENOMEM;
-
- flv->ptr = (uint8_t*)p;
- flv->capacity = bytes;
- return 0;
- }
-
- int flv_muxer_g711a(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- struct flv_audio_tag_header_t audio;
- (void)pts;
-
- if (flv->capacity < bytes + 1)
- {
- if (0 != flv_muxer_alloc(flv, bytes + 4))
- return -ENOMEM;
- }
-
- audio.bits = FLV_SOUND_BIT_16; // 16-bit samples
- audio.channels = FLV_SOUND_CHANNEL_MONO;
- audio.rate = 0;
- audio.codecid = FLV_AUDIO_G711A;
- audio.avpacket = FLV_AVPACKET;
- flv_audio_tag_header_write(&audio, flv->ptr, 1);
- memcpy(flv->ptr + 1, data, bytes);
- return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + 1, dts);
- }
-
- int flv_muxer_g711u(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- struct flv_audio_tag_header_t audio;
- (void)pts;
-
- if (flv->capacity < bytes + 1)
- {
- if (0 != flv_muxer_alloc(flv, bytes + 4))
- return -ENOMEM;
- }
-
- audio.bits = FLV_SOUND_BIT_16; // 16-bit samples
- audio.channels = FLV_SOUND_CHANNEL_MONO;
- audio.rate = 0;
- audio.codecid = FLV_AUDIO_G711U;
- audio.avpacket = FLV_AVPACKET;
- flv_audio_tag_header_write(&audio, flv->ptr, 1);
- memcpy(flv->ptr + 1, data, bytes);
- return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + 1, dts);
- }
-
- int flv_muxer_mp3(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- struct mp3_header_t mp3;
- struct flv_audio_tag_header_t audio;
- (void)pts;
-
- if (0 == mp3_header_load(&mp3, data, (int)bytes))
- {
- return -EINVAL;
- }
- else
- {
- audio.channels = 3 == mp3.mode ? FLV_SOUND_CHANNEL_MONO : FLV_SOUND_CHANNEL_STEREO;
- switch (mp3_get_frequency(&mp3))
- {
- case 5500: audio.rate = FLV_SOUND_RATE_5500; break;
- case 11025: audio.rate = FLV_SOUND_RATE_11025; break;
- case 22050: audio.rate = FLV_SOUND_RATE_22050; break;
- case 44100: audio.rate = FLV_SOUND_RATE_44100; break;
- default: audio.rate = FLV_SOUND_RATE_44100;
- }
- }
-
- if (flv->capacity < bytes + 1)
- {
- if (0 != flv_muxer_alloc(flv, bytes + 4))
- return -ENOMEM;
- }
-
- audio.bits = FLV_SOUND_BIT_16; // 16-bit samples
- audio.codecid = FLV_AUDIO_MP3;
- audio.avpacket = FLV_AVPACKET;
- flv_audio_tag_header_write(&audio, flv->ptr, 1);
- memcpy(flv->ptr + 1, data, bytes); // MP3
- return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + 1, dts);
- }
-
- int flv_muxer_aac(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- int r, n, m;
- struct flv_audio_tag_header_t audio;
- (void)pts;
-
- if (flv->capacity < bytes + 2/*AudioTagHeader*/ + 2/*AudioSpecificConfig*/)
- {
- if (0 != flv_muxer_alloc(flv, bytes + 4))
- return -ENOMEM;
- }
-
- /* ADTS */
- n = mpeg4_aac_adts_load(data, bytes, &flv->a.aac);
- if (n <= 0)
- return -1; // invalid data
-
- audio.codecid = FLV_AUDIO_AAC;
- audio.rate = FLV_SOUND_RATE_44100; // 44k-SoundRate
- audio.bits = FLV_SOUND_BIT_16; // 16-bit samples
- audio.channels = FLV_SOUND_CHANNEL_STEREO; // Stereo sound
- if (0 == flv->audio_sequence_header)
- {
- flv->audio_sequence_header = 1; // once only
- audio.avpacket = FLV_SEQUENCE_HEADER;
-
- // AudioSpecificConfig(AAC sequence header)
- flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
- m = mpeg4_aac_audio_specific_config_save(&flv->a.aac, flv->ptr + 2, flv->capacity - 2);
- assert(m + 2 <= (int)flv->capacity);
- r = flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, m + 2, dts);
- if (0 != r) return r;
- }
-
- audio.avpacket = FLV_AVPACKET;
- flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
- memcpy(flv->ptr + 2, (uint8_t*)data + n, bytes - n); // AAC exclude ADTS
- assert(bytes - n + 2 <= flv->capacity);
- return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes - n + 2, dts);
- }
-
- int flv_muxer_opus(flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- int r, m;
- struct flv_audio_tag_header_t audio;
- (void)pts;
-
- if (flv->capacity < bytes + 2/*AudioTagHeader*/ + 29/*OpusHead*/)
- {
- if (0 != flv_muxer_alloc(flv, bytes + 4))
- return -ENOMEM;
- }
-
- audio.codecid = FLV_AUDIO_OPUS;
- audio.rate = FLV_SOUND_RATE_44100; // 44k-SoundRate
- audio.bits = FLV_SOUND_BIT_16; // 16-bit samples
- audio.channels = FLV_SOUND_CHANNEL_STEREO; // Stereo sound
-
- if (0 == flv->audio_sequence_header)
- {
- if (opus_head_load(data, bytes, &flv->a.opus) < 0)
- return -1;
-
- flv->audio_sequence_header = 1; // once only
- audio.avpacket = FLV_SEQUENCE_HEADER;
-
- // Opus Head
- m = flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
- m += opus_head_save(&flv->a.opus, flv->ptr+m, flv->capacity-m);
- assert(m <= (int)flv->capacity);
- r = flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, m, dts);
- if (0 != r) return r;
- }
-
- audio.avpacket = FLV_AVPACKET;
- m = flv_audio_tag_header_write(&audio, flv->ptr, flv->capacity);
- memcpy(flv->ptr + m, (uint8_t*)data, bytes);
- assert(bytes - m <= flv->capacity);
- return flv->handler(flv->param, FLV_TYPE_AUDIO, flv->ptr, bytes + m, dts);
- }
-
- static int flv_muxer_h264(struct flv_muxer_t* flv, uint32_t pts, uint32_t dts)
- {
- int r;
- int m;
- struct flv_video_tag_header_t video;
-
- video.codecid = FLV_VIDEO_H264;
- if ( /*0 == flv->video_sequence_header &&*/ flv->update && flv->v.avc.nb_sps > 0 && flv->v.avc.nb_pps > 0)
- {
- video.cts = 0;
- video.keyframe = 1; // keyframe
- video.avpacket = FLV_SEQUENCE_HEADER;
- flv_video_tag_header_write(&video, flv->ptr + flv->bytes, flv->capacity - flv->bytes);
- m = mpeg4_avc_decoder_configuration_record_save(&flv->v.avc, flv->ptr + flv->bytes + 5, flv->capacity - flv->bytes - 5);
- if (m <= 0)
- return -1; // invalid data
-
- flv->video_sequence_header = 1; // once only
- assert(flv->bytes + m + 5 <= flv->capacity);
- r = flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr + flv->bytes, m + 5, dts);
- if (0 != r) return r;
- }
-
- // has video frame
- if (flv->vcl && flv->video_sequence_header)
- {
- video.cts = pts - dts;
- video.keyframe = 1 == flv->vcl ? FLV_VIDEO_KEY_FRAME : FLV_VIDEO_INTER_FRAME;
- video.avpacket = FLV_AVPACKET;
- flv_video_tag_header_write(&video, flv->ptr, flv->capacity);
- assert(flv->bytes <= flv->capacity);
- return flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr, flv->bytes, dts);
- }
- return 0;
- }
-
- int flv_muxer_avc(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- if (flv->capacity < bytes + sizeof(flv->v.avc) /*AVCDecoderConfigurationRecord*/)
- {
- if (0 != flv_muxer_alloc(flv, bytes + sizeof(flv->v.avc)))
- return -ENOMEM;
- }
-
- flv->bytes = 5;
- flv->bytes += h264_annexbtomp4(&flv->v.avc, data, bytes, flv->ptr + flv->bytes, flv->capacity - flv->bytes, &flv->vcl, &flv->update);
- if (flv->bytes <= 5)
- return -ENOMEM;
-
- return flv_muxer_h264(flv, pts, dts);
- }
-
- static int flv_muxer_h265(struct flv_muxer_t* flv, uint32_t pts, uint32_t dts)
- {
- int r;
- int m;
- struct flv_video_tag_header_t video;
-
- video.codecid = FLV_VIDEO_H265;
- if ( /*0 == flv->avc_sequence_header &&*/ flv->update && flv->v.hevc.numOfArrays >= 3) // vps + sps + pps
- {
- video.cts = 0;
- video.keyframe = 1; // keyframe
- video.avpacket = FLV_SEQUENCE_HEADER;
- flv_video_tag_header_write(&video, flv->ptr + flv->bytes, flv->capacity - flv->bytes);
- m = mpeg4_hevc_decoder_configuration_record_save(&flv->v.hevc, flv->ptr + flv->bytes + 5, flv->capacity - flv->bytes - 5);
- if (m <= 0)
- return -1; // invalid data
-
- flv->video_sequence_header = 1; // once only
- assert(flv->bytes + m + 5 <= flv->capacity);
- r = flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr + flv->bytes, m + 5, dts);
- if (0 != r) return r;
- }
-
- // has video frame
- if (flv->vcl && flv->video_sequence_header)
- {
- video.cts = pts - dts;
- video.keyframe = 1 == flv->vcl ? FLV_VIDEO_KEY_FRAME : FLV_VIDEO_INTER_FRAME;
- video.avpacket = FLV_AVPACKET;
- flv_video_tag_header_write(&video, flv->ptr, flv->capacity);
- assert(flv->bytes <= flv->capacity);
- return flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr, flv->bytes, dts);
- }
- return 0;
- }
-
- int flv_muxer_hevc(struct flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- if ((size_t)flv->capacity < bytes + sizeof(flv->v.hevc) /*HEVCDecoderConfigurationRecord*/)
- {
- if (0 != flv_muxer_alloc(flv, bytes + sizeof(flv->v.hevc)))
- return -ENOMEM;
- }
-
- flv->bytes = 5;
- flv->bytes += h265_annexbtomp4(&flv->v.hevc, data, bytes, flv->ptr + flv->bytes, flv->capacity - flv->bytes, &flv->vcl, &flv->update);
- if (flv->bytes <= 5)
- return -ENOMEM;
-
- return flv_muxer_h265(flv, pts, dts);
- }
-
- int flv_muxer_av1(flv_muxer_t* flv, const void* data, size_t bytes, uint32_t pts, uint32_t dts)
- {
- int r;
- int m;
- struct flv_video_tag_header_t video;
-
- if ((size_t)flv->capacity < bytes + 5 + sizeof(flv->v.av1) /*HEVCDecoderConfigurationRecord*/)
- {
- if (0 != flv_muxer_alloc(flv, bytes + sizeof(flv->v.av1)))
- return -ENOMEM;
- }
-
- video.codecid = FLV_VIDEO_AV1;
- if (0 == flv->video_sequence_header)
- {
- // load av1 information
- r = aom_av1_codec_configuration_record_init(&flv->v.av1, data, bytes);
- if (0 != r || flv->v.av1.width < 1 || flv->v.av1.height < 1)
- return 0 == r ? -1 : r;
-
- video.cts = 0;
- video.keyframe = 1; // keyframe
- video.avpacket = FLV_SEQUENCE_HEADER;
- flv_video_tag_header_write(&video, flv->ptr + flv->bytes, flv->capacity - flv->bytes);
- m = aom_av1_codec_configuration_record_save(&flv->v.av1, flv->ptr + flv->bytes + 5, flv->capacity - flv->bytes - 5);
- if (m <= 0)
- return -1; // invalid data
-
- flv->video_sequence_header = 1; // once only
- assert(flv->bytes + m + 5 <= flv->capacity);
- r = flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr + flv->bytes, m + 5, dts);
- if (0 != r) return r;
- }
-
- // has video frame
- if (flv->video_sequence_header)
- {
- video.cts = pts - dts;
- video.keyframe = 1 == flv->vcl ? FLV_VIDEO_KEY_FRAME : FLV_VIDEO_INTER_FRAME;
- video.avpacket = FLV_AVPACKET;
- flv_video_tag_header_write(&video, flv->ptr, flv->capacity);
- memcpy(flv->ptr + 5, data, bytes);
- return flv->handler(flv->param, FLV_TYPE_VIDEO, flv->ptr, bytes + 5, dts);
- }
- return 0;
- }
-
- int flv_muxer_metadata(flv_muxer_t* flv, const struct flv_metadata_t* metadata)
- {
- uint8_t* ptr, *end;
- uint32_t count;
-
- if (!metadata) return -1;
-
- count = (metadata->audiocodecid ? 5 : 0) + (metadata->videocodecid ? 5 : 0) + 1;
- if (flv->capacity < 1024)
- {
- if (0 != flv_muxer_alloc(flv, 1024))
- return -ENOMEM;
- }
-
- ptr = flv->ptr;
- end = flv->ptr + flv->capacity;
- count = (metadata->audiocodecid ? 5 : 0) + (metadata->videocodecid ? 5 : 0) + 1;
-
- // ScriptTagBody
-
- // name
- ptr = AMFWriteString(ptr, end, "onMetaData", 10);
-
- // value: SCRIPTDATAECMAARRAY
- ptr[0] = AMF_ECMA_ARRAY;
- ptr[1] = (uint8_t)((count >> 24) & 0xFF);;
- ptr[2] = (uint8_t)((count >> 16) & 0xFF);;
- ptr[3] = (uint8_t)((count >> 8) & 0xFF);
- ptr[4] = (uint8_t)(count & 0xFF);
- ptr += 5;
-
- if (metadata->audiocodecid)
- {
- ptr = AMFWriteNamedDouble(ptr, end, "audiocodecid", 12, metadata->audiocodecid);
- ptr = AMFWriteNamedDouble(ptr, end, "audiodatarate", 13, metadata->audiodatarate /* / 1024.0*/);
- ptr = AMFWriteNamedDouble(ptr, end, "audiosamplerate", 15, metadata->audiosamplerate);
- ptr = AMFWriteNamedDouble(ptr, end, "audiosamplesize", 15, metadata->audiosamplesize);
- ptr = AMFWriteNamedBoolean(ptr, end, "stereo", 6, (uint8_t)metadata->stereo);
- }
-
- if (metadata->videocodecid)
- {
- ptr = AMFWriteNamedDouble(ptr, end, "duration", 8, metadata->duration);
- ptr = AMFWriteNamedDouble(ptr, end, "interval", 8, metadata->interval);
- ptr = AMFWriteNamedDouble(ptr, end, "videocodecid", 12, metadata->videocodecid);
- ptr = AMFWriteNamedDouble(ptr, end, "videodatarate", 13, metadata->videodatarate /* / 1024.0*/);
- ptr = AMFWriteNamedDouble(ptr, end, "framerate", 9, metadata->framerate);
- ptr = AMFWriteNamedDouble(ptr, end, "height", 6, metadata->height);
- ptr = AMFWriteNamedDouble(ptr, end, "width", 5, metadata->width);
- }
-
- ptr = AMFWriteNamedString(ptr, end, "encoder", 7, FLV_MUXER, strlen(FLV_MUXER));
- ptr = AMFWriteObjectEnd(ptr, end);
-
- return flv->handler(flv->param, FLV_TYPE_SCRIPT, flv->ptr, ptr - flv->ptr, 0);
- }
|