artclarke / humble-video

Humble Video: Demuxing, Decoding, Filtering, Encoding and Muxing of 100's of video and audio formats and Codecs from the JVM
GNU Affero General Public License v3.0
561 stars 115 forks source link

Exception being thrown in Muxer: "Could not write header for url: [%s]. avdictionary" #19

Closed kodjobaah closed 10 years ago

kodjobaah commented 10 years ago

Hi Art,

I have been trying to get humble-video working in red5...But when I try to create a muxer it fails when trying to open it...the failure occurs when trying to write the header.. avformat_write_header

The snippet of code that is causing the error is below:

MuxerFormat muxerFormat = MuxerFormat.guessFormat("mpegts",null,null);
    log.info(muxerFormat.toString());
    Muxer container = Muxer.make(outputUrl, muxerFormat,"mpegts");

        KeyValueBag  meta = KeyValueBag.make();
        meta.setValue("service_provider", "Red5 HLS");
        meta.setValue("title", outputUrl.substring(outputUrl.indexOf(':') + 1));
        meta.setValue("map", "0");
        meta.setValue("segment_time", "" + facade.getSegmentTimeLimit() / 1000);
        meta.setValue("segment_format", "mpegts");
        meta.setValue("reset_timestamps", "0");
        container.open(meta, null);

Initially I thought it may have something to do with the header data...I made the following modifications to the Muxer.cpp this is why the error message looks a bit odd...

  AVDictionaryEntry *t = NULL;
  std::string out("");
  while ((t = av_dict_get(tmp, "", t, AV_DICT_IGNORE_SUFFIX))) {
out.append("key=");
out.append(t->key);
out.append("value=");
out.append(t->value);

  }
  out.append(url);

FfmpegException::check(retval, "Could not write header for url: [%s]. avdictionary: ", out.c_str());

This is the error thrown: 2014-03-03 12:31:33,222 [Thread-5] ERROR io.humble.video - HumbleInvalidArgument(finalStr): Could not write header for url: [key=service_providervalue=Red5 HLSkey=titlevalue=stream1393717419736key=mapvalue=0key=segment_timevalue=2key=segment_formatvalue=mpegtskey=reset_timestampsvalue=0redfivempegts:stream1393717419736]. avdictionary: Invalid argument (-22) (VideoExceptions.cpp:101)

when I compared the code in the muxer to this: http://ffmpeg.org/doxygen/trunk/muxing_8c-source.html

I could not see where you was associating the videostream and audiostreams... is it not needed?

Another thing that is slightly confusing me..in the open method of the muxer...when you are checking for customio...not sure what the purpose of that is...

Any help would be greatly appreciated.

Kodjo

kodjobaah commented 10 years ago

Just had another look at the class...and I see the mistake I made...added the following before opening the container...which seems to have introduced a new problem...

java.util.Collection<Codec> retVal = Codec.getInstalledCodecs();
Codec cd = null;
for(Codec codec: retVal) {

    if(codec.getName().equalsIgnoreCase("libx264")) {
    cd = codec;
    break;
    }
}

  Decoder decoder = Decoder.make(cd);
  decoder.open(null,null);
 MuxerStream stream = container.addNewStream(decoder);

Below is the stack trace: Exception in thread "Thread-4" java.lang.IllegalArgumentException: passed in codec cannot decode at io.humble.video.VideoJNI.Decoder_make__SWIG_0(Native Method) at io.humble.video.Decoder.make(Decoder.java:113) at org.red5.xuggler.writer.HLSStreamWriter.open(HLSStreamWriter.java:225) at org.red5.service.httpstream.SegmentFacade.initWriter(SegmentFacade.java:164) at org.red5.service.httpstream.SegmenterService.start(SegmenterService.java:127) at org.red5.service.httpstream.SegmenterService.start(SegmenterService.java:83) at org.red5.hlsapp.Application$1.run(Application.java:92)

aliascarma commented 10 years ago

Decoder?? if i don't mistake there must be an encoder and try using encoder.toString() for debug ... and instead of looping installed codecs you may just check for canEncode() with Codec.findEncodingCodecByName("libx264") ... i think it's faster and low resource consumption

kodjobaah commented 10 years ago

Yes I think you are right...after looking at the test..MuxerTest..it apppears that the way it works is that once you have a Demuxer then you get a stream from demuxer (where each video has multiple streams (audio+video)). Then from the stream you get the decoder for that stream and you then you add the decoder to the muxer (which I assume will use that to decode the packet associated with the stream) then when a user request a stream from the muxer..it will then use the codiec that was associated it when it was created to encode the stream that is returned.

Below is my modified code For the reader(Demuxer):

Demuxer reader = Demuxer.make(); reader.setReadRetryCount(0); reader.setInputBufferLength(4096); reader.setProperty("analyzeduration", 0); java.util.Collection retval = DemuxerFormat.getFormats(); DemuxerFormat df = null; for(DemuxerFormat d: retval) { if (d.getName().equalsIgnoreCase("flv")) { df = d; break; }

} reader.open(inputUrl,df,true,false,null,null);

Below is the code for the write(Muxer)

MuxerFormat muxerFormat = MuxerFormat.guessFormat("mpegts",null,null); Muxer container = Muxer.make(outputUrl, muxerFormat,"mpegts");

int n = reader.getNumStreams(); for(int i =0; i < n; i++) { DemuxerStream ds = reader.getStream(i); Decoder d = ds.getDecoder(); log.info("---- MODEC"+d.getCodec().getName()); container.addNewStream(d);

}

container.open(meta, null);

Unfortunately it is throwing the following error:

Exception in thread "Thread-4" java.lang.RuntimeException: could not find decoding codec[codeic_name=none] at io.humble.video.VideoJNI.DemuxerStream_getDecoder(Native Method) at io.humble.video.DemuxerStream.getDecoder(DemuxerStream.java:134) at org.red5.xuggler.writer.HLSStreamWriter.open(HLSStreamWriter.java:232) at org.red5.service.httpstream.SegmentFacade.initWriter(SegmentFacade.java:165) at org.red5.service.httpstream.SegmenterService.start(SegmenterService.java:127) at org.red5.service.httpstream.SegmenterService.start(SegmenterService.java:83) at org.red5.hlsapp.Application$1.run(Application.java:92) at java.lang.Thread.run(Thread.java:724)

Made the following modification to DemuxerStream_getDecoder to print out the codec name:

RefPointer codec = Codec::findDecodingCodec((Codec::ID)stream->codec->codec_id); std::string out("could not find decoding codec"); out.append("[codeic_name="); const char * avname = avcodec_get_name(stream->codec->codec_id); out.append(avname); out.append("]"); if (!codec) { // VS_THROW(HumbleRuntimeError("could not find decoding codec")); VS_THROW(HumbleRuntimeError(out)); }

aliascarma commented 10 years ago

can you provide the rtmp adress? or it's just local? your stream is in h264/speex or nellymoser or aac format?

aliascarma commented 10 years ago

i've teste with

import io.humble.video.Decoder; import io.humble.video.Demuxer; import io.humble.video.DemuxerStream; import io.humble.video.DemuxerFormat; import io.humble.video.Muxer; import io.humble.video.MuxerFormat; import io.humble.video.Global; import io.humble.video.KeyValueBag; import io.humble.video.MediaAudio; import io.humble.video.MediaDescriptor; import io.humble.video.MediaPacket; import io.humble.video.MediaPicture; import io.humble.video.awt.MediaPictureConverter; import io.humble.video.awt.MediaPictureConverterFactory; import io.humble.video.awt.ImageFrame; import io.humble.video.javaxsound.AudioFrame; import io.humble.video.javaxsound.MediaAudioConverter; import io.humble.video.javaxsound.MediaAudioConverterFactory;

import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.Collection; import java.util.Iterator;

import org.slf4j.Logger; import org.slf4j.LoggerFactory;

public class Test { private static final Logger log = LoggerFactory.getLogger(Test.class); private Demuxer source;

public static void main(String[] arg) { final String f = new File("/home/carma/Desktop/humble-video/humble-video-noarch/target/test-classes/youtube_h264_mp3.flv").getPath(); Test test; try { test = new Test(f); } catch (Exception e){ System.out.println(e); }

}

public Test(String inputURL) throws InterruptedException, IOException{ String outputUrl = "test.mpegts"; System.out.println("Open Demuxer"); source = Demuxer.make(); source.setReadRetryCount(0); source.setInputBufferLength(4092); source.setProperty("analyzeduration", 0);
DemuxerFormat f = DemuxerFormat.findFormat("flv"); source.open(inputURL, f, false, true, null, null); source.queryStreamMetaData();

    System.out.println("Extract MetaData");
    KeyValueBag metadata = source.getMetaData();

   for (Iterator<String> keys = metadata.getKeys().iterator(); keys.hasNext();) {
       String key = keys.next();
       String val = metadata.getValue(key);
       System.out.printf("%s, %s \n", key, val);
   }

    // open output
   MuxerFormat muxerFormat = MuxerFormat.guessFormat("mpegts",null,null);
   Muxer muxer = Muxer.make(outputUrl, muxerFormat,null);      
    int n = source.getNumStreams();
    for(int i = 0; i < n; i++) {
      DemuxerStream ds = source.getStream(i);
      Decoder d = ds.getDecoder();
      System.out.printf("Demuxer codec %s \n",d.toString());
      muxer.addNewStream(d);
    }
    muxer.open(null, null);
    MediaPacket packet = MediaPacket.make();
    while(source.read(packet) >= 0) {
      System.out.printf("write packet %s \n",packet.toString());
      //muxer.write(packet, false);
    }
    muxer.close();      

}

}

and got

Demuxer codec io.humble.video.Decoder@-1139908064[codec=io.humble.video.Codec@-1139908112[type=MEDIA_VIDEO;id=CODEC_ID_H264;name=h264;];time base=1/50;width=320;height=240;pixel format=PIX_FMT_YUV420P;frame size=0;frame count=0;] Demuxer codec io.humble.video.Decoder@-1136110272[codec=io.humble.video.Codec@-1136229296[type=MEDIA_AUDIO;id=CODEC_ID_MP3;name=mp3;];time base=1/44100;sample rate=44100;channels=2;sample format=SAMPLE_FMT_S16P;frame size=1152;frame count=1;] write packet io.humble.video.MediaPacket@bc45b210[pts=0;dts=0;complete=true;size=59;maxSize=75;streamIndex=0;key=true;flags=1;duration=0;position=371;convergenceDuration=0;]

kodjobaah commented 10 years ago

the input stream is flv..and the rtmp server is www.whatamidoing.info...just tried out your code..and it worked...thank you..

aliascarma commented 10 years ago

it also write's ts files? do you still have mpeg handler for creating ts files or those are made by ffmpeg???

kodjobaah commented 10 years ago

have not got to that part..getting the following error...I think it is being caused by the run loop..in the rtmp reader..

WARN] [NioProcessor-3] org.red5.server.net.rtmp.RTMPMinaIoHandler - Exception caught on session: 2 id: F73MIPCPUKTZD java.io.IOException: Connection reset by peer at sun.nio.ch.FileDispatcherImpl.read0(Native Method) ~[na:1.7.0_25] at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:39) ~[na:1.7.0_25] at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:225) ~[na:1.7.0_25] at sun.nio.ch.IOUtil.read(IOUtil.java:198) ~[na:1.7.0_25] at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:375) ~[na:1.7.0_25] at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:273) ~[mina-core-2.0.7.jar:na] at org.apache.mina.transport.socket.nio.NioProcessor.read(NioProcessor.java:44) ~[mina-core-2.0.7.jar:na] at org.apache.mina.core.polling.AbstractPollingIoProcessor.read(AbstractPollingIoProcessor.java:690) [mina-core-2.0.7.jar:na] at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:664) [mina-core-2.0.7.jar:na] at org.apache.mina.core.polling.AbstractPollingIoProcessor.process(AbstractPollingIoProcessor.java:653) [mina-core-2.0.7.jar:na] at org.apache.mina.core.polling.AbstractPollingIoProcessor.access$600(AbstractPollingIoProcessor.java:67) [mina-core-2.0.7.jar:na] at org.apache.mina.core.polling.AbstractPollingIoProcessor$Processor.run(AbstractPollingIoProcessor.java:1124) ~[mina-core-2.0.7.jar:na] at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:64) ~[mina-core-2.0.7.jar:na] at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) ~[na:1.7.0_25] at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) ~[na:1.7.0_25]

aliascarma commented 10 years ago

are you trying to restream to red5?? that is rtmp mina decoder error ... malformed headers or something

kodjobaah commented 10 years ago

I am trying to used red5 as a proxy..so I send it an rtmp stream(flv) and then I want to broadcast that stream so that i can be accessed from an iphone or android device..it works fine if I access the stream using a flash player..ie..the publisher application...

aliascarma commented 10 years ago

actually i'm also trying to make something like that ... transcoding of live stream in different bitrates and transcode it in webm for html5 and m3u8 for non flash browsers(iphone,ipad etc...);

but i'm trying to make this code more available to modify ... i don't want to compile for all platforms every time ... so ... also ... i was able to add libfdkaac support by incarcerate it ...

cwpenhale commented 10 years ago

I started migrating my Red5 / HLS application (online at https://www.drdenver.tv) to humble-video, but I haven't continued, mostly because I wanted to work on some other projects in the mean time. Feel free to check out what I've got here: https://github.com/cwpenhale/red5-mobileconsole. This is a 24/7 streaming "tv/radio station". I might pick it up again soon. I also started forking@mondain's https://github.com/cwpenhale/red5-hls-plugin to use HumbleVideo, but I didn't get particularly far. I might pick that one up again as well.

screen shot 3 screen shot screen shot2

kodjobaah commented 10 years ago

hi connor nice work..I cloned your fork and and I am trying to get it to work..

aliascarma commented 10 years ago

it looks nice ... it's stable? i mean... ts files are ok? with continous video frames?

cwpenhale commented 10 years ago

Hi guys,

The Humble-Video version is not finished (and not working yet ;)) The Xuggler version should be working nicely, try this commit (https://github.com/cwpenhale/red5-mobileconsole/tree/dea47a6b6f2a775e0f8477f228b3123697f8ae6f). Feel free to hit me up over email with any project-specific questions.

For the Xuggler version, I had to hard-code some values in FFMPEG to get the wrapping I wanted on the named pipes, since I couldn't set them from Xuggler. I wanted to move to Humble-Video for that reason.

That version is in production on https://www.drdenver.tv. Try it on your iOS or Android phone.

Thanks! Connor

kodjobaah commented 10 years ago

Hi Guys,

I have forked connor plugin...and checked in my modifications...it is currently generating the ts files..I have not added audio..

https://github.com/kodjobaah/red5-hls-plugin

To get it to work you need to make sure you specifiy the full path of the segmentation directory..when you are wiring the segmentor services..in you red5-web.xml

eg.. <property name="segmentDirectory" value="/usr/local/src/red5-read-only/target/red5-server-1.0.2-RC4/webapps/hlsapp/WEB-INF/segments/" >

Still have lots to do...

artclarke commented 10 years ago

Hey guys, FWIW this is very much a thing in development. Xuggle is more stable (although I much prefer the API I am using for HumbleVideo). Also the encode path is definitely broken right now. I do not have time to work on the project at the moment, but it cheers me up to see folks trying it.

artclarke commented 10 years ago

hey guys; there are a variety of issues in this thread (but no bugs I think):

mekya commented 8 years ago

I would like to add one point to this thread.

I am working on a simple hls plugin for red5. I have just encounter this issue. When I tried to create hls from mp4(encoded with libx264), it does not work. However ContainerSegmenterTest passes in the demos. After that, I realized that h264_mp4toannexb filter may increase the size of the packet. So that vf.filter(packet, null) may return non-valid data. Because the required packet size is bigger than the current one.

I hope this info may help some guys ;)