{"action":"created","comment":{"url":"https://api.github.com/repos/google/ExoPlayer/pulls/comments/42976829","id":42976829,"diff_hunk":"@@ -0,0 +1,125 @@\n+/*\n+ * Copyright (C) 2014 The Android Open Source Project\n+ *\n+ * Licensed under the Apache License, Version 2.0 (the \"License\");\n+ * you may not use this file except in compliance with the License.\n+ * You may obtain a copy of the License at\n+ *\n+ *      http://www.apache.org/licenses/LICENSE-2.0\n+ *\n+ * Unless required by applicable law or agreed to in writing, software\n+ * distributed under the License is distributed on an \"AS IS\" BASIS,\n+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+ * See the License for the specific language governing permissions and\n+ * limitations under the License.\n+ */\n+package com.google.android.exoplayer.extractor.flv;\n+\n+import android.util.Pair;\n+import com.google.android.exoplayer.C;\n+import com.google.android.exoplayer.MediaFormat;\n+import com.google.android.exoplayer.extractor.TrackOutput;\n+import com.google.android.exoplayer.util.CodecSpecificDataUtil;\n+import com.google.android.exoplayer.util.MimeTypes;\n+import com.google.android.exoplayer.util.ParsableBitArray;\n+import com.google.android.exoplayer.util.ParsableByteArray;\n+\n+import java.util.Collections;\n+\n+/**\n+ * Parses audio tags of from an FLV stream and extracts AAC frames.\n+ */\n+final class AudioTagPayloadReader extends TagPayloadReader {\n+  // Sound format\n+  private static final int AUDIO_FORMAT_AAC = 10;\n+\n+  // AAC PACKET TYPE\n+  private static final int AAC_PACKET_TYPE_SEQUENCE_HEADER = 0;\n+  private static final int AAC_PACKET_TYPE_AAC_RAW = 1;\n+\n+  // SAMPLING RATES\n+  private static final int[] AUDIO_SAMPLING_RATE_TABLE = new int[] {\n+      5500, 11000, 22000, 44000\n+  };\n+\n+  // State variables\n+  private boolean hasParsedAudioDataHeader;\n+  private boolean hasOutputFormat;\n+\n+\n+  public AudioTagPayloadReader(TrackOutput output) {\n+    super(output);\n+  }\n+\n+  @Override\n+  public void seek() {\n+\n+  }\n+\n+  @Override\n+  protected boolean parseHeader(ParsableByteArray data) throws UnsupportedTrack {\n+    // Parse audio data header, if it was not done, to extract information\n+    // about the audio codec and audio configuration.\n+    if (!hasParsedAudioDataHeader) {\n+      int header = data.readUnsignedByte();\n+      int soundFormat = (header >> 4) & 0x0F;\n+      int sampleRateIndex = (header >> 2) & 0x03;\n+      int bitsPerSample = (header & 0x02) == 0x02 ? 16 : 8;\n+      int channels = (header & 0x01) + 1;\n+\n+      if (sampleRateIndex < 0 || sampleRateIndex >= AUDIO_SAMPLING_RATE_TABLE.length) {\n+        throw new UnsupportedTrack(\"Invalid sample rate for the audio track\");\n+      }\n+\n+      if (!hasOutputFormat) {\n+        // TODO: Adds support for MP3 and PCM\n+        if (soundFormat != AUDIO_FORMAT_AAC) {\n+          throw new UnsupportedTrack(\"Audio track not supported. Format: \" + soundFormat +\n+              \", Sample rate: \" + sampleRateIndex + \", bps: \" + bitsPerSample + \", channels: \" +\n+              channels);\n+        }\n+      }\n+\n+      hasParsedAudioDataHeader = true;\n+    } else {\n+      // Skip header if it was parsed previously.\n+      data.skipBytes(1);\n+    }\n+\n+    // In all the cases we will be managing AAC format (otherwise an exception would be\n+    // fired so we can just always return true\n+    return true;\n+  }\n+\n+  @Override\n+  protected void parsePayload(ParsableByteArray data, long timeUs) {\n+    int packetType = data.readUnsignedByte();\n+    // Parse sequence header just in case it was not done before.\n+    if (packetType == AAC_PACKET_TYPE_SEQUENCE_HEADER && !hasOutputFormat) {\n+      ParsableBitArray adtsScratch = new ParsableBitArray(new byte[data.bytesLeft()]);\n+      data.readBytes(adtsScratch.data, 0, data.bytesLeft());\n+\n+      int audioObjectType = adtsScratch.readBits(5);\n+      int sampleRateIndex = adtsScratch.readBits(4);\n+      int channelConfig = adtsScratch.readBits(4);\n+\n+      byte[] audioSpecificConfig = CodecSpecificDataUtil.buildAacAudioSpecificConfig(\n+          audioObjectType, sampleRateIndex, channelConfig);\n+      Pair<Integer, Integer> audioParams = CodecSpecificDataUtil.parseAacAudioSpecificConfig(\n+          audioSpecificConfig);\n+\n+      MediaFormat mediaFormat = MediaFormat.createAudioFormat(MimeTypes.AUDIO_AAC,","path":"library/src/main/java/com/google/android/exoplayer/extractor/flv/AudioTagPayloadReader.java","position":111,"original_position":111,"commit_id":"3e36f529f8fe8ef4c3550dc00f16684c532d0da0","original_commit_id":"3e36f529f8fe8ef4c3550dc00f16684c532d0da0","user":{"login":"ojw28","id":7881572,"avatar_url":"https://avatars.githubusercontent.com/u/7881572?v=3","gravatar_id":"","url":"https://api.github.com/users/ojw28","html_url":"https://github.com/ojw28","followers_url":"https://api.github.com/users/ojw28/followers","following_url":"https://api.github.com/users/ojw28/following{/other_user}","gists_url":"https://api.github.com/users/ojw28/gists{/gist_id}","starred_url":"https://api.github.com/users/ojw28/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/ojw28/subscriptions","organizations_url":"https://api.github.com/users/ojw28/orgs","repos_url":"https://api.github.com/users/ojw28/repos","events_url":"https://api.github.com/users/ojw28/events{/privacy}","received_events_url":"https://api.github.com/users/ojw28/received_events","type":"User","site_admin":false},"body":"Yes. I've merged this and fixed the break. Thanks @jeoliva / @winlinvip !","created_at":"2015-10-26T10:25:01Z","updated_at":"2015-10-26T10:25:01Z","html_url":"https://github.com/google/ExoPlayer/pull/828#discussion_r42976829","pull_request_url":"https://api.github.com/repos/google/ExoPlayer/pulls/828","_links":{"self":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/comments/42976829"},"html":{"href":"https://github.com/google/ExoPlayer/pull/828#discussion_r42976829"},"pull_request":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/828"}}},"pull_request":{"url":"https://api.github.com/repos/google/ExoPlayer/pulls/828","id":46291726,"html_url":"https://github.com/google/ExoPlayer/pull/828","diff_url":"https://github.com/google/ExoPlayer/pull/828.diff","patch_url":"https://github.com/google/ExoPlayer/pull/828.patch","issue_url":"https://api.github.com/repos/google/ExoPlayer/issues/828","number":828,"state":"closed","locked":false,"title":"FLV (H.264 + AAC) support","user":{"login":"jeoliva","id":7904247,"avatar_url":"https://avatars.githubusercontent.com/u/7904247?v=3","gravatar_id":"","url":"https://api.github.com/users/jeoliva","html_url":"https://github.com/jeoliva","followers_url":"https://api.github.com/users/jeoliva/followers","following_url":"https://api.github.com/users/jeoliva/following{/other_user}","gists_url":"https://api.github.com/users/jeoliva/gists{/gist_id}","starred_url":"https://api.github.com/users/jeoliva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeoliva/subscriptions","organizations_url":"https://api.github.com/users/jeoliva/orgs","repos_url":"https://api.github.com/users/jeoliva/repos","events_url":"https://api.github.com/users/jeoliva/events{/privacy}","received_events_url":"https://api.github.com/users/jeoliva/received_events","type":"User","site_admin":false},"body":"FLV is not one of the most used containers right now, and of course not a priority for any player, but I though it could be useful adding it to Exoplayer for 2 reasons:\r\n1. FLV was pretty used not too much time ago and still there are a lot of legacy video content libraries that are using this container. I have even seen a couple of reported issues asking for FLV support so there are some interest on it. \r\n2. And second, and most important, a share knowledge reason. In other words, I think this is really useful to allow other developers to better understand how to build their own extractors for Exoplayer.  FLV is one of the simplest and best documented video/audio container formats so it is easy going through the code, looking at the parser, flv extractor, FLV specification, and see what is the architecture behind Exoplayer extractors. MP4 type of containers are much more complex, and are a so hard way of starting coding or trying to understand extractors :). \r\n\r\nRegarding the implementation, as title stated, this pull request is adding support for FLV container, and more specifically, when content within FLV is encoded using H.264 and AAC.  ","created_at":"2015-09-29T15:22:22Z","updated_at":"2015-10-26T10:25:01Z","closed_at":"2015-10-26T10:17:53Z","merged_at":"2015-10-26T10:17:53Z","merge_commit_sha":"0e2382cc2f835d5afbfa4cdc12ab9481e0e2a046","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/google/ExoPlayer/pulls/828/commits","review_comments_url":"https://api.github.com/repos/google/ExoPlayer/pulls/828/comments","review_comment_url":"https://api.github.com/repos/google/ExoPlayer/pulls/comments{/number}","comments_url":"https://api.github.com/repos/google/ExoPlayer/issues/828/comments","statuses_url":"https://api.github.com/repos/google/ExoPlayer/statuses/3e36f529f8fe8ef4c3550dc00f16684c532d0da0","head":{"label":"jeoliva:flv-experiment","ref":"flv-experiment","sha":"3e36f529f8fe8ef4c3550dc00f16684c532d0da0","user":{"login":"jeoliva","id":7904247,"avatar_url":"https://avatars.githubusercontent.com/u/7904247?v=3","gravatar_id":"","url":"https://api.github.com/users/jeoliva","html_url":"https://github.com/jeoliva","followers_url":"https://api.github.com/users/jeoliva/followers","following_url":"https://api.github.com/users/jeoliva/following{/other_user}","gists_url":"https://api.github.com/users/jeoliva/gists{/gist_id}","starred_url":"https://api.github.com/users/jeoliva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeoliva/subscriptions","organizations_url":"https://api.github.com/users/jeoliva/orgs","repos_url":"https://api.github.com/users/jeoliva/repos","events_url":"https://api.github.com/users/jeoliva/events{/privacy}","received_events_url":"https://api.github.com/users/jeoliva/received_events","type":"User","site_admin":false},"repo":{"id":42681453,"name":"ExoPlayer","full_name":"jeoliva/ExoPlayer","owner":{"login":"jeoliva","id":7904247,"avatar_url":"https://avatars.githubusercontent.com/u/7904247?v=3","gravatar_id":"","url":"https://api.github.com/users/jeoliva","html_url":"https://github.com/jeoliva","followers_url":"https://api.github.com/users/jeoliva/followers","following_url":"https://api.github.com/users/jeoliva/following{/other_user}","gists_url":"https://api.github.com/users/jeoliva/gists{/gist_id}","starred_url":"https://api.github.com/users/jeoliva/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jeoliva/subscriptions","organizations_url":"https://api.github.com/users/jeoliva/orgs","repos_url":"https://api.github.com/users/jeoliva/repos","events_url":"https://api.github.com/users/jeoliva/events{/privacy}","received_events_url":"https://api.github.com/users/jeoliva/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/jeoliva/ExoPlayer","description":"An extensible media player for Android","fork":true,"url":"https://api.github.com/repos/jeoliva/ExoPlayer","forks_url":"https://api.github.com/repos/jeoliva/ExoPlayer/forks","keys_url":"https://api.github.com/repos/jeoliva/ExoPlayer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/jeoliva/ExoPlayer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/jeoliva/ExoPlayer/teams","hooks_url":"https://api.github.com/repos/jeoliva/ExoPlayer/hooks","issue_events_url":"https://api.github.com/repos/jeoliva/ExoPlayer/issues/events{/number}","events_url":"https://api.github.com/repos/jeoliva/ExoPlayer/events","assignees_url":"https://api.github.com/repos/jeoliva/ExoPlayer/assignees{/user}","branches_url":"https://api.github.com/repos/jeoliva/ExoPlayer/branches{/branch}","tags_url":"https://api.github.com/repos/jeoliva/ExoPlayer/tags","blobs_url":"https://api.github.com/repos/jeoliva/ExoPlayer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/jeoliva/ExoPlayer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/jeoliva/ExoPlayer/git/refs{/sha}","trees_url":"https://api.github.com/repos/jeoliva/ExoPlayer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/jeoliva/ExoPlayer/statuses/{sha}","languages_url":"https://api.github.com/repos/jeoliva/ExoPlayer/languages","stargazers_url":"https://api.github.com/repos/jeoliva/ExoPlayer/stargazers","contributors_url":"https://api.github.com/repos/jeoliva/ExoPlayer/contributors","subscribers_url":"https://api.github.com/repos/jeoliva/ExoPlayer/subscribers","subscription_url":"https://api.github.com/repos/jeoliva/ExoPlayer/subscription","commits_url":"https://api.github.com/repos/jeoliva/ExoPlayer/commits{/sha}","git_commits_url":"https://api.github.com/repos/jeoliva/ExoPlayer/git/commits{/sha}","comments_url":"https://api.github.com/repos/jeoliva/ExoPlayer/comments{/number}","issue_comment_url":"https://api.github.com/repos/jeoliva/ExoPlayer/issues/comments{/number}","contents_url":"https://api.github.com/repos/jeoliva/ExoPlayer/contents/{+path}","compare_url":"https://api.github.com/repos/jeoliva/ExoPlayer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/jeoliva/ExoPlayer/merges","archive_url":"https://api.github.com/repos/jeoliva/ExoPlayer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/jeoliva/ExoPlayer/downloads","issues_url":"https://api.github.com/repos/jeoliva/ExoPlayer/issues{/number}","pulls_url":"https://api.github.com/repos/jeoliva/ExoPlayer/pulls{/number}","milestones_url":"https://api.github.com/repos/jeoliva/ExoPlayer/milestones{/number}","notifications_url":"https://api.github.com/repos/jeoliva/ExoPlayer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/jeoliva/ExoPlayer/labels{/name}","releases_url":"https://api.github.com/repos/jeoliva/ExoPlayer/releases{/id}","created_at":"2015-09-17T20:55:56Z","updated_at":"2015-09-17T20:55:58Z","pushed_at":"2015-10-22T21:50:55Z","git_url":"git://github.com/jeoliva/ExoPlayer.git","ssh_url":"git@github.com:jeoliva/ExoPlayer.git","clone_url":"https://github.com/jeoliva/ExoPlayer.git","svn_url":"https://github.com/jeoliva/ExoPlayer","homepage":"","size":11686,"stargazers_count":0,"watchers_count":0,"language":"Java","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"google:dev","ref":"dev","sha":"0545c58dee9380ead1d483b99e16d8cc041afef5","user":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"repo":{"id":20818126,"name":"ExoPlayer","full_name":"google/ExoPlayer","owner":{"login":"google","id":1342004,"avatar_url":"https://avatars.githubusercontent.com/u/1342004?v=3","gravatar_id":"","url":"https://api.github.com/users/google","html_url":"https://github.com/google","followers_url":"https://api.github.com/users/google/followers","following_url":"https://api.github.com/users/google/following{/other_user}","gists_url":"https://api.github.com/users/google/gists{/gist_id}","starred_url":"https://api.github.com/users/google/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/google/subscriptions","organizations_url":"https://api.github.com/users/google/orgs","repos_url":"https://api.github.com/users/google/repos","events_url":"https://api.github.com/users/google/events{/privacy}","received_events_url":"https://api.github.com/users/google/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/google/ExoPlayer","description":"An extensible media player for Android","fork":false,"url":"https://api.github.com/repos/google/ExoPlayer","forks_url":"https://api.github.com/repos/google/ExoPlayer/forks","keys_url":"https://api.github.com/repos/google/ExoPlayer/keys{/key_id}","collaborators_url":"https://api.github.com/repos/google/ExoPlayer/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/google/ExoPlayer/teams","hooks_url":"https://api.github.com/repos/google/ExoPlayer/hooks","issue_events_url":"https://api.github.com/repos/google/ExoPlayer/issues/events{/number}","events_url":"https://api.github.com/repos/google/ExoPlayer/events","assignees_url":"https://api.github.com/repos/google/ExoPlayer/assignees{/user}","branches_url":"https://api.github.com/repos/google/ExoPlayer/branches{/branch}","tags_url":"https://api.github.com/repos/google/ExoPlayer/tags","blobs_url":"https://api.github.com/repos/google/ExoPlayer/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/google/ExoPlayer/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/google/ExoPlayer/git/refs{/sha}","trees_url":"https://api.github.com/repos/google/ExoPlayer/git/trees{/sha}","statuses_url":"https://api.github.com/repos/google/ExoPlayer/statuses/{sha}","languages_url":"https://api.github.com/repos/google/ExoPlayer/languages","stargazers_url":"https://api.github.com/repos/google/ExoPlayer/stargazers","contributors_url":"https://api.github.com/repos/google/ExoPlayer/contributors","subscribers_url":"https://api.github.com/repos/google/ExoPlayer/subscribers","subscription_url":"https://api.github.com/repos/google/ExoPlayer/subscription","commits_url":"https://api.github.com/repos/google/ExoPlayer/commits{/sha}","git_commits_url":"https://api.github.com/repos/google/ExoPlayer/git/commits{/sha}","comments_url":"https://api.github.com/repos/google/ExoPlayer/comments{/number}","issue_comment_url":"https://api.github.com/repos/google/ExoPlayer/issues/comments{/number}","contents_url":"https://api.github.com/repos/google/ExoPlayer/contents/{+path}","compare_url":"https://api.github.com/repos/google/ExoPlayer/compare/{base}...{head}","merges_url":"https://api.github.com/repos/google/ExoPlayer/merges","archive_url":"https://api.github.com/repos/google/ExoPlayer/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/google/ExoPlayer/downloads","issues_url":"https://api.github.com/repos/google/ExoPlayer/issues{/number}","pulls_url":"https://api.github.com/repos/google/ExoPlayer/pulls{/number}","milestones_url":"https://api.github.com/repos/google/ExoPlayer/milestones{/number}","notifications_url":"https://api.github.com/repos/google/ExoPlayer/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/google/ExoPlayer/labels{/name}","releases_url":"https://api.github.com/repos/google/ExoPlayer/releases{/id}","created_at":"2014-06-13T21:19:18Z","updated_at":"2015-10-26T04:20:33Z","pushed_at":"2015-10-26T10:24:44Z","git_url":"git://github.com/google/ExoPlayer.git","ssh_url":"git@github.com:google/ExoPlayer.git","clone_url":"https://github.com/google/ExoPlayer.git","svn_url":"https://github.com/google/ExoPlayer","homepage":"","size":19784,"stargazers_count":1972,"watchers_count":1972,"language":"Java","has_issues":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":728,"mirror_url":null,"open_issues_count":144,"forks":728,"open_issues":144,"watchers":1972,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/828"},"html":{"href":"https://github.com/google/ExoPlayer/pull/828"},"issue":{"href":"https://api.github.com/repos/google/ExoPlayer/issues/828"},"comments":{"href":"https://api.github.com/repos/google/ExoPlayer/issues/828/comments"},"review_comments":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/828/comments"},"review_comment":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/google/ExoPlayer/pulls/828/commits"},"statuses":{"href":"https://api.github.com/repos/google/ExoPlayer/statuses/3e36f529f8fe8ef4c3550dc00f16684c532d0da0"}}}}