bool muxerFlv::open(const char *file, ADM_videoStream *s,uint32_t nbAudioTrack,ADM_audioStream **a) { uint32_t fcc=s->getFCC(); bool r=true; char *fileTitle=NULL; if(fourCC::check(fcc,(uint8_t *)"FLV1") || isVP6Compatible(fcc)) { }else { GUI_Error_HIG("Unsupported","Only FLV1 & VP6 supported for video"); return false; } if(nbAudioTrack) for(int i=0;i<nbAudioTrack;i++) { uint32_t acc=a[i]->getInfo()->encoding; if(acc!=WAV_MP2 && acc!=WAV_MP3 && acc!=WAV_AAC) { GUI_Error_HIG("Unsupported","Only AAC & mpegaudio supported for audio"); return false; } uint32_t fq=a[i]->getInfo()->frequency; if(fq!=44100 &&fq!=22050 && fq!=11025) { GUI_Error_HIG("Unsupported","Only 44.1, 22.050 and 11.025 kHz supported"); return false; } } if(false==setupMuxer("flv",file)) { printf("[FLV] Failed to open muxer\n"); return false; } if(initVideo(s)==false) { printf("[FLV] Failed to init video\n"); return false; } AVCodecContext *c = video_st->codec; AVDictionary *dict = NULL; rescaleFps(s->getAvgFps1000(),&(c->time_base)); video_st->time_base=c->time_base; c->gop_size=15; if(initAudio(nbAudioTrack,a)==false) { printf("[FLV] Failed to init audio\n"); return false; } int er=avio_open(&(oc->pb), file, AVIO_FLAG_WRITE); if (er) { ADM_error("[Flv]: Failed to open file :%s, er=%d\n",file,er); r=false; goto finish; } char buf[64]; snprintf(buf, sizeof(buf), "%d", AV_TIME_BASE / 10); av_dict_set(&dict, "preload", buf, 0); av_dict_set(&dict, "max_delay", buf, 0); av_dict_set(&dict, "muxrate", "10080000", 0); if (avformat_write_header(oc, &dict) < 0) { printf("[Flv Muxer] Muxer rejected the parameters\n"); r=false; goto finish; } initialized=true; finish: vStream=s; aStreams=a; nbAStreams=nbAudioTrack; return r; }
/** \fn initVideo \brief setup video part of muxer */ bool muxerFFmpeg::initVideo(ADM_videoStream *stream) { audioDelay=stream->getVideoDelay(); video_st = avformat_new_stream(oc, NULL); if (!video_st) { printf("[FF] new stream failed\n"); return false; } AVCodecContext *c; c = video_st->codec; c->sample_aspect_ratio.num=1; c->sample_aspect_ratio.den=1; video_st->sample_aspect_ratio=c->sample_aspect_ratio; uint32_t videoExtraDataSize=0; uint8_t *videoExtraData; stream->getExtraData(&videoExtraDataSize,&videoExtraData); printf("[FF] Using %d bytes for video extradata\n",(int)videoExtraDataSize); ffmpuxerSetExtradata(c,videoExtraDataSize,videoExtraData); c->rc_buffer_size=8*1024*224; c->rc_max_rate=9500*1000; c->rc_min_rate=0; c->bit_rate=9000*1000; c->codec_type = AVMEDIA_TYPE_VIDEO; c->flags=CODEC_FLAG_QSCALE; c->width = stream->getWidth(); c->height =stream->getHeight(); uint32_t fcc=stream->getFCC(); if(isMpeg4Compatible(fcc)) { c->codec_id = CODEC_ID_MPEG4; if(stream->providePts()==true) { c->has_b_frames=1; // in doubt... c->max_b_frames=2; }else { ADM_warning("Incoming stream does not provide PTS \n"); c->has_b_frames=0; // No PTS=cannot handle CTS... c->max_b_frames=0; } }else { if(isH264Compatible(fcc) || isH265Compatible(fcc)) { if(stream->providePts()==true) { c->has_b_frames=1; // in doubt... c->max_b_frames=2; }else { printf("[MP4] Source video has no PTS information, assuming no b frames\n"); c->has_b_frames=0; // No PTS=cannot handle CTS... c->max_b_frames=0; } if(isH265Compatible(fcc)) { c->codec_id = AV_CODEC_ID_HEVC; setAvCodec(c,AV_CODEC_ID_HEVC); } else { c->codec_id = CODEC_ID_H264; setAvCodec(c,CODEC_ID_H264); } } else { if(isDVCompatible(fcc)) { c->codec_id = CODEC_ID_DVVIDEO; }else { if(fourCC::check(fcc,(uint8_t *)"H263")) { c->codec_id=CODEC_ID_H263; }else if(isVP6Compatible(stream->getFCC())) { c->codec_id=CODEC_ID_VP6F; setAvCodec(c,CODEC_ID_VP6F); c->has_b_frames=0; // No PTS=cannot handle CTS... c->max_b_frames=0; }else if(fourCC::check(stream->getFCC(),(uint8_t *)"FLV1")) { c->has_b_frames=0; // No PTS=cannot handle CTS... c->max_b_frames=0; c->codec_id=CODEC_ID_FLV1; setAvCodec(c,CODEC_ID_FLV1); }else { if(fourCC::check(stream->getFCC(),(uint8_t *)"MPEG1")) { c->has_b_frames=1; // No PTS=cannot handle CTS... c->max_b_frames=2; c->codec_id=CODEC_ID_MPEG1VIDEO; } else if(fourCC::check(stream->getFCC(),(uint8_t *)"MPEG2")) { c->has_b_frames=1; // No PTS=cannot handle CTS... c->max_b_frames=2; c->codec_id=CODEC_ID_MPEG2VIDEO; }else { uint32_t id=stream->getFCC(); AVCodecID cid=ADM_codecIdFindByFourcc(fourCC::tostring(id)); if(cid==CODEC_ID_NONE) { printf("[FF] Unknown video codec\n"); return false; } c->codec_id=cid; } } } } } if(useGlobalHeader()==true) { if(videoExtraDataSize) { ADM_info("Video has extradata and muxer requires globalHeader, assuming it is done so.\n"); c->flags|=CODEC_FLAG_GLOBAL_HEADER; }else { ADM_warning("Video has no extradata but muxer requires globalHeader.\n"); } } printf("[FF] Video initialized\n"); return true; }
/* * \fn Oplug_flv \brief Main function to save in flv format. It is very close to oplug_mp4 but somehow simplified as the following assumptions are made : * No b frame * No 2 pass encoding */ uint8_t oplug_flv(const char *name) { AVDMGenericVideoStream *_incoming=NULL; AVDMGenericAudioStream *audio=NULL; uint8_t audioBuffer[48000]; uint8_t *videoBuffer=NULL; uint32_t alen;//,flags; uint32_t size; uint32_t sample_got=0,sample; uint32_t extraDataSize=0; uint8_t *extraData=NULL; lavMuxer *muxer=NULL; aviInfo info; uint32_t width,height; DIA_encoding *encoding_gui=NULL; Encoder *_encode=NULL; uint32_t total=0; uint32_t videoExtraDataSize=0; uint8_t *videoExtraData=NULL; uint8_t *dummy,err; WAVHeader *audioinfo=NULL; int prefill=0; uint32_t displayFrame=0; ADMBitstream bitstream(0); uint8_t r=0; pthread_t audioThread; audioQueueMT context; PacketQueue *pq=NULL;//("MP4 audioQ",50,2*1024*1024); uint32_t totalAudioSize=0; int frameDelay = 0; bool receivedFrame = false; // Setup video if(videoProcessMode()) { _incoming = getLastVideoFilter (frameStart,frameEnd-frameStart); }else { _incoming = getFirstVideoFilter (frameStart,frameEnd-frameStart); } videoBuffer=new uint8_t[_incoming->getInfo()->width*_incoming->getInfo()->height*3]; // Set global header encoding, needed for H264 _encode = getVideoEncoder (_incoming->getInfo()->width, _incoming->getInfo()->height,1); total= _incoming->getInfo()->nb_frames; info.fcc=*(uint32_t *)_encode->getCodecName(); //FIXME // int supported=0; if(isVP6Compatible(info.fcc)) supported=1; if(fourCC::check(info.fcc,(const uint8_t *)"FLV1")) supported=1; if(!supported) { GUI_Error_HIG(QT_TR_NOOP("Unsupported video"),QT_TR_NOOP("Only FLV1 and VP6 video are supported")); goto stopit; } /* Check audio, we support only mp3 right now * 44100, 22050, 11025 only! * */ if(currentaudiostream) { uint32_t audioCodec=0; uint32_t fq=currentaudiostream->getInfo()->frequency; if(audioProcessMode()) { audioCodec=audioFilter_getOuputCodec(); fq=audioFilter_getOuputFrequency(fq); }else { // copy audioCodec=currentaudiostream->getInfo()->encoding; } if(audioCodec!=WAV_MP3 ) { GUI_Error_HIG(QT_TR_NOOP("Unsupported audio"),QT_TR_NOOP("Audio must be mp3 for flv output.")); goto stopit; } if(fq!=44100 && fq!=22050 && fq!=11025) { GUI_Error_HIG(QT_TR_NOOP("Unsupported audio"),QT_TR_NOOP("Frequency must be 44100, 22050 or 11025 Hz.")); goto stopit; } } encoding_gui=new DIA_encoding(_incoming->getInfo()->fps1000); bitstream.bufferSize=_incoming->getInfo()->width*_incoming->getInfo()->height*3; if (!_encode) { GUI_Error_HIG ("[FLV]",QT_TR_NOOP("Cannot initialize the video stream")); goto stopit; } // init compressor encoding_gui->setContainer(QT_TR_NOOP("FLV")); encoding_gui->setAudioCodec(QT_TR_NOOP("None")); if(!videoProcessMode()) encoding_gui->setCodec(QT_TR_NOOP("Copy")); else encoding_gui->setCodec(_encode->getDisplayName()); if (!_encode->configure (_incoming)) { GUI_Error_HIG (QT_TR_NOOP("Filter init failed"), NULL); goto stopit; }; encoding_gui->setPhasis (QT_TR_NOOP("Encoding")); info.width=_incoming->getInfo()->width; info.height=_incoming->getInfo()->height; info.nb_frames=_incoming->getInfo()->nb_frames; info.fps1000=_incoming->getInfo()->fps1000; _encode->hasExtraHeaderData( &videoExtraDataSize,&dummy); if(videoExtraDataSize) { printf("[FLV]We have extradata for video in copy mode (%d)\n",videoExtraDataSize); videoExtraData=new uint8_t[videoExtraDataSize]; memcpy(videoExtraData,dummy,videoExtraDataSize); } ADM_assert(_encode); bitstream.data=videoBuffer; // ____________Setup audio__________________ if(currentaudiostream) { audio=mpt_getAudioStream(); if(!audio) { GUI_Error_HIG ("[FLV]",QT_TR_NOOP("Cannot initialize the audio stream")); goto stopit; } } if(audio) { audioinfo=audio->getInfo(); audio->extraData(&extraDataSize,&extraData); if(audioProcessMode()) encoding_gui->setAudioCodec(getStrFromAudioCodec(audio->getInfo()->encoding)); else encoding_gui->setAudioCodec(QT_TR_NOOP("Copy")); }else { encoding_gui->setAudioCodec(QT_TR_NOOP("None")); } // ____________Setup Muxer _____________________ muxer= new lavMuxer; if(!muxer->open( name, 2000000, // Muxrate MUXER_FLV, &info,videoExtraDataSize,videoExtraData, audioinfo,extraDataSize,extraData)) goto stopit; //_____________ Loop _____________________ encoding_gui->setContainer(QT_TR_NOOP("FLV")); if(!videoProcessMode()) encoding_gui->setCodec(QT_TR_NOOP("Copy")); else encoding_gui->setCodec(_encode->getDisplayName()); // UI_purge(); //_____________ Start Audio thread _____________________ if(audio) { pq=new PacketQueue("[FLV] audioQ",5000,2*1024*1024); memset(&context,0,sizeof(context)); context.audioEncoder=audio; context.audioTargetSample=0xFFFF0000; ; //FIXME context.packetQueue=pq; // start audio thread ADM_assert(!pthread_create(&audioThread,NULL,(THRINP)defaultAudioQueueSlave,&context)); ADM_usleep(4000); } //_____________GO !___________________ for (uint32_t frame = 0; frame < total; frame++) { if (!encoding_gui->isAlive()) { r = 0; break; } while(muxer->needAudio()) { if(pq->Pop(audioBuffer,&alen,&sample)) { if(alen) { muxer->writeAudioPacket(alen,audioBuffer,sample_got); totalAudioSize+=alen; encoding_gui->setAudioSize(totalAudioSize); sample_got+=sample; } } else { r = 0; break; } } for (;;) { bitstream.cleanup(frame); if (frame + frameDelay >= total) { if (_encode->getRequirements() & ADM_ENC_REQ_NULL_FLUSH) r = _encode->encode(UINT32_MAX, &bitstream); else r = 0; } else r = _encode->encode(frame + frameDelay, &bitstream); if (!r) { printf("Encoding of frame %lu failed!\n", frame); GUI_Error_HIG (QT_TR_NOOP("Error while encoding"), NULL); break; } else if (!receivedFrame && bitstream.len > 0) { if (!(bitstream.flags & AVI_KEY_FRAME)) { GUI_Error_HIG (QT_TR_NOOP("KeyFrame error"), QT_TR_NOOP("The beginning frame is not a key frame.\nPlease move the A marker.")); r = 0; break; } else receivedFrame = true; } if (bitstream.len == 0 && (_encode->getRequirements() & ADM_ENC_REQ_NULL_FLUSH)) { printf("skipping frame: %u size: %i\n", frame + frameDelay, bitstream.len); frameDelay++; } else break; } if (!r) break; muxer->writeVideoPacket(&bitstream); encoding_gui->setFrame(frame, bitstream.len, bitstream.out_quantizer, total); } stopit: // Flush slave Q if(audio&& pq) { context.audioAbort=1; pq->Abort(); // Wait for audio slave to be over while(!context.audioDone) { printf("[FLV]Waiting Audio thread\n"); ADM_usleep(500000); } delete pq; } // if(muxer) muxer->close(); if(encoding_gui) delete encoding_gui; if(videoBuffer) delete [] videoBuffer; if(muxer) delete muxer; if(_encode) delete _encode; if(videoExtraData) delete [] videoExtraData; // Cleanup deleteAudioFilter (audio); return r; }
uint8_t lavMuxer::open(const char *filename,uint32_t inbitrate, ADM_MUXER_TYPE type, aviInfo *info, uint32_t videoExtraDataSize, uint8_t *videoExtraData, WAVHeader *audioheader, uint32_t audioextraSize,uint8_t *audioextraData) { AVCodecContext *c; _type=type; _fps1000=info->fps1000; switch(_type) { case MUXER_TS: fmt = av_guess_format("mpegts", NULL, NULL); break; case MUXER_DVD: fmt = av_guess_format("dvd", NULL, NULL); break; case MUXER_VCD: fmt = av_guess_format("vcd", NULL, NULL); break; case MUXER_SVCD: fmt = av_guess_format("svcd", NULL, NULL); break; case MUXER_MP4: fmt = av_guess_format("mp4", NULL, NULL); break; case MUXER_PSP: fmt = av_guess_format("psp", NULL, NULL); break; case MUXER_FLV: fmt = av_guess_format("flv", NULL, NULL); break; case MUXER_MATROSKA: fmt = av_guess_format("matroska", NULL, NULL); break; default: fmt=NULL; } if (!fmt) { printf("Lav:Cannot guess format\n"); ADM_assert(0); return 0; } oc = avformat_alloc_context(); if (!oc) { printf("Lav:Cannot allocate context\n"); return 0; } oc->oformat = fmt; snprintf(oc->filename,1000,"file://%s",filename); // Video //________ video_st = av_new_stream(oc, 0); if (!video_st) { printf("Lav: new stream failed\n"); return 0; } c = video_st->codec; c->gop_size=15; c->max_b_frames=2; c->has_b_frames=1; switch(_type) { case MUXER_FLV: c->codec=new AVCodec; memset(c->codec,0,sizeof(AVCodec)); if(fourCC::check(info->fcc,(uint8_t *)"FLV1")) { c->codec_id=CODEC_ID_FLV1; c->codec->name=ADM_strdup("FLV1"); }else { if(isVP6Compatible(info->fcc)) { c->codec_id=CODEC_ID_VP6F; c->codec->name=ADM_strdup("VP6F"); } else ADM_assert(0); } break; case MUXER_MATROSKA: strcpy(oc->title,"Avidemux"); strcpy(oc->author,"Avidemux"); if(isMpeg4Compatible(info->fcc)) { c->codec_id = CODEC_ID_MPEG4; }else { if(isH264Compatible(info->fcc)) { c->has_b_frames=2; // let muxer know we may have bpyramid c->codec_id = CODEC_ID_H264; c->codec=new AVCodec; memset(c->codec,0,sizeof(AVCodec)); c->codec->name=ADM_strdup("H264"); } else { if(!ADM_4cc_to_lavcodec((const char *)&(info->fcc),&(c->codec_id))) { printf("[lavFormat] Cannot map this\n"); return 0; } } } if(videoExtraDataSize) { c->extradata=videoExtraData; c->extradata_size= videoExtraDataSize; } break; case MUXER_MP4: case MUXER_PSP: { // probably a memeleak here char *foo=ADM_strdup(filename); strcpy(oc->title,ADM_GetFileName(foo)); strcpy(oc->author,"Avidemux"); if(isMpeg4Compatible(info->fcc)) { c->codec_id = CODEC_ID_MPEG4; }else { if(isH264Compatible(info->fcc)) { c->has_b_frames=2; // let muxer know we may have bpyramid c->codec_id = CODEC_ID_H264; c->codec=new AVCodec; memset(c->codec,0,sizeof(AVCodec)); c->codec->name=ADM_strdup("H264"); } else { if(isDVCompatible(info->fcc)) { c->codec_id = CODEC_ID_DVVIDEO; }else { if(fourCC::check(info->fcc,(uint8_t *)"H263")) { c->codec_id=CODEC_ID_H263; }else{ c->codec_id = CODEC_ID_MPEG4; // Default value printf("Ooops, cant mux that...\n"); printf("Ooops, cant mux that...\n"); printf("Ooops, cant mux that...\n"); } } } } if(videoExtraDataSize) { c->extradata=videoExtraData; c->extradata_size= videoExtraDataSize; } if(MUXER_PSP==_type) { c->rc_buffer_size=0; //8*1024*224; c->rc_max_rate=0; //768*1000; c->rc_min_rate=0; c->bit_rate=768*1000; } else { c->rc_buffer_size=8*1024*224; c->rc_max_rate=9500*1000; c->rc_min_rate=0; if(!inbitrate) c->bit_rate=9000*1000; else c->bit_rate=inbitrate; } } break; case MUXER_TS: c->codec_id = CODEC_ID_MPEG2VIDEO; c->rc_buffer_size=8*1024*224; c->rc_max_rate=9500*1000; c->rc_min_rate=0; if(!inbitrate) c->bit_rate=9000*1000; else c->bit_rate=inbitrate; break; case MUXER_DVD: c->codec_id = CODEC_ID_MPEG2VIDEO; c->rc_buffer_size=8*1024*224; c->rc_max_rate=9500*1000; c->rc_min_rate=0; if(!inbitrate) c->bit_rate=9000*1000; else c->bit_rate=inbitrate; break; case MUXER_VCD: c->codec_id = CODEC_ID_MPEG1VIDEO; c->rc_buffer_size=8*1024*40; c->rc_max_rate=1152*1000; c->rc_min_rate=1152*1000; c->bit_rate=1152*1000; break; case MUXER_SVCD: c->codec_id = CODEC_ID_MPEG2VIDEO; c->rc_buffer_size=8*1024*112; c->rc_max_rate=2500*1000; c->rc_min_rate=0*1000; if(!inbitrate) c->bit_rate=2040*1000; else c->bit_rate=inbitrate; break; default: ADM_assert(0); } c->codec_type = CODEC_TYPE_VIDEO; c->flags=CODEC_FLAG_QSCALE; c->width = info->width; c->height = info->height; AVRational fps25=(AVRational){1001,25025}; AVRational fps24=(AVRational){1001,24000}; AVRational fps30= (AVRational){1001,30000}; AVRational fpsfree= (AVRational){1000,_fps1000}; switch(_fps1000) { case 25000: { c->time_base= fps25; break; } case 23976: if(_type==MUXER_MP4 || _type==MUXER_PSP || _type==MUXER_FLV || _type==MUXER_MATROSKA) { c->time_base= fps24; //(AVRational){1001,24000}; break; } case 29970: c->time_base=fps30; break; default: { if(_type==MUXER_MP4 || _type==MUXER_PSP || _type==MUXER_FLV || _type==MUXER_MATROSKA) { c->time_base=fpsfree;// (AVRational){1000,_fps1000}; break; } else { GUI_Error_HIG(QT_TR_NOOP("Incompatible frame rate"), NULL); return 0; } } break; } // Audio //________ if(audioheader) { audio_st = av_new_stream(oc, 1); if (!audio_st) { printf("Lav: new stream failed\n"); return 0; } c = audio_st->codec; c->frame_size=1024; //For AAC mainly, sample per frame printf("[LavFormat] Bitrate %u\n",(audioheader->byterate*8)/1000); _audioFq=c->sample_rate = audioheader->frequency; #if 0 if(_type== MUXER_PSP && audioheader->encoding==WAV_AAC) { _audioFq=c->sample_rate = audioheader->frequency/2; //_audioFq*=2; // SBR } #endif switch(audioheader->encoding) { case WAV_AC3: c->codec_id = CODEC_ID_AC3;c->frame_size=6*256;break; case WAV_MP2: c->codec_id = CODEC_ID_MP2;break; case WAV_MP3: #warning FIXME : Probe deeper c->frame_size=1152; c->codec_id = CODEC_ID_MP3; break; case WAV_PCM: // One chunk is 10 ms (1/100 of fq) c->frame_size=4; c->codec_id = CODEC_ID_PCM_S16LE;break; case WAV_AAC: c->extradata=audioextraData; c->extradata_size= audioextraSize; c->codec_id = CODEC_ID_AAC; break; default: if(_type==MUXER_MATROSKA) { if(ADM_WaveTag_to_lavcodec(audioheader->encoding, &(c->codec_id))) { if(audioextraData) { c->extradata=audioextraData; c->extradata_size= audioextraSize; } // Put a dummy time increment c->time_base= fps25; break; } } printf("Cant mux that ! audio\n"); printf("Cant mux that ! audio\n"); c->codec_id = CODEC_ID_MP2; return 0; break; } c->codec_type = CODEC_TYPE_AUDIO; c->bit_rate = audioheader->byterate*8; c->rc_buffer_size=(c->bit_rate/(2*8)); // 500 ms worth c->channels = audioheader->channels; _audioByterate=audioheader->byterate; } // /audio //---------------------- switch(_type) { case MUXER_FLV: case MUXER_PSP: case MUXER_MP4: case MUXER_MATROSKA: oc->mux_rate=10080*1000; // Needed ? break; case MUXER_TS: oc->mux_rate=10080*1000; break; case MUXER_DVD: oc->packet_size=2048; oc->mux_rate=10080*1000; break; case MUXER_VCD: oc->packet_size=2324; oc->mux_rate=2352 * 75 * 8; break; case MUXER_SVCD: oc->packet_size=2324; oc->mux_rate=2*2352 * 75 * 8; // ? break; default: ADM_assert(0); } oc->preload=AV_TIME_BASE/10; // 100 ms preloading oc->max_delay=200*1000; // 500 ms if (av_set_parameters(oc, NULL) < 0) { printf("Lav: set param failed \n"); return 0; } if (url_fopen(&(oc->pb), filename, URL_WRONLY) < 0) { printf("Lav: Failed to open file :%s\n",filename); return 0; } ADM_assert(av_write_header(oc)>=0); dump_format(oc, 0, filename, 1); printf("lavformat mpeg muxer initialized\n"); _running=1; one=(1000*1000*1000)/_fps1000; _curDTS=one; return 1; }